ArduinoのRTCモジュール:DS3231・DS1307・DS1302の違いと時刻・アラーム
ArduinoのRTCモジュールガイド。DS3231・DS1307・DS1302の違い、RTClibでの時刻設定、LCD時計、時刻指定の水やり、CR2032の充電の落とし穴、ESP32のNTPまで解説します。
ArduinoのRTCモジュールは、ボードの電源が切れても日付と時刻を数え続ける小さな時計ボードです。RTCは「Real Time Clock」(リアルタイムクロック)の略です。Arduino自身のmillis()カウンターは電源を入れるたびにゼロから始まり、数日で数分ずれることもあります。一方、RTCモジュールはコイン電池(CR2032)のおかげで、何年も秒単位の正確さで動き続けます。このモジュールは、卓上時計、自動水やりシステム、ペット用の自動給餌器、タイムスタンプ付きのデータロガー、タイマー付き照明などのプロジェクトで使われています。
このガイドでは、最もよく使われる3つのチップ、DS1307、DS3231、DS1302を比べ、どれをなぜ選ぶべきかを説明します。続いて、RTClibライブラリで時刻を設定して読み取る方法、I2C LCDに時刻と日付を表示する方法、毎日決まった時刻にポンプを動かすタイマーの作り方、DS3231のハードウェアアラームの使い方を、ステップごとに見ていきます。最後の章では、多くのモジュールをだめにするCR2032の充電回路の落とし穴、ESP32でNTPを使ってインターネットから時刻を取得する方法、Raspberry Pi Picoの内蔵RTCを取り上げます。

RTCモジュールとは?どのように動く?
RTCチップの中心にあるのは、32.768 kHzの時計用水晶振動子です。この数字はでたらめではありません。32,768は2の15乗なので、チップはこの周波数を15回半分にして、ちょうど1秒の刻みを作ります。刻みごとに秒、分、時、日、月、年のレジスタが更新され、月ごとの日数やうるう年も2100年まで自動で処理されます。ArduinoはこれらのレジスタをI2Cで読み取ったり、新しい時刻を書き込んだりします。値はBCD形式で保存されます(たとえば59秒は0x59として保存されます)。この変換はライブラリが代わりに行います。
RTCの本当の工夫は2つの電源入力にあります。VCCがなくなると、チップは自動的に電池入力(VBAT)に切り替わり、時刻を数え続けるのに必要なだけの電流、つまり1マイクロアンペア未満から数マイクロアンペアしか消費しません。このとき、DS1307はI2Cでのアクセスを止めますが、DS3231は電池駆動中でも読み取れます。どちらの場合も時計は止まりません。電源が戻るとArduinoが起動し、RTCから正しい時刻を読み取って、中断したところから処理を続けます。Arduino Unoに載っているセラミック発振子の誤差のため、millis()で管理する時計は1日に数分ずれることがあり、リセットのたびに失われます。だからこそ、時刻を扱うプロジェクトには別にRTCが必要なのです。
DS1307・DS3231・DS1302の違い
| 特徴 | DS1307 | DS3231 | DS1302 |
|---|---|---|---|
| インターフェース | I2C(100 kHz)、アドレス0x68 | I2C(最大400 kHz)、アドレス0x68 | 3線式シリアル:CE(RST)、I/O(DAT)、SCLK(CLK) |
| 電源電圧 | 4.5〜5.5 V | 2.3〜5.5 V | 2.0〜5.5 V |
| 水晶振動子 | 外付け32.768 kHz(12.5 pF) | チップに内蔵、温度補償型(TCXO) | 外付け32.768 kHz(6 pF) |
| 精度 | 水晶次第。±20 ppmなら1日に約1.7秒、1か月に約1分 | ±2 ppm(0〜40 °C):1か月に約5秒、1年に約1分 | 水晶次第。DS1307と同程度かそれ以下 |
| その他の機能 | 電池でバックアップされる56バイトのRAM、矩形波出力(SQW) | アラーム2つ、INT/SQW出力、温度センサー(±3 °C)、エージング補正 | 31バイトのRAM、トリクル充電回路を内蔵 |
| 電池駆動時の電流 | 約300〜500 nA | 約1〜3 µA | 2 Vで300 nA未満 |
| Arduinoライブラリ | RTClib(Adafruit) | RTClib(Adafruit) | Rtc by Makuna |
| おすすめの用途 | シンプルな5 Vの時計プロジェクト | ほぼすべてのプロジェクト。ESP32やPicoにも最適 | 古いキットや学習用のプロジェクト |
精度の違いを具体的に見てみましょう。1 ppmは100万分の1を意味し、1日は86,400秒です。普通の±20 ppmの水晶は1日に最大1.7秒、1か月で約52秒ずれることがあります。さらに室温から離れるほど水晶の周波数が変わり、誤差は大きくなります。一方、DS3231は水晶をチップの中に持ち、64秒ごとに温度を測って周波数を補正します。そのため0〜40 °Cで±2 ppm、つまり1年に約1分の誤差に収まり、-40〜+85 °Cでも±3.5 ppm以内を保ちます。
どのRTCモジュールを選べばいい?
- 新しいプロジェクト、ESP32やPicoで使う場合:DS3231を選びましょう。3.3 Vでも5 Vでも動き、アラームがあり、最も正確に時刻を保ちます。価格差もわずかです。
- すでにDS1307を持っていてArduino Unoを使う場合:そのまま使ってかまいません。ただし1か月に約1分のずれを見込み、ときどき時刻を合わせ直してください。
- スターターキットにDS1302が入っていた場合:RTCの仕組みを学ぶには十分ですが、何か月も正確なままの時計は期待しないでください。
- 常時インターネットにつながるESP32の場合:まずはモジュールなしでNTPを使いましょう。停電のあと、インターネットが戻る前に時刻がわかっている必要があるなら、DS3231を追加します。
よく見かける完成ボードは主に2つです。DS3231チップを載せたZS-042と、DS1307を載せたTiny RTCです。どちらにもデータ保存用の4 KBのAT24C32 EEPROMが載っています。そのため、I2Cスキャナーを実行すると0x68の隣に2つ目のアドレスが表示されるのは正常です(ZS-042ではたいてい0x57、Tiny RTCでは0x50)。このアドレスは時計ではなくEEPROMのものです。DS1302はI2Cをまったく使いません。独自の3線式プロトコルで通信し、デジタルピンを3本使います。
RTCモジュールの配線:Arduino Uno、ESP32、Pico
| RTCのピン | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| VCC | 5V | 3V3(DS3231) | 3V3(OUT)、物理ピン36(DS3231) |
| GND | GND | GND | GND |
| SDA | A4 | GPIO 21 | GP4(I2C0) |
| SCL | A5 | GPIO 22 | GP5(I2C0) |
| SQW / INT(任意) | D2(割り込みピン) | 任意のGPIO(例:GPIO 4) | 任意のGPピン(例:GP15) |
Uno R3では、AREFピンの隣にあるSDAとSCLのヘッダーもA4とA5につながっているので、どちらを使ってもかまいません。RTCは、I2C LCDやOLEDディスプレイと同じ2本の線を共有できます。デバイスごとにアドレスが違うので、衝突は起きません。特にブレッドボードでは、配線を20〜30 cmより短くすると、ランダムな読み取りエラーを防げます。SQW/INTピンは、ハードウェアアラームを使う場合にだけ接続すれば十分です。
RTClibのインストールと時刻の設定
- Arduino IDEでライブラリマネージャを開き、RTClibと入力して、Adafruit製のライブラリをインストールします。依存ライブラリについて聞かれたら全てをインストールを選び、Adafruit BusIOも一緒にインストールします。ライブラリを追加したことがない場合は、Arduinoにライブラリを追加する方法で手順を説明しています。
- 上の表のとおりにモジュールを配線し、ホルダーに電池を入れます。
- 下のコードを書き込みます。初回はモジュールが時刻を知らないので、コードがパソコンのコンパイル時刻をRTCに書き込みます。
- シリアルモニタを9600 baud、改行の設定を「LFのみ」(Newline)にして開きます。秒単位まで時刻を合わせるには、2026-11-03 14:30:00の形式で入力してEnterキーを押します。
- USBケーブルを抜いて数分待ち、もう一度挿します。時計が中断したところから正しく進んでいれば、電池バックアップは機能しています。
1#include <Wire.h>
2#include <RTClib.h>
3
4RTC_DS3231 rtc; // DS1307 kullanıyorsan: RTC_DS1307 rtc; / for a DS1307: RTC_DS1307 rtc;
5
6void setup() {
7 Serial.begin(9600);
8 if (!rtc.begin()) { // modül I2C hattında yok / module not found on the I2C bus
9 Serial.println(F("RTC bulunamadi / RTC not found"));
10 while (true);
11 }
12 if (rtc.lostPower()) { // ilk çalıştırma ya da pil bitmiş / first run or dead battery
13 rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // derleme saatini yaz / write the compile time
14 Serial.println(F("Saat derleme zamanina ayarlandi / Clock set to compile time"));
15 }
16 Serial.println(F("Ayar icin yaz / To set, type: 2026-11-03 14:30:00"));
17}
18
19void loop() {
20 // Seri monitörden saat geldiyse ayarla / set the time sent from the Serial Monitor
21 if (Serial.available()) {
22 String line = Serial.readStringUntil('\n');
23 int y, mo, d, h, mi, s;
24 if (sscanf(line.c_str(), "%d-%d-%d %d:%d:%d", &y, &mo, &d, &h, &mi, &s) == 6) {
25 rtc.adjust(DateTime(y, mo, d, h, mi, s));
26 Serial.println(F("Saat ayarlandi / Time set"));
27 }
28 }
29
30 DateTime now = rtc.now();
31 char buf[] = "DD.MM.YYYY hh:mm:ss"; // biçim şablonu / format template
32 Serial.print(now.toString(buf));
33 Serial.print(F(" "));
34 Serial.print(rtc.getTemperature()); // yalnız DS3231 / DS3231 only
35 Serial.println(F(" C"));
36 delay(1000);
37}rtc.begin()は、I2Cバス上にモジュールが見つからないとfalseを返します。その場合は、まず配線を確認してください。rtc.lostPower()は、DS3231の発振停止フラグ(OSF)を読み取ります。モジュールが初めて動くときや、電池切れで時計が止まったときにtrueを返し、コードは時計をコンパイル時刻に合わせます。DateTime(F(__DATE__), F(__TIME__))は、コンパイラがコードをビルドした瞬間を使います。書き込みに数秒かかるため時計は少し遅れるので、シリアルモニタから微調整できると便利です。toString()は、「DD.MM.YYYY hh:mm:ss」というテンプレートの文字を実際の値に置き換えます。
| 関数 | 動作 |
|---|---|
| rtc.begin() | モジュールを起動する。見つからなければfalseを返す |
| rtc.adjust(DateTime(...)) | 時刻と日付を書き込む |
| rtc.now() | 現在の時刻をDateTimeオブジェクトとして読み取る |
| now.year(), now.month(), now.day()(日付) | 年、月、日 |
| now.hour(), now.minute(), now.second()(時刻) | 時(0〜23)、分、秒 |
| now.dayOfTheWeek() | 曜日:0 = 日曜日、6 = 土曜日 |
| now.unixtime() | 1970年1月1日からの経過秒数。2つの時刻を比べるのに最適 |
| now + TimeSpan(0, 1, 30, 0) | 1時間30分を足す(日、時、分、秒) |
| rtc.lostPower() / rtc.isrunning()(有効性の確認) | 時刻が有効かどうかを返す(DS3231 / DS1307) |
| rtc.getTemperature() | チップの温度(°C、DS3231のみ) |
DS1307を使う場合、違いはオブジェクト名だけです。RTC_DS1307 rtc;と書き、lostPower()の代わりに!rtc.isrunning()でチェックし、getTemperature()の行を削除します。DS1307に初めて電源を入れたとき、発振器は停止した状態(CHビットが1)で始まります。adjust()がこのビットをクリアして時計を動かします。DS1307の電池バックアップ付き56バイトRAMには、rtc.writenvram()とrtc.readnvram()でアラーム時刻などの小さな設定を保存できます。電池のおかげで、このデータも停電で消えません。
LCDに時刻と日付を表示する
時刻をシリアルモニタではなくディスプレイで見られるようにすると、プロジェクトは本物の卓上時計になります。16x2のI2C LCDとRTCは同じSDA-SCLバスにつなぎます。LCDのアドレスはたいてい0x27か0x3F、RTCは0x68です。下のコードは、上の行に日付と曜日の略称を、下の行に時刻とDS3231が測った温度を表示します。LCDの配線とライブラリの設定については、I2C LCDディスプレイの使い方をご覧ください。
1#include <Wire.h>
2#include <RTClib.h>
3#include <LiquidCrystal_I2C.h>
4
5RTC_DS3231 rtc;
6LiquidCrystal_I2C lcd(0x27, 16, 2); // adres 0x27 ya da 0x3F / address 0x27 or 0x3F
7
8// dayOfTheWeek(): 0 = Pazar / 0 = Sunday
9const char* const days[7] = {"Paz", "Pzt", "Sal", "Car", "Per", "Cum", "Cmt"};
10int lastSecond = -1;
11
12void setup() {
13 lcd.init();
14 lcd.backlight();
15 if (!rtc.begin()) {
16 lcd.print("RTC yok!"); // modül bulunamadı / module not found
17 while (true);
18 }
19 if (rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
20}
21
22void loop() {
23 DateTime now = rtc.now();
24 if (now.second() == lastSecond) return; // saniye değişmediyse yazma / skip until the second changes
25 lastSecond = now.second();
26
27 char line1[17], line2[17];
28 snprintf(line1, sizeof(line1), "%02d.%02d.%04d %s",
29 now.day(), now.month(), now.year(), days[now.dayOfTheWeek()]);
30 snprintf(line2, sizeof(line2), "%02d:%02d:%02d %2dC",
31 now.hour(), now.minute(), now.second(), (int)rtc.getTemperature());
32 lcd.setCursor(0, 0);
33 lcd.print(line1); // ör. / e.g. 03.11.2026 Sal
34 lcd.setCursor(0, 1);
35 lcd.print(line2); // ör. / e.g. 14:05:09 23C
36}snprintfの中の%02dは、1桁の数字の前にゼロを付けます。そのため時刻は9:5:3ではなく09:05:03と表示され、行の長さが変わりません。長さが一定なのでlcd.clear()は必要ありません。ループのたびに画面を消すと、ちらつきの原因になります。コードは秒が変わったときだけディスプレイに書き込むので、I2Cバスを無駄に占有しません。days配列にはトルコ語の曜日の略称が入っているので、Sun、Mon、Tueのような略称に置き換えてください(一般的な文字LCDは漢字を表示できないため、英語の略称が確実です)。最初の要素は日曜日のままにします。
アラームと時刻指定のタスク:自動水やりとペットの給餌
RTCの一番便利なところは、決まった時刻に仕事をさせられることです。毎朝08:00に植物に水をやる、19:30に猫のエサを出す、夜に水槽のライトを消す、といった具合です。方法は2つあります。1つ目はソフトウェアでの比較です。loop()の中で時刻を読み、タスクの時刻になったかどうかを確かめます。シンプルで、タスクはいくつでも追加できます。2つ目はDS3231のハードウェアアラームです。設定した時刻になるとチップがINTピンをLOWにし、割り込みでArduinoに知らせます。スリープ中のマイコンを起こすことさえできます。
ソフトウェアで比較するときによくある間違いは、now.second() == 0のように、秒がぴったり一致するかを調べることです。loop()に長いdelay()があると、その1秒を逃してタスクが一度も実行されないことがあります。下のコードは時と分を比べ、同じ分にタスクが2回動かないように日と分からなるキーを保存し、ポンプの動作時間をmillis()で計ります。ポンプはリレーモジュールを介して駆動します。ポンプの配線はミニウォーターポンプの記事にあります。
1#include <Wire.h>
2#include <RTClib.h>
3
4RTC_DS3231 rtc;
5
6const byte RELAY_PIN = 7;
7const byte RELAY_ON = LOW; // çoğu röle modülü LOW ile çeker / most relay modules switch on LOW
8const byte RELAY_OFF = HIGH;
9const unsigned long RUN_MS = 10000; // pompa 10 saniye çalışır / the pump runs for 10 seconds
10
11struct Task { byte hour; byte minute; };
12Task tasks[] = { {8, 0}, {19, 30} }; // her gün 08:00 ve 19:30 / every day at 08:00 and 19:30
13const byte TASK_COUNT = sizeof(tasks) / sizeof(tasks[0]);
14
15long lastRunKey = -1; // aynı dakikada ikinci kez çalışmasın / never run twice in one minute
16bool running = false;
17unsigned long startMs = 0;
18
19void setup() {
20 pinMode(RELAY_PIN, OUTPUT);
21 digitalWrite(RELAY_PIN, RELAY_OFF); // açılışta kapalı / off at start-up
22 rtc.begin();
23}
24
25void loop() {
26 DateTime now = rtc.now();
27 long key = (long)now.day() * 1440 + now.hour() * 60 + now.minute(); // gün + dakika / day + minute
28
29 for (byte i = 0; i < TASK_COUNT; i++) {
30 if (now.hour() == tasks[i].hour && now.minute() == tasks[i].minute && key != lastRunKey) {
31 lastRunKey = key;
32 running = true;
33 startMs = millis();
34 digitalWrite(RELAY_PIN, RELAY_ON); // pompayı aç / pump on
35 }
36 }
37
38 if (running && millis() - startMs >= RUN_MS) {
39 running = false;
40 digitalWrite(RELAY_PIN, RELAY_OFF); // pompayı kapat / pump off
41 }
42 delay(200);
43}キーに日を含めることが大切です。分だけを保存していてタスクが1つしかない場合、翌日の同じ分になってもキーが同じままなので、タスクは二度と実行されません。ペット用の給餌器を作るなら、リレーの代わりにサーボモーターを使います。タスクの時刻にサーボを90度まで回し、1秒後に戻すと、ホッパーのふたが開いて1回分のエサが落ちます。水やりを賢くするには、タスクの時刻に土壌水分センサーを読み、土がすでに湿っていればポンプを動かさないようにします。下の2つ目の例は、同じことをDS3231のハードウェアアラームで行います。
1#include <Wire.h>
2#include <RTClib.h>
3
4RTC_DS3231 rtc;
5const byte ALARM_PIN = 2; // DS3231 SQW/INT -> D2 (kesme pini / interrupt pin)
6volatile bool alarmFlag = false;
7
8void onAlarm() { // kesme fonksiyonu kısa olmalı / keep the ISR short
9 alarmFlag = true;
10}
11
12void setup() {
13 Serial.begin(9600);
14 rtc.begin();
15 rtc.disable32K(); // 32K çıkışını kapat / turn off the 32K output
16 rtc.writeSqwPinMode(DS3231_OFF); // SQW pini alarm çıkışı olur / SQW becomes the alarm output
17 rtc.clearAlarm(1);
18 rtc.clearAlarm(2);
19 rtc.disableAlarm(2);
20
21 // Her gün 07:30:00; tarih kısmı yok sayılır / every day at 07:30:00; the date part is ignored
22 rtc.setAlarm1(DateTime(2026, 1, 1, 7, 30, 0), DS3231_A1_Hour);
23
24 pinMode(ALARM_PIN, INPUT_PULLUP); // INT çıkışı open-drain / INT is an open-drain output
25 attachInterrupt(digitalPinToInterrupt(ALARM_PIN), onAlarm, FALLING);
26}
27
28void loop() {
29 if (alarmFlag) {
30 alarmFlag = false;
31 rtc.clearAlarm(1); // temizlenmezse pin LOW kalır / the pin stays LOW until cleared
32 Serial.println(F("Alarm: mama vakti / Feeding time"));
33 // Servo, röle ya da buzzer kodunu buraya yaz / put your servo, relay or buzzer code here
34 }
35}DS3231にはアラームが2つあります。アラーム1は1秒単位の分解能を持ち、DS3231_A1_Hourモードは時・分・秒が一致したとき、つまり毎日同じ時刻に作動します。ほかのモードを使えば、毎秒(PerSecond)、毎分(Second)、毎時(Minute)、毎月の決まった日(Date)、毎週の決まった曜日(Day)にアラームを設定できます。アラーム2の分解能は1分単位です。SQW/INTピンはオープンドレインでアクティブLOWの出力です。アラームが作動するとLOWになり、clearAlarm()が呼ばれるまでそのままです。そのため、割り込み関数ではフラグを立てるだけにして、実際の処理はloop()で行うのが最善です。割り込みの仕組みについては、Arduinoの割り込みをご覧ください。
RTCの電池はどのくらい持つ?
電池の寿命は簡単に見積もれます。持続時間(時間)= 電池容量(mAh)÷ 消費電流(mA)です。CR2032の容量は約220 mAhです。DS3231は電池モードで最悪でも約3 µA、つまり0.003 mAしか消費しません。220 ÷ 0.003 ≈ 73,000時間、およそ8年です。電池が使われるのはVCCが切れている間だけです。常に電源が入っている機器にモジュールが組み込まれていれば、電池はほとんど減らず、ずっと長持ちします。実際には、電池そのものの保存寿命とモジュールの充電回路が限界を決めることがほとんどです。
| チップ | 電池駆動時の電流 | CR2032(220 mAh)での計算上の持続時間 |
|---|---|---|
| DS1307 | 約0.5 µA | 約50年。実際には電池の保存寿命(約10年)で制限される |
| DS3231 | 約3 µA(最悪の場合) | 約8年 |
| DS1302 | 約0.3 µA | 電池の保存寿命で制限される |
| 充電回路が有効なモジュール + CR2032 | 電池に電流が強制的に流し込まれる | 予測できない。電池が膨らんで早く寿命を迎えることがある |
CR2032電池と充電回路の落とし穴
安価なDS3231(ZS-042)やDS1307(Tiny RTC)モジュールの多くには、本来は充電式のLIR2032用に設計された簡単な充電回路が載っています。ZS-042では、VCCから1N4148ダイオードと200 Ωの抵抗を通って電池に電流が流れます。Tiny RTCにも同じような回路があります。問題は、これらのモジュールが充電できないCR2032付きで売られていることが多い点です。モジュールを5 Vで動かすと、ダイオードを通ったあとに残る約4.3 Vが、3 Vのリチウム電池に電流を流し込み続けます。充電を想定していない電池は、時間がたつと膨らんだり液漏れしたりして早く寿命を迎え、「電池は入っているのに時計が何度もリセットされる」という問題が起きます。
- CR2032を使うなら充電経路を断つ:ZS-042では、ピンヘッダーの近くにある「201」と書かれた200 ΩのSMD抵抗か、その隣のダイオードをはんだごてで取り外します。表記が読めない抵抗は、SMDコード解読ツールで調べられます。これが最も一般的で安全な対処法です。
- 回路をそのままにするならLIR2032を使う:充電式電池の容量(約40 mAh)はCR2032(約220 mAh)のおよそ5分の1ですが、モジュールに電源が入っている限り満充電に保たれます。
- モジュールを3.3 Vで動かす場合:ダイオードを通ったあと電池に届くのは約2.6 Vだけです。LIR2032は満充電にならず、CR2032にはほとんど電流が流れません。それでも、抵抗を外すのが最もすっきりした解決法です。
- DS1302に内蔵されたトリクル充電回路は、電源投入時にはオフです。CR2032を使う場合は、コードでオンにしないでください。
- 電池を測る:新品のCR2032は3.0〜3.2 Vを示します。2.5 Vを下回ったら交換してください。
DS1302の使い方(3線式の接続)

DS1302モジュールのピンはVCC、GND、CLK、DAT、RSTの5本です。I2Cを使わないので、A4-A5ではなく任意の3本のデジタルピンにつなぎます。下の例ではDATをD4、CLKをD5、RSTをD2に接続し、モジュールは5Vから給電します。AdafruitのRTClibはDS1302に対応していないため、代わりにライブラリマネージャからRtc by Makunaライブラリをインストールします。DS1302には保護ビットが2つあります。書き込み保護がオンの間は時刻を設定できず、クロック停止ビットがセットされている間は時計が進みません。コードは両方を確認し、保存されている時刻が有効でなければ、時計をコンパイル時刻に合わせます。
1#include <RtcDS1302.h>
2
3ThreeWire myWire(4, 5, 2); // DAT, CLK, RST pinleri / DAT, CLK, RST pins
4RtcDS1302<ThreeWire> rtc(myWire);
5
6void setup() {
7 Serial.begin(9600);
8 rtc.Begin();
9 if (rtc.GetIsWriteProtected()) rtc.SetIsWriteProtected(false); // yazma korumasını kaldır / remove write protection
10 if (!rtc.GetIsRunning()) rtc.SetIsRunning(true); // saati başlat / start the clock
11
12 RtcDateTime compiled(__DATE__, __TIME__);
13 if (!rtc.IsDateTimeValid() || rtc.GetDateTime() < compiled) {
14 rtc.SetDateTime(compiled); // derleme saatine ayarla / set to compile time
15 }
16}
17
18void loop() {
19 RtcDateTime now = rtc.GetDateTime();
20 char buf[20];
21 snprintf(buf, sizeof(buf), "%02u.%02u.%04u %02u:%02u:%02u",
22 now.Day(), now.Month(), now.Year(), now.Hour(), now.Minute(), now.Second());
23 Serial.println(buf);
24 delay(1000);
25}DS1302モジュールで最も多い不満は、時計が1週間で数分ずれることです。理由の1つは、このチップが6 pFの負荷容量に合わせた水晶を必要とするのに、安価なモジュールでは定格の違う水晶が使われている場合があることです。長くてゆるい配線も、DS1302のシリアルラインで読み取りエラーの原因になります。DS1302を持っているなら、RTCの仕組みを学ぶのには最適です。ただし、何か月も正確でなければならない時計を作るなら、DS3231に切り替えましょう。
ESP32ではRTCの代わりにNTP:インターネットから時刻を取得する
ESP32はWi-Fiにつながるので、RTCモジュールがまったく要らないこともよくあります。NTP(Network Time Protocol)を使えば、pool.ntp.orgなどのタイムサーバーから数秒で正しい時刻を取得できます。ESP32のArduinoコアには、そのためのconfigTzTime()とgetLocalTime()関数が用意されていて、タイムゾーンはルール文字列で指定します。トルコは2016年から夏時間なしのUTC+3なので、コードのルールは+3時間の固定オフセットです。日本も夏時間のないUTC+9なので、ルールを「JST-9」にすれば固定の+9時間になります。ヨーロッパの大部分のように夏時間がある地域では、コードのコメントにある中央ヨーロッパのルールが切り替え日を自動で計算します。イギリスでは「GMT0BST,M3.5.0/1,M10.5.0」を使います。
NTPの弱点は、インターネットに依存することです。停電のあとルーターが起動するまでの間や、Wi-Fiのパスワードが変わったときには、ESP32は時刻を知ることができません。ESP32自身の内部RTCタイマーはディープスリープ中も時刻を保ちますが、電源が完全に切れるとリセットされます。最も確実なのは両方を組み合わせる方法です。起動時にNTPにつながればDS3231をインターネットの時刻に合わせ、つながらなければDS3231から時刻を読み、1日に1回再同期します。NTPについて詳しくは、ESP32のNTPで時刻を取得するをご覧ください。
1#include <WiFi.h>
2#include <Wire.h>
3#include <RTClib.h>
4#include <time.h>
5
6const char* WIFI_SSID = "AgAdin"; // Wi-Fi ağ adı / Wi-Fi network name
7const char* WIFI_PASS = "Sifren"; // Wi-Fi şifresi / Wi-Fi password
8// Türkiye: UTC+3, yaz saati yok / Turkey: UTC+3, no daylight saving
9// Orta Avrupa / Central Europe: "CET-1CEST,M3.5.0,M10.5.0/3"
10const char* TZ_INFO = "<+03>-3";
11
12RTC_DS3231 rtc;
13unsigned long lastSync = 0;
14
15void syncFromNtp() {
16 struct tm t;
17 if (getLocalTime(&t, 10000)) { // en fazla 10 sn bekle / wait up to 10 s
18 rtc.adjust(DateTime(t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec));
19 Serial.println("DS3231 NTP ile ayarlandi / DS3231 synced from NTP");
20 }
21 lastSync = millis();
22}
23
24void setup() {
25 Serial.begin(115200);
26 Wire.begin(21, 22); // SDA, SCL pinleri / SDA, SCL pins
27 rtc.begin();
28
29 WiFi.begin(WIFI_SSID, WIFI_PASS);
30 unsigned long start = millis();
31 while (WiFi.status() != WL_CONNECTED && millis() - start < 15000) delay(250);
32
33 if (WiFi.status() == WL_CONNECTED) {
34 configTzTime(TZ_INFO, "pool.ntp.org", "time.google.com");
35 syncFromNtp();
36 } else {
37 Serial.println("Wi-Fi yok, DS3231 saati kullaniliyor / No Wi-Fi, using the DS3231");
38 }
39}
40
41void loop() {
42 DateTime now = rtc.now();
43 char buf[] = "DD.MM.YYYY hh:mm:ss";
44 Serial.println(now.toString(buf));
45
46 if (WiFi.status() == WL_CONNECTED && millis() - lastSync > 86400000UL) {
47 syncFromNtp(); // günde bir kez eşitle / sync once a day
48 }
49 delay(1000);
50}Raspberry Pi Picoの内蔵RTC
Raspberry Pi PicoのRP2040チップにはRTCブロックが入っています。一方、Pico 2のRP2350は、常時動作するタイマー(AONタイマー)で同じ役割を果たします。MicroPythonでは、どちらも同じmachine.RTCクラスで扱います。RTC().datetime()は時刻を(年、月、日、曜日、時、分、秒、サブ秒)の形式で読み書きし、time.localtime()もこの時計を使います。ただし、この時計には電池がありません。Picoの電源が切れると、MicroPythonは時計を2021年1月1日 00:00から始めます。
ここには初心者がはまりやすい落とし穴があります。Thonnyは初期設定で、接続したときにPicoの時計をパソコンの時計に合わせます。そのためテスト中はすべて正しく見えますが、Picoをスマートフォンの充電器や電池につないだとたん、時計が2021年に戻ってしまいます。解決策は、PicoにDS3231をつなぎ、起動時にそこから内部RTCを設定することです。下のコードはドライバファイルを必要とせず、DS3231の最初の7つのレジスタをI2Cで読み取り、BCDから10進数に変換します。Pico Wならntptime.settime()でインターネットから時刻を取得することもできます。これはUTCを返すので、タイムゾーンの時差は自分で足す必要があります(日本なら9時間)。Picoを初めて使う場合は、Raspberry Pi Picoとは?から始めてください。
1from machine import Pin, I2C, RTC
2import time
3
4i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=100000)
5DS_ADDR = 0x68 # DS3231 ve DS1307 adresi / DS3231 and DS1307 address
6
7def bcd2dec(b):
8 return (b >> 4) * 10 + (b & 0x0F)
9
10def dec2bcd(d):
11 return ((d // 10) << 4) | (d % 10)
12
13def ds_read():
14 b = i2c.readfrom_mem(DS_ADDR, 0x00, 7) # saniyeden yıla 7 kayıt / 7 registers, seconds to year
15 return (2000 + bcd2dec(b[6]), bcd2dec(b[5] & 0x1F), bcd2dec(b[4] & 0x3F),
16 bcd2dec(b[2] & 0x3F), bcd2dec(b[1] & 0x7F), bcd2dec(b[0] & 0x7F))
17
18def ds_write(y, mo, d, h, mi, s):
19 # 24 saat biçiminde yazar; 4. bayt haftanın günü (1-7) / writes 24-hour time; 4th byte is day of week (1-7)
20 i2c.writeto_mem(DS_ADDR, 0x00, bytes([dec2bcd(s), dec2bcd(mi), dec2bcd(h), 1,
21 dec2bcd(d), dec2bcd(mo), dec2bcd(y - 2000)]))
22
23# İlk kurulumda bir kez çalıştır, sonra satırı sil / run once on first setup, then delete the line
24# ds_write(2026, 11, 3, 14, 30, 0)
25
26y, mo, d, h, mi, s = ds_read()
27RTC().datetime((y, mo, d, 0, h, mi, s, 0)) # dahili RTC'yi ayarla / set the internal RTC
28
29while True:
30 t = time.localtime() # artık dahili RTC'den okunur / now read from the internal RTC
31 print("{:02d}.{:02d}.{} {:02d}:{:02d}:{:02d}".format(t[2], t[1], t[0], t[3], t[4], t[5]))
32 time.sleep(1)RTCモジュールを使ったプロジェクトのアイデア
- デジタル卓上時計とカレンダー:時刻、日付、DS3231が測った温度をLCD、OLED、LEDマトリクスに表示します。
- 植物の自動水やり:毎朝土の水分を確認し、必要ならポンプを決まった時間だけ動かします。
- ペット用の自動給餌器:1日に2〜3回、サーボでホッパーのふたを開けて1回分のエサを落とします。
- タイムスタンプ付きデータロガー:温度と湿度の測定値を日付・時刻と一緒にSDカードモジュールに書き込み、Excelでグラフにします。
- 学校や工房のチャイム:各授業の始まりと終わりにブザーやリレーで動くベルを鳴らし、dayOfTheWeek()で週末をスキップします。
- 水槽と植物育成ライトのタイマー:朝にライトをつけて夜に消し、PWMで明るさをゆっくり変えて日の出と日の入りを演出します。
よくある間違いと対処法
- 「RTC not found」(RTCが見つからない)と表示され、begin()がfalseを返す:SDAとSCLが逆になっている、GNDが共通になっていない、またはモジュールに電源が入っていません。I2Cスキャナーのスケッチを実行し、アドレス0x68が表示されるか確認してください。
- 2165年、165、85のようなおかしな値:チップからデータが来ておらず、読み取ったバイトがすべて0xFFになっています。配線を確認し、DS1307を3.3 Vで動かしている場合は5 Vに切り替えてください。
- リセットのたびに時計が同じ時刻に戻る:rtc.adjust()の行が条件なしでsetup()に残っています。lostPower()の条件に結び付けてください。
- 停電のあと時計がリセットされる:電池が切れている、逆向きに入っている、または入っていません。電圧を測り、充電回路がCR2032をだめにした可能性も忘れないでください。
- 時計が1週間で数分ずれる:DS1307やDS1302の水晶と温度変化が原因です。DS3231に切り替えるか、NTPで定期的に時刻を合わせてください。
- 時刻がちょうど1時間か数時間ずれている:タイムゾーンか夏時間の設定が間違っています。RTCにはローカル時刻を書き込み、NTPを使う場合は正しいタイムゾーンのルールを選んでください。
- DS3231の温度が室温より高く出る:この値はチップ自身の温度で、精度は±3 °C、ボードの発熱の影響も受けます。周囲の温度には別のセンサーを使ってください。
まとめると、時刻と日付が必要なほぼすべてのプロジェクトではDS3231が正解です。DS1307はシンプルな5 Vのプロジェクトで役に立ち、DS1302は学習用なら十分です。ESP32では、インターネットがあればNTP、なければDS3231を使い、Picoでは起動時にDS3231から内部RTCを設定するのが最も確実な方法です。BlueGraysアプリでは、DS1302での時計の設定とLCD時計、DS1307のドットマトリクス時計とカレンダー、RTCを使ったESP32デジタル時計、NTPによるインターネット時刻、タイムスタンプ付きの温度ロガーといったプロジェクトを、回路図とコード付きでステップごとに見られます。
よくある質問
RTCモジュールは電池なしで動きますか?
VCCがつながっている間は、時計は動き、読み取ることもできます。しかし電源が切れた瞬間に時計は止まるかリセットされ、次の起動時にもう一度設定しなければなりません。RTCを使う目的そのものが停電中も時刻を保つことなので、電池はほぼ必ず必要です。DS1307を電池なしで使う場合は、VBATピンをGNDに接続する必要があります。
DS3231とDS1307はどちらがいいですか?
ほとんどの場合、DS3231のほうが優れています。温度補償された内蔵水晶のおかげで誤差は1年に約1分ですが、DS1307は1か月に最大1分ずれることがあります。DS3231は2.3〜5.5 Vで動くのでESP32やPicoにそのまま使え、アラーム2つと温度センサーも備えています。RTClibを使えば、どちらもコードはほぼ同じです。
RTCモジュールの電池はどのくらい持ちますか?
モジュールが電池だけで動いているとき、DS3231は最大約3 µA、DS1307は約0.5 µAを消費します。計算上、220 mAhのCR2032はこの電流で8年以上持ちます。実際には電池の保存寿命やモジュールの充電回路によって短くなることがあり、充電回路が有効なモジュールでは、CR2032がずっと早く切れることもあります。
RTCモジュールの時刻が何度もリセットされるのはなぜですか?
よくある原因は3つです。setup()内のrtc.adjust()の行が起動のたびに時計をコンパイル時刻に戻している、電池が切れているか逆向きに入っている、充電回路がCR2032をだめにしている、のいずれかです。まずadjust()の行をlostPower()の条件に結び付け、次にテスターで電池の電圧を測り、2.5 Vを下回っていれば電池を交換してください。
ESP32にRTCモジュールは必要ですか?
常にインターネットにつながっている機器なら必要ありません。ESP32はNTPで数秒以内に正しい時刻を取得できます。インターネットが切れる可能性があるプロジェクト、停電後にルーターが復旧する前に時刻がわかっていなければならないプロジェクト、完全にオフラインで動くプロジェクトでは、DS3231を追加するのが最も安全です。一番よいのは両方を使い、1日に1回DS3231をNTPで同期させる方法です。
DS3231モジュールにCR2032を入れても大丈夫ですか?
大丈夫です。ただし、ZS-042のように充電回路があるモジュールでは、先に充電経路を断つ必要があります。ボード上の200 Ωの抵抗(コード201)かその隣のダイオードを取り外せば、CR2032は何年も安全に動きます。ボードを改造したくない場合は、代わりに充電式のLIR2032を使ってください。
この記事を読んだ後に作れるプロジェクト
全プロジェクトを見る
DS1302 RTC時計モジュールのセットアップ
Arduinoの電源を切っても時刻を保持する、小さな時計モジュールを動かします。モジュールには専用の小さな電池があり、一度時刻を設定すると、パソコンの画面に日付と時刻が毎秒表示されます。アラームやタイマー、記録を残すプロジェクトは、いずれもこのステップから始まります。

DS1302 RTC時計をLCDディスプレイで表示
停電しても時刻を保持する小さな置き時計です。DS1302時計モジュールは専用の小さな電池で時刻を保持します。Arduinoが毎秒その情報を読み取り、LCDディスプレイの上段に時刻と日付を、下段に曜日を英語で表示します。自分だけの時計を手軽に作れます。

ESP32とDS1307 RTCのデジタル時計(I2C + LCD)
このプロジェクトでは、DS1307リアルタイムクロック(RTC)モジュールを使って16x2 I2C LCDに時刻と日付を表示します。DS1307は電池でバックアップされるRTCで、ESP32の電源を切っても時刻を保持します。両方のモジュールが同じI2Cバス(SDA/SCL)を共有するため、2本の信号線だけで複数の機器を制御する方法を学べます。I2C通信プロトコルやRTCモジュールの動作原理、LCDの表示データを随時更新する方法を実践的に学ぶプロジェクトです。

ドットマトリクス時計とカレンダー(MAX7219、DS1307)
机や壁に置き、時刻と日付をスクロール表示する光るパネルです。8x32のLEDディスプレイには大きな数字の時計が表示され、コロンの2つの点が毎秒点滅します。毎分の初めに曜日と日付がスクロールし、その後、時計表示に戻ります。時計モジュールには専用の電池があるため、電源を抜いても時刻を保持します。

ドットマトリクス時計・カレンダー・気象ステーション(MAX7219、DS1307)
机や壁に置いて、時刻、日付、室内の温湿度を順番に表示する、文字がスクロールする表示板です。8x32のLEDディスプレイには、まず摂氏と華氏の温度、次に湿度、そして2つの点が1秒ごとに点滅する時計が表示されます。分が変わると、曜日と日付がスクロール表示されます。時計モジュールが時刻を保持し、DHT22センサーが温度と湿度を測定します。

ESP32温度データロガー(DHT22 + DS1307 RTC + microSD + OLED)
ESP32で日時付きの温度・湿度記録システムを作ります!DHT22で1分ごとに温度と湿度を読み取り、DS1307 RTCから取得した日時とともにmicroSDカードへCSV形式で記録します。OLEDには最新の温度を大きな文字で表示し、湿度のパーセント値、日時、総記録数も表示します。SDカードの/temp_log.csvファイルはExcel、Google Sheets、Pythonのpandasで開いて分析できます。温室の監視、家や部屋の温度管理、実験室のデータ収集、食品の保管環境や養蜂箱の監視に適しています。

ESP32とNTPでインターネットから正確な時刻を取得
ESP32をWi-Fiに接続し、NTP(Network Time Protocol)サーバーからミリ秒精度の現在時刻を取得します。configTzTime()関数でタイムゾーンを自動適用し、夏時間を採用する国ではPOSIX TZ文字列で夏時間と標準時間の切り替えも自動管理します。時刻はシリアルモニタと、必要に応じて接続するI2C LCDに表示します。configTzTime()がSNTPサービスを開始し、ネットワークにアクセスできる間は定期的に再同期します。

4桁の7セグメントディスプレイで作るデジタル時計
キッチンのオーブンの時計のように、4桁の数字が光るデジタル時計です。DS1302時計モジュールが時刻を保持し、Arduinoが毎秒、時と分を読み取って4桁のディスプレイに表示します。時と分の間には小さな点が点灯します。時刻を保持するモジュールとディスプレイを組み合わせる方法を学びます。
ほかの記事
BlueGraysアプリ
この回路をスマホでステップごとに作る
回路図、コード、部品リストを1つの画面で確認できます。Arduino、ESP32、Picoのプロジェクトを200件以上用意しています。

