M5StickC Plus 時計
2021/9/30公開
概要
こちらで公開されているM5StickC用の時計をにM5StickC Plusに対応させてみました。ただそのまま対応させるだけでは面白くないので下記の改良を加えてみました。
- LovyanGFXを使用 LovyanGFXの動作確認の意味も込めて。
- バッテリー残量を表示(4.1Vで100%、3.2Vで0%として計算) 強制的にシャットダウンされるバッテリー電圧は3.0Vですが、実際に使ってみた感じだと3.2Vを下回ってくるとぼちぼちスリープ復帰できなくなったりと動作が怪しくなってくるので3.2Vを下限としています。
- 省電力化のためCPUクロックを下げる フルパワー(240MHz)で動かす必要性は特に無いので80MHzまで下げています。
動作例
ほぼスリープ状態でたまに画面をオンにする運用で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();
}
}
戻る