・スケッチ
スケッチは以下の通りとなります。
/*
* 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)
// BSP Built-in Library
// Installed Library
// Global Variable
/* WiFi */
/* 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 */
/* NTP DATA */
/* RTC8564 */
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 */
Adafruit_SSD1306 dsp(OLED_WIDTH, OLED_HEIGHT, &Wire, OLED_RESET);
static const char *WDay[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
/* BME280 */
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関連
・その他
でそれぞれまとめてあります。