WROOM02でRTC8025を使えるようになる(1) まずはRTC8564で時計作り
◯やりたいこと
値段が爆上がり(秋月:800円)してしまったRTC8564、数をそろえるには少々お高くなってしまったので少し安いRX8025(同:450円)を使いこなせるようになりたいという要求が。そこでRTC8564とSSD1306を利用した時計(BME280:温湿度・気圧計付き)を作り必要な部分のみを変更する事によってRX8025へすんなり移行することを目指します。

◯やったこと
・製作
まずはWROOM02でRTC8564とBME280を使いOLED(SSD1306)にデータを表示する時計を作ります。RTC8564バージョンは過去に温湿度計ロガーやモーションセンサーで必要な関数が出来上がっていますので比較的簡単に作ることができます。
回路的にはWROOM02のi2cラインにSSD1306とBME280をぶら下げるだけですのでそれほど難しい回路ではありません。ブレッドボードにササっと組み上げていきます。
配線をし

WROOM02,SSD1306,BME280,RTC8564をセットしたら


もう完成です。ブレッドボードは手間がかからないのでホント助かります。
・スケッチ
スケッチは以下の通りとなります。
/* * WR02_RTC_OLED v0.0.4 */ // BSP(Board Support Package) // ESP8266 Boards v3.1.2 // FlushSize ESP01:1MB Wroom-02D:2MB -02:4MB // Built-in Library (Arduino IDE v1.8.19) #include <Arduino.h> #include <time.h> #include <Wire.h> // BSP Built-in Library #include <ESP8266WiFi.h> // v1.0 // Installed Library #include <Adafruit_GFX.h> // v1.11.3 #include <Adafruit_SSD1306.h> // v2.5.7 #include <Adafruit_BME280.h> // v2.2.2 // Global Variable /* WiFi */ #define WIFI_SSID "YOUR_SSID" #define WIFI_PASS "YOUR_PASS" /* TCP/IP */ IPAddress ip (YOUR_IP); // ex. (192,168,0,2) IPAddress gw (YOUR_GW); IPAddress dns (YOUR_DNS); // Google:(8,8,8,8) IPAddress mask (YOUR_MASK); // ex. (255,255,255,0) /* I2C */ #define I2C_SDA 2 #define I2C_SCK 0 #define I2C_CLK 100000 // fSCL=100KHz /* NTP DATA */ #define NTP_SRV1 "ntp.nict.jp" #define NTP_SRV2 "ntp.jst.mfeed.ad.jp" /* RTC8564 */ #define RTC_ADR 0x51 // I2Cアドレス #define RTC_CTR1 0x00 // RTC CTRL1レジスタ #define RTC_CTR2 0x01 #define RTC_SEC 0x02 #define RTC_TMRC 0x0E #define RTC_TMR 0x0F struct rtcTime { uint8_t sec; // 0-59 uint8_t min; // 0-59 uint8_t hour; // 0-23 uint8_t day; // 1-31 uint8_t mon; // 1-12 uint8_t year; // 00-199 uint8_t wday; // 0(Sun)-6(Sat) }; rtcTime RT = {0,0,0,1,1,0,0}; /* OLED */ #define OLED_ADR 0x3C // I2Cアドレス #define OLED_RESET -1 #define OLED_WIDTH 128 // OLED width(pixels) #define OLED_HEIGHT 64 // OLED height Adafruit_SSD1306 dsp(OLED_WIDTH, OLED_HEIGHT, &Wire, OLED_RESET); static const char *WDay[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; /* BME280 */ #define BME_ADR 0x76 Adafruit_BME280 bme; float tmp, hum, prs; /* Time */ time_t UXTM = 0; // UnixTime uint32_t STTM = 0; // スタート時間 milli() ///// ハードウエア 初期設定 void setup() { // Start time STTM = millis(); // WiFi //WiFi.setOutputPower(10); // [dBm] max 20.5dBm // I2C Wire.begin(I2C_SDA, I2C_SCK); Wire.setClock(I2C_CLK); delayy(10); // Pin Mode digitalWrite(UART_TX, HIGH); pinMode (UART_TX, OUTPUT); // BME280 bme.begin(BME_ADR, &Wire); // スタート表示 dsp_start(); // RTC VLビットチェック /* VL on */ if (rtc_chkvl()) { // RTC8564 安定化待ち,初期化 while ((millis()-STTM)<1000) delayy(1); rtc_init(); // NTP時刻をRTCにセット WiFi->NTP->UXTM->RT->RTC rtc_setNTP(); } /* VL off */ else { /* RTC時刻->dt, RTCタイマーセット */ rtc_init(); rtc_getRT(); /* 定時時刻合わせチェック */ if(RT.day==1 && RT.hour==1 && (RT.min/10)==3) { // NTP時刻をRTCにセット WiFi->NTP->UXTM->RT->RTC rtc_setNTP(); } } } ///// メインルーチン void loop() { // BME280 温湿度・気圧測定 temperature,humidity,press tmp = bme.readTemperature(); hum = bme.readHumidity(); prs = bme.readPressure()/100; // 時刻・温湿度気圧表示 dsp_datetime(); dsp_data(); // LED点滅 byte led = digitalRead(UART_TX); led = (led==LOW) ? HIGH : LOW; digitalWrite(UART_TX, led); delay(1000); } ///// I2C Control ///// // i2cのデータが確定するまで待つ inline void i2c_wait() { while(Wire.available()<1); } ///// Display Control ///// /* OLED 起動処理 */ void dsp_start() { // 初期化 dsp.begin(SSD1306_SWITCHCAPVCC, OLED_ADR); // 初期表示 dsp.clearDisplay(); dsp.setTextColor(WHITE); dsp.setTextSize(1); dsp.setCursor( 0, 20); dsp.print("Syncing NTP time"); dsp.display(); dsp.clearDisplay(); } /* OLED 時刻表示 */ void dsp_datetime() { // RTC 現在時刻の取得 rtc_getRT(); // DATE 文字列作成 char fmtDate[11]; sprintf( fmtDate, "%02d/%02d(%s)", RT.mon, RT.day, WDay[RT.wday] ); // TIME 文字列作成 char fmtTime[6]; sprintf( fmtTime, "%02d:%02d", RT.hour, RT.min ); // 表示 //dsp.clearDisplay(); dsp.fillRect( 0, 0,128, 16,BLACK); dsp.setTextSize(1); dsp.setCursor( 4, 4); dsp.println(fmtDate); dsp.setTextSize(2); dsp.setCursor( 68, 0); dsp.println(fmtTime); dsp.display(); } /* OLED 測定結果表示 */ void dsp_data() { // 初期化 char buf1[9], buf2[21]; // 測定結果表示 dsp.fillRect( 30, 19,128, 53,BLACK); dsp.setTextColor(WHITE); dsp.setTextSize(1); /* Temp 表示 */ dsp.setCursor( 0, 19); dtostrf (tmp,4,1,buf1); sprintf (buf2, "Tmp: %sC", buf1); dsp.print (buf2); /* Humi 表示 */ dsp.setCursor( 0, 28); dtostrf (hum,4,1,buf1); sprintf (buf2, "Hum: %s", buf1); dsp.print (buf2); dsp.print ('%'); /* Press 表示 */ dsp.setCursor( 0, 37); dtostrf (prs,4,0,buf1); sprintf (buf2, "Prs: %sHp", buf1); dsp.print (buf2); dsp.display(); } ///// RTC8564 Control ///// // RTC 初期化 /* 時刻再設定なし,タイマー初期化 */ void rtc_init() { rtc_setTMR(1); } /* 指定時刻でRTC設定,タイマー初期化 */ void rtc_init(time_t uxtm) { /* UnixTimeを RTC時刻(RT構造)にセット */ uxt2rt(uxtm); /* RTCへ指定時刻を設定,タイマー初期化 */ rtc_setRT(); rtc_setTMR(1); // [sec] タイマーセット } // RTCのレジスタへRT構造から時刻をセット void rtc_setRT() { // stop ビットセット rtc_regWR(RTC_CTR1, 0x20); // RT構造上の時刻データをBCDに変換してセット uint8_t data[7]; data[0] = dec2bcd(RT.sec); data[1] = dec2bcd(RT.min); data[2] = dec2bcd(RT.hour); data[3] = dec2bcd(RT.day); data[4] = dec2bcd(RT.wday); data[5] = dec2bcd(RT.mon); // yearが100年を超えた場合CENTURYビットを立てる if (RT.year > 100) { data[6] = dec2bcd(RT.year - 100); data[5] |= 0x80; // RTCS8564_CAL_CENTURY } else { data[6] = dec2bcd(RT.year); } // 7レジスタ 一気に書込む rtc_regSET(RTC_SEC, 7, data); // stop ビットクリア rtc_regWR(RTC_CTR1, 0x00); } // RTCのレジスタから時刻を読出しRT構造にセット int rtc_getRT() { uint8_t data[7]; rtc_regGET(RTC_SEC, 7, data); RT.sec = bcd2dec(data[0] & 0x7f); RT.min = bcd2dec(data[1] & 0x7f); RT.hour = bcd2dec(data[2] & 0x3f); RT.day = bcd2dec(data[3] & 0x3f); RT.wday = bcd2dec(data[4] & 0x07); RT.mon = bcd2dec(data[5] & 0x1f); RT.year = bcd2dec(data[6]); if (data[5] & 0x80) { // RTCS8564_CAL_CENTURY RT.year += 100; } return 0; } // 指定時間でタイマー初期化 void rtc_setTMR(int tmr) { rtc_regWR(RTC_CTR2, 0x11); // bit4 1:割込繰返し bit0 1:INT発生 rtc_regWR(RTC_TMRC, 0x82); // bit7 1:タイマー有効 bit1,0 3:min 2:sec rtc_regWR(RTC_TMR, tmr); // カウント値 } // VLビット読込み bool rtc_chkvl() { return (rtc_regRD(RTC_SEC) & 0x80); } // RTCのタイマー値を読込む int rtc_gettmr() { return rtc_regRD(RTC_TMR); } // RTCのストップビットクリア void rtc_stpclr() { rtc_regWR(RTC_CTR1, 0x00); } // RTCの指定レジスタからデータを読込む uint8_t rtc_regRD(uint8_t reg) { Wire.beginTransmission(RTC_ADR); Wire.write(reg); Wire.endTransmission(); Wire.requestFrom(RTC_ADR, 1); return Wire.read(); } // RTCのアクセスするレジスタを書込む void rtc_regWR(uint8_t reg, uint8_t value) { Wire.beginTransmission(RTC_ADR); Wire.write(reg); Wire.write(value); Wire.endTransmission(); } void rtc_regSET(uint8_t reg, int num, uint8_t *data) { Wire.beginTransmission(RTC_ADR); Wire.write(reg); Wire.write(data, num); Wire.endTransmission(); } void rtc_regGET(uint8_t reg, int num, uint8_t *data) { Wire.beginTransmission(RTC_ADR); Wire.write(reg); Wire.endTransmission(); Wire.requestFrom(RTC_ADR, num); for (int i = 0; i < num; i++) { i2c_wait(); data[i] = Wire.read(); } } // RTCから時刻(RT構造)を読込みUnixTimeを得る time_t rtc_getUXTM() { rtc_getRT(); return rt2uxt(); } // NTPから時刻を取得しRTCにセット boolean rtc_setNTP() { /* WiFi接続:未接続はRTC時刻を更新しない */ if (wifi_connect(60000)) { if (ntp_connect(10000)) { /* NTP時刻 -> UXTM,RTC */ UXTM = time(NULL); rtc_init(UXTM); return true; } } //wifi_stop(); // stopしない方が稼働時間短縮 return false; } // UnixTimeからRTC時刻(RT構造)を得る (UnixTime->tm構造->RT構造) void uxt2rt(time_t uxtm) { struct tm *t = localtime(&uxtm); RT.year = (t->tm_year + 1900) % 100; // tm_year: 1900年から RT.mon = t->tm_mon + 1; // tm_mon : 0-11 RT.wday = t->tm_wday; RT.day = t->tm_mday; RT.hour = t->tm_hour; RT.min = t->tm_min; RT.sec = t->tm_sec; } // RTC時刻(RT構造)からUnixTimeを得る (RT構造->tm構造->UnixTime) time_t rt2uxt() { struct tm t; t.tm_year = RT.year + 100 ; // tm_year: 1900年から t.tm_mon = RT.mon- 1; // tm_mon : 0-11 t.tm_wday = RT.wday; t.tm_mday = RT.day; t.tm_hour = RT.hour; t.tm_min = RT.min; t.tm_sec = RT.sec; t.tm_isdst = -1; // 夏時間設定を無効化 return mktime(&t); } ///// WiFi Control ///// /* WiFi接続開始処理(ノンブロッキング風) * <引数> Timeout: 接続制限時間(0 or 省略は接続するまで無限ループ) * <戻値> 接続:true 未接続:False */ boolean wifi_connect() { return wifi_connect(0); } boolean wifi_connect(int timeout) { // 初期化 unsigned long start = 0; unsigned long last = 0; // ノンブロッキング用時間変数 unsigned long now = 0; unsigned long itvl = 500; // クライアント再接続時のインターバル時間 // WiFi 初期化 wifi_init(); // WiFiが未接続の場合のみbeginする if (WiFi.status() != WL_CONNECTED) { WiFi.begin(WIFI_SSID, WIFI_PASS); delayy(100); } // WiFi接続(指定時間内) 接続:true 接続失敗:false start = millis(); /* タイムアウトチェック */ while (timeout==0 || millis() - start < timeout) { // WiFi接続は非同期(ノンブロッキング)風で now = millis(); if (now-last >= itvl) { last = now; // 接続チェック if (WiFi.status() == WL_CONNECTED) { return true; } // 未接続の場合,0.3-0.5sec おきに再接続 itvl = random(300, 500); } yield(); } // 接続不可 エラーリターン return false; } /* WiFi 初期化処理 */ boolean wifi_init() { // WiFi初期化 (順番大事) // disconnect(true)されて再接続の場合 mode,configの再設定が必須 WiFi.mode(WIFI_STA); // 1.WiFi モード設定 WiFi.persistent(false); // 2.WiFi設定情報 保存しない if (!WiFi.config(ip, gw, mask, dns)) { // 3.固定ip 設定 /* DHCPフォールバック(DHCP切替え処理) */ if (!WiFi.config(0U, 0U, 0U)) { return false; } } delayy(100); return true; } /* WiFi stop処理 */ void wifi_stop() { // WiFiが接続の場合のみdisconnectする if (WiFi.status() == WL_CONNECTED) { WiFi.disconnect(true); } } ///// NTP Control ///// /* NTP 同期処理(ノンブロッキング風) * <引数> Timeout[ms]:同期制限時間(0 or 省略は同期するまで無限ループ) * <戻値> Unixtime or 0(エラー) */ time_t ntp_connect() { return ntp_connect(0); } time_t ntp_connect(int timeout) { // WiFi接続している時のみ時間取得する if (WiFi.status() != WL_CONNECTED) { return 0; } // 初期化 unsigned long start; unsigned long itvl = 250; // NTP再接続時のインターバル時間 unsigned long last = 0; // ノンブロッキング用時間変数 // タイムゾーン,NTPサーバー設定 configTzTime("JST-9", NTP_SRV1, NTP_SRV2, NTP_SRV3); /* 最初に一定時間待機すると同期時にループが省かれる(短時間化) */ delayy(50); // NTPサーバー接続 (timeout秒間 接続確認) start = millis(); while (timeout==0 || millis()-start < timeout) { // NTP接続は非同期(ノンブロッキング)風で if (millis()-last >= itvl) { last = millis(); // 現在時刻の取得 time_t uxtm = time(NULL); // unix時間 // NPT同期チェック (RTC初期値=1,000,000,000) if(uxtm>1000000000) { return uxtm; } // 未接続の場合,0.2-0.3sec おきに再接続 itvl = random(200, 300); } yield(); } // 同期不可 エラーリターン return 0; } ///// Other Control ///// /* BCD Control */ inline int dec2bcd(int dec) { return (((dec / 10) << 4) | (dec % 10)); } inline int bcd2dec(int bcd) { return ((bcd >> 4) * 10 + (bcd & 0x0f)); } /* yield delay(ノンブロッキング ディレイ) */ inline void delayy(unsigned long tm) { unsigned long now = millis(); while (millis()-now < tm) { yield();} }
(2025.10.25 v0.0.4 曜日表示のバグ修正)
<初期設定>
インクルードファイルや変数の初期化を行なっています。インストールすべきライブラリはInstalled Libraryにまとめてあります。時刻を操作するrtcTime構造はグローバルな変数RTとして定義し各関数でポイント渡しをしなくて済むようにしています。
<setup>
各デバイスの初期化終了後タイトルを表示、RTC8564のVLビットをチェックして初回起動ならばWiFiに接続しNTPから時刻を取得してRTC8564にセットしています。
<loop>
温湿度、気圧を測定しRTC8564から読み込んだ時刻と測定データをOLEDに表示、LEDを反転したら1秒待機、を繰り返します。
<関数>
・i2c関連
・OLED関連
・RTC8564関連
・WiFi関連
・NTP関連
・その他
でそれぞれまとめてあります。
・動作確認
ハード、ソフトともに出来上がった時計へ自家製電源で電源を供給してみると...

やり〜。一発動作です♪
センサーを指で温めると温湿度が変わりますのでうまい具合に動いてくれているようです。

順調、順調。
電流値は平時標準的な20mA〜80mAでの推移でした。
