Mein nächstes Bastelprojekt basiert wieder auf der Arduino Plattform und verwendet auch wieder WS2812B LED Streifen. Diesmal wollte ich die unsägliche Funkuhr im Wohnzimmer ersetzen. Wann immer die Batterie zur Neige geht, geht die Uhr in den full-retard Modus und lässt die Zeiger minutenlang drehen. Dies geht einher mit nervigen Geräuschen. Und sie ist weder schick noch nerdig.

Wie auch schon bei der Fackel, soll auch hier die Technik im fertigen Zustand zu sehen sein, dies lässt sich natürlich beim Nachbauen ändern. Wieder habe ich zuerst geschaut, ob es hier bereits etwas fertiges gibt. Wozu auch das Rad neu erfinden, wenn sich da schon jemand Gedanken zu gemacht hat? Einen (optisch) richtig schicken Sketch habe ich in der led-ring-clock von jackw01 gefunden. Allerdings setzt der Sketch einen RTC-Shield voraus, auf den ich nicht wirklich Lust hatte. Erstens muss ich hier vermutlich bei Sommer- und Winterzeitwechsel die Uhr manuell umstellen, außerdem dürfte sie über Zeit abdriften. Und je schlechter der Klon der Chinesen ist, desto größer / schneller dürfte der Drift sein.

DCF77 vs. NTP

Um den gleichen, bzw. besseren Komfort zur Kauflösung zuvor zu erhalten, bleibt eigentlich nur die Frage, ob ich lieber DCF77 oder eine Netzwerk- bzw. NTP-basierte Lösung erarbeiten möchte. DCF77 ist ein deutscher Zeitsender, welcher nur in Teilen Europas zu empfangen ist. Außerdem kennt dieser keine Zeitzonen. Ferner bin ich Systemintegrator mit der Spezialisierung auf Routing und Netzwerk. Meine Präferenz dürfte damit klar sein

Testsetup für die Codeanpassung

Testsetup für die Anpassung des Codes
Testsetup für die Anpassung des Codes

Um die Funktionsweise meiner Codeanpassungen testen zu können, brauche ich selbstverständlich wieder eine Entwicklungsplattform. Bestellt habe ich einen Arduino Uno samt Ethernet-Shield (mit W5100 Chipset), da ich annahm, dass die ~30KB Flash auch für dieses Projekt locker ausreichen würden – dazu aber später mehr. Bis der ankommt, verwende ich wieder meinen Mega 2560.

Der Sketch erwartet einen Taster, um ‚clockMode‘ und ‚colorScheme‘ umschalten zu können. Der lag im Originalcode auf PIN 4 (SPI), dieser wird allerdings vom Ethernet-Shield verwendet. Somit habe ich ihn in der ‚constants.h‘ auf PIN 5 geändert. Für LED Data verwende ich weiterhin Pin 3. Zur Helligkeitssteuerung wird ein Drehpotentiometer an A0 erwartet. Dieser sollte nicht frei „in der Luft hängen“, da er sonst als Antenne dient, und sich zufällige, nicht nachvollziehbare Werte einfängt. Wenn kein Poti zur Verfügung steht, sollte auch ein einfacher Widerstand gegen 5V funktionieren. Und sehr wichtig für den Ethernet-Part ist natürlich auch noch die MAC-Adresse des Shields. Schließlich sollte man noch die Anzahl der LEDs angeben. Somit ergibt sich dieser geänderte Passus in der ‚constants.h‘.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// IO Pin Assignments
const uint8_t pinLeds = 3;
const uint8_t pinButton = 5;
const uint8_t pinBrightness = 0;

// Define MAC Address
byte mac[] = {
    0xA8, 0x61, 0x0A, 0x10, 0x24, 0x01
};

// NTP Server to use
char* ntp_server = "de.pool.ntp.org";

// Number of LEDs in ring
const int ledRingSize = 60;

Herausschneiden des RTC-Codes

Eigentlich war das Ersetzen der RTClib Funktionsaufrufe ziemlich trivial. Dennoch musste ich weite Teile der Funktionsweise von TimeLib, RTClib und NtpClientLib durchgehen und zumindest grob verstehen um zu der Erkenntnis zu kommen, dass ich folgende Funktionen ersetzen musste:


1
2
3
4
5
6
year(now()
month(now()
day(now()
hour(now()
minute(now()
second(now()

1
2
3
4
5
6
now.year()
now.month()
now.day()
now.hour()
now.minute()
now.second()

Der Originalsketch verwendet Funktionen der RTClib, um Werte für aktuelles Jahr / Minute / Sekunde etc aus der aktuellen Zeit zu extrahieren. Mein Sketch verwendet die Timelib für diese Funktionalität. Die NtpClientLib speist die Zeit beim setup einmal ein (und refresht sie alle 1800s), während der Arduino-interne Timer die Zeit weiterlaufen lässt.

Funktioniert die Uhr nun?

Nun war der Code augenscheinlich funktional, die seriellen Debugausgaben lieferten über längere Laufzeiten hinweg korrekte und konsistente Zeitangaben. Auch der NTP resync findet nun alle halbe Stunde statt.

Arduino Uhr mit der Zielhardware und 60-LED-Ring
Arduino Uhr mit der Zielhardware und 60-LED-Ring

Aber es gab aber einen Bug, der sich auf 2 unterschiedliche Weisen manifestierte:

  1. Der Stunden-„Zeiger“ geht von xx:00:00 – xx:01:00 aus.
  2. Der Minuten-„Zeiger“ geht einmal pro Minute für eine Sekunde aus, wobei die jeweilige absolute Sekunde des Ereignisses driftet.

Ich habe mich tiefer in das Hasenloch hineingewagt und konnte das Problem auf die Funktionen ‚minutePosition()‘ und ‚hourPosition()‘ eingrenzen. Denn eine Multiplikation mit 0 ist immer null. Gefixt habe ich das auf die gleiche schmutzige Variante wie es jackw01 bereits für die Sekunden tat:


1
2
3
4
5
6
7
8
-               return hourt + mapFloat(minute(now()), 0.0, 59.0, 0.0, (ledRingSize / 12.0) - 1.0);
+               return hourt + mapFloat(minute(now()) + 0.001, 0.0, 59.0, 0.0, (ledRingSize / 12.0) - 1.0);

-               return hourt + mapFloat(minute(now()), 0, 59, 0, (ledRingSize / 24.0) - 1.0);
+               return hourt + mapFloat(minute(now()) + 0.001, 0, 59, 0, (ledRingSize / 24.0) - 1.0);

-               (float)minute(now()) + ((1.0 / 60.0) * (float)second(now())), 0.0, 59.0, 0.0, (float)ledRingSize
+               (float)minute(now()) + ((0.001 + 1.0 / 60.0) * (float)second(now())), 0.0, 59.0, 0.0, (float)ledRingSize

Den vollständigen – nun korrekt funktionierenden – Code findet Ihr im warpzone Gitlab.

Der Vollständigkeit halber steht das .zip mit dem Code auch hier zum Download bereit.

Den eigentlichen Bau der Uhr dokumentiere ich dann in einem weiteren Artikel, da dieser schon recht lang ist.