Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計
必要な部品と材料
![]() |
Arduino Nano R3 (Arduino UnoやElegoo Unoでも動作) | × | 1 |
| Adafruit 7セグメントバックパック - 1.2インチ (バンドル購入時は不要) | × | 1 | |
| Adafruit 1.2インチ 4桁 7セグメントディスプレイ I2Cバックパック付き - 緑 | × | 1 | |
| Adafruit DS3231 (DS1307でも可) | × | 1 | |
![]() |
抵抗 100kΩ | × | 1 |
![]() |
フォトレジスタ(CdSセル) | × | 1 |
![]() |
ブレッドボード | × | 1 |
![]() |
ジャンパーワイヤ (10〜14本) | × | 1 |
| コイン電池 CR1220 | × | 1 |
使用するアプリとオンラインサービス
![]() |
Arduino IDE |
プロジェクトについて
イントロ
腕時計やスマートフォンで常に時刻が確認できる現代において、Arduino Uno/Nanoで作る時計は少し地味かもしれません。また、NTPサーバーと同期する常時オンラインのデバイスも存在します。
背景
私の友人が個人的な理由で寝室にWiFi接続デバイスを置きたくないと言ったため、インターネット接続不要の独立した時計をArduinoで作ることにしました。
適切な製品の選択
Adafruitだけでも驚くほど多くのRTCモジュールがあります:電圧や精度の違いなど様々です。ここでは安価なDS1307と、より高精度なDS3231の2つを検討しました。
どちらもこの時計で使用可能です。DS1307を使う場合は、正しいピン配置と温度表示機能の制限(displayTemp()関数をコメントアウト)に注意してください。
スケッチの調整
既存のスケッチを参考にし、再構成しました。元のチェコ語コメントを残しつつ、英語の注釈を追加しています。
表示はできるだけ大きくしたかったため、時刻のみ表示なら7セグメントが最適と判断しました。
スケッチを見て嬉しかったのは、DS3231には温度取得機能があることです。DS1307にはこれがなく、精度も劣ります。
時計の初期化
CR1220ボタン電池がある場合、Adafruitのチュートリアルに従って時計を設定できます。要約すると、インターネットからではなくローカルPCの時刻を書き込みます。アップロード時のタイムスタンプがRTCに転送されます。
機能
LEDマトリクスで時刻と温度を表示します。時刻と温度の表示切替間隔は変数で設定可能です。
LEDマトリクス上の個々のドットや数字の制御(点滅含む)については、スケッチ内で詳しく説明しています。Adafruitのドキュメントも参考にしてください。
さらに、夏時間/冬時間の自動切替と、周囲の明るさに応じたLEDマトリクスの輝度調整(夜間は暗く、昼間は明るく)を実装しています。
動画で時刻と温度の切り替わりに気づいたかもしれません。左側のドットの位置(上/下)が秒に応じて変化しているのも確認できます。
フィードバック募集: 時計読み取り失敗時にLEDマトリクスに「ERR」を表示する方法をご存知の方は教えてください。現在は「bEEF」と表示されます:

// データなし!
Serial.println("DS3231 Sensor Error - cant read temperature");
matrix.print(0xBEEF, HEX); // ユーザーにBEEFで通知
matrix.writeDisplay();
delay(5000);
拡張案
- アラーム/目覚まし機能の追加
- BluetoothモジュールとBlynkによる拡張
更新履歴 (2019/02/01)
ブレッドボードからユニバーサル基板への移行が完了しました。関連写真:





更新版コード v1.1と修正済みFritzing図を添付しています。
コード
MyClock v1.0 C/C++
// MyClock v1.0
// Ingo Lohs
// Hardware: Arduino Uno / Nano (ATmega328P) で動作確認、Arduino IDE v1.8.8使用
/* HT16K33マトリクス理論
print(variable, HEX) - 16進数表示 (0000-FFFF)
print(variable, DEC) / print(variable) - 10進数表示 (0000-9999)
writeDigitNum(location, number) - 指定位置に数字(0-9)書き込み
Location #0: 左端, #2: コロン(ドット), #4: 右端
writeDigitRaw(location, bitmap) - コロン/小数点制御
Location 2のビットマップ:
0x02 - 中央コロン(両ドット)
0x04 - 左コロン・下ドット
0x08 - 左コロン・上ドット
0x10 - 小数点
writeDigitNum(location, number, true) - 小数点付き数字
drawColon(true/false) - コロン表示制御
writeDigitRaw(location, bitmask) - 全セグメント完全制御
全描画後は writeDisplay() でI2C送信必須
Source: https://www.mouser.com/ds/2/737/adafruit-led-backpack-932846.pdf */
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#define DS3231_I2C_ADDRESS 0x68
int ldr_sensor = A0; // LDR + 100kΩ to GND, 他方5V
int ldr_value = 0;
int brightness_matrix;
int threshold_brightness = 400;
int blinkrate_value = 0;
int delay_matrix_time = 20; // 時刻表示時間(秒)
int delay_matrix_temp = 2000; // 温度表示時間(ミリ秒)
int blinky_dot = 2;
Adafruit_7segment matrix = Adafruit_7segment();
byte decToBcd(byte val) { return ((val/10*16) + (val%10)); }
byte bcdToDec(byte val) { return ((val/16*10) + (val%16)); }
void setup() {
Serial.begin(9600);
Wire.begin();
matrix.begin(0x70);
}
void loop() {
lightBrightness();
matrixBlinkrate();
displayTime();
displayTemp();
}
void lightBrightness() {
ldr_value = analogRead(ldr_sensor);
Serial.print("LDR: "); Serial.println(ldr_value);
if (ldr_value <= threshold_brightness) {
brightness_matrix = 0; // 昼: 暗め
} else {
brightness_matrix = 15; // 夜: 明るめ
}
}
void matrixBlinkrate() {
matrix.blinkRate(blinkrate_value);
}
void displayTime() {
matrix.setBrightness(brightness_matrix);
matrix.clear();
for (uint16_t i = 0; i < delay_matrix_time; i++) {
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
getDateDs3231(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
// 夏時間切替: 3月最終日曜日 2時 → 3時
if ((dayOfWeek == 7) && (dayOfMonth >= 25) && (month == 3) && (hour == 2)) {
setDS3231time(second, minute, 3, dayOfWeek, dayOfMonth, month, year);
}
// 冬時間切替: 10月最終日曜日
if ((dayOfWeek == 7) && (dayOfMonth >= 25) && (month == 10) && (hour == 1) && (year != 1)) {
setDS3231time(second, minute, hour, dayOfWeek, dayOfMonth, month, 1);
}
if ((dayOfWeek == 7) && (dayOfMonth >= 25) && (month == 10) && (hour == 3) && (year == 1)) {
setDS3231time(second, minute, 2, dayOfWeek, dayOfMonth, month, 0);
}
// 時刻フォーマット: _0:mm または 00:mm
if (hour > 9) {
matrix.writeDigitNum(0, (hour / 10), false);
matrix.writeDigitNum(1, (hour % 10), false);
} else {
matrix.writeDigitNum(1, hour, false);
}
// 左側ドットの点滅パターン (blinky_dot)
if (second <= 30) {
blinky_dot = 10; // 下ドット + 中央コロン
} else {
blinky_dot = 6; // 上ドット + 中央コロン
}
matrix.writeDigitRaw(2, blinky_dot);
matrix.writeDigitNum(3, (minute / 10), false);
matrix.writeDigitNum(4, (minute % 10), false);
matrix.writeDisplay();
// コロン点滅 (0.5秒ごと)
delay(500);
matrix.drawColon(false);
matrix.writeDisplay();
delay(500);
}
}
void displayTemp() {
matrix.setBrightness(brightness_matrix);
matrix.clear();
byte temp = get3231Temp();
int abs_temp = abs(temp);
matrix.writeDigitNum(1, (abs_temp % 10), false);
if (temp < 0) matrix.writeDigitRaw(0, 64); // マイナス記号
if (temp <= -10) matrix.writeDigitRaw(2, 12); // -10度以下
if (temp >= 10) matrix.writeDigitNum(0, (abs_temp/10), false);
if (temp <= -10) matrix.writeDigitNum(0, (abs_temp/10), false);
matrix.writeDigitRaw(3, 99); // "°"
matrix.writeDigitRaw(4, 57); // "C"
matrix.writeDisplay();
Serial.print("Temperatur in C: ");
Serial.println(get3231Temp());
delay(delay_matrix_temp);
}
void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year) {
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0);
Wire.write(decToBcd(second));
Wire.write(decToBcd(minute));
Wire.write(decToBcd(hour));
Wire.write(decToBcd(dayOfWeek));
Wire.write(decToBcd(dayOfMonth));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.endTransmission();
}
void getDateDs3231(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) {
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f);
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());
}
float get3231Temp() {
byte tMSB, tLSB;
float temp3231;
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0x11);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
if (Wire.available()) {
tMSB = Wire.read();
tLSB = Wire.read();
temp3231 = (tMSB & B01111111);
temp3231 += ((tLSB >> 6) * 0.25);
return temp3231;
} else {
Serial.println("DS3231 Sensor Error - cant read temperature");
matrix.print(0xBEEF, HEX);
matrix.writeDisplay();
delay(5000);
}
}
MyClock v1.1 C/C++
夏時間/冬時間判定の曜日修正(日曜日=1に変更)
// MyClock v1.1
// Ingo Lohs
// 変更点: dayOfWeek == 7 → 1 に修正 (displayTime()内の夏時間/冬時間判定)
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#define DS3231_I2C_ADDRESS 0x68
int ldr_sensor = A0;
int ldr_value = 0;
int brightness_matrix;
int threshold_brightness = 400;
int blinkrate_value = 0;
int delay_matrix_time = 20;
int delay_matrix_temp = 2000;
int blinky_dot = 2;
Adafruit_7segment matrix = Adafruit_7segment();
byte decToBcd(byte val) { return ((val/10*16) + (val%10)); }
byte bcdToDec(byte val) { return ((val/16*10) + (val%16)); }
void setup() {
Serial.begin(9600);
Wire.begin();
matrix.begin(0x70);
}
void loop() {
lightBrightness();
matrixBlinkrate();
displayTime();
displayTemp();
}
void lightBrightness() {
ldr_value = analogRead(ldr_sensor);
Serial.print("LDR: "); Serial.println(ldr_value);
if (ldr_value <= threshold_brightness) brightness_matrix = 0;
else brightness_matrix = 15;
}
void matrixBlinkrate() {
matrix.blinkRate(blinkrate_value);
}
void displayTime() {
matrix.setBrightness(brightness_matrix);
matrix.clear();
for (uint16_t i = 0; i < delay_matrix_time; i++) {
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
getDateDs3231(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
// 夏時間: 3月最終日曜日(=1) 2時
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 3) && (hour == 2)) {
setDS3231time(second, minute, 3, dayOfWeek, dayOfMonth, month, year);
}
// 冬時間
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 10) && (hour == 1) && (year != 1)) {
setDS3231time(second, minute, hour, dayOfWeek, dayOfMonth, month, 1);
}
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 10) && (hour == 3) && (year == 1)) {
setDS3231time(second, minute, 2, dayOfWeek, dayOfMonth, month, 0);
}
if (hour > 9) {
matrix.writeDigitNum(0, (hour / 10), false);
matrix.writeDigitNum(1, (hour % 10), false);
} else {
matrix.writeDigitNum(1, hour, false);
}
if (second <= 30) blinky_dot = 10;
else blinky_dot = 6;
matrix.writeDigitRaw(2, blinky_dot);
matrix.writeDigitNum(3, (minute / 10), false);
matrix.writeDigitNum(4, (minute % 10), false);
matrix.writeDisplay();
delay(500);
matrix.drawColon(false);
matrix.writeDisplay();
delay(500);
}
}
void displayTemp() {
matrix.setBrightness(brightness_matrix);
matrix.clear();
byte temp = get3231Temp();
int abs_temp = abs(temp);
matrix.writeDigitNum(1, (abs_temp % 10), false);
if (temp < 0) matrix.writeDigitRaw(0, 64);
if (temp <= -10) matrix.writeDigitRaw(2, 12);
if (temp >= 10) matrix.writeDigitNum(0, (abs_temp/10), false);
if (temp <= -10) matrix.writeDigitNum(0, (abs_temp/10), false);
matrix.writeDigitRaw(3, 99);
matrix.writeDigitRaw(4, 57);
matrix.writeDisplay();
Serial.print("Temperatur in C: ");
Serial.println(get3231Temp());
delay(delay_matrix_temp);
}
void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year) {
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0);
Wire.write(decToBcd(second));
Wire.write(decToBcd(minute));
Wire.write(decToBcd(hour));
Wire.write(decToBcd(dayOfWeek));
Wire.write(decToBcd(dayOfMonth));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.endTransmission();
}
void getDateDs3231(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) {
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f);
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());
}
float get3231Temp() {
byte tMSB, tLSB;
float temp3231;
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0x11);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
if (Wire.available()) {
tMSB = Wire.read();
tLSB = Wire.read();
temp3231 = (tMSB & B01111111);
temp3231 += ((tLSB >> 6) * 0.25);
return temp3231;
} else {
Serial.println("DS3231 Sensor Error - cant read temperature");
matrix.print(0xBEEF, HEX);
matrix.writeDisplay();
delay(5000);
}
}
GitHub
https://github.com/ILohs/MySketches
回路図
LDR経路修正版
製造プロセス
- Arduino POVクロック:残像効果で浮かび上がるLED時計を自作する
- Arduinoで作るレトロNumitron管時計:シンプル、信頼性高く、省エネ
- Adafruit 60 LED NeoPixelリングとArduinoで作るスタイリッシュな壁掛け時計
- Arduino で作るイスラム教礼拝時刻付きリアルタイムクロック
- 12メートル先から見える大型LEDマスタークロック(アラーム機能付き)を自作しよう
- Arduino Dueと3.2インチTFTで作る温度監視付きリアルタイムクロック
- Arduino Nanoとジョイスティックで2軸ステッピングモーターを制御:Visuinoで簡単プログラミング
- DIY Arduino スマートウォッチ — Apple Watch 風腕時計を自作
- Arduino Pro MiniとDS1302 RTCで作る高精度7セグメント時計
- Arduino Nanoで作る携帯型ガイガーカウンター
- DS1302 RTCとArduino UNOで作る信頼性の高い目覚まし時計





