M5StickC Plus 時計


2021/9/30公開

概要
こちらで公開されているM5StickC用の時計をにM5StickC Plusに対応させてみました。
ただそのまま対応させるだけでは面白くないので下記の改良を加えてみました。

動作例
※無印良品のタグツール用シリコーンケースを装着してます。

ほぼスリープ状態でたまに画面をオンにする運用で10時間程度はバッテリーが持ちましたのでぼちぼち実用的なラインはクリアできているかと思います。
色々と改善の余地は結構ありそうな感じではありますがとりあえず目標とするところは達成できたので公開しておきます。

コード(watch.ino)
  #define LGFX_USE_V1
  #define LGFX_AUTODETECT
  #include <LovyanGFX.hpp>
  
  #define _M5DISPLAY_H_
  class M5Display {};
  #include <M5StickCPlus.h>
  
  static LGFX lcd;
  
  #include <WiFi.h>
  #include <time.h>
  
  // wifiの設定
  const char* ssid     = "SSID"; // WiFiのSSID
  const char* password = "PASSWORD"; // WiFiのパスワード
  
  // 時計の設定
  const char* ntpServer = "ntp.jst.mfeed.ad.jp"; // NTPサーバー
  const long  gmtOffset_sec = 9 * 3600;          // 時差9時間
  const int   daylightOffset_sec = 0;            // サマータイム設定なし
  
  RTC_TimeTypeDef RTC_TimeStruct; // 時刻
  RTC_DateTypeDef RTC_DateStruct; // 日付
  
  unsigned long setuptime; // スリープ開始判定用(ミリ秒)
  int sMin = 0; // 画面書き換え判定用(分)
  
  void printLocalTime() {
    static const char *wd[7] = {"Sun","Mon","Tue","Wed","Thr","Fri","Sat"};
    M5.Rtc.GetTime(&RTC_TimeStruct); // 時刻の取り出し
    M5.Rtc.GetData(&RTC_DateStruct); // 日付の取り出し
  
    if (sMin == RTC_TimeStruct.Minutes) {
      lcd.fillRect(180,10,20,10,TFT_BLACK); // 秒の表示エリアだけ書き換え
    } else {
      lcd.fillScreen(TFT_BLACK); // 「分」が変わったら画面全体を書き換え
      showBatteryStat();
    }
  
    lcd.setTextColor(TFT_GREEN);
    lcd.setCursor(40, 10, 7);  //x,y,font 7:48ピクセル7セグ風フォント
  
    lcd.printf("%02d:%02d", RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes); // 時分を表示
  
    lcd.setTextFont(1); // 1:Adafruit 8ピクセルASCIIフォント
    lcd.printf(":%02d\n",RTC_TimeStruct.Seconds); // 秒を表示
  
    lcd.setTextColor(TFT_WHITE);
    lcd.setCursor(65, 65, 1);  //x,y,font 1:Adafruit 8ピクセルASCIIフォント
    lcd.printf("Date:%04d.%02d.%02d %s\n", RTC_DateStruct.Year, RTC_DateStruct.Month, RTC_DateStruct.Date, wd[RTC_DateStruct.WeekDay]);
    
    sMin = RTC_TimeStruct.Minutes; // 「分」を保存
  }
  
  void showBatteryStat() {
    // バッテリー電圧
    double vbat = M5.Axp.GetVbatData() * 1.1 / 1000;
  
    // バッテリー残量
    // 簡易的に、線形で4.1Vで100%、3.2Vで0%とする
    int8_t bat_charge_p = int8_t((vbat - 3.2) / 0.9 * 100);
    if (bat_charge_p > 100) {
      bat_charge_p = 100;
    } else if (bat_charge_p < 0) {
      bat_charge_p = 0;
    }
  
    // LCDバッテリー残量表示
    lcd.setTextSize(1);      // 文字サイズを1にする
    lcd.setCursor(3, 100, 4);
    lcd.printf("Battery: %3d%%", bat_charge_p);
  }
  
  void setup() {
    M5.begin();
    setCpuFrequencyMhz(80); //電池を持たせるために動作周波数を落とす
  
    lcd.init();
    lcd.setRotation(1);
    lcd.setColorDepth(24);
    
    M5.Axp.ScreenBreath(9);
    lcd.fillScreen(TFT_BLACK);
  
    // 電源オン時は、WiFiでNTPサーバーから時刻を取得
    esp_sleep_wakeup_cause_t wakeup_reason;
    wakeup_reason = esp_sleep_get_wakeup_cause(); // スリープからの復帰理由を取得
  
    switch(wakeup_reason){
      case ESP_SLEEP_WAKEUP_EXT0      :
      Serial.printf("外部割り込み(RTC_IO)で起動\n"); break; // スリープ復帰時はWiFiで時刻取得しない
  
      default                         : 
      Serial.printf("スリープ以外からの起動\n"); // 電源オン時は、WiFiでNTPサーバーから時刻取得
  
      // connect to WiFi
      lcd.setCursor(0, 0, 1);
      lcd.setTextColor(TFT_WHITE);
      lcd.setTextSize(1);
      lcd.print("WiFi connecting");
  
      WiFi.begin(ssid, password);
  
      while (WiFi.status() != WL_CONNECTED) { // 1秒おきに接続状態を確認
        delay(1000);
        lcd.print(".");
      }
      lcd.println("\nconnected");
  
      // Set ntp time to local
      configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  
      // Get local time
      struct tm timeInfo;
      if (getLocalTime(&timeInfo)) {
        lcd.print("NTP : ");
        lcd.println(ntpServer);
  
        // Set RTC time
        RTC_TimeTypeDef TimeStruct;
        TimeStruct.Hours   = timeInfo.tm_hour;
        TimeStruct.Minutes = timeInfo.tm_min;
        TimeStruct.Seconds = timeInfo.tm_sec;
        M5.Rtc.SetTime(&TimeStruct);
  
        RTC_DateTypeDef DateStruct;
        DateStruct.WeekDay = timeInfo.tm_wday;
        DateStruct.Month = timeInfo.tm_mon + 1;
        DateStruct.Date = timeInfo.tm_mday;
        DateStruct.Year = timeInfo.tm_year + 1900;
        M5.Rtc.SetData(&DateStruct);
      }
  
      // disconnect WiFi as it's no longer needed
      WiFi.disconnect(true);
      WiFi.mode(WIFI_OFF);
  
      lcd.println("WiFi disconnected");
      delay(1000);
  
      break; // switch 終わり
    }
    setuptime = millis(); // 起動からの経過時間(ミリ秒)を保存
    showBatteryStat();
  }
  
  void loop() {
    M5.update();
    printLocalTime();
    
    delay(980); // 0.98秒待ち
  
    // GPIO37がLOWになったら(M5StickCのA(HOME)ボタンが押されたら)スリープから復帰
    pinMode(GPIO_NUM_37, INPUT_PULLUP);
    esp_sleep_enable_ext0_wakeup(GPIO_NUM_37, LOW);
  
    // 表示開始から1分経過後にディープスリープスタート
    if(millis() > setuptime + 1000 * 60){
      M5.Axp.ScreenBreath(0);    // 画面の輝度OFF
      esp_deep_sleep_start();
    }
  }
  


戻る