Internet der Dinge WS2015/ CalSwitch

Aus Verteilte Systeme - Wiki
Wechseln zu: Navigation, Suche

CalSwitch

Logo CalSwitch
Schaubild der Funktion

Schaltbare Steckdosen und andere Vorgänge mit einem digitalen Kalender über das Internet fernsteuern.

Einleitung

Dieses Projekt befasst sich mit einer Steuerung von Geräten durch digitale Online-Kalender. Dabei wird eine gewöhnliche Zeitschaltuhr um die gesamte Funktionalität eines solchen Kalenders erweitert. Dadurch ist es möglich komplexe Schaltprogramme von mehreren Benutzern über verschiedene Endgeräte durch das Internet vergleichsweise einfach umzusetzen. Beispielsweise kann so im Winter die Steuerung einer Heizung bzw. deren Umwälzpumpe durch einen Bewohner über Termine organisiert werden. Dazu genügt der bereits integrierte Kalender in seinem Smartphone.

Die Realisierung erfolgt durch einen Raspberry Pi, über dessen Allzweckausgaben (GPIOs) bspw. Relais gesteuert werden.

Es wird nur mit Spannungen im Niedervoltbereich gearbeitet.

Erwartete Ergebnisse

  • Ein System, das Termine aus Online-Kalendern erfolgreich in Schaltvorgänge umwandelt
  • Ein System, das die Möglichkeit der Erweiterung bietet
  • Gute Dokumentation (für Wiederverwendbarkeit und als Informationsquelle für Dritte)

Geplante Arbeitsschritte

  • Inbetriebnahme und Installation von Windows 10 IoT
  • Einrichten eines CalDAV Servers
  • Einarbeitung in die Umgebung
  • Kommunikation mit CalDAV Server
  • Erstellung eines Software Entwurfs
  • Implementierung
  • Test
  • Entwurf einer Benutzer Schnittstelle

Analyse

Raspberry Pi 2

Raspberry Pi 2

Für unser Projekt nutzen wir den Raspberry Pi 2. Da wir unser Projekt mit dem Betriebssystem Windows IoT realisieren wollen, ist die Auswahl an Hardware ohnehin beschränkt (Alternativen siehe Kapitel Windows IoT). Der Raspberry kommt auch deshalb zum Einsatz, weil er im Labor der Hochschule RheinMain verfügbar ist und somit keine zusätzliche Hardware beschafft werden muss. Zu beachten ist dabei, dass Windows IoT momentan nur auf dem Raspberry Pi 2 läuft und nicht auf dessen Vorgängern. Die Hardwareanforderung für die Grundfunktionalität sind vergleichsweise gering. Nötig wären hier ein (drahtloser) Zugriff auf das Internet und ein GPIO. Für eine Umsetzung im großen Stil wäre der Raspberry also "überdimensioniert" in Bezug auf Stromverbrauch und Performance. Für dieses Projekt eignet er sich dennoch sehr gut, weil viele Erweiterungen ausprobiert werden können.
Hauptfeatures des Raspberry 2[1]:

  • 900MHz quad-core ARM Cortex-A7 CPU
  • 1GB RAM
  • 4 USB ports
  • 40 GPIO pins
  • HDMI port
  • Ethernet port
  • Combined 3.5mm audio jack and composite video
  • Camera interface (CSI)
  • Display interface (DSI)
  • Micro SD card slot
  • VideoCore IV 3D graphics core

Windows 10 IoT

IoTCoreDefaultApp Standard-Oberfläche am HDMI Ausgang
Web Konfigurationsoberfläche Windows IoT
IoT Dashboard
Screenshot Powershell
Entwicklung nur über MS VisualStudio 2015 möglich

Zusammen mit Windows 10 hat Microsoft Ende 2015 auch eine IoT Version veröffentlicht. Zur Zeit ist diese kostenlos. Es besteht zu Teilen aus dem Vorgänger Windows Embedded. Die Features sollen auch und auch erweitert werden. Zum Projektstart war die Version 10.0.10586 gerade verfügbar, mit der wir bis auf weitere arbeiten werden. Mit Windows IoT möchte Microsoft auf dem Markt für vernetzte Geräte mit kleinem Footprint mitmischen. Mit dem Windows 10 IoT Core Pro OEM SKU will Microsoft auch im Bereich der Geschäftskunden Geld verdienen.[2] Gedacht ist Windows IoT für kleinere Geräte wie Gateways und mobile Point-of-Sale-Scanner oder leistungsstarke Industriegeräte wie Roboter und spezielle medizinische Apparate. [3] Und damit es auch wirkliche IoT Geräte sind legt der Hersteller besondere Wert auf eine gute Anbindung auf die Cloud Dienste von Microsoft (Azure). Im Moment läuft Windows IoT bereits auf folgenden Geräten

  • Raspberry Pi 2
  • MinnowBoad Max
  • DragonBoard 410c
  • Arduino Uno mit Windows Virtual Shields

Weiterhin hat Microsoft großes Interesse daran, die IoT Version von Windows 10 zusammen mit OEMs auf möglichst vielen Plattformen dieser OEMs zum laufen zu bringen.

Wir haben uns nicht für Windows IoT entschieden weil wir damit die einfachsten und besten Ergebnisse erwartet, sondern weil es noch einen experimentellen Charakter hat und wir auch herausfinden wollen, wo die Stärken liegen bzw. wo noch größere Probleme bestehen. Die Installation ist bereits relativ einfach gestaltet. Benötigt wird zur Installation auf einer SD Karte und zum Entwickeln von Programmen zwingend eine aktuelle Version von Windows 10 auf einem PC. Dort gibt es das IoT Dashboard. Hier können SD karten mit dem Betriebssystem bespielt werden, sowie die aktiven Geräte im Netzwerk angezeigt werden. Hier wird wieder IoT Charakter sichtbar: Das Aufspielen und Debugger von Software funktioniert über Netzwerk. Die Software kann ausschließlich mit Microsoft Visual Studio entwickelt werden. Von dort wird sie auf Wunsch auf dem Rechner oder direkt auf dem Raspberry zur Ausführung gebracht. Ab der Basis-Installation läuft auf dem Raspberry eine Standard Applikation IoTCoreDefaultApp.exe Darüber hinaus läuft auf dem Raspberry ein Webserver mit einer Konfigurationsoberfläche. Hier können viele wichtige Parameter eingesehen bzw. gesetzt werden:

  • Gerätename
  • Betriebssytem-Versionen
  • Gerät herunterfahren / neu starten
  • Apps starten / installieren / als "Default" (Startapp) bestimmen
  • Prozesse anzeigen lassen
  • Performance Daten / Grafen
  • Debug Informationen, process dumps, Fehlerreports
  • ETW Tracing
  • Performance tracing
  • Device Manager (Überblick über angeschlossene Hardware etc)
  • Bluetooth
  • Audio
  • Netzwerk (WiFi / Ethernet)
  • Windows Update

Ähnlich dem Vorgehen bei Unix Maschinen ist es möglich sich per Kommandozeile auf dem Raspberry einzuloggen. Ein mögliches Tool hierfür ist "Windows PowerShell."[4] Leider lassen sich damit keine Screenshots machen. Auf Windows IoT sind momentan nur wenige Funktionen für die PowerShell nutzbar.[5]

Einschränkungen

Da Windows 10 IoT noch recht neu ist und von Microsoft zunächst eher als "Bastelversion" kostenlos verfügbar ist, um auch bei der IoT Bewegung mitzumischen, müssen einige Abstriche gemacht werden. In Visual Studio wird das komplette "Microsoft.NETCore.UniversalWindowsPlatform" als nutzbar angezeigt. Einige Klassen aus dem Framework sind allerdings dennoch nicht nutzbar.

HTTP Request

Die .NET Klasse HttpRequest ist nur eingeschränkt nutzbar. Für Objekte der Klasse "WebRequest" kann die Methode "GetResponse()" nicht ausgeführt werden. Ein einfaches Beispiel, das auf den Seiten von Microsoft zu finden ist[6], lässt sich auf dem Raspberry nicht zur Ausführung bringen.[7]

WLAN Kompatibilität

Wir haben den Raspberry mit drei verschiedenen USB-WLAN Adaptern getestet. Keiner von ihnen wurde erkannt. Funktionieren soll laut Microsoft der "Realtek RTL8188EU Wireless Lan 802.11n USB 2.0 Network adapter". Dieser ist aber nur begrenzt erhältlich.[8] Im Verlauf des Projekts wurde eine neue Liste[9] mit kompatiblen Geräten veröffentlicht. Mit dem TP-Link TL-WN725N (Realtek RTL8188EU Chipsatz) ist eine stabile Verbindung zu einem WLAN verstellbar.

Systemzeit

Da der Raspberry nicht mit einer Batterie bzw. einer eigenen Uhr ausgestattet ist, muss die Systemzeit bei jedem Start aus dem Netzwerk via NTP geladen werden. In der zum Projekt aktuellen Version passiert dies nur wenn der Raspberry über Ethernet-Kabel an das Netzwerk angeschlossen wird. Für WLAN scheint die Umsetzung seitens Windows noch zu fehlen, was für unser IoT recht unpraktisch erscheint.

CalDAV

CalDAV ist ein ein Standard der IETF für den Zugriff auf geteilte Terminkalender. Es setzt auf WebDAV auf und nutzt das iCalendar Dateiformat. WebDAV wiederum nutzt HTTP(S) zur Kommunikation zwischen Server und Client.[10] Für CalSwitch nutzen wir einen bereits bestehenden CalDAV Server des Providers One.com. Grundsätzlich ist aber jeder CalDAV Server nutzbar. Es kann auch selbst ein Server aufgesetzt werden. Kostenlose Implementierung gibt es z.B. von Apache oder Owncloud. Im folgenden einige Beispiele für verschiedene Termintypen:

Einfache Termine

BEGIN:VEVENT
UID:C99BD599-1DC6-4B37-A651-29D9CDE5C2F9 
DTSTART;TZID=Europe/Berlin:20160114T090000 
DTEND;TZID=Europe/Berlin:20160114T100000 
CREATED:20160108T151207Z 
DTSTAMP:20160108T151243Z 
SEQUENCE:0 
SUMMARY:Einfacher Termin 
END:VEVENT

Ganztägige Termine

BEGIN:VEVENT
UID:ECB6DF1B-AE2E-472F-B1DA-29B88ED5796B
DTSTART;VALUE=DATE:20160111
DTEND;VALUE=DATE:20160112
CREATED:20160108T132541Z
DTSTAMP:20160108T132707Z SEQUENCE:0
SUMMARY:Ganztägiges Ereignis
END:VEVENT

Einfach wiederholte Termine

BEGIN:VEVENT UID:6D6DF25B-D3F6-41EF-965B-2F5D81FFE4E1
DTSTART;TZID=Europe/Berlin:20160112T120000
DTEND;TZID=Europe/Berlin:20160112T140000
CREATED:20160108T132426Z
DTSTAMP:20160108T132459Z
RRULE:FREQ=WEEKLY SEQUENCE:0
SUMMARY:Immer Dienstag
END:VEVENT

Komplex wiederholte Termine

BEGIN:VEVENT UID:D87A96F7-DBA3-46B9-9CBC-CDA988CC3BA5 
DTSTART;TZID=Europe/Berlin:20160113T121500 
DTEND;TZID=Europe/Berlin:20160113T131500 
CREATED:20160108T152556Z DTSTAMP:20160108T152824Z LAST-MODIFIED:20160108T152556Z 
RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=SU,SA;BYSETPOS=3 SEQUENCE:0 
SUMMARY:Komplexe Wiederholung 
END:VEVENT

Erster Termin: 13.01.2016 Wiederholung alle 2 Monate am 3 Wochenende

"Simple" Clients

Der CalDAV Standard sieht eine Funktion vor, in der wiederauftretende Ereignisse wie zum Beispiel das obige Beispiel "alle 2 Monate am 3. Wochenende" vom Server umgerechnet werden und als konkrete Termine zurück gegeben werden. Wie oben zu sehen wird bei wiederholten Termin nur ein bestimmtes Datum gegen und eine Beschreibung wie dieses umzurechnen ist. Gerade bei kleinen, passiven Anwendungen wie dem CalSwitch ist es sehr hilfreich wenn der Server diese Termine umrechnet. Ebenfalls werden dann alle Termine in der UTC Zeit geliefert. CalSwitch muss die Zeit also selbst nur noch an die lokale Zeitzone anpassen. Damit die Ergebenisse wie gewünscht umgerechnet werden, muss beim HTTP Request der gewünschte Zeitraum zusätzlich mit "expand" beschrieben werden.[11]

<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:D="DAV:"
xmlns:C="urn:ietf:params:xml:ns:caldav">
 <D:prop>
   <C:calendar-data>
    <C:expand start="20060103T000000Z"
               end="20060105T000000Z"/>
   </C:calendar-data>
 </D:prop>
 <C:filter>
   <C:comp-filter name="VCALENDAR">
     <C:comp-filter name="VEVENT">
       <C:time-range start="20060103T000000Z"
                     end="20060105T000000Z"/>
     </C:comp-filter>
   </C:comp-filter>
 </C:filter>
</C:calendar-query>

Push / Pull

In der jetzigen Umsetzung ist CalSwitch eine Pull Anwendung. Alle 20 Minuten (variabler Zeit) lädt sich das IoT-Device die Schalttermine vom CalDAV Server. Wird also ein Termin weniger als 20 Minuten vor Beginn verändert, wird dies ignoriert.

Unter Push Diensten versteht man allgemein solche, die neue Ereignisse (eintreffen einer Email, eines Termins, einer Sofortnachricht etc.) sofort an den Verbindungspartner mitteilen. Dazu muss eine dauerhafte Verbindung aufgebaut werden. Da auf den verbundenen Geräten mitunter mehrere Anwendungen diese Push-Dienste nutzen, ergibt es Sinn nur ein Push-Dienst zu nutzen, der der jeweiligen Anwendung ein Signal gibt, sobald eine Aktualisierung vorliegt. So geschieht es auf vielen propietären System von z.B. Blackberry, Google oder Apple. Für CalDAV Server sind Push-Dienste standardmäßig nicht vorgesehen, weshalb CalSwitch auch keine Push Funktionalität unterstützt.

Zeitzonen

Die nachfolgenden Beispiele zeigen jeweils ein Kalenderevent mit den Zeitzonen Berlin und Los Angeles. Zu beachten ist hier, dass die Zeitpunkte für Beginn und Ende jeweils in der "echten" Zeit gespeichert werden. Wenn der Nutzer sich in LA befindet und einen Termin einstellt für 10:00 wird der Termin demnach auch exakt mit der Uhrzeit 10 Uhr gespeichert. Findet dieser Termin jedoch in einer anderen Zeitzone statt in die der Nutzer reist, "verstellt" sich die Zeit ungewollt. In einem CalDAV Event wird deshalb auch die Zeitzone des Termins mitgeliefert (siehe folgende Beispiele für zwei verschiedene Zeitzonen). Diese werden unter TZNAME, TZOFFSETFROM und TZOFFSETTO gespeichert. Dabei wird die Abweichung von der Greenwich Mean Time, sowie die Abweichung der Sommer- Winterzeit mit angegeben. Beim CalSwitch gibt es für die Interpretation dieser Daten zwei Sichtweisen:

1. Der Termin muss immer mit der Zeitzone erstellt werden, in der sich der CalSwitch befindet

2. CalSwitch orientiert sich nur an der konkreten Uhrzeit im Termin unabhängig von der Zeitzone

Wir haben uns vorläufig für die zweite Option entschieden, da wir glauben, dass so weniger Fehler in der Benutzung entstehen, was folgendes Beispiel verdeutlicht: Der Benutzer ist verreist und möchte eine Schaltung programmieren, die zuhause Abends ab 18:00 das Licht einschaltet. Legt er diesen Termin mit der Uhrzeit 18:00 an während er mit seinem Handy in einer anderen Zeitzone ist, wird nicht mehr um 18:00 Heimatzeit, sondern 18:00 am Reiseort geschaltet, es sei denn der Nutzer ändern während der Terminerstellung die Zeitzone manuell ab.[12]

Durch den Einsatz der "Expand" Option (siehe: "Simple" Clients) wird Option 1 automatisch verwendet. Was zum Projektende der Fall war. Termine können in unterschiedlichen Zeitzonen erstellt werden. Der Server rechnet sie für die Anfrage des CalSwitch in die UTC-Zeit um. CalSwitch selbst rechnet die Zeit dann in die lokale (Sommer- oder Winter-) Zeit um.


BEGIN:VCALENDAR VERSION:2.0 CALSCALE:GREGORIAN PRODID:
BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT 
DTSTART:19810329T020000 
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 
TZNAME:MESZ TZOFFSETFROM:+0100 TZOFFSETTO:+0200 
END:DAYLIGHT BEGIN:STANDARD DTSTART:19961027T030000 
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 
TZNAME:MEZ TZOFFSETFROM:+0200 TZOFFSETTO:+0100 
END:STANDARD END:VTIMEZONE 
BEGIN:VEVENT UID:8BED25D2-33C2-4BE0-88B9-21F2777184F6 
DTSTART;TZID=Europe/Berlin:20160112T160000 
DTEND;TZID=Europe/Berlin:20160112T170000
CREATED:20160112T125603Z 
DTSTAMP:20160112T130150Z SEQUENCE:0 
SUMMARY:Bsp. mitteleuropäische Zeit
END:VEVENT END:VCALENDAR
BEGIN:VCALENDAR VERSION:2.0 CALSCALE:GREGORIAN PRODID:
BEGIN:VTIMEZONE TZID:America/Los_Angeles BEGIN:DAYLIGHT
DTSTART:20070311T020000 
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 
TZNAME:GMT-7 TZOFFSETFROM:-0800 TZOFFSETTO:-0700 
END:DAYLIGHT BEGIN:STANDARD 
DTSTART:20071104T020000 
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 
TZNAME:GMT-8 TZOFFSETFROM:-0700 TZOFFSETTO:-0800 
END:STANDARD END:VTIMEZONE 
BEGIN:VEVENT UID:2E6E5B58-9631-49BF-BFC7-B81010600395 
DTSTART;TZID=America/Los_Angeles:20160114T120000
DTEND;TZID=America/Los_Angeles:20160114T130000
CREATED:20160112T131359Z
DTSTAMP:20160112T131400Z
SUMMARY:Bsp. entfernte Zeitzone
END:VEVENT END:VCALENDAR

Google Calendar API

Zunächst wollten wir die Google Calenadar API nutzen [13]. Die Vorteile sind hier die hohe Qualität, kostenlose Nutzung und eine einfache Handhabe. Die C# API lässt sich aber nicht ohne weiteres auf dem Raspberry zum laufen bringen und für die Authentifizierung verlangt Google ein eigens Fenster zur Eingabe der Daten durch den Benutzer. Das ist bei unserer Anwendung nur schwer umsetzbar. Wir haben uns für den CalDAV Standard entschieden. Hier gibt es diverse Anbieter, bzw. können auch eigene Server aufgesetzt werden und man ist als Programmier und Nutzer der Anwendung nicht auf Google angewiesen.

CalDAV Abruf über HTTP und PHP

Request und Ergebnis einer Anfrage an einen CalDAV Server mittels eines PHP Skripts

Da wir anfangs Probleme mit dem Windows IoT eignen HTTP-Request für den Zugriff auf einen CalDAV Server hatten, haben wir einen Workaround entwickelt, der sich mittels einer PHP Library mit dem CalDAV Server verbindet. Die Library [14] SimpleCalDAV bietet einen breiten Funktionsumfang für CalDAV Anwendungen. Dabei ruft das Programm auf dem CalSwitch per HTTP-Request das PHP-Skript auf, das wiederum die Daten vom CalDAV Server lädt und weitergibt. Die Kommunikation bleibt per HTTPS verschlüsselt und der Zugriff auf das PHP Script erfolgt per htaccess.

Teil aus einem Programm der Daten von einem PHP-Skript bezieht:

private async Task HoleDaten() {
    WebRequest request = WebRequest.Create("https://clapotis.de/iotSave/getAll.php");
    request.Headers["Authorization"] = "Basic H4FsK3cbvSDoOlHETR4rGD9zZD==";
    WebResponse response = await request.GetResponseAsync();
    Stream dataStream = response.GetResponseStream();
    StreamReader reader = new StreamReader(dataStream);
    string responseFromServer = reader.ReadToEnd();
    textBlock.Text = responseFromServer;
}

Schalten per Fernbedieung

Überbrücken der Schalter auf der Fernbedienung

Im ersten Schritt haben wir die Schalter auf der Fernbedienung geschlossen. Dabei sind je Kanal ein An- sowie ein Ausschalter mit je einem GPIO verbunden. Über jeweils einen Transistor haben wir die Stromkreise auf der Fernbedienung geschlossen. Dies funktioniert in dieser Schaltung nur bedingt, da auf der Fernbedienung für An- und Ausschalter ein gemeinsamer Pluspol besteht und auf dem Raspberry wiederum ein gemeinsamer Minuspol. Dadurch konnten wir immer nur Ein oder Ausschalten und nicht beides gemischt. Außerdem besitzen Fernbedienung und Raspberry unterschiedliche Stromkreise, was ebenfalls zu nicht klaren Schaltvorgängen führt. Für eine korrekte Lösung müssten die Kontakte auf der Fernbedienung per Relais geschaltet werden.

433 MHz Sender/Empfänger

Sender und Empfänger im 433 MHz band
Signal der Fernbedienung

Die Zusatzhardware gibt es in verschiedenen Ausführungen.[15] Die Kosten liegen im einstelligen Eurobereich. Das Grundsätzliche verfahren ist z.B. in der c't 2016 Heft 2 wird das Senden und Empfangen per Raspberry unter Raspbian beschrieben. Eine Ansteuerung des Sende- und Empfangsmodul funktioniert mit der Software Pilight [16]. Das alles gilt jedoch nur für Raspbian. Pilight läuft nicht unter Windows 10 IoT. Deshalb mussten wir die Funktionalität zur Ansteuerung des Sendemoduls selbst implementieren. Im Kern geht es hier darum eine 20-stellige Bitsequenz zu senden. Jedes Bit hat einen Slot von 2,1 ms wobei die 0 aus 700 µs low und 1400 µs high und die 1 genau in der umgekehrte Reihenfolge signalisiert wird. Da die Ausführung von kompiliertem C# Code auf Windows 10 IoT nicht einem Echtzeitsystem entspricht, ist es nur eingeschränkt möglich eine Bitsequenz mit einer gewöhnlichen Kombination aus Timer und Schleife korrekt abzusetzen. Die konkreten Sendesignale der Fernbedienung konnten wir mit dem Oszilloskop beobachten und mit der Linux App Pilight auf dem Raspberry auch aufzeichnen. Außerdem gibt es für die bei uns eingesetzte Fernbedieung, die Daten im Quigg "Protokoll" sendet, bereits eine Auflistung wie die gesendeten Bits zu interpretieren sind. [17]

for ( j = 0; j < 62; j++)
{
 pin.Write(values[j]);
 delay(QUIGG_SHORT);
}
delay(23.8f);
       
private void delay(float a_fDelay_ms)
{
 watch.Start();
 while ((watch.Elapsed).TotalMilliseconds < a_fDelay_ms) { }
 watch.Reset();
}



Entwurf und Planung

Entwurd eines Test-Vorläufers

Um den Überblick zu behalten und Qualität hoch halten zu können arbeiten wir bei diesem Projekt Objekt-orientiert. Dabei geht es aber nicht um eine komplett im Voraus designtes Software-Projekt, sondern mehr um eine Visualisierung der Struktur und um schnelles Verständnis in der Teamarbeit sowie beim Programmieren selbst.



Informationsfluss

Prototyp ohne 433 MHz Sender und PHP Workarround
Aktuelle Umsetzung mit 433 MHz Sender und direkter Kommunikation zum CalDAV Server


Funktionsweise

In unsere Umsetzung wird testweise nur ein Port zur Schaltung verwendet. Mit seinen 40 GPI können 40 und mehr Geräte geschaltet werden. Wichtig ist, dass je schaltbares Gerät ein eigener Kalender bestehen muss. Denn ansonsten müsste die Information welches schaltbare Gerät gemeint ist, in einem Termin als Pflichtfeld existieren. Dies ist aber bei allen gängigen Terminkalendern nicht der Fall. Es ließen sich natürlich mehrere Steckdosen über einen Kalender steuern, z.B. mit bestimmten Stichwörtern in den Textfeldern. Hier könnten jedoch Fehler in der Benutzung entstehen. Außerdem gilt:

  • Jeder Termin im Kalender schaltet den Ausgang genau so lange an wie der Termin selbst dauert (Ein Termin für das Ein- und Ausschalten)
  • Überlappen sich zwei Termin schaltet der früheste Termin das Gerät ein und der letzte schaltet es aus.
  • Beim Booten des Raspberry werden per Default alle Ausgänge zunächst ausgeschaltet
  • Besteht über den Aktualisierungszeitraum (20 min) hinaus keine Verbindung zum Internet, werden alle Ausgänge ausgeschaltet (Sicherheitsaspekt)
  • Der Benutzer kann durch einen Taster am Gerät eine Aktualisierung erzwingen (pull)

Ausschaltregel bei überlappenden Schaltpunkten

Liegen mehrere Termine für einen Schalter zeitlich übereinander muss speziell beim Ausschalten überprüft werden, dass nicht noch ein weiterer Termin besteht und der Ausgang fälschlicherweise auf Aus gesetzt wird. Um dies zu vermeiden müssen die Einschaltvorgänge gezählt werden. Erst wenn dieser Zähler, der bei 0 beginnt, wieder auf 0 steht, darf der Ausgang ausgeschaltet werden.

Ausschaltszenario


UML Diagramm und Ablauf

UML-Klassendiagramm

Um Code im Hintergrund des Windows IoT Betriebssystems laufen zu lassen, muss eine Klasse die Schnittstelle "IBackgroundTask" implementieren.[18] Da bei CalSwitch generell die ganze Applikation im Hintergrund arbeiten soll, wurde ein von Windows gestelltes Template für "Background Applications" genutzt. Dieses Template enthält eine Klasse "StartupTask", die die entsprechende Schnittstelle und deren Methode "Run" implementiert und somit die Ausgangsposition unseres Programms bildet.

Für jeden Kalender wird anschließend ein "CalendarSwitch" Objekt erzeugt. Diese Klasse zeichnet sich dadurch aus, dass sie jeweils Implementationen der Schnittstellen "ICalDavClient" und "ISwitch" verwendet. Erstere muss die Möglichkeit bieten Anfragen an einen CalDAV-Server zu senden, letztere eine beliebige Aktion, die beim An- bzw. Ausschalten ausgeführt wird. Für das Senden bestimmter Sequenzen im "QUIGG"-Protokoll wird beispielsweise die Klasse "SwitchRadio" benutzt.

Da die Abfragen an den CalDAV-Server asynchron gesendet werden, wird das "CalendarSwitch" Objekt durch Events über die erhaltene Antwort informiert. Die Antwort des Servers kann anschließend verarbeitet und nach Events durchsucht werden. Für jedes Event werden dann "CalEventTimer" Objekte instanziert, die aus dem Event selbst, sowie einem Timer bestehen, der beim nächstfolgenden Schaltvorgang (Terminbeginn bzw. -ende) ein Event an den "CalendarSwitch" sendet. Dieser speichert die "CalEventTimer" in einem Dictionary, das den Zugriff über ihre Event-Ids ermöglicht.

Bei jedem Schaltevent entscheidet das "CalendarSwitch" Objekt gemäß den bereits erwähnten Ausschaltregeln, welche Aktion ausgeführt wird.

Benutzeroberfläche

Entwurf für die GUI

Für den alltäglichen Betrieb wird keine eigene Benutzeroberfläche benötigt, da die Bedienung direkt über die Kalender in den Endgeräten der Benutzer statt findet. Eine Benutzeroberfläche würde im realen Betrieb zumindest benötigt um die Kalender zur Steuerung der Ausgänge einzubetten und z.B. um die anzusteuernden Geräte im ISM Band zu Speichern.

Implementierung

Erzeugen des HTTP Requests

Für die Erzeugung des HTTP-Requests an den CalDAV Server wird die Klasse "HttpClient" aus den Namespace "Windows.Web.Http" der Universal Windows Platform API verwendet.[19] Es ist wichtig diese Klasse von derjenigen unter dem Namespace "System.Net.Http" zu unterscheiden.[20] So ist letztere geläufiger und auch bei der Programmierung für Windows IoT verfügbar, verursacht jedoch in unserer Version Fehler beim Senden von Requests. Ein einfacher Wechsel dieser Klassen ist zudem nicht ohne Weiteres möglich, da sich die Verwendung der HTTP-Clients unterscheidet.

           HttpRequestMessage request;
           HttpResponseMessage response;
           CalDavDialog cdd = new CalDavDialog();
           HttpBaseProtocolFilter pFilter = new HttpBaseProtocolFilter();
           pFilter.AllowAutoRedirect = false;
           pFilter.ServerCredential = new PasswordCredential("caldav", User, Password);
           try
           {
               using (HttpClient client = new HttpClient(pFilter))
               {
                   request = CalDavHelper.getRequest(a_type, Url);
                   cdd.requestType = a_type;
                   cdd.reqHeader = request.Headers.ToString();
                   cdd.reqBody = await request.Content.ReadAsStringAsync();
                   response = await client.SendRequestAsync(request);
                   if (response != null)
                   {
                       cdd.resHeader = response.Headers.ToString();
                       cdd.resBody = await response.Content.ReadAsStringAsync();
                       OnRequestFinished(CalDavRequestStatus.SUCCESSFULL, cdd);
                   }
               }
           }

Für die gezielte CalDAV Abfrage nach Terminen in einem bestimmten Zeitraum wird die Methode "REPORT" des WebDAV Protkolls verwendet. Die "HttpClient" Klasse erwartet als Methode ein Objekt des Typs "HttpMethod", welches laut Dokumentation nur die HTTP-Methoden unterstützt.[21] Jedoch lässt sich beim Anlegen einer neuen Instanz dem Konstruktor ein beliebiger String übergeben, welcher anschließend als Methode für den Request verwendet wird.

           HttpRequestMessage httpRequest = null;
           HttpMethod method = null;
           method = new HttpMethod("REPORT");
           httpRequest = new HttpRequestMessage(method, new Uri(a_url));
           httpRequest.Headers.Add("Depth", "1");
           httpRequest.Headers.Add("Accept", "application/xml; charset=utf-8");
           httpRequest.Content = new HttpStringContent(createReportIntervalXmlString(DateTime.Now, DateTime.Now.AddHours(1)));

Bei der Erstellung der "HttpRequestMessage" wird anschließend ein String übergeben, der die gewünschte CalDAV-Abfrage im XML-Format enthält.

CalDAV-Request


Verarbeiten der Response

Die Antwort des Servers liegt auch im XML-Format vor. Dabei können mehrere ICalendar Dateien übermittelt werden, deren Inhalt sich jeweils in CDATA-Abschnitten befindet. Somit können aus der Antwort zunächst die CDATA-Abschnitte extrahiert werden um diese anschließend auf Events zu untersuchen. Diese sind jeweils eingeschlossen zwischen einem "BEGIN:VEVENT" und "END:VEVENT". Aus jedem Event werden wiederrum bestimmte Eigenschaften ausgelesen und daraus schließlich ein "CalEvent" Objekt erstellt.

CalDAV-Response


Verarbeitung von Schaltzeitpunkten (Zeit und Datum)

Die bei der oben genannten Abfrage vom CalDAV-Server übermittelten Zeiten liegen als Text in drei bekannten Formaten vor. Für die weitere Verarbeitung werden diese in "DateTime" Objekte umgewandelt, wobei die bereits erwähnte UTC-Zeit direkt in die lokale vom Windows IoT System konvertiert wird.

       public static DateTime convertDateFromCalFormat(string a_date)
       {
           DateTime date = date_invalid;
           if (a_date.Length == 8)             // Format: 20160131
           {
               date = DateTime.ParseExact(a_date, "yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture);
           }
           else if (a_date.Length == 15)       // Format: 20160131T174000
           {
               date = DateTime.ParseExact(a_date, "yyyyMMddTHHmmss", System.Globalization.CultureInfo.InvariantCulture).ToLocalTime();
           }
           else if (a_date.Length == 16)       // Format: 20160131T174000Z
           {
               date = DateTime.ParseExact(a_date, "yyyyMMddTHHmmssZ", System.Globalization.CultureInfo.InvariantCulture).ToLocalTime();
           }
           return date;
       }

Automatisches Starten der Applikation auf dem IoT-Gerät

Windows Iot unterstützt 2 unterschiedliche Betriebsarten: "headed" und "headless". Bei der ersten Betriebsart wird eine graphische Benutzeroberfläche über den HDMI-Ausgang angezeigt und es kann jeweils eine Applikation mit einer solchen Oberfläche gestartet werden. Letztere unterstützt nur "headless" Applikationen ohne graphische Oberfläche, was zu erhöhter Stabilität und Performance führt.[22] In Beiden Fällen ist die Festlegung eines Autostart-Programmes möglich, das bei Systemstart automatisch ausgeführt wird.

Die Konfiguration erfolgt über die Powershell:

 [192.168.0.20]: PS C:\> setbootoption.exe headless
 [192.168.0.20]: PS C:\> shutdown /r /t 0
 [192.168.0.20]: PS C:\> iotstartup add headless CalSwitch.exe
 Added Headless:  CalSwitch.exe
 [192.168.0.20]: PS C:\>

Tests & Evaluation

Testcases

Natürlich wurde während der Entwicklung fortlaufend das Programm getestet. Darüber hinaus haben wir Testcases geschrieben um einige wichtige Eckpunkte strukturiert abtesten zu können. Es ist wichtig, dass man im Verlauf immer wieder einige Grundfunktionalitäten testet, damit der Blick für die gesamte angestrebte Funktionalität nicht verloren geht. Die "einfache" Grundfunktionalität die das Ein- und Ausschalten mehrerer Geräte nach Terminkalendern umfasst, erzeugt bereits über 30 Testcases.

Datei:Testcases.pdf

Evaluation

Wie anfangs erwartet hat sich die Entwicklung des CalSwitch durch die Nutzung von Windows IoT erschwert. Nicht alle Klassen aus dem .NET Framework die als nutzbar gelten, sind tatsächlich verfügbar. Das hat uns gerade bei der Abfrage der Daten vom CalDAV-Server per HTTP Request vor Probleme gestellt. Bei der Umsetzung einer Applikation ist es oft hilfreich, wenn man für Teilaufgaben auf Beispiele und freien Code zurückgreifen kann. Obwohl es für Windows IoT schon einiges an Beispielen gibt, sind für Raspberry-Projekte unter Linux bzw. Raspbian sicherlich weitaus mehr Lösungen vorhanden. Ein Konkretes Beispiel hierfür ist die Konfiguration des IoT über einen Webserver. Was für Raspbian relativ einfach umzusetzen und dokumentiert ist, ist auf dem Windows IoT mit großem Aufwand oder gar nicht umzusetzen. Es gibt keine passende Lösung für einen Webserver, der zudem noch Konfigurationen auf dem internen Speicher ablegen könnte.

Für wen ist die Nutzung von Windows IoT zu empfehlen?

Im Normalfall der Entwicklung von Apps auf dem Raspberry würden wir aufgrund der genannten Probleme nicht zur Windows IoT Umgebung raten. Der Einsatz dieser Technologie kann allerdings sinnvoll sein, wenn man Bereits einige Erfahrung in der Entwicklung mit dem .NET Framework hat und das zu entwickelnde IoT im Verbund mit anderen Geräten und Diensten, ebenfalls aus der .NET Umgebung, einsetzen möchte. Das gilt auch besonders dann, wenn das IoT in einer Cloud von Microsoft (Azure) arbeiten soll. Ein weiterer Punkt, der für die Nutzung von Windows IoT spricht, ist die Nutzbarkeit auf verschiedenen Hardwareplattformen. Hier ist es auch möglich (Und erklärtes Ziel von Microsoft) seine eigene Hardware zu entwickeln, auf der dann Windows IoT läuft.

Betriebssystem-bedingte Fehler

Relativ neu auf dem Markt, ist Windows 10 IoT gerade in der Entwicklung von Software noch nicht besonders zuverlässig. Aus unbekannten Gründen hatte wir mehrfach das Problem, das im laufenden (Debug-)Betrieb der Dienst quittiert wurde. Auf dem HDMI Ausgang lag dann bildschirmfüllend ein ":-(" Smiley an. Beheben ließ sich dieses Problem nur mit einer kompletten Neubespieglung der MicroSD-Karte.

Uhrzeit-Synchronisation fehlerhaft

Die Synchronisation der Uhrzeit über WLAN funktioniert in Windows 10 IoT nicht. Da der Raspberry keine Batterie oder ähnliches hat, muss die Uhrzeit bei jedem Systemstart neu synchronisiert werden. Siehe auch: Kapitel Systemzeit Die Uhrzeit kann manuell über die Konsole gesetzt werden. Da die Apps im Sandbox Prinzip laufen, ist es nicht ohne weiteres möglich eine App mit dem Systemstart auszuführen, die die Systemzeit über einen NTP synchronisiert. Abhilfe würde nur ein Workarround schaffen der einen "eigene Systemzeit" umsetzt.[23] Da hier aber auch viele Fehler entstehen können und dieser Workarround mit einem neuen Built des Betriebssystems überflüssig werden könnte, haben wir auf die Umsetzung eines eigenen Workarrounds zur Zeitsynchronisation verzichtet.

Unidirektionales Senden

Als Testgeräte haben wir in unserem Projekt konventionelle Funksteckdosen verwendet. Diese senden nach dem Schaltvorgang keine Bestätigung an den Sender zurück. Das kann dazu führen, das durch diverse äußere Einflüsse ein nicht erfolgreich übertragenes Signal auch nicht ausgeführt wird. Das kann je nach zu schaltendem Gerät gefährlich werden.

Mögliche Erweiterung

Erweiterung mit Sensorik und Aktorik

Bei möglichen Erweiterungen sind der Phantasie nur wenige Grenzen gesetzt. Am Ende sollte die gesamte Logik aber auf einem möglichst kleinem und kostengünstigem IoT laufen. Das spricht zumindest gegen eine HDMI Schnittstelle und eine aufwendige Bedienoberfläche auf einem Webserver.

Sensorik / Aktorik

  • Einbindung weiterer Sensoren: Licht, Beschleunigung etc.
  • Zeitlich programmiertes Abspielen von Audiostreams (z.B. Internetradio) über den HDMI-Ausgang

M2M / Konditionierung

  • M2M Schnittstelle zum Betrieb als Aktor für andere IoTs
  • Unterstützung weiterer (drahtloser) Protokolle der Home Automation
  • Idee für folgende Kurse: Einzelne über gemeinsames Protokoll miteinander "sprechen" lassen
  • "If this then that" Bedienung unter Einfluss anderer IoT Sensoren

Portierung auf kleinere Hardware

  • Reduktion von Kosten
  • Reduktion des Energieverbrauchs

(Web-) User-Interface

  • Mini Webserver auf dem Gerät, über den Parameter angezeigt und gesetzt werden können (Probleme siehe Evaluation)
  • Bedienoberfläche auf dem Gerät selbst, die über den HDMI Ausgang z.B. auf einem Fernseher ausgegeben werden kann

Verwendung einer Datenbank

Je nach Systemarchitektur funktioniert der CalSwitch bis auf einen CalDAV Server autonom. Ein weiterer Server auf dem eine Datenbank mit den zu schaltenden Terminen würde eine unnötige Redundanz darstellen. Dennoch würde eine Datenbank zur Organisation der verschiedenen Termine durchaus Sinn ergeben, da die Termine auf dem CalDAV Server quasi nur im Textformat vorliegen. Diese Datenbank sollte allerdings auf dem Windows IoT Gerät selbst laufen. Dafür würde sich SQLite anbieten, welches sich auch in VisualStudio Projekte einbinden lässt[24]. In SQLite gibt es allerdings keine temporalen Erweiterungen, die speziell im Zusammenhang mit Schaltterminen natürlich Vorteile bieten.

Vortrag

Folien:

Datei:CalSwitchPraesPPT.zip

Datei:CalSwitchPraesKEY.zip

Datei:CalSwitchPraes.pdf


Demo-Video:

https://bit.ly/CalSwitch

Referenzen

  1. https://www.raspberrypi.org/products/raspberry-pi-2-model-b/ Abgerufen am 22.12.2105
  2. http://bit.ly/1Ol7YzG What’s new for Windows 10 IoT this fall Abgerufen am 22.12.2105
  3. http://bit.ly/1Ol92nf Abgerufen am 22.12.2105
  4. https://ms-iot.github.io/content/en-US/win10/samples/PowerShell.htm
  5. https://ms-iot.github.io/content/en-US/win10/tools/CommandLineUtils.htm
  6. https://msdn.microsoft.com/de-de/library/system.net.webrequest(v=vs.110).aspx#Anchor_7
  7. http://geekswithblogs.net/WindowsEmbeddedCookbook/archive/2014/07/27/windows-for-iot-first-impressions.aspx " ... there are no libraries ... to make HTTP request, ..."
  8. http://ms-iot.github.io/content/en-US/win10/ReleaseNotes.htm
  9. http://ms-iot.github.io/content/en-US/win10/SupportedInterfaces.htm#WiFi-Dongles
  10. https://tools.ietf.org/html/rfc4791#section-1
  11. https://tools.ietf.org/html/rfc4791#section-7.6
  12. https://tools.ietf.org/html/rfc4791#section-5.2.2
  13. https://developers.google.com/google-apps/calendar/quickstart/dotnet Abgerufen am 22.12.2015
  14. https://github.com/wvrzel/simpleCalDAV
  15. http://www.avc-shop.de/433Mhz-Sender-Empfaenger-Superregeneration-Modul-FS1000A-XY-FST-XY-MK-5V
  16. https://www.pilight.org
  17. https://wiki.pilight.org/doku.php/quigg
  18. https://msdn.microsoft.com/en-us/library/windows/apps/mt299100.aspx
  19. https://msdn.microsoft.com/en-US/library/windows/apps/windows.web.http.httpclient
  20. https://msdn.microsoft.com/de-de/library/system.net.http.httpclient%28v=vs.118%29.aspx
  21. https://msdn.microsoft.com/library/windows/apps/windows.web.http.httpmethod.aspx
  22. https://ms-iot.github.io/content/en-US/win10/HeadlessMode.htm
  23. http://stackoverflow.com/questions/30585900/how-to-set-system-time-in-windows-10-iot
  24. http://blog.chrisbriggsy.com/Using-SQLITE-in-Windows-10-IoT-Core-Insider-Preview/