Arduino IDE + Raspberry Pi Pico + ESP-01(AT Command)で時計

## 概要
Raspberry Pi Pico と ESP-01 で時計を作成します。
ESP-01をATコマンドで制御し、SNTPで時刻同期します。
時刻同期は毎正時に行います。

![IMG_20250129_190410508_S.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670759/c2e50699-5210-e645-cc0f-55a668b4daa0.jpeg)
写真の赤線の枠内が ESP-01 です。
## ソフトウエア資材
+ Arduino IDE for Raspberry Pi Pico on Windows 11
## ハードウエア資材
+ [Cytron] Maker Pi Pico Base　(開発用基板)
+ Raspberry Pi Pico
+ ESP-01(WiFi module)
+ SSD1306(OLED)
+ GROVE - 4ピン-ジャンパメスケーブル
## プログラム(スケッチ)
```c:EspATClock.ino
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <TimeLib.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

String sendCommand(String command, String replyEnd="OK");
time_t getSNTPTIME();
time_t parseTimeString(String timeString);
void displayDateTime();

bool syncTime = false;

void setup() {
  Wire.setSDA(4);
  Wire.setSCL(5);
  Wire.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.println("Initializing");

  Serial1.setTX(16);
  Serial1.setRX(17); 
  Serial1.begin(115200);

  sendCommand("RST","ready");
  sendCommand("CWJAP=\"<your ssid>\",\"<your password>\"");
  sendCommand("CIPSNTPCFG=1,9,\"ntp.nict.jp\"");
  time_t t = getSNTPTIME();
  setTime(t);
}

void loop() {
  if (minute() == 0) {
    if (!syncTime) {
      syncTime = true;
      time_t t = getSNTPTIME();
      setTime(t);
    }
  } else {
    syncTime = false;
  }
  displayDateTime();
}
```
```c:getSNTPTIME()
time_t getSNTPTIME() {
  while (true) {
    String response = sendCommand("CIPSNTPTIME?");
    time_t t = parseTimeString(response);
    if (year(t) > 1970) return t;
    delay(500);
  }
}
```
```c:parseTimeString()
time_t parseTimeString(String timeString) {
  char dayName[4], monthName[4], buff[256];
  int year, month;
  struct tm tm;
  
  strcpy(buff,timeString.c_str());
  char* pt = strchr(buff,':') + 1;
  sscanf(pt, "%s %s %d %d:%d:%d %d",
  	dayName, monthName, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &year);
  for(month=1; month<=12; month++) if (strcmp(monthShortStr(month), monthName) == 0) break;
  tm.tm_year = year - 1900;
  tm.tm_mon = month - 1;
  return mktime(&tm);
}
```
```c:sendCommand()
String sendCommand(String command, String replyEnd) {
  const int timeout = 5000;
  String response = "";
  String strLine = "";
  bool blnOK = false;
  
  Serial1.print("AT+");
  Serial1.print(command);
  Serial1.println();
  
  long int time = millis();
  while(true) {
    while(Serial1.available()) {
      char c = Serial1.read();
      if (c == '\r') {
      } else if (c == '\n') {
        if (strLine.charAt(0) == '+') response = strLine;
        else if (strLine == replyEnd) blnOK = true;
        strLine = "";
      } else strLine += c;
    }
    if (blnOK) break;
    if ((millis()-time) > timeout) break;
  }
  return response;
}
```
```c:displayDateTime()
void displayDateTime() {
  char bufTime[32], bufDate[32], dayName[4], monthName[4];
  
  strcpy(dayName, dayShortStr(weekday()));
  strcpy(monthName, monthShortStr(month()));
  sprintf(bufTime, "%02d:%02d:%02d", hour(), minute(), second());
  sprintf(bufDate, "%s %02d %s %d", dayName, day(), monthName, year());
  display.clearDisplay();
  display.setTextSize(2);
  display.setCursor(0, 0);
  display.println(bufTime);
  display.setTextSize(1);
  display.setCursor(0, 20);
  display.println(bufDate);
  display.display();
}
```

## まとめ
動作確認に使用した ESP-01 のATファームウエアのバージョンは下記のとおりです。

AT+GMR
AT version:1.7.4.0(Jul  8 2020 15:53:04)
SDK version:3.0.5-dev(52383f9)
compile time:Aug 28 2020 14:37:33
OK

ATファームウエアの説明は下記です。
https://docs.espressif.com/projects/esp-at/en/release-v2.2.0.0_esp8266/index.html