Met baby steps, zo komen we er wel. Eerder had ik het erover dat de Arduino geen eigen RTC (Real Time Clock) heeft en daardoor ten eerste een vrij grote afwijking van enkele seconden per dag heeft en ten tweede de tijd-instelling steeds kwijtraakt bij herstarten.

Er zijn twee goed ondersteunde RTCs voor Arduino verkrijgbaar; de DS1307 en de DS3231. Qua prijs ontlopen ze elkaar niet veel, maar qua precisie is de DS3231 absoluut superieur met een afwijking van maar ±2 minuten per jaar. Aansluiten op de Arduino, het wordt langzaam een beetje voorspelbaar, is weer een fluitje van een cent. VCC en GND naar de plus en min, SDA direct op analoge poort A4 en SCL direct naar analoge poort A5.


DS3231 met batterijhouder en pins

Vervolgens kan je met de DS3231 library de boel besturen:

#include <DS3231.h>
#include <AceTime.h>

BasicZoneProcessor zoneProcessor;
TimeZone tz = TimeZone::forZoneInfo(&zonedb::kZoneEurope_Amsterdam, &zoneProcessor);

void setup() {
    // Initialiseer de verbinding
    Wire.begin();
    Serial.begin(9600);
}

void loop() {
    // Haal de huidige tijd op uit de RTC
    DateTime rtcNow = RTClib::now();
    ZonedDateTime now = ZonedDateTime::forUnixSeconds64(rtcNow.unixtime(), tz);
    now.printTo(Serial);
    delay(100);
}

De tijd instellen

De kans dat de RTC de juiste tijd geeft als je hem voor het eerst gebruikt is op zich vrij klein, dus moet je hem eerst instellen. Via de seriële poort van de Arduino kunnen we de module instellen:

void loop() {
    // Als er iets op de seriële poort klaar staat...
    if (Serial.available()) {
        // ... dan lees het als een integer
        long readLong = Serial.parseInt();
        // ... en lees de rest van de buffer leeg, zoals een <enter> of spaties
        while (Serial.available()) {
            Serial.read();
        }
        // stel de RTC in op de integer die van de seriële poort is ingelezen
        rtc.setEpoch(readLong);
    }
    ZonedDateTime now = ZonedDateTime::forUnixSeconds64(RTClib::now().unixtime(), tz);
    now.printTo(Serial);
    delay(100);
}

Vervolgens kunnen we de tijd instellen door de Unix epoch timestamp over de seriële poort naar de Arduino te sturen. De huidige Unix epoch krijg je met het commando date +%s. Je kan de uitvoer daarvan in de Arduino IDE naar de seriële poort sturen (Tools | Serial monitor), maar het kan ook vanaf de command line met het cu commando. Eerst moet je weten welk device je seriële poort heeft; in de Arduino IDE zie je dat onderin het scherm als je Arduino verbonden is. Bij mij is dat /dev/cu.usbmodem101, vervolgens start je cu met het commando:

$ sudo cu -l /dev/cu.usbmodem101

Nu zie je alles wat over de seriële poort binnenkomt, dus in dit geval elke 100 milliseconden de huidige tijd. Het cu commando kan echter ook berichten terugsturen (zie man cu), en heeft zelfs nog functionaliteit om de invoer uit een ander programma te halen door middel van ~$<command>. We krijgen de huidige epoch met date +%s (zie ook man date).

Als je door de stroom aan timestamps heen het commando <enter>~$date +%s invoert, zul je zien dat de timestamps opeens de huidige tijd weergeven. Aangezien je dit maar af en toe hoeft te doen zou je kunnen overwegen dit in een apart programma te doen, want dat scheelt weer in het geheugengebruik. Je sluit cu netjes af met het commando <enter>~..

Andere tijdsignaal-bronnen

Een RTC is zeker niet de enige bron van tijdsignalen. Als je een WiFi module toevoegt aan de Arduino, kan de tijd opgehaald worden vanaf een NTP-server. Met een GPS-ontvanger kan je de tijd ophalen van satellieten. Een nog andere optie is om een tijdsignaal van de DCF77-zender in Mainflingen bij Frankfurt op te vangen. Ook de originele klok in de Rheinturm doet dit!

Ik wil daar in de toekomst nog wel eens naar kijken, maar op dit moment is de klok volledig zelfstandig operationeel! De volgende stap is eerst iets compleet anders: de lichtstrip moet worden ingebouwd in een meer permanente opstelling.