こんにちは、ENGかぴです。
ESP32-WROOM-32EのWiFi通信でブラウザーから時刻を送信しRTCモジュールに時刻を設定することができます。NTPサーバーにアクセスする方法など時刻管理の方法は様々ですが、外部の回線に接続しない場合に有効な方法です。
ESP32-WROOM-32E開発ボード(秋月電子)を使用しArduino IDEで開発を行います。
RTCモジュールはRX8900を搭載したAE-RX8900(秋月電子)使用しています。温度補償発振器(DTCXO)を内蔵しており高精度で最大で月差13秒で時刻を管理できます。時刻データはI2C通信(Wireライブラリ)を使用することで取得することができます。RX8900を以下ではRTCと表記します。
ESP32-WROOM-32Eで動作確認したことについてリンクをまとめています。
ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方
RTCの時刻データの更新
RX8900モジュールを使用した別の例を下記記事にまとめています。
Seeeduino XIAOでRTCモジュールの情報を更新する
ArduinoでRTCを使用してファイル管理とAPIの実装する
今回は上記2記事とは少し内容を変更して動作確認を行っています。
RTC専用のファイルを作成する
Arduino IDEのプロジェクトファイル表示欄横の▼マークから新規タブを押すと新規ファイル追加することができますが、ここではプロジェクトのフォルダ内にファイルを直接追加する方法を説明しています。

Arduinoプロジェクトとしてメインファイルである「RTC.ino」を新規作成するとフォルダが生成されます。このフォルダの中に以下のファイルを追加します。
- rx8900.ico:RX8900に関する処理を行うファイル
- rx8900.h:RX8900処理に使用する定義をまとめたファイル
これらのファイルをArduinoプロジェクト(RTC.ino)内で作成しArduino IDEを再起動するかArduino IDE内のタグ部分にドロップすることで追加します。
PR:わからないを放置せず、あなたにあったスキルを身に着けるコツを教える テックジム 「書けるが先で、理解が後」を体験しよう!
ファイルの管理の方法
追加したrx8900.icoとrx8900.hの構成を示します。基本的にRTCの操作に関する処理はこれら専用のファイル内で完結するように作ります。
//rx8900.ico
#define RTC_C
#include "rx8900.h"
#undef RTC_C
rx8900.icoファイルでは専用のヘッダーファイルである「rx8900.h」をインクルードする前にRTC_Cを定義しています。コンパイルする手順はコンパイラーに依存するため他のファイルで「RX8900.h」をインクルードしていた場合に重複して変数の定義をしないようにするために実装します。
//-------rx8900.h----------------
#ifndef RX8900_H
#define RX8900_H
//この間に専用の定義を入れる
#endif
#ifdef RTC_C
#define GLOBAL //rx8900.icoの最初でRTC_Cを定義している場合
#else
#define GLOBAL extern //RTC_Cが定義されていなければ外部参照になる
#endif
GLOBAL uint16_t data; //他のファイルでも共通で使用できる変数
#undef GLOBAL
rx8900.hでは一度でも読み込まれているかの確認を行うため#ifndef RX8900_Hの定義の確認を行っています。初めて読み込まれるときはRX8900_Hが定義されていないためRX8900_Hを定義して以降の専用の定義(#endifまで)を行います。
2度目に読み込まれた時はRX8900_Hが定義されているため無視されます。これにより重複した読み込みが発生しません。
次にRTC_Cについてですが、rx8900.icoが読み込まれた時にRTC_Cが定義されるため#define GLOBALが有効になります。GLOBALの後には何も記述がないため空欄と同じ扱いになるので変数の宣言となります。
一方rx8900.ico以外のファイルからrx8900.hが参照された場合はRTC_Hが定義されていないことから#define GLOBAL externが有効になるため外部参照の変数として宣言することになります。
RTCを操作する関数を実装
RTCにデータの読み書きを行うために以下の関数を実装しています。
関 数 | 説 明 |
---|---|
Rx8900begin() | RX8900の初期化を行います。 バックアップ電源が有効であるか判断して時刻の初期化を行います。 |
Rx8900setUint(引数1,引数2) | 引数1に時刻データをセットすると時刻を書き込みます。 引数2に曜日の番号を指定します。 |
Rx8900getDateTime() | 時刻データを引数に指定した変数にセットします。 |
getTemp(引数) | 温度データを引数に指定した変数にセットします。 温度表示するときは換算が必要です。 |
setRegisters(引数,引数2,引数3) | レジスタの設定を変更するときに使用します。 引数には変更するレジスタのアドレスを指定します。 引数2は書き込みするバイト数です。 引数3はデータのアドレスです。 |
getRegisters(引数,引数2,引数3) | レジスタの設定を読み込むときに使用します。 引数には読み込むレジスタのアドレスを指定します。 引数2は読み込むバイト数です。 引数3はデータのアドレスです。 |
実装した関数の使用例は以下の通りとなります。
void setup() {
Rx8900begin();
}
void loop() {
Serial.print(Rx8900getDateTime()); //日時を表示
Serial.print(" ");
Serial.print(Rx8900getTemp());
Serial.println("deg"); //温度を表示
delay(1000);
}
setup()内でRx8900begin()関数をコールすることでRTCの初期化を行います。初期化は各種レジスタのフラグを初期化し時刻更新を通知する割り込みの許可やバックアップ電源が有効であるかの確認を行います。
Rx8900getDataTime()はRX8900から読み込んだ時刻(例:2021-12-16/23:16:30-SAT)を表示します。Rx8900getTemp()ではRX8900が測定している温度を表示します。
関数内の処理の詳細はソースコード全体を確認してください。
PR:スキマ時間で自己啓発!スマホで学べる人気のオンライン資格講座【スタディング】まずは気になる講座を無料で体験しよう!
RTCの時刻データの指定方法
void Rx8900setUint(uint8_t *dt, uint8_t wk){
dateTime buf;
buf.year = (dt[2] << 4 ) + dt[3];
buf.month = (dt[5] << 4 ) + dt[6];
//省略
switch (wk)
{
case 0:
buf.week = SUN;
break;
case 1:
buf.week = MON;
break;
//省略
}
Rx8900setRegisters(SEC_REG,sizeof(buf),(uint8_t*)&buf);
}
Rx8900setUint()はRTCに指定した時刻を書き込みますが、レジスタはBCDデータで管理されているため10進数をBCDデータに変換する必要があります。
例)引数が10進数で1文字ずつ指定されている場合(2021年の下2桁の21)
dat[0]=2 dat[1] = 1とするとBCDで値を表現すると0x21になります。10進数で表現する場合は33になるため分かりにくくなってしまいます。そのため 時間データを10進数で1桁ずつに分割してBCDデータに変換する方法を採用しています。
曜日は曜日に対するビットをセットすることで判断する使用になっているため取得した曜日データに相当するビットを選択しています。
RTCから時刻データを読み出す際は逆にBCD値を10進数に変換します。
Webサーバーを使用する
Webサーバーライブラリの使い方は下記記事まとめています。ここでは要点のみまとめています。
ESP32-WROOM-32EでWebServerを実装する
#include <WebServer.h>
void setup() {
Wserver.on("/", HTTP_GET, HtmlSet); //URLを指定して処理する関数を指定
Wserver.on("/", HTTP_POST, HtmlPost); //URLを指定して処理する関数を指定
Wserver.onNotFound(handleNotFound); //URLが存在しない場合の処理する関数を指定
Wserver.begin(); //Webサーバーの開始
}
void loop(){
Wserver.handleClient();
}
WebServerライブラリを使用するためWebServer.hをインクルードします。on()関数を使用するとブラウザーで指定したURLで表示するページを分岐させることができます。
on()関数では第1引数にURLアドレスの階層を示す文字、第2引数にHTTPの種類を指定、第3引数に接続要求の処理を行う関数を指定します。
onNotFound()関数はページが存在しない時に表示する関数を指定します。on()関数やonNotFound()関数の指定後はbegin()関数でWebサーバーを開始します。
handleClient()関数でクライアントからのリクエストを常時監視します。クライアントからリクエストがあれば登録したURLに対応する処理が呼び出されます。
void HtmlPost(void){
String str = "";
uint8_t wk;
dateTime tim;
NowTime = Wserver.arg("NowTime"); //時間データを取得
weekday = Wserver.arg("weekday"); //曜日データを取得
wk = weekday.toInt();
if( NowTime.length() > 1 ){ //書き込み
for(uint8_t i=0; i< NowTime.length();i++){
stimData[i] = *NowTime.substring(i).c_str(); //char型に変換
stimData[i] = strtol((char*)&stimData[i],NULL,10); //char→bin変換
}
Rx8900setUint(&stimData[0],wk);
}
//ブラウザーに表示するhtml
}
HtmlPost()はブラウザーから送信したデータを取得したときに呼び出されるようにしています。Webサーバーから取得したNowTimeとweekdayをarg()関数で取得します。NowTimeはString型なのchar型に変換してRTC設定用のデータを生成しています。
PR:アクセンチュアの転職なら【コンサルアクシスコンサルティング】
動作確認

ESP32-WROOM-32EとAE-RX8900(RTC)を組み合わせた配線例を示しています。I2CのSCLとSDAはプルアップする必要がありますがAE-RX8900モジュール内でプルアップ抵抗を有効にすると10kΩでプルアップすることができます。
RX8900モジュールは1秒経過(変更可能)する毎に/INTを出力するため時刻が更新されたタイミングを取得することができます。
RX8900モジュールの7ピン(VBAT)は時刻情報をバックアップしたい場合に電気二重層コンデンサなどのバックアップ電源を接続します。
電源を入れると初期の日付がシリアルモニタに表示されて1秒ごとに更新されていますが、スマホの通信開始ボタンを押すと日付が変更されシリアルモニタの表示が指定した時刻になっていることが確認できました。
更新した日時は電源のUSBを抜いて電源をOFFした後に再び電源をONしてシリアルモニタでRTCの時刻を確認すると電気二重層コンデンサによるバックアップによりデータが保持されていました。
iOSの場合はカレンダー上で一度何か変更して元に戻すことで値が変更されセットされます。例えば19日→20日→19日に戻すなど一度でも操作すると動作するようです。
ソースコード全体
ソースコードは記事作成時点において動作確認できていますが、使用しているライブラリの更新により動作が保証できなくなる可能性があります。また、ソースコードを使用したことによって生じた不利益などの一切の責任を負いかねます。参考資料としてお使いください。
リンクからZIPファイル形式のファイルをダウンロードし、任意の場所に展開していただくとテキストファイルが生成されます。
メインのソースコードのesp32-wroom-32-rtcと同じ場所にrx8900.txtとrx8900.hを配置してください。
関連リンク
Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。
Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方
Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方
ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方
PR:
わからないを放置せず、あなたにあったスキルを身に着けるコツを教える テックジムPython入門講座の申込
最後まで、読んでいただきありがとうございました。