Internet der Dinge trifft Maker: Genuino MKR1000

Seite 3: Zeit & Fazit

Inhaltsverzeichnis

Es gibt noch eine Verbesserungsmöglichkeit: Sensormessungen benötigen Zeitstempel, speziell wenn im Falle von Sensornetzwerken viele Messungen eintreffen. Ein Bonus, den der MKR1000 mit dem Arduino-Zero-Board teilt, ist eine integrierte Echtzeituhr. Wer den MKR1000 mit einer Li-Po-Zelle betreibt, genießt den Vorteil, dass die Uhr nur bei gelegentlichen Neustarts eine Initialisierung benötigt.

Bevor sich die Echtzeituhr überhaupt in Betrieb nehmen lässt, benötigen Entwickler für ihre Ansteuerung eine Bibliothek. Auf der Arduino-Bibliotheksseite von GitHub lässt sich die entsprechende RTCzero-Bibliothek herunterladen.

Der nachfolgende Sketch verbindet sich mit der Echtzeituhr, und stellt programmatisch Datum und Zeit ein:

/*******************************************************/
/* Einfaches Beispiel zur Benutzung der Echtzeituhr */
/*******************************************************/
include <RTCZero.h> // die benötigte Bibliothek
/* Eine Instanz anlegen: */
RTCZero rtc;
// Jetzige Zeit:
const byte _sec = 0;
const byte _min = 55;
const byte _std = 23;
// und das Datum:
const byte _tag = 15;
const byte _mon = 6;
const byte _jhr = 16;
void setup()
{
Serial.begin(57600);

rtc.begin(); // Starten mit der RTC

// Zeit und Datum setzen:
rtc.setTime(_std,_min,_sec);
rtc.setDate(_jhr,_mon,_tag);
}
void loop()
{
// Datum ausgeben:
Serial.print("Datum: ");
Serial.print(rtc.getDay());
Serial.print(".");
Serial.print(rtc.getMonth());
Serial.print(".");
Serial.print(rtc.getYear());

// ...und Uhrzeit:
Serial.print(" Zeit: ");
Serial.print(rtc.getHours());
Serial.print(":");
Serial.print(rtc.getMinutes());
Serial.print(":");
Serial.println(rtc.getSeconds());

delay(1000); // und tick
}

Mit Hilfe der RTCzero-Bibliothek ist es des Weiteren möglich, Alarme zu setzen, in den Standby-Modus zu wechseln und externe Interrupts zu benutzen, damit sich beim Auslösen eines Alarms automatisch die Interruptbehandlung um das Ereignis kümmern kann. Eine Beschreibung der kompletten RTC-API ist auf arduino.cc zu finden.

Die Zeit und das Datum manuell wie im obigen Listing auf dem MKR1000 zu initialisieren, ist lästig und ineffizient. Da das Board als IoT-Gerät firmiert und demzufolge internetfähig ist, lautet die Alternative NTP. Das Network Time Protocol gilt als eines der ältesten Internetprotokolle überhaupt. Sein Zweck ist die Synchronisation von Computeruhren auf paketbasierten Netzwerken mit einem NTP-Server, von denen weltweit eine ganze Menge existieren. Die zeitliche Abweichung sollte nur im Bereich von wenigen Millisekunden liegen. Basis ist die Coordinated Universal Time (UTC).

Paketbasiert heißt in diesem Falle, dass UDP zum Einsatz kommt. Die Bibliothek WiFi101 bietet eine entsprechende Implementierung an. Es gibt auf arduino.cc ein Beispielprogramm UdpNTPClient, das die gewünschten Funktionen implementiert und als Muster dient.

Nachfolgend die wichtigsten Ausschnitte aus dem genannten Beispielssketch. Zunächst ist die entsprechende Header-Datei einzubinden:

#include <WiFi101.h>

Der lokale Port für UDP-Kommunikation (eingehende Pakete) ist 2390:

unsigned int localPort = 2390; 

Der verwendete Server im Beispiel lautet time.nist.gov:
IPAddress timeServer(129, 6, 15, 28); 

Um am Port auf eingehende Pakete zu warten, startet das Programm die Kommunikation mit

WiFiUDP Udp; // Variablenvereinbarung
Udp.begin(localPort); // Kommunikation starten

Anschließend schickt das Programm ein Anfragepaket an den NTP-Server. Beim dafür bereitgestellten packetBuffer handelt es sich um einen Puffer mit 48 Bytes Kapazität (NTP_PACKET_SIZE := 48). Der Client erzeugt ein NTP-konformes Anfragepaket und sendet es an den Server, der es daraufhin analysiert, an einigen Stellen ändert, und zurücksendet. Was es mit den nachfolgenden Daten des Anfragepakets im Detail auf sich hat, lässt sich im Dokument RFC 958 nachlesen.

Zunächst erstellt der Sketch das Anfragepaket:

memset(packetBuffer, 0, NTP_PACKET_SIZE);
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
Udp.beginPacket(timeServer, 123); //NTP Anfragen gehen an Port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
Danach wartet der Arduino auf die Antwort des NTP-Servers:
if ( Udp.parsePacket() ) {
// Daten lesen und in Puffer speichern
Udp.read(packetBuffer, NTP_PACKET_SIZE);

Die gewünschten Informationen sind in den vier Bytes ab packetBuffer[40]abgelegt. Der Zeitstempel liegt im Big-Endian Format vor (Reihenfolge: erst High Word, dann Low Word), woraus sich mit highWord << 16 | lowWord die Zahl der Sekunden seit dem 1. Januar 1900 errechnet. Daraus lassen sich momentane Uhrzeit und Datum ermitteln. Im Beispielssketch finden sich die entsprechenden Details. Mit den gewonnenen Werten erfolgt die Initialisierung der Echtzeituhr:

rtc.setTime(_std,_min,_sec);
rtc.setDate(_jhr,_mon,_tag);

Die gezeigten Codefragmente würden Entwickler entweder in setup() platzieren, was allerdings dem Clean-Code-Gedanken zuwiderläuft. Besser ist eine Auslagerung in eine eigene Funktion setRTCviaNTPServer(), deren Aufruf von setup() aus erfolgt. Dann kann das Board bei jedem Neustart mit einer frisch synchronisierten Zeit starten, die anschließend gelegentlich mit dem NTP-Server abgeglichen werden sollte.

Das MKR1000-Board bietet mit einem integriertenWLAN-Chip, einer Echtzeituhr, und einer Anschlussmöglichkeit für eine Li-Po-Zelle ausreichende Funktionen für kleinere IoT-Projekte. Durch entsprechende Vorkehrungen ist zudem ein stromsparender Betrieb auf kleiner Flamme möglich. Der Einsatz empfiehlt sich vor allem für Anwendungsszenarien, bei denen andere Mikrocontroller-Boards schlicht überdimensioniert wären.

Um den MKR1000 und potenziell viele weitere Boards in ein Backend zu integrieren, genügt systemnahe Kommunikation mit TCP/IP oder UDP nicht. MQTT eignet sich zum Schließen dieser Lücke. Eine Beschreibung des anwendungsnahen IoT-Protokolls mit praktischen Beispielen für Arduino-Boards ist auf heise Developer im Blog des Autors zu finden.

Mit einem empfohlenen Preis von 30,99 Euro zählt der MKR1000 zwar nicht unbedingt zu den Schnäppchen, offeriert aber trotzdem ein passables Preis/Leistungsverhältnis. Wer Erfahrung in Punkto IoT sammeln möchte, dem sei der MKR1000 jedenfalls wärmstens ans Herz gelegt.

Dr. Michael Stal
arbeitet seit 1991 hauptberuflich als Experte für Softwarearchitekturen und Middleware für verteilte Systeme bei der Corporate Technology der Siemens AG. Hauptfokus seiner Arbeit sind Konzepte, d.h. Muster, Bausteine und Methoden, um große, komplexe System- und Softwarearchitekturen effizient und mit hoher Qualität zu erstellen.

(rme)