気象庁天気予報JSONを取得して天気予報を表示させてみた

気象庁ホームページからJSON取得してESP32で天気予報表示させる ESP32 ( ESP-WROOM-32 )

過去のM5Stack版Yahoo! ニュース電光掲示板コードを天気予報を気象庁に移行

以前、以下の記事で M5StackにYahoo! Japan RSS を利用した天気予報表示と、ニュース電光掲示板を紹介しました。

M5Stack と ESP32 のボタンで記事を選択できる Yahoo News 電光掲示板 天気予報 Watch

M5Stack Yahooニュース・天気予報・時計に、電波時計 JJY 発信モジュールを追加して、マルチタスクで動かしてみた

先に述べたように、2022/03/31でYahoo RSS 天気予報配信が終了してしまったので、気象庁のホームページから天気予報JSONデータを取得できるように自作ライブラリを更新しました。
なかなか忙しくて時間が無かったんですが、かなりがんばりましたよ。

自作ライブラリを beta ver 1.0.83 に更新する

私の素人自作ライブラリを beta ver 1.0.83 に更新してください。
(※1.0.81~82では雪の時に正しいアイコンが表示されなかったので修正しました。2024/02/05)
GitHubの以下のリンクにあります。

https://github.com/mgo-tec/ESP32_mgo_tec

ZIPファイルをダウンロードし、Arduino IDEにインストールする方法は以下を参照してください。
古いライブラリは必ず削除してからインストールしてください。

GitHubにある ZIP形式ライブラリ のインストール方法 ( Arduino IDE )

Arduino core for the ESP32 のバージョンに注意。(ver 2.0.2は動作不良)

Arduino core for the ESP32 のバージョン 2.0.2 ではSDカードの読み取り不良で動きませんでしたので注意してください。
ver 2.0.1 または ver 2.0.3-RC1 または 2.0.11 で動作確認しました。

サンプルスケッチ(サンプルプログラム)

では、サンプルスケッチです。

WiFiルータのSSIDやパスワードは書き換えてください。
ただ、ここに入力してコンパイルしたパスワードは、ESP32が第三者に渡ると、専用ツールで簡単に読み取られてしまうので注意してください。

また、office_code_strarea_code_str は、前節を参照して、自分の地域のコードに書き換えてください。

yahoo_root_ca はYahoo! Japan RSS のルートCA証明書公開鍵です。
jp_weather_root_ca は気象庁ホームページのルートCA証明書公開鍵です。
ルートCA証明書の公開鍵の取得方法は以下の記事を参照してください。

Arduino – ESP32 WiFiClientSecure ライブラリで、安定して https ( SSL )記事をGETする方法

M5Stack Yahoo電光掲示板の修正プログラム

まずは、こちらの記事
M5Stack と ESP32 のボタンで記事を選択できる Yahoo News 電光掲示板 天気予報 Watch
の修正版サンプルスケッチ

// mgo_tecライブラリは beta ver 1.0.83 を使って下さい。
// ※Arduino core ESP32 は ver 2.0.2は動作不良起こすので使わないこと
#define MGO_TEC_BV1_M5STACK_SD_SKETCH
#include <mgo_tec_bv1_m5stack_sd_yahoo.h>
 
static const char* ssid = "xxxxxxxxx"; //ご自分のルーターのSSIDに書き換えてください
static const char* password = "xxxxxxxxx"; //ご自分のルーターのパスワードに書き換えてください
 
const char* utf8sjis_file = "/font/Utf8Sjis.tbl"; //UTF8 Shift_JIS 変換テーブルファイル名を記載しておく
//const char* shino_full_font_file = "/font/shnmk16.bdf"; //オリジナル東雲全角フォントファイル
const char* shino_half_font_file = "/font/shnm8x16.bdf"; //半角フォントファイル名を定義
const char* shino_full_font_file = "/font/MYshnmk16.bdf"; //自作改変全角東雲フォントファイル
const char* my_font_file = "/font/MyFont.fnt"; //自作フォントファイル名を定義(天気予報用)
 
//-----Yahooニュース切替select box関連初期化-----
const uint8_t max_select2 = 9;
mgo_tec_esp32_bv1::SelectUrl sel_news[ max_select2 ] = {
  { "主要", "/rss/topics/top-picks.xml" },
  { "スポーツ", "/rss/topics/sports.xml" },
  { "エンタメ", "/rss/topics/entertainment.xml" },
  { "IT", "/rss/topics/it.xml" },
  { "経済", "/rss/topics/business.xml" },
  { "科学", "/rss/topics/science.xml" },
  { "国際", "/rss/topics/world.xml" },
  { "地域", "/rss/topics/local.xml" },
  { "国内", "/rss/topics/domestic.xml" }
};
//------Yahooニュース表示関連初期化----------------
const char* yahoo_news_host = "news.yahoo.co.jp";
const char* yahoo_weather_host = "rss-weather.yahoo.co.jp";
const char* yahoo_weather_target_url = "/rss/days/4410.xml"; //地域:東京
uint8_t url0_num = 0;
uint8_t url1_num = 1;
boolean isChange_news = false;
//------ NTP時刻文字表示系 引数初期化-----
int timezone = 9; //Tokyo
const char *ntp_server_name = "time.windows.com";
//-----Webデータ取得変数-------------
uint32_t wifi_connect_last_time = 0;
boolean isWifi_connect_first = true;
boolean isClear_disp = false;

const char* yahoo_root_ca= \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
//.................................
//.................................
//.................................
//.................................
//.................................
//.................................
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"-----END CERTIFICATE-----\n";

//------気象庁ホームページ天気予報用初期化-----------------------
const char* jp_weather_host = "www.jma.go.jp";

String office_code_str = "130000"; //東京都
String area_code_str = "130010"; //東京地方

String jp_weather_target_url = "/bosai/forecast/data/forecast/" + office_code_str + ".json";

//気象庁ホームページ ルートCA公開鍵
const char* jp_weather_root_ca= \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
//.................................
//.................................
//.................................
//.................................
//.................................
//.................................
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"-----END CERTIFICATE-----\n";
 
//***********セットアップ****************************
void setup() {
  delay(1000);
  Serial.begin(115200);
 
  mM5.init( utf8sjis_file, shino_half_font_file, shino_full_font_file );
  //天気予報用自作フォント初期化
  mM5.yahoo[0].initWeather( my_font_file );
  LCD.brightness( 50 ); //LCD LED Full brightness
 
  setupWatchFont(); //時計表示フォントセットアップ
  setupYmdFont(); //年月日表示フォントセットアップ
  setupWeatherFont(); //天気予報フォントセットアップ
  setupNewsFont(); //ニュース記事文字列のセットアップ
  setupMessageWindow(); //メッセージウィンドウ表示セットアップ
  //起動時のみのメッセージウィンドウ設定
  mM5.msg.m_padding = 4;
  mM5.msg.dispMsgWindow( 0, "WiFi Connecting..." );
 
  getWeb();
  mM5.watch.watchDispReset(); //時計表示が崩れる為に一旦リセット
 
  TaskHandle_t th; //マルチタスクハンドル定義
  xTaskCreatePinnedToCore(Task1, "Task1", 8192, NULL, 5, &th, 0); //マルチタスク起動
}
//***********メインループ****************************
void loop(){
  //Statusメッセージ表示
  mM5.wifi_msg.dispWifiStatusMsgShort(); //WiFiステータス表示
  mM5.watch.ntp_msg.dispWebGetStatusMsgShort( mM5.watch.ntp_msg_status, "NTP" );
  mM5.yahoo[0].weather_msg.dispWebGetStatusMsgShort( mM5.yahoo[0].weather_msg_status, "天" );
  mM5.yahoo[0].news_msg.dispWebGetStatusMsgShort( mM5.yahoo[0].news_msg_status, sel_news[url0_num].name );
  mM5.yahoo[1].news_msg.dispWebGetStatusMsgShort( mM5.yahoo[1].news_msg_status, sel_news[url1_num].name );
 
  if( isChange_news == true ){
    LCD.displayClear( 0, 24, 319, 239 );
    mM5.watch.watchDispReset();
    mM5.yahoo[0].m_isWeather_get = true;
    mM5.watch.m_changeYMD = true;
    isChange_news = false;
  }
  mM5.watch.scrolleWatch(); //時計スクロール
  mM5.watch.displayColon2(); //時計コロン表示
  mM5.watch.displayYMDW(); //年月日、曜日表示
 
  //気象庁天気予報表示
  mM5.yahoo[0].dispJapanWeatherMyFont( mM5.yahoo[0].weather_fnt );
  //Yahoo! Japan RSS ニューススクロール
  mM5.yahoo[0].scrolleYahooJnews2( mM5.yahoo[0].news_font, mM5.yahoo[0].news_scl_set );
  mM5.yahoo[1].scrolleYahooJnews2( mM5.yahoo[1].news_font, mM5.yahoo[1].news_scl_set );
 
  button_action(); //ボタン操作
}
//************ マルチタスクループ ******************
void Task1( void *pvParameters ) {
  while(1){
    getWeb(); //Web記事およびNTP時刻取得
    delay(1); //マルチタスクの場合、これ絶対必要!
  }
}
//******時計表示フォント設定****************************
void setupWatchFont(){
  uint8_t i;
  for( i = 0; i < 4; i++ ){ //時、分のフォント色設定
    mM5.watch.font[i].red = 31, mM5.watch.font[i].green = 0, mM5.watch.font[i].blue = 31;
  }
  for( i = 4; i < 6; i++ ){ //秒のフォント色設定
    mM5.watch.font[i].red = 0, mM5.watch.font[i].green = 63, mM5.watch.font[i].blue = 31;
  }
  //コロンフォント色設定 red(0-31), green(0-63), blue(0-31)
  mM5.watch.colon1_font.red = 31, mM5.watch.colon1_font.green = 63, mM5.watch.colon1_font.blue = 0;
  mM5.watch.colon2_font.red = 0, mM5.watch.colon2_font.green = 0, mM5.watch.colon2_font.blue = 31;
  uint16_t x0 = 0, y0 = 29; //時計表示左端の座標
  uint8_t x_size = 3, y_size = 3;
  mM5.watch.init( x0, y0, x_size, y_size );
}
//******年月日表示フォント設定**************************
void setupYmdFont(){
  //red(0-31), green(0-63), blue(0-31)
  mM5.watch.ymd_font.y0 = 79;
  mM5.watch.ymd_font.Xsize = 2, mM5.watch.ymd_font.Ysize = 2;
}
//******Yahooニュース天気予報フォント設定****************
void setupWeatherFont(){
  mM5.yahoo[0].weather_fnt.Xsize = 2, mM5.yahoo[0].weather_fnt.Ysize = 2;
  mM5.yahoo[0].weather_fnt.y0 = 111;
}
//******Yahooニュース記事フォント設定********************
void setupNewsFont(){
  mM5.yahoo[0].news_font.y0 = 144;
  mM5.yahoo[0].news_font.Xsize = 3, mM5.yahoo[0].news_font.Ysize = 3;
  // red(0-31), green(0-63), blue(0-31)
  mM5.yahoo[0].news_font.red = 31, mM5.yahoo[0].news_font.green = 63, mM5.yahoo[0].news_font.blue = 0;
  mM5.yahoo[0].news_font.bg_red = 0, mM5.yahoo[0].news_font.bg_green = 0, mM5.yahoo[0].news_font.bg_blue = 25;
 
  mM5.yahoo[1].news_font.y0 = 192;
  mM5.yahoo[1].news_font.Xsize = 3, mM5.yahoo[1].news_font.Ysize = 3;
  // red(0-31), green(0-63), blue(0-31)
  mM5.yahoo[1].news_font.red = 31, mM5.yahoo[1].news_font.green = 63, mM5.yahoo[1].news_font.blue = 0;
  mM5.yahoo[1].news_font.bg_red = 0, mM5.yahoo[1].news_font.bg_green = 0, mM5.yahoo[1].news_font.bg_blue = 25;
  //ヒープ領域配列確保。※不要な時は必ずdeleteしておく。
  for( int i = 0; i < 2; i++ ){
    mM5.yahoo[i].news_scl_set.disp_txt_len = 13; //ディスプレイに表示する半角相当文字数
    mM5.yahoo[i].initScrolle( mM5.yahoo[i].news_font, mM5.yahoo[i].news_scl_set );
  }
  Serial.printf("Free Heap Size = %d\r\n", esp_get_free_heap_size());
}
//*****メッセージ表示設定**************************
void setupMessageWindow(){
  mM5.wifi_msg.m_padding = 4; //pixel単位
  mM5.wifi_msg.m_txt_length = 6; //文字表示数(半角相当)
  //背景色のみ設定 red(0-31), green(0-63), blue(0-31)
 
  mM5.watch.ntp_msg.m_x0 = 56;
  mM5.watch.ntp_msg.m_padding = 4; //pixel単位
  mM5.watch.ntp_msg.m_txt_length = 5; //文字表示数(半角相当)
  //背景色のみ設定 red(0-31), green(0-63), blue(0-31)
  mM5.watch.ntp_msg.m_bg_red = 10;
  mM5.watch.ntp_msg.m_bg_green = 10;
  mM5.watch.ntp_msg.m_bg_blue = 15;
 
  mM5.yahoo[0].weather_msg.m_x0 = 104;
  mM5.yahoo[0].weather_msg.m_padding = 4; //pixel単位
  mM5.yahoo[0].weather_msg.m_txt_length = 4; //文字表示数(半角相当)
  //背景色のみ設定 red(0-31), green(0-63), blue(0-31)
  mM5.yahoo[0].weather_msg.m_bg_red = 10;
  mM5.yahoo[0].weather_msg.m_bg_green = 10;
  mM5.yahoo[0].weather_msg.m_bg_blue = 15;
 
  mM5.yahoo[0].news_msg.m_x0 = 144;
  mM5.yahoo[0].news_msg.m_padding = 4; //pixel単位
  mM5.yahoo[0].news_msg.m_txt_length = 10; //文字表示数(半角相当)
  //背景色のみ設定 red(0-31), green(0-63), blue(0-31)
  mM5.yahoo[0].news_msg.m_bg_red = 10;
  mM5.yahoo[0].news_msg.m_bg_green = 10;
  mM5.yahoo[0].news_msg.m_bg_blue = 15;
 
  mM5.yahoo[1].news_msg.m_x0 = 232;
  mM5.yahoo[1].news_msg.m_padding = 4; //pixel単位
  mM5.yahoo[1].news_msg.m_txt_length = 10; //文字表示数(半角相当)
  //背景色のみ設定 red(0-31), green(0-63), blue(0-31)
  mM5.yahoo[1].news_msg.m_bg_red = 10;
  mM5.yahoo[1].news_msg.m_bg_green = 10;
  mM5.yahoo[1].news_msg.m_bg_blue = 15;
}
//*******NTPサーバー、Yahoo! Japan RSS 記事取得*******
void getWeb(){
  if( isWifi_connect_first || isChange_news || (millis() - wifi_connect_last_time > 180000) ){ //3分毎に記事取得
    WiFi_AP_Connect(); //WiFi起動、アクセスポイント接続
    if( isChange_news == false ){
      mM5.watch.getNTPserverSel( timezone, ntp_server_name );
    }
    //気象庁ホームページJSON取得
    mM5.yahoo[0].getJpWeatherRCA( jp_weather_root_ca, 1, jp_weather_host, jp_weather_target_url, area_code_str );
    //ルートCA無しの場合、以下にする
    //mM5.yahoo[0].getJpWeatherRCA( nullptr, 0, jp_weather_host, jp_weather_target_url, area_code_str );

    mM5.yahoo[0].getYahooJnewsRCA( yahoo_root_ca, 1, yahoo_news_host, sel_news[url0_num].url );
    mM5.yahoo[1].getYahooJnewsRCA( yahoo_root_ca, 1, yahoo_news_host, sel_news[url1_num].url );
    
    delay(1000);
    WiFi.disconnect( true,true ); //WiFi OFF, eraseAP=true これを使う場合、Core Debug Level "なし" に設定する
    WiFi.mode( WIFI_OFF ); //これを使う場合、Core Debug Level "なし" に設定する
 
    Serial.printf( "Free heap after TLS %u\r\n", xPortGetFreeHeapSize() );
    isWifi_connect_first = false;
    wifi_connect_last_time = millis();
  }
}
//*******WiFiアクセスポイント接続*************
void WiFi_AP_Connect(){
  mM5.wifi_msg.WifiStatus = mM5.wifi_msg.WifiConnecting; //WiFiメッセージウィンドウ設定
  Serial.println();
  Serial.println( F("Connecting Wifi...") );
  Serial.println( ssid );
 
  int16_t wifi_state = WiFi.status();
  Serial.printf( "\r\nWiFi.status = %d\r\n", wifi_state );
 
  if( isWifi_connect_first == true ){
    //WiFiが急に接続できなくなった場合の応急処置
    WiFi.disconnect( true, true ); //WiFi OFF, eraseAP=true
    delay(1000);
    WiFi.begin( ssid, password );
 
    while ( WiFi.status() != WL_CONNECTED ) {
      delay(500);
      Serial.print(".");
    }
    isWifi_connect_first = false;
  }else{
    uint32_t last_time = millis();
    if( wifi_state != WL_CONNECTED ){
      WiFi.begin( ssid, password ); //常時 WiFi ON の場合、ここをコメントアウト
      while ( wifi_state != WL_CONNECTED ) {
        delay(500);
        Serial.print(".");
        if( millis() - last_time > 20000 ) break; //Time OUT
      }
    }
    //マルチタスクでメッセージウィンドウを正しく表示させるための処置
    if( millis() - last_time < 1000 ) delay(2000); 
  }
  wifi_state = WiFi.status();
  Serial.printf("\r\nWiFi.status = %d\r\n", wifi_state);
  if( wifi_state == WL_CONNECTED ){
    Serial.println("");
    Serial.println( "WiFi connected" );
    Serial.println( "IP address: " );
    Serial.println( WiFi.localIP() );
    wifi_connect_last_time = millis();
  }
 
  if( wifi_state == WL_CONNECTED ){
    mM5.wifi_msg.WifiStatus = mM5.wifi_msg.WifiConnected; //WiFiメッセージウィンドウ設定
  }else{
    mM5.wifi_msg.WifiStatus = mM5.wifi_msg.WifiFailed; //WiFiメッセージウィンドウ設定
    Serial.println( F("WiFi AP Not Found") );
  }
}
//***** セレクトボックス表示設定 ***************
void selectUrl(){
  mgo_tec_esp32_bv1::BtnDispSelectBox Sel;
  const uint8_t max_select1 = 2;
  String sel_news_line[ max_select1 ] = { "News 0", "News 1" };
  uint16_t x0 = 96, y0 = 50, x1 = 240; //セレクトボックス位置
  uint8_t max_in_page_num1 = 3; //セレクトボックスの行数設定
  uint8_t default_sel_num = 0; //デフォルトのセレクト
  uint8_t news_num = Sel.dispBtnSelStrDef( mM5.btnA, sel_news_line, x0, y0, x1, default_sel_num, max_in_page_num1, max_select1 );
 
  uint8_t max_in_page_num2 = 5; //セレクトボックスの行数設定
  if( news_num == 0 ){
    url0_num = Sel.dispBtnSelUrlDef( mM5.btnA, sel_news, x0, y0, x1, url0_num, max_in_page_num2, max_select2 );
    isChange_news = true;
  }else if( news_num == 1 ){
    url1_num = Sel.dispBtnSelUrlDef( mM5.btnA, sel_news, x0, y0, x1, url1_num, max_in_page_num2, max_select2 );
    isChange_news = true;
  }
}
//****************************************
void button_action(){
  mM5.btnA.buttonAction();
  switch( mM5.btnA.ButtonStatus ){
    case mM5.btnA.MomentPress:
      Serial.println("Button A Moment Press");
      selectUrl();
      break;
    case mM5.btnA.ContPress:
      Serial.println("-------------Button A Cont Press");
      break;
    default:
      break;
  }
 
  mM5.btnB.buttonAction();
  switch( mM5.btnB.ButtonStatus ){
    case mM5.btnB.MomentPress:
      Serial.println("Button B Moment Press");
      mM5.yahoo[0].news_scl_set.interval++;
      if( mM5.yahoo[0].news_scl_set.interval> 200 ) mM5.yahoo[0].news_scl_set.interval = 200;
      break;
    case mM5.btnB.ContPress:
      Serial.println("-------------Button B Cont Press");
      mM5.yahoo[0].news_scl_set.interval--;
      if( mM5.yahoo[0].news_scl_set.interval < 0 ) mM5.yahoo[0].news_scl_set.interval = 0;
      break;
    default:
      break;
  }
 
  mM5.btnC.buttonAction();
  switch( mM5.btnC.ButtonStatus ){
    case mM5.btnC.MomentPress:
      Serial.println("Button C Moment Press");
      mM5.yahoo[1].news_scl_set.interval++;
      if( mM5.yahoo[1].news_scl_set.interval> 200 ) mM5.yahoo[1].news_scl_set.interval = 200;
      break;
    case mM5.btnC.ContPress:
      Serial.println("-------------Button C Cont Press");
      mM5.yahoo[1].news_scl_set.interval--;
      if( mM5.yahoo[1].news_scl_set.interval < 0 ) mM5.yahoo[1].news_scl_set.interval = 0;
      break;
    default:
      break;
  }
}

M5Stack 電波時計合わせYahoo電光掲示板の修正プログラム

次に、こちらの記事
M5Stack Yahooニュース・天気予報・時計に、電波時計 JJY 発信モジュールを追加して、マルチタスクで動かしてみた
の修正版サンプルスケッチ

// mgo_tecライブラリは beta ver 1.0.83 を使って下さい。
// ※Arduino core ESP32 は ver 2.0.2は動作不良起こすので使わないこと
#define MGO_TEC_BV1_M5STACK_SD_SKETCH
#include <mgo_tec_bv1_m5stack_sd_yahoo.h>
  
static const char* ssid = "xxxxxxxxx"; //ご自分のルーターのSSIDに書き換えてください
static const char* password = "xxxxxxxxx"; //ご自分のルーターのパスワードに書き換えてください
   
const char* utf8sjis_file = "/font/Utf8Sjis.tbl"; //UTF8 Shift_JIS 変換テーブルファイル名を記載しておく
//const char* shino_full_font_file = "/font/shnmk16.bdf"; //オリジナル東雲全角フォントファイル
const char* shino_half_font_file = "/font/shnm8x16.bdf"; //半角フォントファイル名を定義
const char* shino_full_font_file = "/font/MYshnmk16.bdf"; //自作改変全角東雲フォントファイル
const char* my_font_file = "/font/MyFont.fnt"; //自作フォントファイル名を定義(天気予報用)
   
//-----Yahooニュース切替select box関連初期化-----
const uint8_t max_select2 = 9;
mgo_tec_esp32_bv1::SelectUrl sel_news[ max_select2 ] = {
  { "主要", "/rss/topics/top-picks.xml" },
  { "スポーツ", "/rss/topics/sports.xml" },
  { "エンタメ", "/rss/topics/entertainment.xml" },
  { "IT", "/rss/topics/it.xml" },
  { "経済", "/rss/topics/business.xml" },
  { "科学", "/rss/topics/science.xml" },
  { "国際", "/rss/topics/world.xml" },
  { "地域", "/rss/topics/local.xml" },
  { "国内", "/rss/topics/domestic.xml" }
};
//------Yahooニュース表示関連初期化----------------
const char* yahoo_news_host = "news.yahoo.co.jp";

uint8_t url0_num = 0;
uint8_t url1_num = 1;
boolean isChange_news = false;
//------ NTP時刻文字表示系 引数初期化-----
int timezone = 9; //Tokyo
const char *ntp_server_name = "time.windows.com";
//-----Webデータ取得変数-------------
const uint32_t interval_web_get_time = 1200000; //20分間隔でNTP時刻およびYahoo記事取得
uint32_t wifi_connect_last_time = 0;
boolean isWifi_connect_first = true;
boolean isClear_disp = false;
//-----電波時計合わせ用定義----------
const uint8_t gpio_high = 17;
const uint8_t gpio_low = 16;
uint8_t min_now, hour_now, year_now, weekday_now;
uint16_t day_total;
uint8_t parity1, parity2; //パリティ定義
uint32_t last_time_jjy = 0;
boolean isReset_jjy = true; //NTP時刻取得したり、Web記事取得した場合 true;
boolean isJJY_through = false;
//-----LEDC PWM用定義----------
const uint8_t ledc_ch_low = 6; //channel max 7
const uint8_t ledc_ch_high = 7; //channel max 7
const uint8_t ledc_timer_bit = 1; //max 16bit
const uint8_t ledc_duty = 1;
double ledc_base_freq = 40000.0;

const char* yahoo_root_ca= \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
//.................................
//.................................
//.................................
//.................................
//.................................
//.................................
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"-----END CERTIFICATE-----\n";

//------気象庁ホームページ天気予報用初期化-----------------------
const char* jp_weather_host = "www.jma.go.jp";

String office_code_str = "130000"; //東京都
String area_code_str = "130010"; //東京地方

String jp_weather_target_url = "/bosai/forecast/data/forecast/" + office_code_str + ".json";
//ルートCA公開鍵
const char* jp_weather_root_ca= \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
//.................................
//.................................
//.................................
//.................................
//.................................
//.................................
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"-----END CERTIFICATE-----\n";

//***********セットアップ****************************
void setup() {
  delay(1000);
  Serial.begin(115200);
  
  mM5.init( utf8sjis_file, shino_half_font_file, shino_full_font_file );
  //天気予報用自作フォント初期化
  mM5.yahoo[0].initWeather( my_font_file );
  LCD.brightness( 50 ); //LCD LED Full brightness
  
  setupWatchFont(); //時計表示フォントセットアップ
  setupYmdFont(); //年月日表示フォントセットアップ
  setupWeatherFont(); //天気予報フォントセットアップ
  setupNewsFont(); //ニュース記事文字列のセットアップ
  setupMessageWindow(); //メッセージウィンドウ表示セットアップ
  //起動時のみのメッセージウィンドウ設定
  mM5.msg.m_padding = 4;
  mM5.msg.dispMsgWindow( 0, "WiFi Connecting..." );
  
  getWeb();
  mM5.watch.watchDispReset(); //時計表示が崩れる為に一旦リセット
  
  //----------------------------------
  setTime( now() + 1 ); //標準時とキッチリ合わせるため、1秒早める。
  
  TaskHandle_t th; //マルチタスクハンドル定義
  xTaskCreatePinnedToCore(Task1, "Task1", 8192, NULL, 5, &th, 0); //マルチタスク起動
}
//***********メインループ****************************
void loop(){
  //Statusメッセージ表示
  mM5.wifi_msg.dispWifiStatusMsgShort(); //WiFiステータス表示
  mM5.watch.ntp_msg.dispWebGetStatusMsgShort( mM5.watch.ntp_msg_status, "NTP" );
  mM5.yahoo[0].weather_msg.dispWebGetStatusMsgShort( mM5.yahoo[0].weather_msg_status, "天" );
  mM5.yahoo[0].news_msg.dispWebGetStatusMsgShort( mM5.yahoo[0].news_msg_status, sel_news[url0_num].name );
  mM5.yahoo[1].news_msg.dispWebGetStatusMsgShort( mM5.yahoo[1].news_msg_status, sel_news[url1_num].name );
  
  if( isChange_news == true ){
    LCD.displayClear( 0, 24, 319, 239 );
    mM5.watch.watchDispReset();
    mM5.yahoo[0].m_isWeather_get = true;
    mM5.watch.m_changeYMD = true;
    isChange_news = false;
  }
  mM5.watch.scrolleWatch(); //時計スクロール
  mM5.watch.displayColon2(); //時計コロン表示
  mM5.watch.displayYMDW(); //年月日、曜日表示
  
  //気象庁天気予報表示
  mM5.yahoo[0].dispJapanWeatherMyFont( mM5.yahoo[0].weather_fnt );
  //Yahoo! Japan RSS ニューススクロール
  mM5.yahoo[0].scrolleYahooJnews2( mM5.yahoo[0].news_font, mM5.yahoo[0].news_scl_set );
  mM5.yahoo[1].scrolleYahooJnews2( mM5.yahoo[1].news_font, mM5.yahoo[1].news_scl_set );
  
  button_action(); //ボタン操作
}
//************ マルチタスクループ ******************
void Task1( void *pvParameters ) {
  ledcSetup(ledc_ch_high, ledc_base_freq, ledc_timer_bit);
  ledcSetup(ledc_ch_low, ledc_base_freq, ledc_timer_bit);
  ledcAttachPin(gpio_high, ledc_ch_high);
  ledcAttachPin(gpio_low, ledc_ch_low);
  
  disableCore0WDT(); //Arduino-ESP32 stable 1.0.1 WDT動作をストップ
  for(;;){
    last_time_jjy = millis();
    getWeb(); //Web記事およびNTP時刻取得
    sendJJY(); //Web記事およびNTP時刻取得
  }
}
//*******NTPサーバー、Yahoo! Japan RSS 記事取得*******
void getWeb(){
  if( isWifi_connect_first
   || isChange_news
   || isJJY_through
   || (millis() - wifi_connect_last_time > interval_web_get_time) ){
    WiFi_AP_Connect(); //WiFi起動、アクセスポイント接続
    if( isChange_news == false ){
      mM5.watch.getNTPserverSel( timezone, ntp_server_name );
    }
    mM5.yahoo[0].getJpWeatherRCA( jp_weather_root_ca, 1, jp_weather_host, jp_weather_target_url, area_code_str );
    //ルートCA無しの場合、以下にする
    //mM5.yahoo[0].getJpWeatherRCA( nullptr, 0, jp_weather_host, jp_weather_target_url, area_code_str );

    mM5.yahoo[0].getYahooJnewsRCA( yahoo_root_ca, 1, yahoo_news_host, sel_news[url0_num].url );
    mM5.yahoo[1].getYahooJnewsRCA( yahoo_root_ca, 1, yahoo_news_host, sel_news[url1_num].url );
    delay(1000);
    WiFi.disconnect( true,true ); //WiFi OFF, eraseAP=true これを使う場合、Core Debug Level "なし" に設定する
    WiFi.mode( WIFI_OFF ); //これを使う場合、Core Debug Level "なし" に設定する
  
    Serial.printf( "Free heap after TLS %u\r\n", xPortGetFreeHeapSize() );
    isWifi_connect_first = false;
    wifi_connect_last_time = millis();
    isReset_jjy = true;
    isJJY_through = false;
  }
}
//******** 電波時計用JJY信号発信 **********************
void sendJJY() {
  if(isReset_jjy == true) {
    if(second() == 0){
      delay(1500); //ゼロ秒の場合は次のゼロ秒までやり過ごすために、1.5秒待つ。
    }
    while(second() != 0){ //ゼロ秒になるまで待つ
      delay(1); //ウォッチドッグタイマを動作させるために必要
    }
    last_time_jjy = millis();
    isReset_jjy = false;
  }
  getNowTime();                   //現在時刻初期化
  sendMarker();                     //M マーカー送信
  sendMinuteTimeCode();             //タイムコード(分)送信
  sendMarker();                     //P1 ポジションマーカー送信
  sendHourTimeCode();            //タイムコード(時)送信
  sendMarker();                     //P2 ポジションマーカー送信
  sendDayTotalParityTimeCode(); //タイムコード(通算日)送信
  sendMarker();                     //P4 ポジションマーカー送信
  sendYearTimeCode();            //タイムコード(年)送信
  sendMarker();                     //P5 ポジションマーカー送信
  sendWeekdayUruuTimeCode();    //タイムコード(曜日、うるう秒)送信
  sendMarker();                     //P0 ポジションマーカー送信
  
  if(isJJY_through == true) return;
  Serial.printf("\r\n[Total = %ld ms]\r\n", millis() - last_time_jjy);
  
  while(second() != 0){ //もし、時間がズレた場合、ゼロ秒になるまで時間調整する
    Serial.print('.');
    delay(1); //ウォッチドッグタイマを動作させるために必要
  }
}
//******** マーカーおよびポジションマーカー送信 ************************
void sendMarker(){
  if(isJJY_through == true) return; //ボタンスイッチで記事変更決定したらこの関数をスルーする
  Serial.print(".M.");
  oscillator(ledc_ch_high, 200);
  oscillator(ledc_ch_low, 800);
}
//******** タイムコード(分)送信 **************
void sendMinuteTimeCode(){
  if(isJJY_through == true) return; //ボタンスイッチで記事変更決定したらこの関数をスルーする
  uint8_t min10 = (uint8_t)floor((double)min_now / 10.0);
  uint8_t min01 = min_now % 10;
  uint8_t m[7] = {0}; //分のパリティ parity2 計算用配列初期化
  
  //---------10の位-------------
  m[6] = bitRead( min10, 2 );
  Serial.print( m[6] );
  oscillatorTimeCodeBit( m[6] );
  m[5] = bitRead( min10, 1 );
  Serial.print( m[5] );
  oscillatorTimeCodeBit( m[5] );
  m[4] = bitRead( min10, 0 );
  Serial.print( m[4] );
  oscillatorTimeCodeBit( m[4] );
  Serial.print('0');
  oscillatorTimeCodeBit(0);
  //---------1の位--------------
  m[3] = bitRead( min01, 3 );
  Serial.print( m[3] );
  oscillatorTimeCodeBit( m[3] );
  m[2] = bitRead( min01, 2 );
  Serial.print( m[2] );
  oscillatorTimeCodeBit( m[2] );
  m[1] = bitRead( min01, 1 );
  Serial.print( m[1] );
  oscillatorTimeCodeBit( m[1] );
  m[0] = bitRead( min01, 0 );
  Serial.print( m[0] );
  oscillatorTimeCodeBit( m[0] );
  
  //-------分パリティ計算-------
  parity2 = (m[6] + m[5] + m[4] + m[3] + m[2] + m[1] + m[0]) % 2;
}
//******** タイムコード(時)送信 *************
void sendHourTimeCode(){
  if(isJJY_through == true) return; //ボタンスイッチで記事変更決定したらこの関数をスルーする
  uint8_t hour10 = (uint8_t)floor((double)hour_now / 10.0);
  uint8_t hour01 = hour_now % 10;
  uint8_t h[6] = {0}; //時のパリティ parity1 計算用配列初期化
  
  Serial.print('0');
  oscillatorTimeCodeBit(0);
  Serial.print('0');
  oscillatorTimeCodeBit(0);
  
  //---------10の位-------------
  h[5] = bitRead( hour10, 1 );
  Serial.print( h[5] );
  oscillatorTimeCodeBit( h[5] );
  h[4] = bitRead( hour10, 0 );
  Serial.print( h[4] );
  oscillatorTimeCodeBit( h[4] );
  Serial.print('0');
  oscillatorTimeCodeBit(0);
  //---------1の位-------------
  h[3] = bitRead( hour01, 3 );
  Serial.print( h[3] );
  oscillatorTimeCodeBit( h[3] );
  h[2] = bitRead( hour01, 2 );
  Serial.print( h[2] );
  oscillatorTimeCodeBit( h[2] );
  h[1] = bitRead( hour01, 1 );
  Serial.print( h[1] );
  oscillatorTimeCodeBit( h[1] );
  h[0] = bitRead( hour01, 0 );
  Serial.print( h[0] );
  oscillatorTimeCodeBit( h[0] );
  
  //-------分パリティ計算-------
  parity1 = (h[5] + h[4] + h[3] + h[2] + h[1] + h[0]) % 2;
}
//******** タイムコード(その年の1月1日からの通算日)送信 ***********
void sendDayTotalParityTimeCode(){
  if(isJJY_through == true) return; //ボタンスイッチで記事変更決定したらこの関数をスルーする
  uint8_t t_day100 = (uint8_t)floor((double)day_total / 100.0);
  uint8_t t_day10 = ((uint8_t)(floor((double)day_total / 10.0))) % 10;
  uint8_t t_day01 = day_total % 10;
  uint8_t b;
  
  Serial.print('0');
  oscillatorTimeCodeBit(0);
  Serial.print('0');
  oscillatorTimeCodeBit(0);
  
  //---------100の位-------------
  b = bitRead( t_day100, 1 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( t_day100, 0 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  Serial.print('0');
  oscillatorTimeCodeBit(0);
  //---------10の位-------------
  b = bitRead( t_day10, 3 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( t_day10, 2 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( t_day10, 1 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( t_day10, 0 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  
  sendMarker(); //P3 ポジションマーカー
  
  //---------1の位-------------
  b = bitRead( t_day01, 3 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( t_day01, 2 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( t_day01, 1 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( t_day01, 0 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  
  Serial.print('0');
  oscillatorTimeCodeBit(0);
  Serial.print('0');
  oscillatorTimeCodeBit(0);
  
  //---------パリティビット---------
  Serial.print(parity1);
  oscillatorTimeCodeBit(parity1);
  Serial.print(parity2);
  oscillatorTimeCodeBit(parity2);
  //---------予備ビット-------------
  Serial.print('0');
  oscillatorTimeCodeBit(0); //SU1 予備ビット
}
//**********タイムコード(年)下2桁送信**********************
void sendYearTimeCode(){
  if(isJJY_through == true) return; //ボタンスイッチで記事変更決定したらこの関数をスルーする
  uint8_t year10 = (uint8_t)(floor((double)year_now / 10.0));
  uint8_t year01 = year_now % 10;
  uint8_t b;
  
  //---------予備ビット---------
  Serial.print('0');
  oscillatorTimeCodeBit(0); //SU2 予備ビット
  //---------10の位-------------
  b = bitRead( year10, 3 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( year10, 2 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( year10, 1 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( year10, 0 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  //---------1の位-------------
  b = bitRead( year01, 3 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( year01, 2 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( year01, 1 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( year01, 0 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
}
//********** タイムコード(曜日、うるう秒)送信 ************
void sendWeekdayUruuTimeCode(){
  uint8_t b;
  
  //---------曜日-------------
  b = bitRead( weekday_now, 2 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( weekday_now, 1 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  b = bitRead( weekday_now, 0 );
  Serial.print( b );
  oscillatorTimeCodeBit( b );
  //---------うるう秒---------
  Serial.print('0');
  oscillatorTimeCodeBit(0); //LS1:うるう秒
  Serial.print('0');
  oscillatorTimeCodeBit(0); //LS2:うるう秒
  //--------------------------
  Serial.print('0');
  oscillatorTimeCodeBit(0);
  Serial.print('0');
  oscillatorTimeCodeBit(0);
  Serial.print('0');
  oscillatorTimeCodeBit(0);
  Serial.print('0');
  oscillatorTimeCodeBit(0);
}
//********** タイムコードビット送信 *****************
void oscillatorTimeCodeBit(uint8_t b){
  if(isJJY_through == true) return; //ボタンスイッチで記事変更決定したらこの関数をスルーする
  if( b ){ //2進数の1
    oscillator(ledc_ch_high, 500);
    oscillator(ledc_ch_low, 500);
  }else{ //2進数の0
    oscillator(ledc_ch_high, 800);
    oscillator(ledc_ch_low, 200);
  }
}
//*********** 40kHz High レベル発信******************
void oscillator(uint8_t ledc_ch, uint16_t time_osc){
  uint32_t last_time = millis();
  ledcWrite(ledc_ch, ledc_duty); //しきい値 1 duty: 50%
  for(;;){
    if(millis() - last_time >= time_osc) {
      ledcWrite(ledc_ch, 0);
      return;
    }
  }
}
//*********** その年の1月1日からの通算日計算 ******
uint16_t calcDayTotal(){
  //Arduino Timeライブラリ関数を使う
  tmElements_t tm = {0, 0, 0, 0, 1, 1, (uint8_t)(year()-1970)};
  time_t t = makeTime(tm);
  return (now() - t) / SECS_PER_DAY + 1;
}
//*********** 現在時刻代入 ************************
void getNowTime(){
  min_now = minute();
  hour_now = hour();
  year_now = year() - 2000;
  if(year_now > 100) year_now = 0; //有り得ない数値は0にする
  weekday_now = weekday() - 1;
  day_total = calcDayTotal();
  Serial.printf("\r\nYear=%d / TotalDay=%d / Weekday=%d / %02d:%02d\r\n", year_now, day_total, weekday_now, hour_now, min_now);
}
//******時計表示フォント設定****************************
void setupWatchFont(){
  uint8_t i;
  for( i = 0; i < 4; i++ ){ //時、分のフォント色設定
    mM5.watch.font[i].red = 31, mM5.watch.font[i].green = 0, mM5.watch.font[i].blue = 31;
  }
  for( i = 4; i < 6; i++ ){ //秒のフォント色設定
    mM5.watch.font[i].red = 0, mM5.watch.font[i].green = 63, mM5.watch.font[i].blue = 31;
  }
  //コロンフォント色設定 red(0-31), green(0-63), blue(0-31)
  mM5.watch.colon1_font.red = 31, mM5.watch.colon1_font.green = 63, mM5.watch.colon1_font.blue = 0;
  mM5.watch.colon2_font.red = 0, mM5.watch.colon2_font.green = 0, mM5.watch.colon2_font.blue = 31;
  uint16_t x0 = 0, y0 = 29; //時計表示左端の座標
  uint8_t x_size = 3, y_size = 3;
  mM5.watch.init( x0, y0, x_size, y_size );
}
//******年月日表示フォント設定**************************
void setupYmdFont(){
  //red(0-31), green(0-63), blue(0-31)
  mM5.watch.ymd_font.y0 = 79;
  mM5.watch.ymd_font.Xsize = 2, mM5.watch.ymd_font.Ysize = 2;
}
//******Yahooニュース天気予報フォント設定****************
void setupWeatherFont(){
  mM5.yahoo[0].weather_fnt.Xsize = 2, mM5.yahoo[0].weather_fnt.Ysize = 2;
  mM5.yahoo[0].weather_fnt.y0 = 111;
}
//******Yahooニュース記事フォント設定********************
void setupNewsFont(){
  mM5.yahoo[0].news_font.y0 = 144;
  mM5.yahoo[0].news_font.Xsize = 3, mM5.yahoo[0].news_font.Ysize = 3;
  // red(0-31), green(0-63), blue(0-31)
  mM5.yahoo[0].news_font.red = 31, mM5.yahoo[0].news_font.green = 63, mM5.yahoo[0].news_font.blue = 0;
  mM5.yahoo[0].news_font.bg_red = 0, mM5.yahoo[0].news_font.bg_green = 0, mM5.yahoo[0].news_font.bg_blue = 25;
  
  mM5.yahoo[1].news_font.y0 = 192;
  mM5.yahoo[1].news_font.Xsize = 3, mM5.yahoo[1].news_font.Ysize = 3;
  // red(0-31), green(0-63), blue(0-31)
  mM5.yahoo[1].news_font.red = 31, mM5.yahoo[1].news_font.green = 63, mM5.yahoo[1].news_font.blue = 0;
  mM5.yahoo[1].news_font.bg_red = 0, mM5.yahoo[1].news_font.bg_green = 0, mM5.yahoo[1].news_font.bg_blue = 25;
  //ヒープ領域配列確保。※不要な時は必ずdeleteしておく。
  for( int i = 0; i < 2; i++ ){
    mM5.yahoo[i].news_scl_set.disp_txt_len = 13; //ディスプレイに表示する半角相当文字数
    mM5.yahoo[i].initScrolle( mM5.yahoo[i].news_font, mM5.yahoo[i].news_scl_set );
  }
  Serial.printf("Free Heap Size = %d\r\n", esp_get_free_heap_size());
}
//*****メッセージ表示設定**************************
void setupMessageWindow(){
  mM5.wifi_msg.m_padding = 4; //pixel単位
  mM5.wifi_msg.m_txt_length = 6; //文字表示数(半角相当)
  //背景色のみ設定 red(0-31), green(0-63), blue(0-31)
  
  mM5.watch.ntp_msg.m_x0 = 56;
  mM5.watch.ntp_msg.m_padding = 4; //pixel単位
  mM5.watch.ntp_msg.m_txt_length = 5; //文字表示数(半角相当)
  //背景色のみ設定 red(0-31), green(0-63), blue(0-31)
  mM5.watch.ntp_msg.m_bg_red = 10;
  mM5.watch.ntp_msg.m_bg_green = 10;
  mM5.watch.ntp_msg.m_bg_blue = 15;
  
  mM5.yahoo[0].weather_msg.m_x0 = 104;
  mM5.yahoo[0].weather_msg.m_padding = 4; //pixel単位
  mM5.yahoo[0].weather_msg.m_txt_length = 4; //文字表示数(半角相当)
  //背景色のみ設定 red(0-31), green(0-63), blue(0-31)
  mM5.yahoo[0].weather_msg.m_bg_red = 10;
  mM5.yahoo[0].weather_msg.m_bg_green = 10;
  mM5.yahoo[0].weather_msg.m_bg_blue = 15;
  
  mM5.yahoo[0].news_msg.m_x0 = 144;
  mM5.yahoo[0].news_msg.m_padding = 4; //pixel単位
  mM5.yahoo[0].news_msg.m_txt_length = 10; //文字表示数(半角相当)
  //背景色のみ設定 red(0-31), green(0-63), blue(0-31)
  mM5.yahoo[0].news_msg.m_bg_red = 10;
  mM5.yahoo[0].news_msg.m_bg_green = 10;
  mM5.yahoo[0].news_msg.m_bg_blue = 15;
  
  mM5.yahoo[1].news_msg.m_x0 = 232;
  mM5.yahoo[1].news_msg.m_padding = 4; //pixel単位
  mM5.yahoo[1].news_msg.m_txt_length = 10; //文字表示数(半角相当)
  //背景色のみ設定 red(0-31), green(0-63), blue(0-31)
  mM5.yahoo[1].news_msg.m_bg_red = 10;
  mM5.yahoo[1].news_msg.m_bg_green = 10;
  mM5.yahoo[1].news_msg.m_bg_blue = 15;
}
//*******WiFiアクセスポイント接続*************
void WiFi_AP_Connect(){
  mM5.wifi_msg.WifiStatus = mM5.wifi_msg.WifiConnecting; //WiFiメッセージウィンドウ設定
  Serial.println();
  Serial.println( F("Connecting Wifi...") );
  Serial.println( ssid );
  
  int16_t wifi_state = WiFi.status();
  Serial.printf( "\r\nWiFi.status = %d\r\n", wifi_state );
  
  if( isWifi_connect_first == true ){
    //WiFiが急に接続できなくなった場合の応急処置
    WiFi.disconnect( true, true ); //WiFi OFF, eraseAP=true
    delay(1000);
    WiFi.begin( ssid, password );
   
    while ( WiFi.status() != WL_CONNECTED ) {
      delay(500);
      Serial.print(".");
    }
    isWifi_connect_first = false;
  }else{
    uint32_t last_time = millis();
    if( wifi_state != WL_CONNECTED ){
      WiFi.begin( ssid, password ); //常時 WiFi ON の場合、ここをコメントアウト
      while ( wifi_state != WL_CONNECTED ) {
        delay(500);
        Serial.print(".");
        if( millis() - last_time > 20000 ) break; //Time OUT
      }
    }
    //マルチタスクでメッセージウィンドウを正しく表示させるための処置
    if( millis() - last_time < 1000 ) delay(2000); 
  }
  wifi_state = WiFi.status();
  Serial.printf("\r\nWiFi.status = %d\r\n", wifi_state);
  if( wifi_state == WL_CONNECTED ){
    Serial.println("");
    Serial.println( "WiFi connected" );
    Serial.println( "IP address: " );
    Serial.println( WiFi.localIP() );
    wifi_connect_last_time = millis();
  }
   
  if( wifi_state == WL_CONNECTED ){
    mM5.wifi_msg.WifiStatus = mM5.wifi_msg.WifiConnected; //WiFiメッセージウィンドウ設定
  }else{
    mM5.wifi_msg.WifiStatus = mM5.wifi_msg.WifiFailed; //WiFiメッセージウィンドウ設定
    Serial.println( F("WiFi AP Not Found") );
  }
}
//***** セレクトボックス表示設定 ***************
void selectUrl(){
  mgo_tec_esp32_bv1::BtnDispSelectBox Sel;
  const uint8_t max_select1 = 2;
  String sel_news_line[ max_select1 ] = { "News 0", "News 1" };
  uint16_t x0 = 96, y0 = 50, x1 = 240; //セレクトボックス位置
  uint8_t max_in_page_num1 = 3; //セレクトボックスの行数設定
  uint8_t default_sel_num = 0; //デフォルトのセレクト
  uint8_t news_num = Sel.dispBtnSelStrDef( mM5.btnA, sel_news_line, x0, y0, x1, default_sel_num, max_in_page_num1, max_select1 );
  
  uint8_t max_in_page_num2 = 5; //セレクトボックスの行数設定
  if( news_num == 0 ){
    url0_num = Sel.dispBtnSelUrlDef( mM5.btnA, sel_news, x0, y0, x1, url0_num, max_in_page_num2, max_select2 );
    isChange_news = true;
    isJJY_through = true;
  }else if( news_num == 1 ){
    url1_num = Sel.dispBtnSelUrlDef( mM5.btnA, sel_news, x0, y0, x1, url1_num, max_in_page_num2, max_select2 );
    isChange_news = true;
    isJJY_through = true;
  }
}
//****************************************
void button_action(){
  mM5.btnA.buttonAction();
  switch( mM5.btnA.ButtonStatus ){
    case mM5.btnA.MomentPress:
      Serial.println("\r\nButton A Moment Press");
      selectUrl();
      break;
    case mM5.btnA.ContPress:
      Serial.println("\r\n-------------Button A Cont Press");
      break;
    default:
      break;
  }
  
  mM5.btnB.buttonAction();
  switch( mM5.btnB.ButtonStatus ){
    case mM5.btnB.MomentPress:
      Serial.println("\r\nButton B Moment Press");
      mM5.yahoo[0].news_scl_set.interval++;
      if( mM5.yahoo[0].news_scl_set.interval> 200 ) mM5.yahoo[0].news_scl_set.interval = 200;
      break;
    case mM5.btnB.ContPress:
      Serial.println("\r\n-------------Button B Cont Press");
      mM5.yahoo[0].news_scl_set.interval--;
      if( mM5.yahoo[0].news_scl_set.interval < 0 ) mM5.yahoo[0].news_scl_set.interval = 0;
      break;
    default:
      break;
  }
  
  mM5.btnC.buttonAction();
  switch( mM5.btnC.ButtonStatus ){
    case mM5.btnC.MomentPress:
      Serial.println("\r\nButton C Moment Press");
      mM5.yahoo[1].news_scl_set.interval++;
      if( mM5.yahoo[1].news_scl_set.interval> 200 ) mM5.yahoo[1].news_scl_set.interval = 200;
      break;
    case mM5.btnC.ContPress:
      Serial.println("\r\n-------------Button C Cont Press");
      mM5.yahoo[1].news_scl_set.interval--;
      if( mM5.yahoo[1].news_scl_set.interval < 0 ) mM5.yahoo[1].news_scl_set.interval = 0;
      break;
    default:
      break;
  }
}

まとめ

そんなこんなで、Yahoo RSSの天気予報が配信終了に伴い、急遽、気象庁ホームページから天気予報を取得する方法に変更した訳ですが、気象庁のホームページもつい最近一新されたこともあり、結果的にこの方法で良かったのかなと思います。
降水確率を取得する方法も分かったので、今後のプログラミングにいろいろ応用できそうです。

ただ、過去の自分のプログラムを修正するのは大変ですね。ハッキリ言って無理です。
たまにコードを改良して欲しいという要望がありますが、それはご理解頂きたいと思います。

さて、最近はラズパイとPythonに夢中だったので、今後時間があったらブログにアップできればと思っていますが、ちょっと無理かも、、、。

というわけで、今回はここまでです。
ではまた…。

コメント

  1. sbtns より:

    こんにちは。私も以前はESP32を使っていましたが、消費電流をほぼ0にできるくらいで、
    あまり使い勝手よいとは思いませんでした。現在はLinuxで遊んでいます。RPIを使っているそうですが、これは価格が安く、実行速度もそこそこでとても良いM/Cです。さらに開発言語にGO言語をサポートしているので、プログラムがとても組みやすいです。もうC/C++には戻れません。

    • mgo-tec mgo-tec より:

      sbtnsさん

      記事をご覧いただき、ありがとうございます。
      自分も最近はESP32を殆ど触っていません。ブログのコメント質問に答える時に触るくらいです。
      ラズパイも最近は放置状態ですが、WEBアプリをPythonで作るにはとても重宝しています。
      GO言語はまったく触ったこと無いです。
      C/C++はM5Stackみたいな組み込みマイコンには良いですが、汎用性はやっぱりラズパイですね~。
      (^^)

  2. @m_c_turbo より:

    JSON形式の予報取得までは出来ましたが、ArduinoJsonで処理出来ず困っていたところ、
    ArduinoJSONに頼らない方法のヒントを頂けました。
    また、classifyWeatherCodeも流用させて頂きました。
    どうもありがとうございました。

    • mgo-tec mgo-tec より:

      @m_c_turboさん

      記事をご覧いただき、ありがとうございます。

      しばらくESP32触ってないので、すっかり忘れていましたが、確かArduino JSON については自分もよく解らなかった記憶があります。
      こんなコードでもお役に立てて、ちょっとウレシイです。
      (^^)

タイトルとURLをコピーしました