zurück zum Artikel

Internet der Dinge trifft Maker: Genuino MKR1000

Dr. Michael Stal
Genuino MKR1000, Teil 1: Internet der Dinge trifft Maker

Bisherige Arduino-Boards waren eher auf Elektronikprojekte mit gelegentlichem Internetzugriff zugeschnitten. Das könnte sich jetzt ändern.

Mikrocontroller-Boards wie die der Arduino-Open-Source-Hardware-Familie bieten die Möglichkeit, mit relativ wenig finanziellem Einsatz aktiv am Internet der Dinge zu partizipieren. Informationsquellen zu den Boards und der notwendigen Elektronik gibt es in Hülle und Fülle. Mit dem speziell für IoT-Entwickler konzipierten Board MKR1000 öffnet sich nun die Tür zu professionellen Anwendungen.

Das Board basiert auf dem Atmel ATSAMMW25 SoC (System on a Chip), einem WLAN-fähigen System von Atmel mit speziellem Fokus auf das Internet der Dinge. Als Herz des MKR1000 dient ein SAMD21 Cortex-M0+-32Bit-Mikrocontroller aus der ARM-Familie, dessen Cousin beim Arduino Zero für Tempo sorgt. Entsprechend seinem angestrebten Anwendungsgebiet handelt es sich um einen Chip mit niedrigem Stromverbrauch. Flash-Speicher und SRAM bewegen sich mit 32 KByte und 256 KByte im Mittelfeld der Arduino/Genuino-Familie.

Der Genuino/Arduino MKR1000 ist der IoT-Spezialist in der Arduino-Familie (Abb. 1).

Der Genuino/Arduino MKR1000 ist der IoT-Spezialist in der Arduino-Familie (Abb. 1).

Um die damit realisierten Geräte unabhängig betreiben zu können, unterstützt das Board eine Lithium-Polymer-Zelle (Li-Po) mit 3.7 V und mindestens 700 mAh Ladungsmenge. Auf dem rechten Foto ist oben links der beige Li-Po-Anschluss zu sehen. Bei Betrieb am Stromnetz lädt das Board die Zelle wenigstens zwei Stunden lang mit einem Ladestrom von 350 mA, weshalb die verwendete Li-Po eine Speicherkapazität von 700 mAh haben sollte.

Während ein MKR1000 eine Spannungsversorgung von 5 V über USB oder Netzteil erwartet, arbeitet das System intern mit 3,3 V. Am Spannungseingang sollte im Idealfall eine geregelte 5V-Spannung angeschlossen sein. Maximal 6V-Eingangsspannung empfiehlt die Dokumentation. Bei vorhandenem Netzanschluss verwendet das Board keinen Ladestrom des USB-Anschlusses. Zudem leuchtet die grüne Betriebs-LED nur, wenn sich das Board nicht aus der Batterie speist.

Für die digitalen Ein-/Ausgabe-Pins unterstützt der MKR1000 einen Gleichstrom von bis zu 7 mA, liegt damit also deutlich unterhalb der rund 20 mA eines Genuino/Arduino Uno.

Die Zahl der digitalen I/0-Pins beträgt acht, von denen die meisten PWM (Pulsweitenmodulation) und externe Interrupts unterstützen. Von den analogen Eingängen gibt es sieben an der Zahl, allesamt mit einer Analog-Digital-Wandlung mit 8, 10 oder 12 Bit ausgestattet. A0 als einziger analoger Ausgang erlaubt eine Digital-Analog-Umwandlung mit 10 Bit Auflösung.

Das Board unterstützt die obligatorischen internen Bussysteme I2C (Inter-IC) und SPI (Serial Peripheral Interface) sowie als Schnittstelle zwischen serieller und paralleler Kommunikation einen UART. Schaut man von oben auf das Board, ergibt sich schematisch folgendes Layout (vgl. Abb. 1):

       |micro|
| USB |
---------------------
LIPO | |
Anschluss| |
---------------------
o AREF 5V o
o DAC0/A0 Vin o
o A1 +3V3 o
o A2 GND o
o A3 RESET o
o A4 <- TX o
o A5 -> RX o
o A6 SCL o
o 0 SDA o
o 1 MISO o
o ~2 SCK o
o ~3 MOSI o
o ~4 7 o
o ~5 6 o
----------------------

Soweit zur Theorie, doch wie bewährt sich das Board in der Praxis? Um den MKR1000 aus der Arduino-IDE nutzen zu können, lässt sich über den Boards Manager die entsprechende Unterstützung aufspüren und laden Tools | Boards | Boards Manager. Unter "Arduino SAM Boards (32-Bit Cortex-M0) by Arduino" gibt es den passenden Treiber für die Boards Genuino/Arduino Zero und MKR1000.

Unter Mac OS X haben einige Nutzer in Foren Probleme bei der Erkennung des USB-Ports gemeldet, an dem der MKR1000 angeschlossen ist. Sollte ein Mac nicht den entsprechenden Port unter Tools | Ports anzeigen, erweist sich das Betätigen des Reset-Tasters auf dem Board zweimal kurz hintereinander als Ausweg, der in den meisten Fällen das Problem behebt.

Um das Board einem ersten Test zu unterziehen, bietet sich das unter File | Examples | Basics | AnalogReadSerial verfügbare Beispiel an, das über den seriellen Monitor zufällige Werte ausgibt. Zufällig deshalb, weil sich die Werte durch Auslesen des analogen Eingangs A0 ergeben. Ohne Anschluss einer Komponente entstehen dort Signalfluktuationen.

Ein kleiner Hinweis am Rande: Die Onboard-LED des MKR1000 befindet sich am Digital-Port 6 statt 13 bei Arduino Uno und Mega. Das obligatorische Blink-Programm benötigt deshalb eine Anpassung.

Zum Betrieb des MKR1000 im WLAN ist die Bibliothek WiFi101 von der GitHub-Seite [1] erforderlich. Die heruntergeladene ZIP-Datei lässt sich der IDE folgendermaßen bekanntmachen: Sketch | Include Library | Add .ZIP Library.

Nach der Installation der Bibliothek ist ein Probelauf empfehlenswert. Ein passender Sketch für WLANs mit WPA findet sich zum Beispiel unter File | Examples | WiFi101 | ConnectWithWPA. Für die aus Sicherheitsgründen nicht ratsame Nutzung des WEP-Protokolls steht alternativ der Sketch "ConnectWithWEP" zur Verfügung.

Im folgenden Code müssen Entwickler die konkreten Zugangsdaten (SSID, Passwort) des eingesetzten WLANs eintragen:

...
by Tom Igoe
*/
#include <SPI.h>
#include <WiFi101.h>
char ssid[] = "Network";         // network SSID (name)
char pass[] = "secretPassword"; // network password
int status = WL_IDLE_STATUS; // the Wifi radio's status
...

Im nächsten Schritt erfolgt der Upload des Sketches auf das Board, das im seriellen Monitor einen erfolgreichen Verbindungsaufbau oder Fehler meldet.

Die Ausgabe sollte ähnlich wie die folgende aussehen:

Attempting to connect to WPA SSID: StalWLAN
You're connected to the networkSSID: StalWLAN
BSSID: CA:B2:95:D7:96:8
signal strength (RSSI):-65
Encryption Type:2
IP Address: 192.168.178.97
192.168.178.97
MAC address: F8:F0:5:F5:DB:AB
SSID: StalWLAN
BSSID: CA:B2:95:D7:96:8
signal strength (RSSI):-62
Encryption Type:2

Der MKR1000 kann sich nun mit dem Internet verbinden. Was noch fehlt, ist Sensorik. Im nachfolgenden Beispielsszenario kommt ein Temperatur- & Feuchtigkeitssensor des Typs DHT11, DHT22 oder DHT21 zum Einsatz. DHT steht für Digital Humidity and Temperature.

Die Temperaturmessung des Sensors funktioniert über einen Thermistor, das heißt einen temperaturabhängigen Widerstand mit großer Empfindlichkeit, der aus Keramik oder ähnlichem Material besteht. Zur Feuchtigkeitsmessung befindet sich ein Substrat zwischen zwei elektrisch geladenen Platten. Die Feuchtigkeit gelangt über die Poren der Sensoraußenhülle zum Substrat, dessen Leitfähigkeit sich entsprechend ändert. Eine Logik im Sensor ermittelt Feuchtigkeit und Temperatur.

DHT22-Sensor am Genuino/Arduino MKR1000 (Abb. 2)

DHT22-Sensor am Genuino/Arduino MKR1000 (Abb. 2)

Beim in Abbildung 2 gezeigten Versuchsaufbau ist ein DHT22-Sensor mit vier Pins zu sehen, wobei Pin 3 unbelegt bleibt. Zwischen dem Spannungseingang (Pin 1, ganz links) und dem Signaleingang (Pin 2) empfiehlt sich ein Pull-up-Widerstand mit 5 bis 10 kOhm. Pin 4 liegt an Erde (GND).

Einige DHT22-Bausteine besitzen nur drei Pins und haben den Pull-up-Widerstand bereits auf dem Board. Der DHT22 ist zwar teurer als sein kleinerer Bruder DHT11, dafür aber auch leistungsfähiger. Während der DHT22 den Feuchtigkeitsbereich von 0-100% mit einer Abweichung von +- 2% erfasst, deckt der DHT11 etwa 10-90% mit +-5% Genauigkeit ab. Temperaturen misst der DHT11 von 0 bis 50 Grad Celsius bei einer Abweichung von bis zu 2 Grad. Der DHT22 schafft dagegen -40 bis 125 Grad Celsius mit einer Fehlertoleranz von maximal 0.5 Grad. Im Vorteil ist der DHT11 dagegen mit seiner Messfrequenz von 1 Hz im Vergleich zu 0.5 Hz des DHT22.

Erwartungsgemäß ist keine Eigenentwicklung eines DHTxx-Treibers notwendig. Eine passende Open-Source-Bibliothek gibt es auf der GitHub-Seite von Adafruit [2]. Nach dem Download der Bibliothek bleibt nur noch die Integration in die Arduino-IDE.

Der Rest des Sketches ist selbsterklärend. Dass der Sensor ein sehr genaues Timing benötigt, macht die Programmierung etwas diffiziler. Darum kümmert sich aber zum Glück die DHT-Bibliothek.

In jedem Messzyklus greift der Sketch auf den Sensor zu, fragt die momentane Temperatur in Grad Celsius sowie die Feuchtigkeit in Prozent ab und gibt die Daten neben einem Hitzeindex auf dem seriellen Monitor aus.

/**************************************************
Nutzer anderer Sensoren müssen statt DHT22
entweder DHT21 oder DHT11 eintragen:
***************************************************/
#include "DHT.h" // Bibliothek verwenden
#define HUMSENSORTYPE DHT22 // Genutzt wird DHT22
// am Digital-Pin 6
// des MKR1000
#define HUMSENSORPIN 6
// Zeit zwischen Messungen
const int waitingTimeBetweenMeasures = 5000;
DHT humiditySensor(HUMSENSORPIN, HUMSENSORTYPE);
void setup() {
Serial.begin(9600); // Seriellen Monitor initialisieren
humiditySensor.begin(); // Start der Messung
}
void loop() {
// Notwendiges Pausieren zwischen Messungen:
delay(waitingTimeBetweenMeasures);
  // Lesen der Daten dauert etwa eine Viertel Sekunde:
float humidity = humiditySensor.readHumidity();
float temperature = humiditySensor.readTemperature();

// Folgende Prüfung empfiehlt Adafruit:
if (isnan(humidity) || isnan(temperature)) {
Serial.println("Fehler beim Lesen des Sensors!");
return;
}
  // Hitzeindikator in Celsius. Für Fahrenheit,       
// einfach den Parameter false weglassen:
float heatIndexInCelsius =
humiditySensor.computeHeatIndex(temperature,
humidity, false);
  Serial.print("Feuchtigkeit: ");
Serial.print(humidity);
Serial.println(" %");
Serial.print("Temperatur: ");
Serial.print(temperature);
Serial.println(" °C");
Serial.print("Hitzeindikator: ");
Serial.print(heatIndexInCelsius);
Serial.println(" in Celsius");
}

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 [3] 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 [4] 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 [5]. 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 [6], 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 [7] 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 [8] 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 [9])


URL dieses Artikels:
https://www.heise.de/-3254522

Links in diesem Artikel:
[1] https://github.com/arduino-libraries/WiFi101
[2] https://github.com/adafruit/DHT-sensor-library/archive/master.zip
[3] https://github.com/arduino-libraries/RTCZero/archive/master.zip
[4] https://www.arduino.cc/en/Reference/RTC
[5] https://de.wikipedia.org/wiki/Network_Time_Protocol
[6] https://www.arduino.cc/en/Tutorial/UdpNtpClient
[7] https://tools.ietf.org/html/rfc958
[8] https://www.heise.de/blog/Kommunikation-ueber-das-Ethernet-Shield-mit-MQTT-3238975.html
[9] mailto:rme@ix.de