e-paperに天気予報と為替レートを表示させてみるTTGO T5

ESP32

BanggoodからLilyGo TTGO T5 2.13インチのe-paper付きESP32開発ボードがやってきたので、省エネで目に優しいE-paperを使って天気予報と為替レートを表示させてみます。

ESP32なのでコンパクトなのにWifi付きで色々とできるボードです。

TTGO T5

完成の図↓は以下のように2つのE-paperに為替レートと天気予報を表示させた。どちらもESP32ボードを使ってE-paperで表示させています。

Lilygo TTGO T5 2.13インチE-paper レビュー

パッケージ

付属品

パッケージ

バッテリーを接続する電源ケーブルとピンゲッダも付いてます。。

こういうのってスーパーとかで価格表示に使ってますね。ESP32はWifiついてるので価格の変更もサーバーで変更したらすぐに変更可能で便利そう。

ピン配置とポート

ピンは一部E-paperやMicroSDカードで使用しているものの、まだ使えるピンがあるので十分開発できそう。

Wifiアンテナ用の丸いポートもありますが、アンテナは同梱されていない。アンテナなくても特に問題なく普通にWifiにつながりました。

思っていたよりちょい小さい

TTGO T5のE-paperはちょっと画面が小さいのでもう少し大きいE-paperが欲しくなってきた。

ちょっと小さすぎて近くで見ないとよく分からない。4インチほどあったほうがより楽しいのかもしれません。

TTGO T5に住所の天気予報を表示させる

Arduino IDEでも作れますが、遅いし、何回もビルド&アップロードを繰り返すと「rst:0x8 (TG1WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)」のエラーを繰り返しておかしくなるので、エラーになった場合は再起動したりEsptoolでブートローダーを書き込む作業が必要になったりでなにかと面倒。

ブートローダー書き込んでもスクリプトをアップロードできないことも多数ある。

VS CodeのPlatformIO(←インストール・設定などの説明)でやることにしました。

VS Code PlatformIO

ま←あPlatformIOのHomeを表示させてNew Projectでプロジェクトを作成します。

BoardはEspressif ESP32 Dev Moduleにしました。

My_OpenWeather_generic.inoをベースに作成

ここからDownloadを押して、「My_OpenWeather_generic.ino」をダウンロードして利用させていただきます。

Case for TTGO T5 ESP32 epaper by MegaCadler | Download free STL model | Printables.com
A simple housing for LILYGO® TTGO T5 V2.0 WiFi Wireless Module bluetooth ESP32 2.13 e-Paper Display Development Board… |...

と、思いましたが、ボードの種類が違うのか?分かりませんが、こちらのコードをベースに改造して作りました。

Downloadボタンを押して下のほうにあるMy_OpenWeather_generic.inoをダウンロードする。↓

LilyGo-T5-Epaper-Series

以下をZipでダウンロードして解凍後、libフォルダの中身を「C:\Users\ユーザ名\OneDrive\ドキュメント\PlatformIO\Projects\TTGO_T5_Weather\lib」に移動する。

GitHub - Xinyuan-LilyGO/LilyGo-T5-Epaper-Series
Contribute to Xinyuan-LilyGO/LilyGo-T5-Epaper-Series development by creating an account on GitHub.

6個のライブラリを入れました。

OpenWeather

OpenWeather API取得

こちら↓からユーザー登録してMy API KeysからAPIを取得しておきましょう。

https://openweathermap.org/

OpenWeather Librarie

こちらはOpenWeatherで天気情報を取得するのに使います。

GitHub - Bodmer/OpenWeather: Arduino library to fetch weather information from OpenWeather
Arduino library to fetch weather information from OpenWeather - Bodmer/OpenWeather

同様にOpenWeatherもダウンロードして解凍した中身のOpenWeather-mainフォルダを「C:\Users\ユーザ名\OneDrive\ドキュメント\PlatformIO\Projects\TTGO_T5_Weather\lib」に移動する。

Json_decoder

OpenWeatherで取得したJsonの天気情報をデコードするライブラリ。

GitHub - Bodmer/JSON_Decoder: Streaming parser for decoding JSON streams on small devices
Streaming parser for decoding JSON streams on small devices - Bodmer/JSON_Decoder

PlatformIOのLibrariesでJson_decoderと検索すれば出てきますので、Addを押してインストールする。

platformio.ini

platformio.iniは以下のようにしました。

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
upload_port = COM9
monitor_port = COM9
lib_deps = 
	bodmer/JSON_Decoder@^0.0.7

main.cpp

このファイルにスクリプト本体を書きます。Arduino IDEでのinoファイルと同じ。

My_OpenWeather_generic.inoファイルを変更して

WIFI_SSID、WIFI_PASSWORD、api_keyの部分をご自分の環境に置き換えてください。

あと、latitudeとlongitude の部分を天気を表示させたい緯度経度に変更してくださいね。

#define LILYGO_T5_V213

#include <boards.h>
#include <GxEPD.h>
#include <GxDEPG0213BN/GxDEPG0213BN.h>

#include <U8g2_for_Adafruit_GFX.h>
#include <GxIO/GxIO_SPI/GxIO_SPI.h>
#include <GxIO/GxIO.h>

#include <WiFi.h>
#define WIFI_SSID     "Xiaomi_wifi"
#define WIFI_PASSWORD "xxxxxxxxxxx"

#include <JSON_Decoder.h>
#include <Time.h>
#define TIME_OFFSET 9UL * 3600UL

#include <OpenWeather.h>
String api_key = "xxxxxxx"; // Obtain this from your OpenWeather account
String location = "東京";
String latitude =  "35.6895";
String longitude = "139.6917";
String units = "metric";  // or "imperial"
String language = "ja";   // See notes tab
OW_Weather ow; // Weather forecast library instance
// Create Global structures that hold the retrieved weather
OW_current *current = new OW_current;
OW_hourly *hourly = new OW_hourly;
OW_daily  *daily = new OW_daily;
// weather variables
#define SUN  0
#define SUN_CLOUD  1
#define CLOUD 2
#define RAIN 3
#define THUNDER 4
#define MIST 5
#define SNOW 6
int8_t Symbol;
char buf[80];    // for sprintf
//#define ADC_PIN   35
#define VREF      1100

GxIO_Class io(SPI,  EPD_CS, EPD_DC,  EPD_RSET);
GxEPD_Class display(io, EPD_RSET, EPD_BUSY);
U8G2_FOR_ADAFRUIT_GFX u8g2Fonts;

String strTime(time_t unixTime){
  unixTime += TIME_OFFSET;
  return ctime(&unixTime); // c-lib time.h : Convert time_t value to string (https://www.cplusplus.com/reference/ctime/strftime/)
}


/********************************************************************************************************************************
   Draw Weather Symbols
 * ******************************************************************************************************************************/
void drawWeatherSymbol(uint8_t x, uint8_t y, uint8_t symbol)
{
  // fonts used:
  // u8g2_font_open_iconic_embedded_6x_t
  // u8g2_font_open_iconic_weather_6x_t
  // 1x = 8x8 px, 2x = 16x16 px ... 6x = 48x48 px,  8x = 64x64 px
  // encoding values, see: https://github.com/olikraus/u8g2/wiki/fntgrpiconic


  
  switch (symbol)
  {
    case SUN:
      u8g2Fonts.setFont(u8g2_font_open_iconic_weather_2x_t);  // 16x16 px
      u8g2Fonts.drawGlyph(x, y, 69);
      break;
    case SUN_CLOUD:
      u8g2Fonts.setFont(u8g2_font_open_iconic_weather_2x_t);
      u8g2Fonts.drawGlyph(x, y, 65);
      break;
    case CLOUD:
      u8g2Fonts.setFont(u8g2_font_open_iconic_weather_2x_t);
      u8g2Fonts.drawGlyph(x, y, 64);
      break;
    case RAIN:
      u8g2Fonts.setFont(u8g2_font_open_iconic_weather_2x_t);
      u8g2Fonts.drawGlyph(x, y, 67);
      break;
    case THUNDER:
      u8g2Fonts.setFont(u8g2_font_open_iconic_embedded_2x_t);
      u8g2Fonts.drawGlyph(x, y, 67);
      break;
    case MIST:
      u8g2Fonts.setFont(u8g2_font_open_iconic_other_2x_t);
      u8g2Fonts.drawGlyph(x, y, 67);
      break;
    case SNOW:
      u8g2Fonts.setFont(u8g2_font_open_iconic_embedded_2x_t);
      u8g2Fonts.drawGlyph(x, y, 66);
      break;
  }
}

/********************************************************************************************************************************
   Draw Weather Symbols
 * ******************************************************************************************************************************/
int getSymbol(int id) {
  //Maps weather id to symbol
  int symbol = 0;

  if ((id >= 200) && (id <= 232)) {
    symbol = THUNDER;
  }
  else if ((id >= 300) && (id <= 531)) {
    symbol = RAIN;
  }
  else if ((id >= 600) && (id <= 622)) {
    symbol = SNOW;
  }
  else if ((id >= 700) && (id <= 781)) {
    symbol = MIST;
  }
  else if (id == 800) {
    symbol = SUN;
  }
  else if ((id >= 801) && (id <= 804)) {
    symbol = CLOUD;
  }

  return symbol;
}

void drawWeatherSymbolBig(uint8_t x, uint8_t y, uint8_t symbol)
{
  // fonts used:
  // u8g2_font_open_iconic_embedded_6x_t
  // u8g2_font_open_iconic_weather_6x_t
  // 1x = 8x8 px, 2x = 16x16 px ... 6x = 48x48 px,  8x = 64x64 px
  // encoding values, see: https://github.com/olikraus/u8g2/wiki/fntgrpiconic

  switch (symbol)
  {
    case SUN:
      u8g2Fonts.setFont(u8g2_font_open_iconic_weather_6x_t);  // 48x48 px
      u8g2Fonts.drawGlyph(x, y, 69);
      break;
    case SUN_CLOUD:
      u8g2Fonts.setFont(u8g2_font_open_iconic_weather_6x_t);
      u8g2Fonts.drawGlyph(x, y, 65);
      break;
    case CLOUD:
      u8g2Fonts.setFont(u8g2_font_open_iconic_weather_6x_t);
      u8g2Fonts.drawGlyph(x, y, 64);
      break;
    case RAIN:
      u8g2Fonts.setFont(u8g2_font_open_iconic_weather_6x_t);
      u8g2Fonts.drawGlyph(x, y, 67);
      break;
    case THUNDER:
      u8g2Fonts.setFont(u8g2_font_open_iconic_embedded_6x_t);
      u8g2Fonts.drawGlyph(x, y, 67);
      break;
    case MIST:
      u8g2Fonts.setFont(u8g2_font_open_iconic_other_6x_t);
      u8g2Fonts.drawGlyph(x, y, 67);
      break;
    case SNOW:
      u8g2Fonts.setFont(u8g2_font_open_iconic_embedded_6x_t);
      u8g2Fonts.drawGlyph(x, y, 66);
      break;
  }
}
/***************************************************************************************
**                          Send weather info to serial port
***************************************************************************************/
void printCurrentWeather()
{
  // Create the structures that hold the retrieved weather
  //OW_current *current = new OW_current;
  //OW_hourly *hourly = new OW_hourly;
  //OW_daily  *daily = new OW_daily;

  //time_t time;

  Serial.print("\nRequesting weather information from OpenWeather... ");

  ow.getForecast(current, hourly, daily, api_key, latitude, longitude, units, language);

  
    Serial.println("Weather from Open Weather\n");

    // We can use the timezone to set the offset eventually...
    /* シリアルモニタに表示する場合はこの行削除
    //うまく取得できない時はプリントして確認するためにここを解除。最後は「*/」ここを削除
    Serial.println("############### Current weather ###############\n");
    Serial.print("dt (time)        : "); Serial.print(strTime(current->dt));
    Serial.print("sunrise          : "); Serial.print(strTime(current->sunrise));
    Serial.print("sunset           : "); Serial.print(strTime(current->sunset));
    Serial.print("temp             : "); Serial.println(current->temp);
    Serial.print("feels_like       : "); Serial.println(current->feels_like);
    Serial.print("pressure         : "); Serial.println(current->pressure);
    Serial.print("humidity         : "); Serial.println(current->humidity);
    Serial.print("dew_point        : "); Serial.println(current->dew_point);
    Serial.print("uvi              : "); Serial.println(current->uvi);
    Serial.print("clouds           : "); Serial.println(current->clouds);
    Serial.print("visibility       : "); Serial.println(current->visibility);
    Serial.print("wind_speed       : "); Serial.println(current->wind_speed);
    Serial.print("wind_gust        : "); Serial.println(current->wind_gust);
    Serial.print("wind_deg         : "); Serial.println(current->wind_deg);
    Serial.print("rain             : "); Serial.println(current->rain);
    Serial.print("snow             : "); Serial.println(current->snow);
    Serial.println();
    Serial.print("id               : "); Serial.println(current->id);
    Serial.print("main             : "); Serial.println(current->main);
    Serial.print("description      : "); Serial.println(current->description);
    Serial.print("icon             : "); Serial.println(current->icon);

    Serial.println();


      Serial.println("############### Hourly weather  ###############\n");
      for (int i = 0; i < MAX_HOURS; i++)
      {
        Serial.print("Hourly summary  "); if (i < 10) Serial.print(" "); Serial.print(i);
        Serial.println();
        Serial.print("dt (time)        : "); Serial.print(strTime(hourly->dt[i]));
        Serial.print("temp             : "); Serial.println(hourly->temp[i]);
        Serial.print("feels_like       : "); Serial.println(hourly->feels_like[i]);
        Serial.print("pressure         : "); Serial.println(hourly->pressure[i]);
        Serial.print("humidity         : "); Serial.println(hourly->humidity[i]);
        Serial.print("dew_point        : "); Serial.println(hourly->dew_point[i]);
        Serial.print("clouds           : "); Serial.println(hourly->clouds[i]);
        Serial.print("wind_speed       : "); Serial.println(hourly->wind_speed[i]);
        Serial.print("wind_gust        : "); Serial.println(hourly->wind_gust[i]);
        Serial.print("wind_deg         : "); Serial.println(hourly->wind_deg[i]);
        Serial.print("rain             : "); Serial.println(hourly->rain[i]);
        Serial.print("snow             : "); Serial.println(hourly->snow[i]);
        Serial.println();
        Serial.print("id               : "); Serial.println(hourly->id[i]);
        Serial.print("main             : "); Serial.println(hourly->main[i]);
        Serial.print("description      : "); Serial.println(hourly->description[i]);
        Serial.print("icon             : "); Serial.println(hourly->icon[i]);

        Serial.println();
      }


    Serial.println("###############  Daily weather  ###############\n");
    for (int i = 0; i < MAX_DAYS; i++)
    {
      Serial.print("Daily summary   "); if (i < 10) Serial.print(" "); Serial.print(i);
      Serial.println();
      Serial.print("dt (time)        : "); Serial.print(strTime(daily->dt[i]));
      Serial.print("sunrise          : "); Serial.print(strTime(daily->sunrise[i]));
      Serial.print("sunset           : "); Serial.print(strTime(daily->sunset[i]));

      Serial.print("temp.morn        : "); Serial.println(daily->temp_morn[i]);
      Serial.print("temp.day         : "); Serial.println(daily->temp_day[i]);
      Serial.print("temp.eve         : "); Serial.println(daily->temp_eve[i]);
      Serial.print("temp.night       : "); Serial.println(daily->temp_night[i]);
      Serial.print("temp.min         : "); Serial.println(daily->temp_min[i]);
      Serial.print("temp.max         : "); Serial.println(daily->temp_max[i]);

      Serial.print("feels_like.morn  : "); Serial.println(daily->feels_like_morn[i]);
      Serial.print("feels_like.day   : "); Serial.println(daily->feels_like_day[i]);
      Serial.print("feels_like.eve   : "); Serial.println(daily->feels_like_eve[i]);
      Serial.print("feels_like.night : "); Serial.println(daily->feels_like_night[i]);

      Serial.print("pressure         : "); Serial.println(daily->pressure[i]);
      Serial.print("humidity         : "); Serial.println(daily->humidity[i]);
      Serial.print("dew_point        : "); Serial.println(daily->dew_point[i]);
      Serial.print("uvi              : "); Serial.println(daily->uvi[i]);
      Serial.print("clouds           : "); Serial.println(daily->clouds[i]);
      Serial.print("visibility       : "); Serial.println(daily->visibility[i]);
      Serial.print("wind_speed       : "); Serial.println(daily->wind_speed[i]);
      Serial.print("wind_gust        : "); Serial.println(daily->wind_gust[i]);
      Serial.print("wind_deg         : "); Serial.println(daily->wind_deg[i]);
      Serial.print("rain             : "); Serial.println(daily->rain[i]);
      Serial.print("snow             : "); Serial.println(daily->snow[i]);
      Serial.println();
      Serial.print("id               : "); Serial.println(daily->id[i]);
      Serial.print("main             : "); Serial.println(daily->main[i]);
      Serial.print("description      : "); Serial.println(daily->description[i]);
      Serial.print("icon             : "); Serial.println(daily->icon[i]);

      Serial.println();

    }
    */

}

void setup(void){
    Serial.begin(115200);
    Serial.println();
    Serial.println("setup");

    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    Serial.print("WiFi connecting");
    while (WiFi.status() != WL_CONNECTED) {
      Serial.print(".");
      delay(500);
    }
    Serial.println(" connected:");
    Serial.println(WiFi.localIP());


    SPI.begin(EPD_SCLK, EPD_MISO, EPD_MOSI);
    display.init(); // enable diagnostic output on Serial
    display.setRotation(3); // 0--> No rotation ,  1--> rotate 90 deg
    u8g2Fonts.begin(display);

    //  Get and Display Weather to Screen
    printCurrentWeather();  // get weather data
}

void loop(){
    u8g2Fonts.setFontMode(1);                           // use u8g2 transparent mode (this is default)
    u8g2Fonts.setFontDirection(0);                      // left to right (this is default)
    u8g2Fonts.setForegroundColor(GxEPD_BLACK);          // apply Adafruit GFX color
    u8g2Fonts.setBackgroundColor(GxEPD_WHITE);          // apply Adafruit GFX color

    //u8g2Fonts.setFont(u8g2_font_b16_t_japanese2);            // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall

    uint16_t x = display.width() / 2 - 60 ;
    uint16_t y = display.height() / 2;

    display.fillScreen(GxEPD_WHITE);

    //********************************************************
    // Draw Forecast
    //********************************************************

    // Draw Forecast DOW
    //u8g2Fonts.setFont( u8g2_font_cupcakemetoyourleader_tr  );
    u8g2Fonts.setFont( u8g2_font_helvB12_tf  );
    uint16_t y_top = 15;

    
    // issue:  if these for loops are combined, the days info only shows the first day...
    for (int i = 0; i <= (MAX_DAYS - 1); i++) {
      u8g2Fonts.setCursor(10 + i * 51, y_top); //set to row 10 for DOW
      u8g2Fonts.print(strTime(daily->dt[i]).substring(0, 3));
    }

    // Draw Forecast Glyphs
    // font is defined in drawWeatherSymbol()
    uint16_t y_symb = y_top + 20;
    for (int i = 0; i <= (MAX_DAYS - 1); i++) {
      drawWeatherSymbol(19 + i * 50, y_symb, getSymbol(daily->id[i])); //drawGlyph(x,y, Glyph number), x, y is the lower left corner of the glyph.
    }

    // Draw Forecast Hi / Lo temps
    u8g2Fonts.setFont( u8g2_font_helvR08_tf  );
    uint16_t y_hilo = y_symb + 12;
    for (int i = 0; i <= (MAX_DAYS - 1); i++) {
      String highTemp = String(daily->temp_max[i], 0);
      String lowTemp = String(daily->temp_min[i], 0);
      u8g2Fonts.setCursor(15 + i * 50, y_hilo); //set to row 80 for Max / Min temp
      u8g2Fonts.print(highTemp + " " + lowTemp);
    }

    //********************************************************
    // Draw Current Weather
    //********************************************************

    // Draw Current Weather Glyph
    uint16_t y_glyph = y_hilo + 58;
    drawWeatherSymbolBig(15, y_glyph, getSymbol(current->id));

    // Draw Current Condition
    u8g2Fonts.setFont( u8g2_font_unifont_t_japanese3  );
    uint16_t y_main = y_hilo + 25;
    u8g2Fonts.setCursor(70, y_main);
    //u8g2Fonts.print(current->main);
    u8g2Fonts.print(current->description);

    // Draw Current Temp
    uint16_t y_temp = y_main + 16;
    u8g2Fonts.setCursor(70, y_temp);
    if (units == "imperial") {
      u8g2Fonts.print(String(current->temp, 1) + "°F");   //Alt + 0176 for degree symbol
    } else
    {
      u8g2Fonts.print(String(current->temp, 1) + "°C");
    }

    // Draw Current Humidity
    uint16_t y_humid = y_temp + 15;
    u8g2Fonts.setCursor(70, y_humid);
    //u8g2Fonts.print(String(current->humidity) + " %");
    u8g2Fonts.printf("%02d%%", current->humidity);  //printf works too

    // Draw Location w: 120 - 255 / h: 73 - 110

    //u8g2Fonts.setFont( u8g2_font_cupcakemetoyourleader_tr  );
    //u8g2Fonts.setFont( u8g2_font_unifont_t_japanese1  );
    uint16_t y_loc = y_hilo + 20;
    //u8g2Fonts.setCursor(140, y_loc);
    //u8g2Fonts.print(location);

    // Draw Feels Like Temp
    u8g2Fonts.setFont( u8g2_font_b12_t_japanese3  );
    uint16_t y_feel = y_loc - 7;
    u8g2Fonts.setCursor(140, y_feel);
    u8g2Fonts.print("体感: "  + String(current->feels_like, 1) + "°C");
    // Draw Dew Point Temp
    uint16_t y_dew = y_feel + 15;
    u8g2Fonts.setCursor(140, y_dew);
    u8g2Fonts.print("露点: "  + String(current->dew_point, 1) + "°C");
    // Draw Wind Speed
    uint16_t y_wind = y_dew + 15;
    u8g2Fonts.setCursor(140, y_wind);
    u8g2Fonts.print("風 : "  + String(current->wind_speed, 1) + "km/h");
    // UVI
    uint16_t y_uvi = y_wind + 15;
    u8g2Fonts.setCursor(140, y_uvi);
    //u8g2Fonts.print("紫外線:"  + String(current->uvi, 1) + "mW/cm2");
    u8g2Fonts.print("紫外線:"  + String(current->uvi, 1));

    // Draw last update time at bottom of screen
    u8g2Fonts.setCursor(5, 120);

    //strftime(sunrise_time, sizeof(sunrise_time), "%I:%M %p", localtime((const time_t*)&current->sunrise));
    u8g2Fonts.setFont( u8g2_font_b12_t_japanese3  );
    u8g2Fonts.print("出:");
    u8g2Fonts.setFont( u8g2_font_finderskeepers_tr  );
    //日の出
    time_t sunrise = current->sunrise;
    sunrise += TIME_OFFSET;
    struct tm * nowsunrise;
    nowsunrise = localtime(&sunrise);
    int hour_rise = nowsunrise->tm_hour;
    int minute_rise = nowsunrise->tm_min;
    char buf[6];
    sprintf(buf, "%02d:%02d", hour_rise, minute_rise); 
    u8g2Fonts.print(buf);

    u8g2Fonts.setFont( u8g2_font_b12_t_japanese3  );
    u8g2Fonts.print(" 入:");
    u8g2Fonts.setFont( u8g2_font_finderskeepers_tr  );
    //日の入
    time_t sunset = current->sunset;
    sunset += TIME_OFFSET;
    struct tm * nowsunset;
    nowsunset = localtime(&sunset);
    int hour_set = nowsunset->tm_hour;
    int minute_set = nowsunset->tm_min;
    char busset[6];
    sprintf(busset, "%02d:%02d", hour_set, minute_set); 
    u8g2Fonts.print(busset);
    //取得時間
    u8g2Fonts.setFont( u8g2_font_finderskeepers_tr  );
    u8g2Fonts.print("   " + strTime(current->dt));

    display.update();
    delay(3600000UL);
}

更新は適当に・・・delay(3600000UL);などとして、1時間?くらいおきにOpenWeatherに取得しに行きます。

データをシリアルモニタに表示させる

シリアルモニタを表示させる(上記スクリプトの/* シリアルモニタに表示する場合はこの行削除~*/を取り除く)ようにしたら以下のようなデータを表示されます。

当日の天気予報と6日分の天気予報の詳細が表示されます。

setup
WiFi connecting.. connected:
192.168.31.209

Requesting weather information from OpenWeather...

The connection to server is secure (https). Certificate not checked.

Sending GET request to api.openweathermap.org port 443
Header end found

Parsing JSON

Done in 5926 ms

Weather from Open Weather

############### Current weather ###############

dt (time)        : Thu Mar  9 17:05:50 2023
sunrise          : Thu Mar  9 06:37:31 2023
sunset           : Thu Mar  9 18:20:12 2023
temp             : 17.80
feels_like       : 16.99
pressure         : 1008.00
humidity         : 52
dew_point        : 7.82
uvi              : 0.27
clouds           : 75
visibility       : 10000
wind_speed       : 7.72
wind_gust        : 0.00
wind_deg         : 150
rain             : 0.00
snow             : 0.00

id               : 803
main             : Clouds
description      : 曇りがち
icon             : 04d

############### Hourly weather  ###############

Hourly summary   0
dt (time)        : Thu Mar  9 17:00:00 2023
temp             : 17.80
feels_like       : 16.99
pressure         : 1008.00
humidity         : 52
dew_point        : 7.82
clouds           : 75
wind_speed       : 4.07
wind_gust        : 8.44
wind_deg         : 181
rain             : 0.00
snow             : 0.00

id               : 803
main             : Clouds
description      : 曇りがち
icon             : 04d

Hourly summary   1
dt (time)        : Thu Mar  9 18:00:00 2023
temp             : 17.29
feels_like       : 16.59
pressure         : 1010.00
humidity         : 58
dew_point        : 8.96
clouds           : 80
wind_speed       : 4.10
wind_gust        : 9.14
wind_deg         : 187
rain             : 0.00
snow             : 0.00

id               : 803
main             : Clouds
description      : 曇りがち
icon             : 04d

Hourly summary   2
dt (time)        : Thu Mar  9 19:00:00 2023
temp             : 16.49
feels_like       : 15.87
pressure         : 1012.00
humidity         : 64
dew_point        : 9.67
clouds           : 85
wind_speed       : 3.81
wind_gust        : 9.21
wind_deg         : 187
rain             : 0.00
snow             : 0.00

id               : 804
main             : Clouds
description      : 厚い雲
icon             : 04n

Hourly summary   3
dt (time)        : Thu Mar  9 20:00:00 2023
temp             : 15.20
feels_like       : 14.71
pressure         : 1015.00
humidity         : 74
dew_point        : 10.60
clouds           : 88
wind_speed       : 3.04
wind_gust        : 8.04
wind_deg         : 187
rain             : 0.00
snow             : 0.00

id               : 500
main             : Rain
description      : 小雨
icon             : 10n

Hourly summary   4
dt (time)        : Thu Mar  9 21:00:00 2023
temp             : 14.11
feels_like       : 13.74
pressure         : 1017.00
humidity         : 83
dew_point        : 11.27
clouds           : 82
wind_speed       : 2.89
wind_gust        : 7.24
wind_deg         : 188
rain             : 0.00
snow             : 0.00

id               : 803
main             : Clouds
description      : 曇りがち
icon             : 04n

Hourly summary   5
dt (time)        : Thu Mar  9 22:00:00 2023
temp             : 12.74
feels_like       : 12.47
pressure         : 1020.00
humidity         : 92
dew_point        : 10.94
clouds           : 20
wind_speed       : 2.33
wind_gust        : 4.65
wind_deg         : 195
rain             : 0.00
snow             : 0.00

id               : 801
main             : Clouds
description      : 薄い雲
icon             : 02n

###############  Daily weather  ###############

Daily summary    0
dt (time)        : Thu Mar  9 12:00:00 2023
sunrise          : Thu Mar  9 06:37:31 2023
sunset           : Thu Mar  9 18:20:12 2023
temp.morn        : 10.26
temp.day         : 16.87
temp.eve         : 17.29
temp.night       : 12.33
temp.min         : 10.26
temp.max         : 17.80
feels_like.morn  : 9.53
feels_like.day   : 16.36
feels_like.eve   : 16.59
feels_like.night : 12.07
pressure         : 1018.00
humidity         : 67
dew_point        : 10.18
uvi              : 0.00
clouds           : 100
visibility       : 0
wind_speed       : 4.10
wind_gust        : 9.21
wind_deg         : 187
rain             : 0.14
snow             : 0.00

id               : 500
main             : Rain
description      : 小雨
icon             : 10d

Daily summary    1
dt (time)        : Fri Mar 10 12:00:00 2023
sunrise          : Fri Mar 10 06:36:13 2023
sunset           : Fri Mar 10 18:20:59 2023
temp.morn        : 11.04
temp.day         : 19.78
temp.eve         : 16.86
temp.night       : 13.58
temp.min         : 11.04
temp.max         : 20.70
feels_like.morn  : 10.73
feels_like.day   : 19.15
feels_like.eve   : 16.30
feels_like.night : 13.11
pressure         : 1021.00
humidity         : 51
dew_point        : 8.85
uvi              : 0.00
clouds           : 4
visibility       : 0
wind_speed       : 2.33
wind_gust        : 4.59
wind_deg         : 184
rain             : 0.00
snow             : 0.00

id               : 800
main             : Clear
description      : 晴天
icon             : 01d

Daily summary    2
dt (time)        : Sat Mar 11 12:00:00 2023
sunrise          : Sat Mar 11 06:34:54 2023
sunset           : Sat Mar 11 18:21:45 2023
temp.morn        : 11.66
temp.day         : 21.02
temp.eve         : 16.94
temp.night       : 14.36
temp.min         : 11.66
temp.max         : 21.51
feels_like.morn  : 11.02
feels_like.day   : 20.33
feels_like.eve   : 16.36
feels_like.night : 13.78
pressure         : 1024.00
humidity         : 44
dew_point        : 7.72
uvi              : 0.00
clouds           : 31
visibility       : 0
wind_speed       : 2.26
wind_gust        : 2.58
wind_deg         : 303
rain             : 0.00
snow             : 0.00

id               : 802
main             : Clouds
description      : 雲
icon             : 03d

Daily summary    3
dt (time)        : Sun Mar 12 12:00:00 2023
sunrise          : Sun Mar 12 06:33:35 2023
sunset           : Sun Mar 12 18:22:32 2023
temp.morn        : 12.56
temp.day         : 19.13
temp.eve         : 13.34
temp.night       : 10.24
temp.min         : 10.24
temp.max         : 19.13
feels_like.morn  : 12.25
feels_like.day   : 18.61
feels_like.eve   : 13.11
feels_like.night : 9.30
pressure         : 1018.00
humidity         : 58
dew_point        : 10.23
uvi              : 0.00
clouds           : 100
visibility       : 0
wind_speed       : 6.55
wind_gust        : 11.36
wind_deg         : 190
rain             : 10.00
snow             : 0.00

id               : 501
main             : Rain
description      : 適度な雨
icon             : 10d

Daily summary    4
dt (time)        : Mon Mar 13 12:00:00 2023
sunrise          : Mon Mar 13 06:32:16 2023
sunset           : Mon Mar 13 18:23:18 2023
temp.morn        : 4.32
temp.day         : 7.81
temp.eve         : 7.11
temp.night       : 5.99
temp.min         : 4.32
temp.max         : 8.15
feels_like.morn  : -0.08
feels_like.day   : 4.17
feels_like.eve   : 4.40
feels_like.night : 4.22
pressure         : 1021.00
humidity         : 42
dew_point        : -4.69
uvi              : 0.00
clouds           : 79
visibility       : 0
wind_speed       : 7.50
wind_gust        : 13.33
wind_deg         : 286
rain             : 0.14
snow             : 0.00

id               : 500
main             : Rain
description      : 小雨
icon             : 10d

E-paperにうまく表示できなかったらOpenWeatherからちゃんとデータを取得できているか確認のためにシリアルモニタに表示させてくださいね。

天気予報 E-paperで表示 完成

できるだけ日本語表示にしてみました。

「出」「入」は日の出・日の入りです。紫外線は?単位がわからなかった(mW/cm2かな?)ので書かなかった。

画面がちょっと小さいので不満は残りますが、一応完成。

おまけ 為替レートをe-paperで表示させる

ついでにe-paperでもう一つ使って為替レートを表示させてみます。

Waveshare 2.9 Inch E-ink Screen DisplayとESP32

Banggoodで売ってる2.9インチE-inkディスプレイ、これを使います。

こちらもESP32を接続して使います。

配線

ESP32とe-paperを以下のように接続しました。TTGO T5のように一体型じゃないので配線するのもちょっと面倒。

ESP32 GPIO2.9inch e-Paper Module
3.3V3.3V(赤)
GNDGND(黒)
MOSI(GPIO10/19)DIN(青)
SCLK(GPIO11/23)CLK(黃)
CE0(GPIO8/24)CS(橙)
(GPIO25/22)DC(緑)
(GPIO17/11)RST(白)
(GPIO24/18)BUSY(紫)

platformio.ini

ライブラリは4つ必要。

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
upload_port = COM7
lib_deps = 
	bblanchon/ArduinoJson@^6.20.0
	adafruit/Adafruit GFX Library@^1.11.5
	adafruit/Adafruit SSD1306@^2.5.7
	zinggjm/GxEPD@^3.1.3

platformio.iniに上記をコピペすればライブラリ(lib_depsの部分)が勝手にインストールされると思います。(COMポートはご自分の環境に合わせてくださいね。)

main.cpp

2.9インチのE-paperでは以下のようにして出力しました。

為替レートはたくさんありますので、自分は持ってる通貨の為替を表示させるようにしてます。

あと、一定時間ごとにデータを更新しますが、ディープスリープなどを使わずに単純にdelay(7200000);としてループさせてるだけなのでお好みで変更してみてくださいね。

こちらはWifiのSSIDとパスワードのみ変更すればいけると思います。

#include "esp_system.h"
#include <ArduinoJson.h>
#include <Wire.h>
#include <HTTPClient.h>

//Wi-Fi情報
#include <WiFi.h>
#define WIFI_SSID "Xiaomi_wifi"
#define WIFI_PASSWORD "xxxxxxxxx"
WiFiServer server(80);
#include <Arduino.h>
//時計
struct tm timeInfo;//時刻を格納するオブジェクト
char s[20];//文字格納用

//e-paper
#include <GxEPD.h>
#include <GxGDEH029A1/GxGDEH029A1.h>      // 2.9" b/w
#include <GxIO/GxIO_SPI/GxIO_SPI.h>
#include <GxIO/GxIO.h>
#include <Fonts/FreeSansBold18pt7b.h>
#include <Fonts/FreeMonoOblique9pt7b.h>
#include <Fonts/FreeMonoOblique24pt7b.h>
#include <Fonts/FreeSans9pt7b.h>
#include GxEPD_BitmapExamples
GxIO_Class io(SPI, /*CS=5*/ SS, /*DC=*/ 17, /*RST=*/ 16); // arbitrary selection of 17, 16
GxEPD_Class display(io, /*RST=*/ 16, /*BUSY=*/ 4); // arbitrary selection of (16), 4

void setup(void) {
  Serial.begin(115200);
  //e-paper display
  display.init(115200); // enable diagnostic output on Serial
  display.setRotation(1);//1,90度回転~3,270度

  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("WiFi connecting");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println(" connected:");
  server.begin();
  Serial.println(WiFi.localIP());

  //TIME NTPの設定
  configTime(9 * 3600L, 0, "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp");
}

void showFontCallback() {
  //時計
  getLocalTime(&timeInfo);//tmオブジェクトのtimeInfoに現在時刻
  sprintf(s, " %04d/%02d/%02d %02d:%02d:%02d",
          timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday,
          timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec);//人間が読める形式に変換
  Serial.println(s);//時間をシリアルモニタへ出力

  //為替レート取ってくる JSON リアルタイムではないページ(個人?)
  String url = "http://api.aoikujira.com/kawase/json/jp";
  Serial.print("Requesting URL ");
  Serial.println(url);

  HTTPClient httpClient;
  httpClient.begin(url);
  int httpCode = httpClient.GET();
  String httpResponse = httpClient.getString();


  //jsonに変換するためにメモリを確保 2048だとUSDまでしか取得できなかった
  DynamicJsonDocument doc(4000);
  //httpResponseをjson objectに変換する
  deserializeJson(doc, httpResponse);

  JsonObject obj = doc.as<JsonObject>();

  float usd = obj[String("USD")];
  float krw = obj[String("KRW")];      
  float cny = obj[String("CNY")];
  float eur = obj[String("EUR")];

  //日本円に換算したときの価格にする
  usd = 1 / usd;
  krw = 1 / krw;
  cny = 1 / cny;
  eur = 1 / eur;
  //Serial.println(PAB);
  Serial.println(usd,3);
  Serial.println(cny,3);
  Serial.println(krw,2);
  Serial.println(eur,4);

  //e-paper表示
  const GFXfont* f = &FreeSansBold18pt7b;
  display.fillScreen(GxEPD_WHITE);
  display.setTextColor(GxEPD_BLACK);
  display.setFont(f);
  display.setCursor(0, 0);
  display.println();

  display.setFont(&FreeMonoOblique9pt7b);
  display.print("$");
  display.setFont(f);
  display.print(usd,3);
  display.setFont(&FreeMonoOblique9pt7b);
  display.print(" EUR");
  display.setFont(f);
  display.println(eur);
  display.setFont(&FreeMonoOblique9pt7b);
  display.print("CN");
  display.setFont(f);
  display.print(cny,3);
  display.setFont(&FreeMonoOblique9pt7b);
  display.print("  W ");
  display.setFont(f);
  display.println(krw,4);
  display.setFont(&FreeSans9pt7b);
  display.println(s);
  //ライン
  display.fillRect(0, 50, 300, 2, GxEPD_BLACK);
  display.fillRect(0, 100, 300, 3, GxEPD_BLACK);

  httpClient.end();
}

void loop() {
  delay(7200000);//2時間
  //時刻表示
  display.drawPaged(showFontCallback);
  exit(0);
}

為替レート E-paperで表示 完成

こんな感じに↓表示させてみました。しかしドル相場は変動して上昇してますが、韓国ウォンはあまり変わらないのが辛いところ。もう少しウォン高になってほしい。

いつも通りにテレワークの壁に磁石でくっつけてます。

E-paperは磁力が弱点なので、3Dプリンターで磁石から2cmほど離して設置してます。2cm離れていれば強力磁石でも磁力の影響を受けないようですね。

表示が小さいのでもう少し大きいE-paper欲しくなってきました。

ESP32

コメント

  1. masa より:

    Lilygo TTGO T5 2.13インチE-paper をwindows11にusbで接続しましたが、e-paperを認識しません。そのためプログラムの書き込みができません。
    何かドライバー必要でしたでしょうか?
    お教えいただけますと幸いです。
    う今困っています。