11.Wio Terminalで天気(気象庁)更新版


11.Wio Terminalで天気(気象庁)更新版

drk7さんからのデータ日の仕様変更により、スケッチも修正します。

気象庁の天気予報の一部をdrk7さんがjson形式で配信しています。以前"09.CoreInkで天気(気象庁)"では毎日更新だったと思いますが、今は週1回月曜日の更新の様です。△毎日更新に変更されたようです。
配信フォーマットはそのまま使えました。また、"08.CoreInkにjpg表示"の天気マークもそのまま使用します。
充電マークを表示します。("07.Wio Terminalのバッテリー残量表示"参照)
Wi-Fiから時刻を取得します。表示は1分ごとに更新します。("06.Wio TerminalでWi-Fiから時刻取得"参照)
起動時に1度だけ時刻と天気を取得して、内蔵時計で更新するだけなので、動作させ日が変わっても天気は変わりません。

気象庁の天気予報情報をxmlとjsonで配信しているサイト
https://www.drk7.jp/weather/
に各県のデータがあります。これは、気象庁の天気予報(例 神奈川県)
https://www.jma.go.jp/bosai/forecast/#area_type=class20s&area_code=1410000
から独自にメタデータ化してjson形式で配信してくれます。神奈川県の場合、 https://www.drk7.jp/weather/json/14.js
です。ファイル内容を見る場合、
https://www.drk7.jp/weather/xml/14.xml
の方がわかりやすいです。

xmlの内容

https://www.drk7.jp/weather/xml/14.xml
の一部と解説です。

<info date="2022/02/21">←年/月/日
  晴</weather>←概略天気
  <img>http://www.drk7.jp/MT/images/MTWeather/100.gif</img>←天気マーク
  <weather_detail>晴れ</weather_detail>←詳細天気
  <wave>1.5メートル</wave>←波
  <temperature unit="摂氏">
    <range centigrade="max">10</range>←最高気温
    <range centigrade="min">1</range>←最低気温
  </temperature>
  <rainfallchance unit="%">←降水確率
    <period hour="00-06">10</period>←0-6時
    <period hour="06-12">0</period>←6-12時
    <period hour="12-18">0</period>←12-18時
    <period hour="18-24">0</period>←18-24時
  </rainfallchance>
</info>
内容は、
・7日分のデータを収集(7日先まででは無い)
・AM6:00ごろ更新とあるが、その週の月曜からのデータなので、月曜日のみ更新の模様です。
・神奈川県は東部(横浜)と西部(小田原)があります。(気象庁)
・drk7jpweather.callback() で呼出します。

画面内容

今日分のdrk7の天気データは全て表示させました。

(令和年)西暦年/月/日(曜日)

・画面位置(2,5)
・wifiから得た時刻の年月日・曜日を表示します。
・令和=西暦-2018 で算出しています。

充電マーク

・画面位置(190,5)
・充電中は赤色の稲妻マークを表示します。
・写真は、充電率100%でも充電していますが、その後すぐに充電マークは消えました。

電池充電率

・画面位置(204,2)
・充電率を%表示します。(満充電で100%)
・10%以下:赤地、11-25%:黄色地、26%以上:黒地に白文字で表示します。

AM/PMと時分

・画面位置(55,40)
・wifiから得た時刻の時分を表示します。

概略天気

・画面位置(2,68)
・空白を詰める等の変換をします。

天気マーク

・画面位置(40,96)
・"08.CoreInkにjpg表示"で作った天気マーク6個をそのまま使用します。CoreInk用に作ったので白地に黒マークとなり他と色反転で地味になってしまいました。実際の天気マークより極端に少ない種類となっています。

最高・最低気温

・画面位置(145,100)

詳細天気

・画面位置(2,176)
・木~日曜日は無し。
・空白を詰める等の変換をします。

降水確率

・画面位置(2,219)
・月~火曜日は0-6時、6-12時、12-18時、18-24時の各6時間値
・水~日曜日は24時間値
・上記時間内に雨(or雪)の降水が1mm以上の確率を、四捨五入して10%きざみで表示します。降水確率30%は、30%予報を100回発表時、およそ30回は1mm以上の降水があるという意味で、降水量は予報していません。

・画面位置(2,259)
・木~日曜日は表示無し。
・空白を詰める等の変換をします。
・風の情報は表示しません。

備考欄1

・表示位置(下から1行上)
・確認のため天気表示日を表示しています。

備考欄2

・表示位置(最下行)
・参考

スケッチ

"icon6.h"("08.CoreInkにjpg表示"参照)が同ホルダに必要です。
2ケ所の*****は自分のwifiのssidとパスワードを記入してください。
loopでのボタン操作は動作しません。削除可です。

// 天気情報 気象庁 tenkiWio1A.ino修正版
// www.drk7.jpからjsonデータを得る
// 2022/2/20現在 drk7では月曜日から日曜日までを表示している
// 2022/2/24 drk7のデータ日仕様変更により修正
// エラーや情報はシリアルモニタにも表示
#include "icon6.h"            // 天気マーク スケッチと同一ホルダに置く
#include <SparkFunBQ27441.h>  // バッテリー残量IC BQ27441を使用
#include <rpcWiFi.h>          // Wi-Fiを使用
#include <HTTPClient.h>       // httpと通信
#include <WiFiClientSecure.h> // httpsと通信
#include <ArduinoJson.h>      // json型式で抽出
#include <millisDelay.h>      // ノンブロック遅延を使用。システムが停止しないタイマー
#include <RTC_SAMD51.h>       // RTC(real-time clock)を使用
#define LGFX_AUTODETECT       // init時に対応機種を自動認識
#include <LovyanGFX.hpp>      // 描画ライブラリ
const char* ssid = "*****"; // 自分のwifiのSSID
const char* password = "*****"; // wifiのパスワード
const char* weatherURL = "https://www.drk7.jp/weather/json/14.js"; // 神奈川県 天気
const char* region = "東部";             // 横浜
char timeServer[] = "ntp.nict.jp";      // 情報通信研究機構のNTPサーバー 時刻
DynamicJsonDocument weatherInfo(20000); //
WiFiClient client;        // clientを定義
WiFiClientSecure clientS; // clientSを定義
WiFiUDP udp;              // udpを定義
millisDelay updateDelay;  // updateDelayを定義
RTC_SAMD51 rtc;           // rtcを定義
static LGFX lcd;          // LGFXのインスタンスを作成
DateTime now;             // nowを定義
unsigned long devicetime; // 受信時刻
const int NTP_PACKET_SIZE = 48;     // NTPタイムスタンプはメッセージの最初の48バイト
unsigned int localPort = 2390;      // udpローカルポート番号
byte packetBuffer[NTP_PACKET_SIZE]; // パケットを保持するためのバッファ
char daysOfTheWeek[7][4] = {"日", "月", "火", "水", "木", "金", "土"}; // 曜日
const unsigned int BATTERY_CAPACITY = 650; // バッテリー設計容量650mAh
int yBikou = 299; // 備考欄のy
int youbiNo;      // 0:日,・・・,6:土

DynamicJsonDocument getJson() {          //
  DynamicJsonDocument doc(20000);        //
  if ((WiFi.status() == WL_CONNECTED)) { // wifiが接続されていたら
    HTTPClient https;                    // 定義
    Serial.print("天気URL: "); Serial.println(weatherURL); // シリアルモニタに表示
    https.begin(clientS, weatherURL);      //
    Serial.println("httpCodeを送信します。"); // シリアルモニタに表示
    int httpCode = https.GET();             // 登録URLに、GETリクエスト送信
    Serial.printf("httpCode: %d (200台は正常)\n", httpCode); // シリアルモニタに表示
    if (httpCode > 0) {                    // HTTPのリターンコード
      // マイナス エラー
      // 100台 Webサーバがもう少し情報が欲しい、処理に時間がかかる
      // 200台 処理が成功して正常
      // 300台 リクエストの完了に追加処理が必要である状態
      // 400台 Webブラウザからのリクエストに誤りがあって、レスポンスを返せなかった
      // 500台 サーバーがブラウザからのリクエストの処理に失敗
      String jsonString = createJson(https.getString());
      //メッセージのボディ部,確保できなかった場合は空文字列,jsonオブジェクトの作成
      Serial.print("取得: "); Serial.println(jsonString); // シリアルモニタに表示
      Serial.printf("%d文字\n", jsonString.length());     // 取得文字数表示
      deserializeJson(doc, jsonString);//直列化されたデータをDynamicJsonDocumentに変換
    } else {
      Serial.println("エラー HTTPリクエスト");  // シリアルモニタに表示
    }
    https.end();  // TCPコネクションを切断しHTTP通信を終了
  }
  return doc;     //
}

String createJson(String jsonString) { //JSONP→JSON getJsonから1回呼ばれる
  jsonString.replace("drk7jpweather.callback(", "");       //
  return jsonString.substring(0, jsonString.length() - 2); //
}

void printtime() {                           // 時刻画面表示関数
  lcd.setCursor(2, 5); lcd.setTextSize(1.0); // 表示位置文字サイズ
  lcd.printf("(R%d)%d", now.year() - 2018, now.year()); // 年表示
  lcd.printf("/%d/%d(", now.month(), now.day()); // 月日表示
  youbiNo = now.dayOfTheWeek(); //  曜日表示で使用
  lcd.print(daysOfTheWeek[youbiNo]); // 曜日表示 0→日・・・
  lcd.print(")");               // 表示
  /***** AM/PM *****/
  lcd.setCursor(55, 40);  // 表示位置
  int hourP = now.hour(); // 時刻
  if (hourP > 12) {       // 0-12時の場合
    hourP = hourP - 12;   // 12引く
    lcd.print("PM");      // 表示
  } else {                // 13-23時の場合
    lcd.print("AM");      // 表示
  }
  /***** 時分 *****/
  lcd.setCursor(75, 30); lcd.setTextSize(2.0);  // 表示位置,文字サイズ
  lcd.printf(" %2d:%02d", hourP, now.minute()); // 時分表示
  lcd.setTextSize(1.0);                         // 文字サイズ
  lcd.setCursor(0, yBikou); lcd.print("■■■■■■■■■■■■"); // 表示
  delay(500);                // 0.5秒待つ
  lcd.setCursor(0, yBikou); lcd.print("時刻更新        "); // 表示
}

void drawWeather(String infoWeather) {  // 天気表示関数
  DynamicJsonDocument doc(20000);       //
  deserializeJson(doc, infoWeather);    //
  /***** (参考)年月日 *****/
  String myd = doc["date"];                  // 年月日
  lcd.setCursor(0, yBikou - 20);             // 画面表示位置
  lcd.print(myd); lcd.print("       ");  // 表示
  Serial.print("天気: "); Serial.println(myd); // シリアルモニタへ表示
  /***** 最高最低気温 *****/
  String maxTmp = doc["temperature"]["range"][0]["content"]; // 最高気温
  String minTmp = doc["temperature"]["range"][1]["content"]; // 最低気温
  lcd.setCursor(145, 100);                 // 画面表示位置
  lcd.printf("最高:%2d℃", maxTmp.toInt()); // 画面表示
  lcd.setCursor(145, 120);                 // 画面表示位置
  lcd.printf("最低:%2d℃", minTmp.toInt()); // 画面表示
  Serial.printf("最高: %d℃/最低: %d℃\n", maxTmp.toInt(), minTmp.toInt()); // シリアルモニタへ表示
  /***** 概略天気 *****/
  String weather = doc["weather"];         // 概略天気
  weather = mojiHenkan(weather);           // 文字変換
  lcd.setCursor(2, 68); lcd.print(weather); Serial.println(weather); // 表示
  /***** 天気マーク *****/
  int x = 40, y = 96, W = 72, H = 72;           // 天気マークの表示位置と大きさ
  if (weather.indexOf("雨") != -1) {            // (雨なら)
    if (weather.indexOf("曇") != -1) {          // 雨&曇りなら
      lcd.pushImage(x, y, W, H, imgKumoriAme);  // 曇り雨 (x,y,W,H,imgデータ)
    } else {                                    // 雨で、曇りが無かったら
      lcd.pushImage(x, y, W, H, imgAme);        // 雨
    }
  } else if (weather.indexOf("晴") != -1) {     // (晴れで、雨が無かったら)
    if (weather.indexOf("曇") != -1) {          // 晴れ&曇りで、雨が無かったら
      lcd.pushImage(x, y, W, H, imgHareKumori); // 晴れ曇
    } else {                                    // 晴れで、雨&曇りが無かったら
      lcd.pushImage(x, y, W, H, imgHare);       // 晴れ
    }
  } else if (weather.indexOf("雪") != -1) {     // 雪で、雨&晴れが無かったら
    lcd.pushImage(x, y, W, H, imgYuki);         // 雪
  } else if (weather.indexOf("曇") != -1) {     // 曇りで、雨&晴&雪が無かったら
    lcd.pushImage(x, y, W, H, imgKumori);       // 曇り
  }
  /***** 詳細天気 *****/
  String weather_d = doc["weather_detail"];     // 詳細天気
  weather_d = mojiHenkan(weather_d);            // 文字変換
  lcd.setCursor(2, 176); lcd.print(weather_d);  // 画面表示
  Serial.println(weather_d);                    // シリアルモニタへ表示
  /***** 降水確率 *****/
  String rain0_6 = doc["rainfallchance"]["period"][0]["content"];  // 降水確率0-6時
  String rain6_12 = doc["rainfallchance"]["period"][1]["content"]; // 6-12時
  String rain12_18 = doc["rainfallchance"]["period"][2]["content"];// 12-18時
  String rain18_24 = doc["rainfallchance"]["period"][3]["content"];// 18-24時
  lcd.setCursor(2, 219);   // 画面表示位置
  lcd.printf("降水 0- 6:%2d%%  6-12:%2d%%", rain0_6.toInt(), rain6_12.toInt()); // 表示
  lcd.setCursor(2, 239); // 画面表示位置
  lcd.printf("  12-18:%2d%% 18-24:%2d%%", rain12_18.toInt(), rain18_24.toInt()); // 表示
  Serial.print("降水確率: "); Serial.print(rain0_6); // シリアルモニタへ表示
  Serial.print(" "); Serial.print(rain6_12);       // シリアルモニタへ表示
  Serial.print(" "); Serial.print(rain12_18);      // シリアルモニタへ表示
  Serial.print(" "); Serial.println(rain18_24);    // シリアルモニタへ表示
  /***** 波 *****/
  String wave = doc["wave"];                       // 波
  wave = mojiHenkan(wave);                         // 文字変換
  lcd.setCursor(2, 259); lcd.print("波: " + wave); // 表示
  Serial.print("波: "); Serial.println(wave);      // シリアルモニタへ表示
}

unsigned long sendNTPpacket(const char* address) {  // 指定アドレスにNTP要求送信関数
  for (int i = 0; i < NTP_PACKET_SIZE; ++i) {       // 0-47の48バイトについて
    packetBuffer[i] = 0;         // バッファ内の全バイトを0
  }                              // 以下にNTP要求を形成するための値を設定
  packetBuffer[0] = 0b11100011;  // うるう年=同期無し,Ver4(通常),Mode=Client
  packetBuffer[1] = 0;           // 地層=原子時計???
  packetBuffer[2] = 6;           // 送出間隔 2^6=64秒
  packetBuffer[3] = 0xEC;        // 精度
  //                             // packetBuffer[4]-[7]:ルート遅延は8バイトのゼロ
  //                             // packetBuffer[8]-[11]:ルート拡散は8バイトのゼロ
  packetBuffer[12] = 49;         // [12]-[15]はIPアドレス[49,78,49,52]
  packetBuffer[13] = 0x4E;       // IPアドレス
  packetBuffer[14] = 49;         // IPアドレス
  packetBuffer[15] = 52;         // IPアドレス
  udp.beginPacket(address, 123);           // addressのポート123へ書込開始
  udp.write(packetBuffer, NTP_PACKET_SIZE);// udpデータを書出す(送信バッファ,メッセージ長)
  udp.endPacket();                         // udpデータを書込んだ後に送信
}

unsigned long getNTPtime() {             // 時刻受信関数 UNIXの日本時間を返す
  if (WiFi.status() == WL_CONNECTED) {   // WiFiネットに接続したら
    udp.begin(WiFi.localIP(), localPort);// udp(転送バッファ)を初期化 (IPアドレス,ポート番号)
    sendNTPpacket(timeServer);           // NTP要求送信関数へ
    delay(1000);                         // 1秒待つ
    if (udp.parsePacket()) {             // 次に利用可能な着信パケットの処理を開始し、
      // パケットの存在を確認。戻り値:パケットのサイズ(バイト)。udp.read()の前に必要
      //lcd.setCursor(0, yBikou); lcd.print("時刻受信           "); // 表示
      udp.read(packetBuffer, NTP_PACKET_SIZE);  // パケットをバッファに読込む
      // (格納バッファ(char or 配列byte), 読取バイト数(int))
      // NTPサーバのパケットの送信時刻(1900/1/1 0:0:0からの秒数)は、41-48バイト目で整数部は41-44バイト目
      // 1ワード=2バイト=16ビット
      unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);// 値をwordデータ型に変換
      unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); // (上位(左)バイト,下位(右)バイト)
      unsigned long secsSince1900 = highWord << 16 | lowWord; // 16ビット左にシフトしビットごとにORする
      const unsigned long seventyYears = 2208988800;      // 70年=(70x365+閏日17)x24x60x60秒
      unsigned long epoch = secsSince1900 - seventyYears; // UNIX=UTC-70年
      long tzOffset = 32400;                              // 9H=9x60x60=32400S
      unsigned long adjustedTime;                         // JST(日本時間)
      return adjustedTime = epoch + tzOffset;             // JST=UTC+9H
    } else {       // 利用可能なパケットがない場合
      udp.stop();  // サーバーから切断し、udpセッション中に使用されていたリソースを解放
      return 0;    // 0は失敗、利用可能なパケットがない
    }
    udp.stop();    //ntp timeを頻繁に呼び出さないで、リソースのリリースを停止
  } else {
    return 0;      // 0は失敗、ネットに接続できない
  }
}

void printCharge() {                        // 充電マーク表示関数
  int current = lipo.current(AVG);          // 平均電流取得(mA) +充電,-放電
  //Serial.printf("充電%dmA\n", current);    // 電流値
  int x = 190, y = 5, w = 4, h = 8, ww = 2; // W=幅/2,h=高さ/2,w=重なり/2
  if (current > 0) {                        // 充電中なら
    lcd.fillTriangle(x , y + h + ww, x + w + ww, y, x + w + ww, y + h + ww, TFT_RED); // 三角形
    lcd.fillTriangle(x + w - ww , y + h - ww, x + w - ww, y + h + h, x + w + w, y + h - ww, TFT_RED);
  } else {                                  // 放電中なら
    lcd.fillRect(x , y, w * 2 + 1, h * 2 + 1, TFT_BLACK);  // 表示を消す
  }
}

void printBT() {                             // バッテリー状態を読取り&表示関数
  int x0 = 204, y0 = 2, W0 = 32, H0 = 22, W1 = 3;  // 電池マーク大きさ 0:本体,1:+極
  lcd.fillRect(x0, y0, W0, H0, TFT_SILVER);  // 電池本体 四角形(x0,y0,W,H[,塗りつぶし色])
  lcd.fillRect(x0 + W0, y0 + 3, W1, H0 - 6, TFT_SILVER);  // 電池プラス極
  unsigned int soc = lipo.soc();             // 充電率を取得(%) 満充電100% 完全放電0%
  if (soc <= 10) {                           // 0-10%の時
    lcd.setTextColor(TFT_BLACK, TFT_RED);    // 赤地に黒文字
  } else if (soc <= 25) {                    // 11-25%の時
    lcd.setTextColor(TFT_BLACK, TFT_YELLOW); // 黄地に黒文字
  } else {                                   // 26-100%の時
    lcd.setTextColor(TFT_WHITE, TFT_BLACK);  // 黒地に白文字
  }
  lcd.setCursor(x0 + 1, y0 + 1); lcd.printf("%3d", soc); // 充電率
  lcd.setTextColor(TFT_GREEN, TFT_BLACK);    // 文字色を戻す
}

void setBQ27441() {                   // バッテリー残量IC(BQ27441-G1A)初期設定
  if (!lipo.begin()) {                // I2Cを初期化、通信確認出来ない時
    lcd.setTextColor(TFT_RED);        // 赤文字
    lcd.setCursor(0, yBikou); lcd.println("バッテリー残量ICと通信できません"); // 表示
    delay(500);                       // 0.5秒待つ
    lcd.setCursor(0, yBikou); lcd.println("バッテリーを接続してください");     // 表示
    lcd.setTextColor(TFT_GREEN, TFT_BLACK);                              // 文字色を戻す
    while (1);                        // 永久ループ
  }                                   // 通信出来たら
  lcd.setCursor(0, yBikou); lcd.println("バッテリー残量ICに接続");           // 表示
  lipo.setCapacity(BATTERY_CAPACITY); // バッテリー設計容量650mAhに設定
}

void setGamen() {                         // 画面初期設定関数
  lcd.init();                             // LCD初期化
  lcd.setBrightness(64);                  // バックライト輝度(暗0-255明)
  lcd.setRotation(2);                     // LCD表示向き
  lcd.setFont(&lgfxJapanGothic_20);       // ゴシック固定長 縦ドット(16,20,24,28,32)
  //lcd.setTextScroll(true);              // 画面下端に到達後スクロール
  //lcd.setScrollRect(0, 0, lcd.width(), lcd.height());//スクロール範囲(X,Y,W,H)
  lcd.setTextSize(1.0);                   // 文字サイズ(横縦幅倍数)
  lcd.setTextColor(TFT_GREEN, TFT_BLACK); // (文字色,背景色)
  //lcd.setCursor(0, 0);                  // 画面左上から開始
  lcd.fillScreen(TFT_BLACK);              // 画面クリア
}

void setWifi() {                  // Wifi設定関数
  WiFi.mode(WIFI_STA);//++++++++++++++++++++
  lcd.setCursor(0, yBikou - 20); lcd.print("接続:" + String(ssid)); // 接続するSSID表示
  WiFi.disconnect();              // Wi-Fiシールドをネットから切断
  lcd.setCursor(0, yBikou); lcd.print("Wi-Fi接続待ち。        "); // 表示
  WiFi.begin(ssid, password);     // WiFiライブラリのネット設定を初期化し、接続開始
  while (WiFi.status() != WL_CONNECTED) {  // WiFiネットに接続していなかったら
    WiFi.begin(ssid, password);   // WiFiライブラリのネット設定を初期化し、接続開始
    delay(500);                   // 0.5秒待つ
  }
  long rssi = WiFi.RSSI();                                   // 受信信号強度を取得
  lcd.setCursor(0, yBikou); lcd.printf("受信:%3lddBm", rssi); // 受信信号強度を表示
  Serial.printf("\n受信強度:%4lddBm\n", rssi);                 //
  if (rssi <= -81) {         // -81dBm以下の時
    lcd.print("(使えない)");   // 表示
  } else if (rssi <= -71) {  // -71~-80dBmの時
    lcd.print("(弱い)");      // 表示
  } else if (rssi <= -68) {  // -68~-70dBmの時
    lcd.print("(強い)");      // 表示
  } else if (rssi <= -31) {  // -31~-67dBmの時
    lcd.print("(とても強い)"); // 表示
  } else {                   // 0~-30dBmの時
    lcd.print("(非常に強い)"); // 表示
  }
}

void setButton() { // ボタン設定関数
  pinMode(WIO_KEY_A, INPUT_PULLUP);    // 内側ボタンをプルアップ抵抗入りデジタル入力に
  pinMode(WIO_KEY_B, INPUT_PULLUP);    // 中間ボタンをプルアップ抵抗入りデジタル入力に
  pinMode(WIO_KEY_C, INPUT_PULLUP);    // 外側ボタンをプルアップ抵抗入りデジタル入力に
  pinMode(WIO_5S_UP, INPUT_PULLUP);    // 5wey上ボタンをプルアップ抵抗入りデジタル入力に
  pinMode(WIO_5S_DOWN, INPUT_PULLUP);  // 5wey下ボタンをプルアップ抵抗入りデジタル入力に
  pinMode(WIO_5S_LEFT, INPUT_PULLUP);  // 5wey左ボタンをプルアップ抵抗入りデジタル入力に
  pinMode(WIO_5S_RIGHT, INPUT_PULLUP); // 5wey右ボタンをプルアップ抵抗入りデジタル入力に
  pinMode(WIO_5S_PRESS, INPUT_PULLUP); // 5wey押ボタンをプルアップ抵抗入りデジタル入力に
}

String mojiHenkan(String moji) { // 文字変換関数
  moji.replace(" ", "");         // 全角空白を削除
  moji.replace("メートル", "m");   // 波で使用
  moji.replace("くもり", "曇");    // 天気で使用
  moji.replace("0", "0");  // 半角変換 波で使用
  moji.replace("1", "1");  //
  moji.replace("2", "2");  //
  moji.replace("3", "3");  //
  moji.replace("4", "4");  //
  moji.replace("5", "5");  //
  moji.replace("6", "6");  //
  moji.replace("7", "7");  //
  moji.replace("8", "8");  //
  moji.replace("9", "9");  //
  moji.replace(".", ".");  //
  moji.replace("-", "/");   //
  moji.replace("T", " ");   //
  moji.replace("\n", "");   // 予報文で使用
  return moji;
}

void setup() {
  setGamen();     // 画面設定の関数へ
  setBQ27441();   // バッテリー残量表示関数へ
  printCharge();  // 充電マーク表示関数へ
  printBT();      // 残量等表示関数へ
  setButton();    // ボタン設定の関数へ
  setWifi();      // wifi設定の関数へ
  devicetime = getNTPtime(); // 時刻受信関数へ
  if (devicetime == 0) {     // 時刻取得出来ない場合
    lcd.setCursor(0, yBikou); lcd.print("時刻を受信できません。 "); // 表示
  }
  rtc.begin();                      // 内部RTCを初期化
  rtc.adjust(DateTime(devicetime)); // 受信時刻に更新
  now = rtc.now();                  // 更新後のWioのRTC時刻
  printtime();                      // 時刻画面表示関数へ
  updateDelay.start(60 * 1000);     // ノンブロック遅延開始 遅延1分
  weatherInfo = getJson();          // 関数へ 止まる
  WiFi.disconnect(true);            // ネットから切断
  WiFi.mode(WIFI_OFF);              // wifiの動作モード
  Serial.print("wifi offか=");      // シリアルモニタに表示
  Serial.println(WiFi.status());    // wifiの接続状態
  // 0(WiFi.beginが呼出され3or4になるまで),1(使用可能なSSIDがない)
  // 2(スキャン完了),3(WiFi接続),4(すべての試行で接続失敗)
  // 5(接続が失われた),255(WiFiシールドがない)
  String today = weatherInfo["pref"]["area"][region]["info"][0];
  //                         神奈川県   area = 東部     info =今日
  drawWeather(today);               // 天気表示関数へ
}

void loop() {                                    // 現在ボタンは無効
  if (digitalRead(WIO_KEY_A) == LOW) {           // 内側ボタンを押したら
    Serial.println("内側ボタンを押しました ");       // シリアルモニタに表示
  } else if (digitalRead(WIO_KEY_B) == LOW) {    // 中間ボタンを押したら
    Serial.println("中間ボタンを押しました ");       // シリアルモニタに表示
  } else if (digitalRead(WIO_KEY_C) == LOW) {    // 外側ボタンを押したら
    Serial.println("外側ボタンを押しました ");       // シリアルモニタに表示
  } else if (digitalRead(WIO_5S_UP) == LOW) {    // 5weyボタンを上へ押すと
    Serial.println("5wayを上に押しました ");        // シリアルモニタに表示
  } else if (digitalRead(WIO_5S_DOWN) == LOW) {  // 5Wayボタンを下へ押すと
    Serial.println("5wayを下に押しました ");        // シリアルモニタに表示
  } else if (digitalRead(WIO_5S_LEFT) == LOW) {  // 5weyボタンを左へ押すと
    Serial.println("5wayを左に押しました ");        // シリアルモニタに表示
  } else if (digitalRead(WIO_5S_RIGHT) == LOW) { // 5weyボタンを右へ押すと
    Serial.println("5wayを右に押しました ");        // シリアルモニタに表示
  } else if (digitalRead(WIO_5S_PRESS) == LOW) { // 5weyボタンを押し込むと
    Serial.println("5wayを押し込みました ");        // シリアルモニタに表示
  }
  printCharge();                      // 充電マーク表示関数へ
  delay(200);                         // 0.2秒待つ
  if (updateDelay.justFinished()) {   // ノンブロック遅延が終了したら 1分ごとに更新
    updateDelay.repeat();             // ノンブロック遅延を再開 定期更新
    now = rtc.now();                  // RTC時刻
    printtime();                      // 時刻画面表示関数へ
    printBT();                        // 残量等表示関数へ
  }
}
* フラッシュメモリの77%を使用。