ESP32によるWebApiを使った天候表示

 arduinoでWebApiを使ったデータ取得をやってみたくて、ネットを探し見つかったものを順番に試してみたが、サイトが変わっていたりしてなかなか上手く動かない。
 結局行きついたのはESP32によるOpenWeatherMapのWebApiを使ったものであった。
 だだ、これもねっとにあったのはArduiniJsonのバージョンが5のもので使っているVer6ではエラーとなってしまったので、別のサイトにあったVer6で書かれたスケッチを参考に修正する必要があった。

 いずれにせよ一つ動けば他のサイトのものについても動かせそうな気がする。

   このページの作成に当たってはESP32でお天気ステーションを作ろうを参考にさせていただいた。

〇配線
 天気の表示用にSSD1306を接続した簡単なものである。
esp32-ssd1306
 sda,sclラインにプルアップ抵抗を挿入しているケースもあるようであるが、ここでは直結で動作した。

〇スケッチ
 参考にさせて頂いたページはArduinoJson.hのバージョンが5なのかコマンドがエラーとなってコンパイルできない。
 そこで別のページからVer6用に書かれたスケッチを拝借してきて書き換えてみた。バッファサイズを相当大きくしないといけないなどの問題はあったが、試行錯誤で徐々に大きくしていきエラーの出ないところで止めた。

 これで何とか動いている。

 これが動いたので、ESP8266への移植とか別のWebApiを使ったスケッチにも挑戦してみたい。

 
#include "SSD1306Wire.h"
#include "OLEDDisplayUi.h"
#include 
#include 
#include 
#include 

Ticker tickerWeatherUpdate;

SSD1306Wire  display(0x3c, 21, 22);
OLEDDisplayUi ui( &display );


const char* ssid = "XXXXXXXXXXXXXXXXX";
const char* password =  "************";

//NTP関連
struct tm timeInfo;
char date[20],hour_minute_sec[20];
int now_hour;

//天気関連
String now_weather,now_temp,tom_weather,tom_temp;
const String endpoint = "http://api.openweathermap.org/data/2.5/weather?q=takamatsu,jp&APPID=";
const String endpoint_forecast = "http://api.openweathermap.org/data/2.5/forecast?q=takamatsu,jp&APPID=";
const String key = "********************************";

//12時間後の天気を取得する関数
void get_tomorrow_weather(){
  HTTPClient http;
 
    http.begin(endpoint_forecast + key); //URLを指定
    int httpCode = http.GET();  //GETリクエストを送信
 
    if (httpCode > 0) { //返答がある場合
 
        String payload = http.getString();  //返答(JSON形式)を取得

        //jsonオブジェクトの作成
/*        DynamicJsonBuffer jsonBuffer;
        String json = payload;
        JsonObject& weatherdata = jsonBuffer.parseObject(json);

        //パースが成功したかどうかを確認
        if(!weatherdata.success()){
          Serial.println("parseObject() failed");
        }
*/        
 String json = payload;
// Serial.println(json);
 const size_t capacity = JSON_OBJECT_SIZE(13) + 20000;
  DynamicJsonDocument doc(capacity);

  // Parse JSON object
  DeserializationError error = deserializeJson(doc, json);
  if (error) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.c_str());
    return;
  }
        //抜き出すデータの番号
        int data_number = now_hour/3 + 4;
        //12時間後のデータを抜き出し
        const char* weather = doc["list"][data_number]["weather"][0]["main"].as();
        const double temp = doc["list"][data_number]["main"]["temp"].as();
        //表示用変数に各要素をセット
        tom_weather = weather;
        tom_temp = String(temp-273.15);
      }
 
    else {
      Serial.println("Error on HTTP request");
    }
 
    http.end();
}

//今日の天気を取得する関数
void get_today_weather(){
  HTTPClient http;
 
    http.begin(endpoint + key); //URLを指定
    int httpCode = http.GET();  //GETリクエストを送信
 
    if (httpCode > 0) { //返答がある場合
 
        String payload = http.getString();  //返答(JSON形式)を取得
        Serial.println(httpCode);
        Serial.println(payload);

        //jsonオブジェクトの作成
    /*    DynamicJsonBuffer jsonBuffer;
        String json = payload;
        JsonObject& weatherdata = jsonBuffer.parseObject(json);

        //パースが成功したかどうかを確認
        if(!weatherdata.success()){
          Serial.println("parseObject() failed");
        }
*/
 String json = payload;
 const size_t capacity = JSON_OBJECT_SIZE(13) + 1000;
  DynamicJsonDocument doc(capacity);
// Serial.println(json);
  // Parse JSON object
  DeserializationError error = deserializeJson(doc, json);
  if (error) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.c_str());
    return;
  }

        //各データを抜き出し
        const char* weather = doc["weather"][0]["main"].as();
        const double temp = doc["main"]["temp"].as();

        //表示用変数に各要素をセット
        now_weather = weather;
        now_temp = String(temp-273.15);
      }
 
    else {
      Serial.println("Error on HTTP request");
    }
 
    http.end(); //Free the resources
}

//ticker用の天気データまとめ関数
void get_weather_data(){
  get_today_weather();
  get_tomorrow_weather();
}

void get_time(){
  getLocalTime(&timeInfo);//tmオブジェクトのtimeInfoに現在時刻を入れ込む
  sprintf(date, " %04d/%02d/%02d ",timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday);//日付に変換
  sprintf(hour_minute_sec, "%02d:%02d:%02d",timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec);//時間に変換
  now_hour = timeInfo.tm_hour;
}

void ClockOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
  display->setTextAlignment(TEXT_ALIGN_LEFT);
  display->setFont(ArialMT_Plain_10);
  display->drawString(25, 0, String(date) + String(hour_minute_sec));
}

void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  //今日の天気を表示
  display->setTextAlignment(TEXT_ALIGN_LEFT);
  display->setFont(ArialMT_Plain_16);
  display->drawString(10 + x,10 + y,"weather: " + String(now_weather));
  display->setFont(ArialMT_Plain_24);
  display->setTextAlignment(TEXT_ALIGN_CENTER);
  display->drawString(64 + x,25 + y,String(now_temp) + "°C");
}

void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  //明日の天気を表示
  display->setTextAlignment(TEXT_ALIGN_LEFT);
  display->setFont(ArialMT_Plain_10);
  display->drawString(0 + x,10 + y,"12 hours after");
  display->setFont(ArialMT_Plain_16);
  display->drawString(0+x,22+y,String(tom_weather));
  display->setFont(ArialMT_Plain_24);
  display->drawString(50+x,22+y,String(tom_temp) + "°C");
}

//SSD1306関連
FrameCallback frames[] = { drawFrame1, drawFrame2};
int frameCount = 2;
OverlayCallback overlays[] = { ClockOverlay };
int overlaysCount = 1;

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

  //Wi-Fi関連
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }
  Serial.println("Connected to the WiFi network");
  
  configTime(9 * 3600L, 0, "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp");//NTPの設定

  //Tickerの設定
  tickerWeatherUpdate.attach(60, get_weather_data);
  get_weather_data();
  
  //ssd1306のui設定
  ui.setTargetFPS(30);

  // You can change this to
  // TOP, LEFT, BOTTOM, RIGHT
  ui.setIndicatorPosition(BOTTOM);

  // Defines where the first frame is located in the bar.
  ui.setIndicatorDirection(LEFT_RIGHT);

  // You can change the transition that is used
  // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN
  ui.setFrameAnimation(SLIDE_LEFT);

  // Add frames
  ui.setFrames(frames, frameCount);

  // Add overlays
  ui.setOverlays(overlays, overlaysCount);

  // Initialising the UI will init the display too.
  ui.init();

}


void loop() {
  int remainingTimeBudget = ui.update();

  if (remainingTimeBudget > 0) {
    delay(remainingTimeBudget);
    get_time();
  }
}

動作状況は写真のとおりである。
全景  表示部分