7-Segment-Uhr mit Arduino Nano, DS3231 RTC und LDR-Autobrightness bauen
Komponenten und Zubehör
| Arduino Nano R3 (funktioniert auch mit Arduino Uno oder Elegoo Uno) | × | 1 |
| Adafruit 7-Segment Backpack – 1,2″ hohe Ziffern | × | 1 |
| Adafruit 1,2″ 4-Digit 7-Segment Display mit I2C Backpack – Grün | × | 1 |
| Adafruit DS3231 (alternativ DS1307) | × | 1 |
| Widerstand 100 kΩ | × | 1 |
| Fotowiderstand (LDR) | × | 1 |
| Steckbrett (Breadboard) | × | 1 |
| Jumper-Kabel (10–14 Stück) | × | 1 |
| Knopfzelle CR1220 | × | 1 |
Apps und Onlinedienste
- Arduino IDE
Über dieses Projekt
Einleitung
In einer Zeit, in der man ständig eine Uhr am Handgelenk oder Smartphone dabei hat, wirkt eine Arduino Uno/Nano-Uhr etwas altmodisch. Andernorts gibt es NTP-synchronisierte Geräte, die permanent online sind.
Hintergrund
Ein Freund wollte aus persönlichen Gründen kein WLAN-Gerät im Schlafzimmer. Deshalb habe ich den Arduino mit einer separaten Echtzeituhr ausgestattet.
Die richtige Hardware wählen
Ich war überrascht, wie viele verschiedene Produkte selbst Adafruit anbietet – je nach gewünschter Spannung und Genauigkeit. Ich entschied mich für zwei Varianten: den günstigeren DS1307 und den wahrscheinlich präziseren DS3231.
Beide können in dieser Uhr verwendet werden. Für den DS1307 beachten Sie die korrekte Pin-Belegung und die Einschränkung bei der Temperaturanzeige: Kommentieren Sie die displayTemp()-Funktion in loop() aus.
Sketch anpassen
Ich fand einen Sketch eines anderen Nutzers, habe ihn umstrukturiert und angepasst, dabei die tschechischen Kommentare beibehalten und englische Hinweise hinzugefügt.
Die Anzeige sollte so groß wie möglich sein, daher fiel die Wahl auf ein 7-Segment-Display – schließlich wollte ich nur die Uhrzeit im Blick haben.
Eine positive Überraschung: Der DS3231 bietet die Möglichkeit, die Temperatur auszulesen, was dem DS1307 neben seiner Ungenauigkeit fehlt.
Uhren initialisieren
Falls Sie eine CR1220-Knopfzelle haben, folgen Sie der kurzen Adafruit-Anleitung zur Einrichtung der Uhren. Kurz gesagt: Die Zeit wird nicht aus dem Internet, sondern vom lokalen PC übernommen. Der Upload-Zeitstempel wird an die Uhren übertragen.
Funktionen
Anschließend habe ich die LED-Matrix für die Anzeige von Uhrzeit und Temperatur hinzugefügt. Das Intervall für den Wechsel zwischen Zeit und Temperatur lässt sich über Variablen einstellen.
Besonders gut im Sketch beschrieben ist die Steuerung der einzelnen – blinkenden – Punkte und Ziffern auf der LED-Matrix.
Der Sketch wird abgerundet durch die Automatische Sommer-/Winterzeitumstellung und die Helligkeitsanpassung der LED-Matrix per Umgebungslichtsensor (nachts dunkler als bei Tageslicht).
Im Video sehen Sie den Wechsel zwischen Zeit und Temperatur. Haben Sie den Punkt links im Bild bemerkt? Er wechselt seine Position (oben/unten) sekündlich.
Feedback ist willkommen, wie man im Fehlerfall ein ERR
auf der LED-Matrix ausgibt. Aktuell erscheint bEEF
:

// oh no, no data!
Serial.println("DS3231 Sensor Error - cant read temperature");
matrix.print(0xBEEF, HEX); // inform the user with BEEF
matrix.writeDisplay();
delay(5000);
Erweiterungsmöglichkeiten
- Ein Wecker oder Alarmzeit ist noch nicht integriert.
- Eventuell eine Erweiterung per Bluetooth-Modul und Blynk.
Update 01.02.2019
Der Umbau vom Steckbrett auf eine Lochrasterplatine ist abgeschlossen. Einige Bilder für Interessierte:





Den aktualisierten Code v1.1 sowie ein angepasstes Fritzing-Bild finden Sie im Anhang.
Code
MyClock v1.0 (C/C++)
// MyClock v1.0
// Ingo Lohs
// Hardware: getestet mit Arduino Uno und Arduino Nano (ATmega328P [Old Bootloader]) unter Arduino IDE v1.8.8
/* Theorie zur Matrix ht16k33
Am einfachsten ruft man print auf – genau wie bei Serial:
print(variable, HEX) – gibt Hexadezimalzahl 0000 bis FFFF aus
print(variable, DEC) oder print(variable) – gibt Dezimalzahl 0000 bis 9999 aus
Für mehr Kontrolle: writeDigitNum(location, number) – schreibt Zahl (0-9) an einzelne Position.
Location #0 ganz links, #2 sind die Doppelpunkt-Punkte (überspringen), #4 ganz rechts.
Für Doppelpunkt und Dezimalpunkte: writeDigitRaw(location, bitmap).
(Beide Punkte des mittleren Doppelpunkts sind intern verdrahtet, können nicht separat angesprochen werden.)
Location 2 Bits: 0x02 = mittlerer Doppelpunkt (beide Punkte), 0x04 = linker Doppelpunkt unterer Punkt,
0x08 = linker Doppelpunkt oberer Punkt, 0x10 = Dezimalpunkt.
Für Dezimalpunkt: writeDigitNum(location, number, true).
Für Doppelpunkt: drawColon(true/false).
Volle Segmentkontrolle: writeDigitRaw(location, bitmask) – rohes 8-Bit-Mask.
Alle Zeichnungsroutinen ändern nur den Display-Speicher im Arduino.
Vergessen Sie nicht writeDisplay() aufzurufen, um den Speicher via I2C an die Matrix zu senden.
Quelle: 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;
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; // hell bei Tageslicht
} else {
brightness_matrix = 15; // dunkel bei Nacht
}
}
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);
byte se = second, mi = minute, ho = hour, we = dayOfWeek, dm = dayOfMonth, mo = month, ye = year;
// Sommerzeit – letzter Sonntag im März um 2 Uhr
if ((dayOfWeek == 7) && (dayOfMonth >= 25) && (month == 3) && (hour == 2)) {
setDS3231time(se, mi, 3, we, dm, mo, ye);
}
// Winterzeit
if ((dayOfWeek == 7) && (dayOfMonth >= 25) && (month == 10) && (hour == 1) && (year != 1)) {
setDS3231time(se, mi, ho, we, dm, mo, 1);
}
if ((dayOfWeek == 7) && (dayOfMonth >= 25) && (month == 10) && (hour == 3) && (year == 1)) {
setDS3231time(se, mi, 2, we, dm, mo, 0);
}
/* Location #2 steuert auch die beiden einzelnen Punkte links (blinky_dot):
2 = nichts blinkt, 3 = nichts blinkt, 4 = oberer linker Punkt blinkt, 5 = oberer linker Punkt blinkt,
6 = oberer linker Punkt + beide Doppelpunkte Mitte, 7 = oberer linker Punkt + beide Doppelpunkte Mitte,
8 = unterer linker Punkt blinkt, 9 = unterer linker Punkt blinkt,
10 = unterer linker Punkt + beide Doppelpunkte Mitte, 11 = unterer linker Punkt + beide Doppelpunkte Mitte,
12 = beide einzelnen Punkte links blinken ohne mittleren Doppelpunkt
*/
// Stunden formatieren: _0:mm
if (hour > 9) {
matrix.writeDigitNum(0, (hour / 10), false);
matrix.writeDigitNum(1, (hour % 10), false);
} else {
matrix.writeDigitNum(1, hour, false);
}
// Sekundenanzeige an linken Punkten
if (se <= 30) blinky_dot = 10;
else if (se > 30) blinky_dot = 6;
matrix.writeDigitRaw(2, blinky_dot);
matrix.writeDigitNum(3, (minute / 10), false);
matrix.writeDigitNum(4, (minute % 10), false);
matrix.writeDisplay();
// Blinkender Doppelpunkt für halbe Sekunden
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); // 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++) – Korrigierte Sommer-/Winterzeit
// MyClock v1.1
// Ingo Lohs
// Hardware: getestet mit Arduino Uno und Arduino Nano (ATmega328P [Old Bootloader]) unter Arduino IDE v1.8.8
// Änderung v1.0 > v1.1: dayOfWeek == 1 statt 7 in displayTime() für korrekte Sonntagserkennung
/* Theorie zur Matrix ht16k33 ... (wie oben) */
#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);
byte se = second, mi = minute, ho = hour, we = dayOfWeek, dm = dayOfMonth, mo = month, ye = year;
// Sommerzeit – letzter Sonntag im März um 2 Uhr (dayOfWeek == 1 = Sonntag)
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 3) && (hour == 2)) {
setDS3231time(se, mi, 3, we, dm, mo, ye);
}
// Winterzeit
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 10) && (hour == 1) && (year != 1)) {
setDS3231time(se, mi, ho, we, dm, mo, 1);
}
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 10) && (hour == 3) && (year == 1)) {
setDS3231time(se, mi, 2, we, dm, mo, 0);
}
/* Location #2 Steuerung (blinky_dot) wie oben */
if (hour > 9) {
matrix.writeDigitNum(0, (hour / 10), false);
matrix.writeDigitNum(1, (hour % 10), false);
} else {
matrix.writeDigitNum(1, hour, false);
}
if (se <= 30) blinky_dot = 10;
else if (se > 30) 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
Schaltpläne
LDR-Pfad korrigiert


Herstellungsprozess
- Arduino POV-Uhr: Persistence-of-Vision LED-Display selbst bauen
- Retro-Numitron-Uhr mit Arduino bauen: Einfach, zuverlässig & energieeffizient
- Stilvolle Wanduhr mit Adafruit 60-LED Neopixel-Ring & Arduino
- Arduino Echtzeituhr für islamische Gebetszeiten bauen
- Einen eigenen LED-Master-Clock mit Alarm bauen – aus 12 Metern Entfernung ablesbar
- Arduino Temperaturoverwachung & Echtzeituhr mit 3,2″ TFT-Display
- Zwei Schrittmotoren mit Arduino Nano & Joystick steuern – Einfaches Tutorial
- DIY Arduino Smartwatch: Eigene Apple-Watch-Alternative bauen
- Präzise 7-Segment-Uhr mit Arduino Pro Mini & DS1302 RTC – Selbstbau
- Zuverlässigen Wecker mit DS1302 RTC und Arduino UNO bauen