ESP-WROOM-02でTFTに漢字を表示する

週間天気予報 その6


こちらでRTCのユーザエリアとリセット動作を利用した時 刻合わせを紹介しています。
また、こちらではESP8266の内蔵タイマーの精度を確 認しています。
そこで、時刻合わせの機能を組み込んで、livedoorの Weather HacksのRSSフィードのページから必要な情報を抜き出して、
1日1回、定時に最新の情報に書き換えることにします。


NTPサーバーから現在時刻を取得すると、一旦再起動してから時刻を設定しますので、若干の誤差はあります。
表示スタイル、表示更新時刻、表示消去時刻は適当に変更してください。
/*
 *  週間天気予報を表示する
 *  毎日決まった時間に表示を更新、消去
 * 
 *  ESP.reset()/ESP.resart()ではRTC User Memoryがクリアされない。
 *  この性質を利用して時刻設定を行う。
 *  初回起動時のみNTPに時刻問い合わせ。
 *  RTC User Memoryに現在日時を設定し強制Reset。
 *  復帰時にRTC User Memoryから時刻を取り出して設定。
 * 
 *  int ON[] = {7, 0}; // 表示更新時刻
 *  int OFF[] = {23, 35}; // 表示消去時刻
 * 
 */

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

#include <Adafruit_GFX.h>    // https://github.com/adafruit/Adafruit-GFX-Library
#include <Fontx.h>              // https://github.com/h-nari/Fontx
#include <Humblesoft_GFX.h>     // https://github.com/h-nari/Humblesoft_GFX
#include <Humblesoft_ILI9341.h> // https://github.com/h-nari/Humblesoft_ILI9341

#include <WiFiUdp.h>
#include <TimeLib.h> // https://github.com/PaulStoffregen/Time
#include <time.h>

#define TAGMAX 10
#define LENMAX 128

#define LAYOUT 1 // 横書き 小さい文字  7日分
//#define LAYOUT 2 // 横書き 大きい文字 2日分
//#define LAYOUT 3 // 縦書き 小さい文字  7日分
//#define LAYOUT 4 // 縦書き 大きい文字  3日分

IMPORT_BIN("/fontx/ILGH16XB.FNT", ILH16XB); //16ドット半角ゴシックフォント
IMPORT_BIN("/fontx/ILGZ16XB.FNT", ILZ16XB); //16ドット全角ゴシックフォント
//IMPORT_BIN("/fontx/ILMH16XB.FNT", ILH16XB); //16ドット半角明朝フォント
//IMPORT_BIN("/fontx/ILMZ16XB.FNT", ILZ16XB); //16ドット全角明朝フォント
extern const uint8_t ILH16XB[], ILZ16XB[];

Humblesoft_ILI9341 tft = Humblesoft_ILI9341();
RomFontx fontx(ILH16XB,ILZ16XB);

const char* ssid = "Your SSID";
const char* password = "Your Password";

HTTPClient http;

//RTC memory(512Byte)の定義
struct {
  uint32_t crc32; // CRC(4 Byte)
  uint8_t data[472]; // User Data(512-4-36)
  struct tm compile; // Compile Date & time(36 Byte)
} rtcData;

// UDPローカルポート番号
unsigned int localPort = 2390;
// NTPタイムサーバIPアドレス(ntp.nict.jp NTP server)
IPAddress timeServer(133, 243, 238, 164);
// NTPパケットバッファサイズ
const int NTP_PACKET_SIZE= 48;
// NTP送受信用パケットバッファ
byte packetBuffer[NTP_PACKET_SIZE];
// 最後に時刻を確認した時間(ミリ秒)
unsigned long lastCheckTime = 0;
// コンパイルした日時
struct tm ctm;

// Udpクラス
WiFiUDP udp;

struct TAG {
  uint8_t StartName;
  uint8_t StartValue;
  uint8_t TagPos;
  unsigned char TagName[LENMAX];
  unsigned char TagCurrent[LENMAX];
  uint8_t ValueCount;
  unsigned char TagValue[TAGMAX][LENMAX];
};

struct TAG description;
struct TAG title;

int ON[] = {7, 0}; // 表示更新時刻
int OFF[] = {23, 35}; // 表示消去時刻

void httpPrint(int len, uint8_t *buff) {
  char ch[2];
  ch[1] = 0;
  for(int i=0; i<len; i++) {
    ch[0] = buff[i];
    if (isAlphaNumeric(ch[0])) {
      Serial.printf("%c",ch[0]);
    } else if (strstr("\"!#$%&'()=-<> ;:/,.@[]+*",ch)) {
      Serial.printf("%c",ch[0]);
    } else if (ch[0] == 0x0a) {
      Serial.printf("\n");
    } else {
//      Serial.printf("0x%02x",ch[0]);
      Serial.printf("*");
    }
  }
}

void initTag(struct TAG* hoge,char* name) {
   hoge->StartName=0;
   hoge->StartValue=0;
   hoge->TagPos=0;
   strcpy((char *)hoge->TagName,name);
   memset(hoge->TagCurrent,0,sizeof(hoge->TagCurrent));
   hoge->ValueCount=0;
   memset(hoge->TagValue,0,sizeof(hoge->TagValue));
}

void addTagCurrent(struct TAG* hoge, char ch) {
   int pos;
   pos=hoge->TagPos;
   if (pos == LENMAX-1) return;
   hoge->TagCurrent[pos]=ch;
   hoge->TagCurrent[++pos]=0;
   (hoge->TagPos)++;
}

void startTag(struct TAG* hoge) {
   hoge->StartName=1;
   hoge->TagPos=0;
   if (hoge->StartValue) {
     hoge->StartValue=0;
     (hoge->ValueCount)++;
   }
}

void endTag(struct TAG* hoge) {
   hoge->StartName=0;
   hoge->TagPos=0;
   if (strcmp ((char *)hoge->TagName, (char *)hoge->TagCurrent) == 0) {
     if (hoge->ValueCount == TAGMAX) return;
     hoge->StartValue=1;
   }
}

void addTagValue(struct TAG* hoge, char ch) {
   int pos;
   int count;
   pos=hoge->TagPos;
   count=hoge->ValueCount;
   if (pos == LENMAX-1) return;
   hoge->TagValue[count][pos]=ch;
   hoge->TagValue[count][++pos]=0;
   (hoge->TagPos)++;
}

void dumpTag(struct TAG hoge) {
   int i;
   Serial.printf("\nTagName=%s\n",hoge.TagName);
   Serial.printf("TagCurrent=%s\n",hoge.TagCurrent);
   Serial.printf("ValueCount=%d\n",hoge.ValueCount);
   for(i=0;i<hoge.ValueCount;i++){
     Serial.printf("TagValue[%d]=%s\n",i,hoge.TagValue[i]);
   }
}


void httpParse(int len, uint8_t *buff, struct TAG *hoge) {
   int i;
   char ch;
   for(i=0;i<len;i++) {
     ch=buff[i];
     if (ch == '<') {
       startTag(hoge);
     } else if (ch == '>') {
       endTag(hoge);
     } else {
       if (hoge->StartName) addTagCurrent(hoge,ch);
       if (hoge->StartValue) addTagValue(hoge,ch);
     }
   }
}

/*
 * 指定した漢字で文字列を分解する
 */
void splitUTF8(char *in, char* utf8, char* out1, char* out2) {
  int pos=0;
  int len=strlen(in);
  char wk[4]={0};
//  Serial.printf("len=%d\n",len);
  out1[pos]=0;
  out2[0]=0;
  for(int i=0;i<len;) {
//    Serial.printf("%d>%02x\n",i,in[i]);
    if (in[i] < 0x80) {
      out1[pos++]=in[i];
      out1[pos]=0;
      i++;
    } else {
      wk[0]=in[i];
      wk[1]=in[i+1];
      wk[2]=in[i+2];
//      Serial.printf("wk=%02x%02x%02x utf8=%02x%02x%02x\n",
//      wk[0],wk[1],wk[2],utf8[0],utf8[1],utf8[2]);
      if (strcmp(wk,utf8) == 0) {
//        Serial.printf("len-i=%d\n",len-i);
        memcpy(out2,&in[i],len-i);
        out2[len-i]=0;
        return;
      } else {
        out1[pos++]=wk[0];
        out1[pos++]=wk[1];
        out1[pos++]=wk[2];
        out1[pos]=0;
      }
//      delay(10);
      i=i+3;
    }
  }
}

/*
 * 指定した漢字を削除する
 */
void removeUTF8(char *in, char* utf8, char* out1) {
  int pos=0;
  int len=strlen(in);
  char wk[4]={0};
//  Serial.printf("len=%d\n",len);
  out1[0]=0;
  for(int i=0;i<len;) {
//    Serial.printf("%d>%02x\n",i,in[i]);
    if (in[i] < 0x80) {
      out1[pos++]=in[i];
      out1[pos]=0;
      i++;
    } else {
      wk[0]=in[i];
      wk[1]=in[i+1];
      wk[2]=in[i+2];
//      Serial.printf("wk=%02x%02x%02x utf8=%02x%02x%02x\n",
//      wk[0],wk[1],wk[2],utf8[0],utf8[1],utf8[2]);
      if (strcmp(wk,utf8) != 0) {
        out1[pos++]=wk[0];
        out1[pos++]=wk[1];
        out1[pos++]=wk[2];
        out1[pos]=0;
      }
//      delay(10);
      i=i+3;
    }
  }
}

/*
 * 指定した文字で文字列を分解する
 */
void splitASCII(char *in, char ascii, char* out1, char* out2) {
  int pos=0;
  int len=strlen(in);
//  Serial.printf("len=%d\n",len);
  out1[pos]=0;
  out2[0]=0;
  for(int i=0;i<len;) {
//    Serial.printf("%d>%02x %02x\n",i,in[i],ascii);
    if (in[i] < 0x80) {
      if (in[i] == ascii) {
        memcpy(out2,&in[i],len-i);
        out2[len-i]=0;
        return;
      }
      out1[pos++]=in[i];
      out1[pos]=0;
      i++;
    } else {
      out1[pos++]=in[i];
      out1[pos++]=in[i+1];
      out1[pos++]=in[i+2];
      out1[pos]=0;
      i=i+3;
    }
  }
}

void displayHTML(int layout) {
  int httpCode;

  initTag(&description,"description");
  initTag(&title,"title");

  Serial.printf("[HTTP]begin..\n");
  http.begin("weather.livedoor.com", 80, "/forecast/rss/area/230010.xml");

  httpCode = http.GET();
  if(httpCode > 0){
    Serial.printf("[HTTP] GET ... code: %d\n",httpCode);
    if(httpCode == HTTP_CODE_OK) {
      Serial.printf("[HTTP] OK\n");
      int len = http.getSize();
      uint8_t buff[128] = {0};
      WiFiClient *stream = http.getStreamPtr();
      while(http.connected() && (len > 0 || len == -1)){
        size_t size = stream->available();
        if(size){
          if(size > sizeof buff) size = sizeof buff;
          int c = stream->readBytes(buff,size);
//          httpPrint(c,buff);
          httpParse(c,buff,&description);
          httpParse(c,buff,&title);
        }
        delay(1);
      }
      Serial.printf("[HTTP] Disconnected\n");
//      dumpTag(description);
//      dumpTag(title);

      char address1[32];
      char address2[32];
      char tdate[32];
      char weater[32];
      char temp[64];
      char temph[32];
      char templ[32];
      char in[LENMAX];
      char out[LENMAX];

      tft.setCursor(0, 0);
      tft.setFont(&fontx);
      tft.fillScreen("BLACK");

      int loopMax;
//#if defined(LAYOUT) && LAYOUT == 1
if (layout == 1) {
      tft.setTextSize(1);
      tft.setRotation(3);
      loopMax=description.ValueCount;
}
//#endif

//#if defined(LAYOUT) && LAYOUT == 2
if (layout == 2) {
      tft.setTextSize(2);
      tft.setRotation(3);
      loopMax=4;
}
//#endif

//#if defined(LAYOUT) && LAYOUT == 3
if (layout == 3) {
      tft.setTextSize(1);
      tft.setRotation(4);
      loopMax=description.ValueCount;
}
//#endif

//#if defined(LAYOUT) && LAYOUT == 4
if (layout == 4) {
      tft.setTextSize(2);
      tft.setRotation(4);
      loopMax=5;
}
//#endif

//#if defined(LAYOUT) && LAYOUT > 2
if (layout > 2) {
      strcpy(in,(char *)title.TagValue[0]);
      splitASCII(in,'-',address1,out);
      strcpy(in,&out[1]);
      splitASCII(in,'-',address2,out);
      tft.printf("%s%s\n\n",address1,address2);
}
//#endif

      for(int i=2;i<loopMax;i++) {
//        tft.printf("%s\n",description.TagValue[i]);
        strcpy(in,(char *)description.TagValue[i]);
        splitUTF8(in,"の",tdate,out);
        tft.setTextColor("WHITE");
        if (strstr(tdate,"(土)") != 0) tft.setTextColor("CYAN");
        if (strstr(tdate,"(日)") != 0) tft.setTextColor("RED");
        tft.printf("%s",tdate);
//#if defined(LAYOUT) && LAYOUT == 2
if (layout == 2) {
        tft.printf("\n");
//#endif
}
//#if defined(LAYOUT) && LAYOUT == 4
if (layout == 4) {
        tft.printf("\n");
//#endif
}
        strcpy(in,out);
        splitUTF8(in,"は",weater,out);
        strcpy(in,&out[3]);
        splitUTF8(in,"、",weater,out);
        tft.setTextColor("WHITE");
//#if defined(LAYOUT) && LAYOUT == 2
if (layout == 2) {
        tft.printf(" %s  ",weater);
//#else
} else {
        tft.printf(" %s\n",weater);
//#endif
}


//#if defined(LAYOUT) && LAYOUT < 4
if (layout < 4) {
        strcpy(in,&out[3]);
        splitUTF8(in,"で",temp,out);
        strcpy(in,temp);
        splitASCII(in,' ',temph,templ);
        strcpy(in,temph);
        removeUTF8(in,"は",temph);
        strcpy(in,templ);
        removeUTF8(in,"は",templ);
        tft.printf("%s%s\n",temph,templ);
}
//#endif
        delay(10);
      }

    } else {
      Serial.printf("[HTTP] Server response Not OK\n");
    }
  } else {
    Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
  }
  http.end();
}

// Calculate CRC
uint32_t calculateCRC32(const uint8_t *data, size_t length)
{
  uint32_t crc = 0xffffffff;
  while (length--) {
    uint8_t c = *data++;
    for (uint32_t i = 0x80; i > 0; i >>= 1) {
      bool bit = crc & 0x80000000;
      if (c & i) {
        bit = !bit;
      }
      crc <<= 1;
      if (bit) {
        crc ^= 0x04c11db7;
      }
    }
  }
  return crc;
}

// Print RTC memory
void printMemory(int sz) {
  char buf[3];
//  for (int i = 0; i < sizeof(rtcData); i++) {
  for (int i = 0; i < sz; i++) {
    sprintf(buf, "%02X", rtcData.data[i]);
    Serial.print(buf);
    if ((i + 1) % 32 == 0) {
      Serial.println();
    }
    else {
      Serial.print(" ");
    }
  }
  Serial.println();
}

// Connect AP
void setup_wifi() {
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Wait for WiFi...");
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
  Serial.println("sending NTP packet...");
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  udp.beginPacket(address, 123); //NTP requests are to port 123
  udp.write(packetBuffer, NTP_PACKET_SIZE);
  udp.endPacket();
}

/*
  * UnixTimeをYY/MM/DD hh:mm:ssで表示する
  */
void showTime(char * title, time_t timet) {
   Serial.print(title);
   Serial.print(":");
   Serial.print(year(timet));
   Serial.print("/");
   Serial.print(month(timet));
   Serial.print("/");
   Serial.print(day(timet));
   Serial.print(" ");
   Serial.print(hour(timet));
   Serial.print(":");
   Serial.print(minute(timet));
   Serial.print(":");
   Serial.println(second(timet));
}

/*
  * 現在日時をYY/MM/DD hh:mm:ssで表示する
  */
void showNow(char * title) {
   Serial.print(title);
   Serial.print(":");
   Serial.print(year());
   Serial.print("/");
   Serial.print(month());
   Serial.print("/");
   Serial.print(day());
   Serial.print(" ");
   Serial.print(hour());
   Serial.print(":");
   Serial.print(minute());
   Serial.print(":");
   Serial.println(second());
}

const char *monthName[12] = {
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};



// dow() 曜日を示す数値を戻す[0-Sunday, 1-Monday etc.]
int dow(int y, int m, int d) {
  static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
//  y -= m < 3;
  if (m < 3) y--;
  return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}

// コンパイル時間をtm構造体に格納
bool buildTime(const char *str, struct tm *tm)
{
  int Hour, Min, Sec;

  if (sscanf(str, "%d:%d:%d", &Hour, &Min, &Sec) != 3) return false;
  tm->tm_hour = Hour;
  tm->tm_min = Min;
  tm->tm_sec = Sec;
  return true;
}

// コンパイル年月日をtm構造体に格納
bool buildDate(const char *str, struct tm *tm)
{
  char Month[12];
  int Day, Year;
  uint8_t monthIndex;

  if (sscanf(str, "%s %d %d", Month, &Day, &Year) != 3) return false;
  for (monthIndex = 0; monthIndex < 12; monthIndex++) {
    if (strcmp(Month, monthName[monthIndex]) == 0) break;
  }
  if (monthIndex >= 12) return false;
  tm->tm_mday = Day;
  tm->tm_mon = monthIndex + 1;
  tm->tm_year = Year-1900;
  tm->tm_wday = dow(Year, tm->tm_mon, tm->tm_mday);
  tm->tm_yday = 0;
  tm->tm_isdst = 0;
  return true;
}

// tm構造体の表示
void displayTM(char *title, struct tm hoge) {
  Serial.print(title);
  Serial.print(" year=");
  Serial.print(hoge.tm_year);
  Serial.print(" mon=");
  Serial.print(hoge.tm_mon);
  Serial.print(" mday=");
  Serial.print(hoge.tm_mday);
  Serial.print(" hour=");
  Serial.print(hoge.tm_hour);
  Serial.print(" min=");
  Serial.print(hoge.tm_min);
  Serial.print(" sec=");
  Serial.print(hoge.tm_sec);
  Serial.print(" wday=");
  Serial.print(hoge.tm_wday);
  Serial.print(" yday=");
  Serial.print(hoge.tm_yday);
  Serial.print(" isdst=");
  Serial.print(hoge.tm_isdst);
  Serial.println();
}

// tm構造体の比較
int compTM(struct tm tm1, struct tm tm2) {
  if (tm1.tm_year != tm2.tm_year) return 1;
  if (tm1.tm_mon != tm2.tm_mon) return 1;
  if (tm1.tm_mday != tm2.tm_mday) return 1;
  if (tm1.tm_hour != tm2.tm_hour) return 1;
  if (tm1.tm_min != tm2.tm_min) return 1;
  if (tm1.tm_sec != tm2.tm_sec) return 1;
  if (tm1.tm_wday != tm2.tm_wday) return 1;
  return 0;
}

void setup() {
  Serial.begin(9600);
  delay(500);
  Serial.println();
  Serial.println();
  Serial.println("Wake Up");

  Serial.print("__TIME__=[");
  Serial.print(__TIME__);
  Serial.print("] __DATE__=[");
  Serial.print(__DATE__);
  Serial.println("]");

  // コンパイルした日時を求める
  buildDate(__DATE__, &ctm);
  buildTime(__TIME__, &ctm);
  displayTM("[ctm]",ctm);
  Serial.print("sizeof(ctm)=");
  Serial.println(sizeof(ctm));

 
  // RTC memoryからデータを読み込む
  if (ESP.rtcUserMemoryRead(0, (uint32_t*) &rtcData, sizeof(rtcData))) {
    Serial.println("Read: ");
    printMemory(10);
  // 読み込んだデータでCRC32を計算する
    uint32_t crcOfData = calculateCRC32(((uint8_t*) &rtcData) + 4, sizeof(rtcData) - 4);
    Serial.print("CRC32 of data: ");
    Serial.println(crcOfData, HEX);
    Serial.print("CRC32 read from RTC: ");
    Serial.println(rtcData.crc32, HEX);
  // CRC32が一致しないので初回起動時
    if (crcOfData != rtcData.crc32) {
      Serial.println("CRC32 in RTC memory doesn't match CRC32 of data. Data is probably invalid!");
      Serial.println("sizeof(rtcData.data)=" + String(sizeof(rtcData.data)));
      for (int i = 0; i < sizeof(rtcData.data); i++) {
        rtcData.data[i] = 0;
      }
    }
  // CRC32が一致したのでResetからの復帰、あるいは再コンパイル
    else {
      Serial.println("CRC32 check ok, data is probably valid.");
  // コンパイル日時を比較
      displayTM("[compile]",rtcData.compile);
      if (compTM(ctm, rtcData.compile) == 1) {
        Serial.println("Compile Date & Time doesn't match.");
        rtcData.data[0] = 0;
      } else {
        Serial.println("Compile Date & Time match.");
      }
     
    }
  }

 
  // NTPクライントモード
  if (rtcData.data[0] == 0) {
    // Wifi接続
    setup_wifi();
    Serial.println("Starting UDP");
    udp.begin(localPort);

  // 最初の時刻リクエストを送信
    sendNTPpacket(timeServer);

  // HTTPクライアントモード
  } else {
  // 時刻設定
    showNow("Before");
    setTime(rtcData.data[3],rtcData.data[4],rtcData.data[5],
    rtcData.data[2],rtcData.data[1],rtcData.data[0]);
    showNow("After");
  // Wifi接続
    setup_wifi();
  // start tft
    tft.begin();
  // HTML表示
    displayHTML(LAYOUT);
  }
}

void loop() {

  // NTPサーバからのパケット受信(初回起動時のみ)
  int cb = udp.parsePacket();
  if (cb) {
    Serial.print("packet received, length=");
    Serial.println(cb);
    // バッファに受信データを読み込む
    udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

    // 時刻情報はパケットの40バイト目からはじまる4バイトのデータ
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);

    // NTPタイムスタンプは64ビットの符号無し固定小数点数(整数部32ビット、小数部32ビット)
    // 1900年1月1日0時との相対的な差を秒単位で表している
    // 小数部は切り捨てて、秒を求めている
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    Serial.print("Seconds since Jan 1 1900 = " );
    Serial.println(secsSince1900);

    // NTPタイムスタンプをUNIXタイムに変換する
    // UNITタイムは1970年1月1日0時からはじまる
    // 1900年から1970年の70年を秒で表すと2208988800秒になる
    const unsigned long seventyYears = 2208988800UL;
    // NTPタイムスタンプから70年分の秒を引くとUNIXタイムが得られる
    unsigned long epoch = secsSince1900 - seventyYears;
    Serial.print("Unix time = ");
    Serial.println(epoch);
    showTime("UTC",epoch);
    showTime("JST",epoch + (9 * 60 * 60));

    // Timeライブラリに時間を設定(UNIXタイム)
    // 日本標準時にあわせるために+9時間しておく
    setTime(epoch + (9 * 60 * 60));

    // 現在の時間を設定
    rtcData.data[0] = year();
    rtcData.data[1] = month();
    rtcData.data[2] = day();
    rtcData.data[3] = hour();
    rtcData.data[4] = minute();
    rtcData.data[5] = second();

    // コンパイル日時を格納
    memcpy(&(rtcData.compile), &ctm, sizeof(ctm));
   
    // CRC32を再計算
    rtcData.crc32 = calculateCRC32(((uint8_t*) &rtcData) + 4, sizeof(rtcData) - 4);

    // RTC memoryにデータを書き込む
    if (ESP.rtcUserMemoryWrite(0, (uint32_t*) &rtcData, sizeof(rtcData))) {
      Serial.println("Write: ");
      printMemory(10);
    }

    // Reset
    Serial.println("Now reset......");
    WiFi.disconnect();
    ESP.reset();
    while(1) {}
  }

  static int counter = 0;
  static int interval = 30;
  long now = millis();
  // 時刻確認
  if (lastCheckTime == 0) lastCheckTime = now;
  if (now - lastCheckTime > 1000) { // 1秒経過
    lastCheckTime = now;
    counter++;
    if (counter > interval) {
      showNow("Current Date & Time");
      if (hour() == ON[0] && minute() == ON[1]) { // 表示の更新
        displayHTML(LAYOUT);
        interval = 60; // 次回は60秒後に時刻確認
      } else if (hour() == OFF[0] && minute() == OFF[1]) { // 表示の消去
        tft.fillScreen("BLACK");
        interval = 60; // 次回は60秒後に時刻確認

#if 0
//ここを有効にすると、表示OFFにした後で時刻合わせを行う
        // CRC32をクリア
        rtcData.crc32 = 0;
        // RTC memoryにデータを書き込む
        if (ESP.rtcUserMemoryWrite(0, (uint32_t*) &rtcData, sizeof(rtcData))) {
          Serial.println("Write: ");
          printMemory(10);
        }
        // Reset
        Serial.println("Now reset......");
        WiFi.disconnect();
        ESP.reset();
        while(1) {}
#endif     
     
      } else {
        interval = 30; // 次回は30秒後に時刻確認
      }
      counter=0;
    }
  }
 
}

ライブラリをたくさんインクルードしているので、結構大きなスケッチですがまだメモリーには余裕が有ります。


何回かに分けて、インターネットから文字データを抜き出して表示する方法を紹介しましたが、
こちらではインターネットから画像データを抜き出して表示する方法を紹介し てい ます。