M5StickC Plusには「ESP32-PICO-D4」が使われているので、ESP32に搭載されているRTC(リアルタイムクロック)を使用することができます。
WiFi通信もできるのでインターネットに接続して時刻合わせをすることもできます。
つまり機能的には時計として使うことも可能というわけです。
なので今回はM5StickC PlusのRTCを使って簡単な時計を作ってみたのでその備忘録です。
では、始めます。
1:RTCの使い方
RTCの日時は以下のように取得できます。
#include <M5StickCPlus.h>
RTC_DateTypeDef rtc_date;
M5.Rtc.GetData(&rtc_date);
int year = rtc_date.Year;
int month = rtc_date.Month;
int date = rtc_date.Date;
int wd = rtc_date.WeekDay;
RTC_TimeTypeDef rtc_time;
M5.Rtc.GetTime(&rtc_time);
int hours = rtc_time.Hours;
int Minutes = rtc_time.Minutes;
int Seconds = rtc_time.Seconds;
またインターネットに接続できている状態で時刻合わせをするには以下のようにします。
#include <M5StickCPlus.h>
configTime([時差], [サマータイム有効/無効], [接続NTPサーバ(複数可能)]);
configTime(3600L*9, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
M5StickC Plusは出荷時点ではRTCの時刻は特に設定されていないので、ちゃんとした時刻を知りたい場合は一度は時刻合わせをしておくとよいです。
2:時計のサンプル
RTCの使い方がわかったので時計のサンプルスケッチを作りました。
・RTCClock.h
#include <M5StickCPlus.h>
#include <WiFi.h>
class RTCClock {
public:
const char *wd[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
const char *defaultFormat = "%04d/%02d/%02d %s\n%02d:%02d:%02d";
const int O_YEAR = 0;
const int O_MONTH = 1;
const int O_DAY = 2;
const int O_WD = 3;
const int O_HOUR = 4;
const int O_MINUTE = 5;
const int O_SECOND = 6;
RTCClock();
void setDefault();
void setSpriteArea(int width, int height);
void setShowPosition(int x, int y);
void setFontAndSize(int fontNum, int textSize);
void setTextAndBgColor(uint32_t textColor, uint32_t bgColor);
void setFormat(const char *str, ...);
void setWiFi(const char *ssid, const char *password);
void connectWifFi();
void syncTime();
void disconnectWifFi();
RTC_TimeTypeDef getRTCTime();
RTC_DateTypeDef getRTCDate();
void showDateAndTime();
void deleteDateAndTime();
void updateDateAndTime();
private:
static const int MAX_SHOW_DATE_TIME = 7;
static const int HIDE_ORDER = -1;
TFT_eSprite _sprite = TFT_eSprite(&M5.Lcd);
const char *_ssid_p;
const char *_password_p;
const char *_format;
int _orderArray[MAX_SHOW_DATE_TIME];
int _orderArraySize;
bool _showFlg;
String _dateFormat;
String _timeFormat;
int _fontNum;
int _textSize;
uint32_t _text_color;
uint32_t _bg_color;
int _x;
int _y;
int _width;
int _height;
int getArgumentsSize(const char *str);
void printDateAntTime(int a[]);
};
RTCClock::RTCClock() {
_showFlg = true;
_ssid_p = "";
_password_p = "";
setDefault();
}
void RTCClock::setDefault() {
_format = defaultFormat;
for (int i=0; i<MAX_SHOW_DATE_TIME; i++) {
_orderArray[i] = i;
}
_orderArraySize = MAX_SHOW_DATE_TIME;
_fontNum = 1;
_textSize = 1;
_text_color = TFT_WHITE;
_bg_color = TFT_BLACK;
_x = 0;
_y = 0;
_width = 84 * _textSize;
_height = 16 * _textSize;
}
void RTCClock::setSpriteArea(int width, int height) {
_width = width;
_height = height;
}
void RTCClock::setShowPosition(int x, int y) {
_x = x;
_y = y;
}
void RTCClock::setFontAndSize(int fontNum, int textSize) {
_fontNum = fontNum;
_textSize = textSize;
}
void RTCClock::setTextAndBgColor(uint32_t textColor, uint32_t bgColor) {
_text_color = textColor;
_bg_color = bgColor;
}
void RTCClock::setFormat(const char *str, ...) {
_format = str;
_orderArraySize = getArgumentsSize(str);
for (int i=0; i<MAX_SHOW_DATE_TIME; i++) {
_orderArray[i] = HIDE_ORDER;
}
va_list ap;
va_start(ap, str);
for (int i=0; i<_orderArraySize; i++) {
_orderArray[i] = va_arg(ap, int);
}
va_end(ap);
}
void RTCClock::setWiFi(const char *ssid, const char *password) {
_ssid_p = ssid;
_password_p = password;
}
void RTCClock::connectWifFi() {
_showFlg = false;
_sprite.fillSprite(_bg_color);
_sprite.setCursor(0, 0);
_sprite.setTextFont(1);
_sprite.setTextSize(1);
_sprite.print("connecting");
int counter = 0;
WiFi.begin(_ssid_p, _password_p);
while (counter < 10) {
_sprite.pushSprite(_x, _y);
if (WiFi.status() == WL_CONNECTED) {
_sprite.print(" OK");
_sprite.pushSprite(_x, _y);
break;
} else {
counter++;
_sprite.print(".");
delay(500);
}
}
delay(1000);
_showFlg = true;
}
void RTCClock::syncTime() {
_showFlg = false;
_sprite.fillSprite(_bg_color);
_sprite.setCursor(0, 0);
_sprite.setTextFont(1);
_sprite.setTextSize(1);
if (WiFi.status() == WL_CONNECTED) {
configTime(3600L*9, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
_sprite.print("sync OK");
} else {
_sprite.print("sync NG");
}
_sprite.pushSprite(_x, _y);
delay(1000);
_showFlg = true;
}
void RTCClock::disconnectWifFi() {
_showFlg = false;
_sprite.fillSprite(_bg_color);
_sprite.setCursor(0, 0);
_sprite.setTextFont(1);
_sprite.setTextSize(1);
if (WiFi.status() == WL_CONNECTED) {
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
_sprite.print("disconnected.");
} else {
_sprite.print("disconnect NG");
}
_sprite.pushSprite(_x, _y);
delay(1000);
_showFlg = true;
}
int RTCClock::getArgumentsSize(const char *str) {
int sizeCount = 0;
while( *str != 0x00 ) {
char c = str[0];
if (c == '%') {
sizeCount += 1;
}
str++;
}
return sizeCount;
}
RTC_TimeTypeDef RTCClock::getRTCTime() {
RTC_TimeTypeDef rtc_time;
M5.Rtc.GetTime(&rtc_time);
return rtc_time;
}
RTC_DateTypeDef RTCClock::getRTCDate() {
RTC_DateTypeDef rtc_date;
M5.Rtc.GetData(&rtc_date);
return rtc_date;
}
void RTCClock::showDateAndTime() {
_showFlg = true;
_sprite.setColorDepth(16);
_sprite.createSprite(_width, _height);
}
void RTCClock::deleteDateAndTime() {
_sprite.fillSprite(_bg_color);
_sprite.pushSprite(_x, _y);
_sprite.deleteSprite();
_showFlg = false;
}
void RTCClock::updateDateAndTime() {
if (!_showFlg) { return; }
_sprite.fillSprite(_bg_color);
_sprite.setCursor(0, 0);
_sprite.setTextFont(_fontNum);
_sprite.setTextColor(_text_color);
_sprite.setTextSize(_textSize);
RTC_TimeTypeDef rtc_time = getRTCTime();
RTC_DateTypeDef rtc_date = getRTCDate();
int dateTimeArray[MAX_SHOW_DATE_TIME] = {
rtc_date.Year,
rtc_date.Month,
rtc_date.Date,
rtc_date.WeekDay,
rtc_time.Hours,
rtc_time.Minutes,
rtc_time.Seconds
};
printDateAntTime(dateTimeArray);
_sprite.pushSprite(_x, _y);
}
void RTCClock::printDateAntTime(int a[]) {
String s = _format;
int oa[MAX_SHOW_DATE_TIME] = { -1,-1,-1,-1,-1,-1,-1 };
int count = 0;
for (int i=0; i<_orderArraySize; i++) {
int orderNum = _orderArray[i];
if (orderNum == O_WD) {
s.replace("%s", wd[a[O_WD]]);
} else {
oa[count] = orderNum;
count++;
}
}
switch (count) {
case 1:
_sprite.printf(s.c_str(), a[oa[0]]);
break;
case 2:
_sprite.printf(s.c_str(), a[oa[0]], a[oa[1]]);
break;
case 3:
_sprite.printf(s.c_str(), a[oa[0]], a[oa[1]], a[oa[2]]);
break;
case 4:
_sprite.printf(s.c_str(), a[oa[0]], a[oa[1]], a[oa[2]], a[oa[3]]);
break;
case 5:
_sprite.printf(s.c_str(), a[oa[0]], a[oa[1]], a[oa[2]], a[oa[3]], a[oa[4]]);
break;
case 6:
_sprite.printf(s.c_str(), a[oa[0]], a[oa[1]], a[oa[2]], a[oa[3]], a[oa[4]], a[oa[5]]);
break;
case 7:
_sprite.printf(s.c_str(), a[oa[0]], a[oa[1]], a[oa[2]], a[oa[3]], a[oa[4]], a[oa[5]], a[oa[6]]);
break;
}
}
・M5StickCPlus_clock.ino
#include <M5StickCPlus.h>
#include "RTCClock.h"
const char *SSID_CHAR = "*********";
const char *PASS_CHAR = "********";
RTCClock rtcClock = RTCClock();
void setup() {
Serial.begin(115200);
M5.begin();
M5.Axp.ScreenBreath(10);
M5.Lcd.setRotation(3);
M5.Lcd.fillScreen(GREEN);
setCustomClock();
rtcClock.setWiFi(SSID_CHAR, PASS_CHAR);
rtcClock.showDateAndTime();
}
bool defaultFlg = false;
void loop() {
M5.update();
if (M5.BtnA.wasPressed()) {
rtcClock.connectWifFi();
rtcClock.syncTime();
rtcClock.disconnectWifFi();
defaultFlg = !defaultFlg;
rtcClock.deleteDateAndTime();
if (defaultFlg) {
rtcClock.setDefault();
} else {
setCustomClock();
}
rtcClock.showDateAndTime();
}
rtcClock.updateDateAndTime();
delay(100);
}
void setCustomClock() {
int textSize = 2;
int width = 84 * textSize;
int height = 16 * textSize;
rtcClock.setSpriteArea(width, height);
rtcClock.setShowPosition(5, 5);
rtcClock.setFontAndSize(1, textSize);
rtcClock.setTextAndBgColor(TFT_PINK, TFT_BLUE);
const char *formatChar = "%s %04d/%02d/%02d\n%02d:%02d:%02d";
rtcClock.setFormat(formatChar, rtcClock.O_WD, rtcClock.O_YEAR, rtcClock.O_MONTH, rtcClock.O_DAY, rtcClock.O_HOUR, rtcClock.O_MINUTE, rtcClock.O_SECOND);
}
少し解説すると、このサンプルを書き込むと時刻が表示されます。「M5」と書かれたボタンを押すとWiFiに接続して時刻合わせをして切断されます。
ボタンが押されるとデフォルトと自分で設定した時刻表示が切り替わるようになっています。
以上がM5StickC Plusで時計を作った内容です。
IoT機器として使い、時刻毎に何かを行うような場合には何かと便利かと思います。
今回のサンプルを使って時刻合わせを行うことでかなり近い時刻になるのですが、なぜか自分の場合だと14秒ほど遅れて時刻が表示されるという現象が発生しました。何度時刻合わせをしてもその遅延は変わらなかったので何か知っている方はコメントを頂けると助かります…。
・参考資料