Aus Linux-Magazin 12/2016

Hausautomatisierung mit I2C-Buskomponenten

© Felix Pergande, 123RF

Standardisierte Protokolle und Schnittstellen bieten im Vergleich zu proprietären viele Vorteile. Einer davon: Sie sind deutlich günstiger. Das zeigt auch das Beispiel einer Automatisierung von Lichtern, Jalousien und der Alarmanlage mit Komponenten für den I2C-Bus in diesem Artikel.

Taugt das I2C-Protokoll überhaupt für die Hausautomation? Mancher mag da Zweifel hegen angesichts der beschränkten Leitungslänge und der fehlenden Fehlererkennungs- und Fehlerkorrekturmechanismen [1]. Einerseits sind diese Bedenken nicht wegzudiskutieren, andererseits steht für ambitionierte Häuslebauer eine Fülle an Erweiterungshardware für I2C bereit, die sich an aktuelle System-on-Chip-Systeme anschließen lässt.

Obendrein sind dabei Standard-Haustechnikkomponenten wie etwa günstige Lichtschalter aus dem Baumarkt einsetzbar. Und nicht zuletzt vereinfacht die Wahl von I2C auch die Ersatzteilbeschaffung wesentlich.

Mit der hier vorgestellten Lösung kann man fast alle Anforderungen in der Hausautomation bewältigen: Eine automatische Lichtsteuerung könnte etwa potenzielle Eindringlinge abschrecken. Motoren von Hausgeräten sollten zum Beispiel anlaufen, wenn der Strom gerade günstig oder im Überfluss vorhanden ist [2]. Diverse Internetservices ließen sich einbinden, die Informationen von überallher beisteuerten. Auch der Zugriff aus der Ferne auf die häusliche Technik gelingt leicht, inklusive der Verschlüsselung durch das SSH-Protokoll.

Dank der Leistungsfähigkeit aktueller Single Board Computer ist es zudem möglich, komplexe Berechnungen direkt durch die Steuerungshardware ausführen zu lassen. Die offene Architektur macht die Anwender unabhängig von einer bestimmten Programmiersprache. Während man bei Komplettsystemen oft sowohl Hard- als auch Software-seitig an einen bestimmten Hersteller gebunden ist, kann man bei diesem System jede unter Linux lauffähige Software einsetzen und auf das Know-how einer riesigen Community zurückgreifen.

Der vorliegende Artikel gliedert sich in einen Hard- und einen Softwareteil. Zuerst präsentiert er Beispiele geeigneter I2C-Hardware. Anhand der Steuerungshardware geht er dann auch auf die Verkabelung des Eigenheims ein. Der Software-Teil widmet sich der grundlegenden Konfiguration des I2C-Busses unter Raspbian-basierten Distributionen. Zwei Beispiele runden den Artikel ab: Eines zeigt eine in Python 3 programmierte wettergesteuerte Jalousie, das zweite, wie sich eine Alarmanlage nach der IEC-61131-3-Norm unter Linux realisieren lässt.

Bleibt noch darauf hinzuweisen, dass der Bastler bei allen Experimenten unbedingt mit der nötigen Vorsicht zu Werke gehen sollte. Denn manchmal muss er mit Netzspannung hantieren – unsachgemäße Ausführung kann hier Lebensgefahr heraufbeschwören! Wer sich nicht sicher ist, sollte sich zur Unterstützung an einen Elektrofachbetrieb wenden.

Auf Komponentenjagd

Ein Raspberry Pi ist die zentrale Komponente des Schaltkastens, um den es hier gehen soll. Ihn bekommt man in jedem besseren Elektronikfachgeschäft oder im Online-Versandhandel. Grundsätzlich ist jedes der verfügbaren Modelle geeignet, da immer mindestens ein I2C-Bus an der 26- beziehungsweise 40-poligen Stiftleiste herausgeführt ist. Als Entscheidungsgrundlage darf daher die benötigte Rechenleistung dienen.

Bei den eigentlichen I2C-Komponenten ist die Suche etwas komplizierter. Es zahlt sich aus, jene Komponenten zu bevorzugen, die weit verbreitet sind. Automatisch gibt es für sie auch die meisten Beispielprogramme. Ein typischer Vertreter dieser Kategorie ist beispielsweise der PCF8574-Chip. Dabei handelt es sich um einen so genannten 8-Bit-Portexpander, der am I2C-Bus acht zusätzliche Ein- oder Ausgänge bereitstellt. Er lässt sich besonders einfach ansprechen. Für andere Anwendungsfelder, zum Beispiel zur Fernsteuerung von Funksteckdosen [3] sowie zur Verarbeitung analoger Signale [4], stehen ebenfalls Chips und fertige Module zur Verfügung.

Vorsicht ist bei der Wahl der Ausgangsmodule mit Blick auf den zulässigen Schaltstrom geboten. Viele Klemmen bewältigen die hohen Einschaltströme von Motoren oder Leuchten nicht. Daher ist es ratsam, die Datenblätter potenzieller Hardware zu studieren.

Eine große Auswahl aller für den Bereich Heimautomatisierung benötigten I2C-Komponenten bieten zum Beispiel die Firmen Horter & Kalb [5] oder CC-Tools [6] an. Die Produkte beider Firmen zeichnen sich durch einen hohen Standardisierungsgrad aus, was die Ersatzteilbeschaffung und Reparatur wesentlich vereinfacht. Zusätzlich gibt es zu allen Produkten eine umfassende Dokumentation, die auch Beispielprogramme, Schaltpläne und Bauteillisten umfasst.

Die Eingangsmodule für den I2C-Bus beider Hersteller können die in der SPS-Programmierung üblichen 24-Volt-Signale verarbeiten, die beispielsweise Taster liefern. Bei den Ausgangsmodulen unterscheiden sich die beiden Ansätze.

Die Ausgangsmodule von Horter & Kalb können Gleichstromrelais ansteuern. Mit so genannten Koppelrelais ist es dann möglich, Netzspannung zu schalten. Die schmalen Relais der Firma Finder [7] passen auch in kleinere Schaltschränke und bewältigen trotz ihrer vorteilhaften Bauform bis zu 5 Ampere Schaltstrom, was für Lichter oder Jalousiemotoren völlig ausreichend ist. Die Ausgangsmodule der Firma CC-Tools hingegen vereinen üblicherweise sowohl die Bus-Komponenten als auch die Leistungsstufen (Relais) auf einer Platine. Durch diesen Ansatz lässt sich der Verkabelungsaufwand gering halten.

Im Lieferprogramm beider Hersteller finden sich auch die für den Betrieb der Ein-/Ausgabemodule notwendigen Spannungswandler. Ein für den I2C-Bus typisches Spannungsniveau ist 5 Volt.

Intern arbeitet der Raspberry Pi aber trotz seiner 5-Volt-Spannungsversorgung mit 3,3 Volt. Wer also I2C-Komponenten direkt an den Raspberry Pi anschließt, fügt dem Rechner mit der Zeit Schaden zu. Um dies zu verhindern, hängt man zwischen die I2C-Komponenten und den Raspberry Pi einen Spannungswandler, der auch als I2C-Buffer bekannt ist. Ein solches Gerät der Firma CC-Tools findet in einem separaten Hutschienengehäuse Platz. Die Variante der Firma Horter & Kalb lässt sich direkt auf die 26- beziehungsweise 40-polige Stiftleiste des Raspberry Pi stecken.

Außerdem braucht der Bastler noch Komponenten zum Schaltschrankbau. Die Stromversorgung (etwa ein 24-Volt-Hutschienen-Netzteil), Datenkabel und so weiter besorgt er sich üblicherweise im Elektrofachgeschäft oder im Online-Versandhandel. Diverse andere Hardware, Hutschienen und Befestigungsmaterial liefert am besten ein Fachhändler. Zum Anschluss von Handtastern bieten sich mehrpolige, flexible Litzen an, etwa die siebenpolige Unitronic LiYCY-Datenleitung von Lapp Kabel oder die siebenpolige Steuerleitung LiYCY von Faber Kabel mit einem Leitungsquerschnitt von 0,25 mm2 je Ader.

Mit Adernhülsen versehen lässt sich das Kabel problemlos an herkömmliche Lichtschalter anschließen. Es wird von den Schaltschränken aus sternförmig bis zu jedem Taster im Haus verlegt. Ein siebenpoliges Kabel kann bis zu sechs verschiedene Taster bedienen. Auch wenn nur ein Einfach- oder ein Doppeltaster anzuschließen sind, ist der Einsatz eines mehrpoligen Kabels ratsam, weil der Hausherr so später zusätzliche Taster ohne Neuverlegung von Kabeln an das System anschließen kann.

Wie ein Schaltschrank mit den in diesem Kapitel beschriebenen Komponenten strukturiert sein kann, zeigt der Prototyp in Abbildung 1. Die Leitungsschutz-Automaten sowie die Gleichspannungs-Stromversorgungen für die Steuerungskomponenten (Raspberry Pi, Ein-/Ausgabemodule) sind in der oberen Reihe des Aufbaus angebracht. In der unteren Reihe befinden sich von links nach rechts Klemmen, die verschiedene Gleichspannungsniveaus (bei diesem Aufbau: 5 Volt und 24 Volt) für die Steuerung bereitstellen. Dann folgen der Steuerungscomputer (ein Raspberry Pi) und ein Eingangs- sowie ein Ausgangs-Modul jeweils von der Firma Horter & Kalb.

Abbildung 1: Prototyp eines Schaltschranks, aufgebaut auf einer OSB-Platte.

Abbildung 1: Prototyp eines Schaltschranks, aufgebaut auf einer OSB-Platte.

An das Eingangsmodul ist ein improvisiertes Tastfeld angeschlossen. Um die Glühbirnen mit Netzspannung vom Ausgangsmodul versorgen zu können, kommen Koppelrelais der Firma Finder zum Einsatz. Den Abschluss bildet ein Ausgangsmodul der Firma CC-Tools, das direkt mit Relais-Ausgängen aufwarten kann (in der Abbildung noch ohne Stromversorgung).

Die Materialkosten des hier vorgestellten Ansatzes betragen für ein durchschnittliches Einfamilienhaus pro Schaltschrank und Stockwerk rund 300 bis 500 Euro. Im Vergleich mit anderen Heimautomatisierungslösungen ist das günstig. Für Eingabegeräte und Aktoren lässt sich Standardhardware einsetzen, die es in jedem Baumarkt gibt. Aufgrund der niedrigen Materialkosten bietet es sich auch an, für den Fehlerfall mindestens eine Komponente jeder Kategorie (Raspberry Pi, Inputkarte, Outputkarte, Koppelrelais) auf Lager zu legen.

Strukturierte Verkabelung

Auf den ersten Blick mögen sich Bussysteme – zum Beispiel KNX – für das gesamte Haus anbieten, um alle Komponenten zu verbinden. Tatsächlich lässt sich damit der Verkabelungsaufwand reduzieren. Doch müssten alle an das Bussystem angeschlossenen Komponenten das auf dem Bus eingesetzte Protokoll implementieren, was die Kosten für die Hardware wesentlich in die Höhe treibt. Beispielsweise sind KNX-Taster zurzeit nicht unter 40 Euro pro Stück erhältlich. Ein herkömmlicher, im hier präsentierten Ansatz einsetzbarer Doppeltaster hingegen kostet rund 5 Euro. Unter Umständen ist man beim Einsatz eines Bussystems außerhalb des Schaltschranks auch an die Lösung eines ganz bestimmten Herstellers gebunden.

Als besonders Vorteilhaft hat sich bei der Verkabelung des Eigenheims die Topologie der strukturierten Verkabelung erwiesen. Dazu führt der Installateur alle Steuer- und Lastleitungen eines Stockwerks an einen Punkt, dem so genannten Stockwerksverteiler, zusammen. Die Stockwerksverteiler enthalten neben den Ein- und Ausgangsmodulen (»I« und »O« ) deren Stromversorgung (»PSU« ; Power Supply Unit) sowie jeweils mindestens einen Raspberry Pi. Zwischen den Stockwerken sind dann nur noch die Stromversorgungskabel (Nullleiter »N« und Phase »L« ) sowie Datenkabel (Abbildung 2) erforderlich.

Abbildung 2: Einsatz der strukturierten Verkabelung in der Hausautomatisierung.

Abbildung 2: Einsatz der strukturierten Verkabelung in der Hausautomatisierung.

Aufgrund der Beschränkung der zulässigen Leitungslänge, des kleinen Adressraums für Komponenten sowie fehlender Fehlererkennungs- und Fehlerkorrekturmechanismen ist ein einziger I2C-Bus nur bedingt als Bussystem für die Verkabelung aller Komponenten eines mehrstöckigen Hauses geeignet [8]. Das Bussystem kommt daher nur innerhalb der Schaltkästen und damit auf einer Leitungslänge von maximal einem Meter zum Einsatz.

Die Kommunikation zwischen den Stockwerksverteilungen kann dagegen aus programmiertechnischer Sicht auf einem Ethernet-Protokoll fußen. Das aus der Internet-of-Things-Domäne stammende MQTT-Protokoll (MQ Telemetry Transport) bietet sich an. Interessierte Leser seien auf das Open-HAB-Projekt [9] oder das Programmiermittel Logi-CAD 3 [10] verwiesen, die unter vielen anderen auch dieses Protokoll unterstützen.

Als Eingangssignal, zum Beispiel von Lichtschaltern, lässt sich Kleinspannung verwenden. Üblich sind entweder 12 Volt oder 24 Volt (SPS-Spannungsniveau). Die geschalteten Komponenten laufen in dem gezeigten Setup dagegen unter Netzspannung, das heißt mit 230 Volt Wechselstrom.

Abbildung 3 zeigt, wie die Komponenten der Hausautomatisierung an die Ein-/Ausgabehardware anzuschließen sind. Das Ein- und das Ausgangsmodul setzen zwischen den I2C-Signalen und in diesem Fall digitalen Ein- und Ausgängen um. Um Netzspannung schalten zu können, kommen Relais zum Einsatz. Die Aktoren im Heimbereich, also etwa die Lichter, Alarmanlagen sowie Jalousien, lassen sich mit Hilfe digitaler Ein-/Ausgänge ansteuern.

Abbildung 3: Übersicht über die Verkabelung eines Schaltschranks mit Stromversorgung, Raspi und Relais zum Schalten von Verbrauchern.

Abbildung 3: Übersicht über die Verkabelung eines Schaltschranks mit Stromversorgung, Raspi und Relais zum Schalten von Verbrauchern.

Alarmkontakte bildet man ähnlich wie Taster in Form digitaler Eingänge ab, Lichter und Jalousien entsprechen digitalen Ausgängen. Zur Ansteuerung eines Jalousiemotors sind üblicherweise zwei digitale Ausgänge notwendig, da jede Bewegungsrichtung separat zu schalten ist. Die Ausgänge müssen dabei zumindest Software-seitig gegeneinander verriegelt sein, um Beschädigungen des Antriebs durch gleichzeitiges Aktivieren beider Richtungen zu verhindern.

Die Stromversorgung ist so ausgeführt, dass den I2C-Bus samt seinen Komponenten sowie den Raspberry Pi jeweils ein separates 5-Volt-Netzteil versorgt. In der Praxis ist es aber auch üblich, alle 5-Volt-Komponenten über dieselbe Stromversorgung zu speisen. Das 12-Volt-Netzteil ist für die Taster sowie für den Betrieb der Relais vorgesehen.

Kleines Einmaleins des I2C

Die Datenübertragung benötigt beim I2C-Bus neben der Stromversorgung der Buskomponenten noch drei weitere Leitungen: »SDA« (die Datenleitung), »SCL« (das Taktsignal) und »GND« (die Masseleitung. An einem I2C-Bus lassen sich bis zu 112 verschiedene Geräte betreiben. Diese Zahl ergibt sich aus dem 7 Bit breiten Adressraum (das sind 128 Adressen), wobei 16 Adressen für Sonderzwecke reserviert sind.

Um von einem Linux-Rechner aus I2C-Geräte anzusprechen, müssen die I2C-Kernelmodule geladen sein. Das sind:

  • Das »i2c-dev« Modul, das von verschiedenen Hardware-Implementierungen des I2C-Busses abstrahiert. Es bietet einheitliche Schnittstellen in Form zeichenbasierter Gerätedateien.
  • Ein Hardware-spezifisches Modul, das die Implementierung für die konkrete I2C-Hardware enthält. Im Falle des Raspberry Pi heißt das Modul »i2c-bcm2708« .

Zum Betrieb müssen beide Module geladen sein. Bei Debian bewerkstelligt die »/etc/modules« -Datei das automatisierte Laden durch die Einträge:

i2c-dev
i2c-bcm2708

Neuere Kernel (Version 3.18 aufwärts) mit Unterstützung des so genannten Device Tree brauchen außerdem noch einen Eintrag in der »config.txt« -Datei in der Bootpartition des Raspberry: »dtparam=i2c_arm=on« .

Da der I2C-Bus keine Mechanismen zur Fehlerkorrektur kennt, gilt es, bei der Datenübertragung mögliche Fehlerquellen und Störeinflüsse zu minimieren [8]. Eine Möglichkeit besteht in der Konfiguration einer geringeren Datenrate. Das gelingt durch die Angabe von Parametern beim Laden der relevanten Kernelmodule. Im Schaltschrankbau hat sich in der Praxis eine Datenrate von 32 kBaud bewährt:

modprobe i2c_bcm2708 baudrate=32000

Die aktuell konfigurierte Frequenz lässt sich durch das Auslesen von Einträgen im »/sys« -Verzeichnis ermitteln:

cat /sys/module/i2c_bcm2708/parameters/baudrate

Eine weitere Möglichkeit sind die im Kernel-Ringpuffer enthaltenen Log-Nachrichten. Zum Beispiel:

dmesg | grep 'i2c.*baudrate'

Die dem »modprobe« -Aufruf übergebenen Optionen sind bis zum Entladen des Moduls gültig. Einträge in einer ».conf« -Datei (zum Beispiel »/etc/modprobe.d/i2c.conf« ) machen die Einstellung der Datenrate permanent:

options i2c_bcm2708 baudrate=32000

Der I2C-Bus präsentiert sich nach erfolgreichem Laden der Kernelmodule in Form von zeichenorientierten Gerätedateien wie »/dev/i2c-1« , wobei die Zahl nach dem »i2c« der Bus-ID entspricht. Beim Raspberry Pi ist der Bus mit der ID 1 an der 26- beziehungsweise 40-poligen Stiftleiste (Pin 3 … SDA, Pin 5 … SCL) nach außen geführt.

Sind die Komponenten miteinander verkabelt, ermittelt das Kommando »i2cdetect« , ob die Module am Bus zu finden sind:

i2cdetect -y 1

Dieses Kommando sucht am Bus mit der ID »1« nach Komponenten. Der »-y« Parameter deaktiviert den interaktiven Modus. Dadurch greift es direkt und ohne Rücksicht auf andere den Bus benutzende Programme auf ihn zu. Eine Ausgabe sieht beispielsweise wie in Listing 1 gezeigt aus.

Listing 1

Ausgabe des i2cdetect-Kommandos

0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- 38 -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Zu sehen sind alle Adressen, hinter denen sich I2C-Komponenten verbergen. Im Beispiel des Listings 1 sind das Geräte mit den Adressen 0x20 und 0x38. Was sich hinter den Adressen verbirgt, ist jedoch nicht einsehbar. Um dies manuell zu erforschen, trennt der Bastler nacheinander jede Komponente von der Stromversorgung und beobachtet, welche Adresse beim erneuten Aufruf von »i2cdetect« fehlt.

Einen unprivilegierter Benutzer fügt unter Debian folgendes Kommando der »i2c« -Gruppe hinzu:

usermod -a -G i2c Username

Die Rechte der Gerätedateien sind standardmäßig so eingestellt, dass der Root-Benutzer sowie Benutzer, die der »i2c« -Gruppe angehören, Zugriff auf den Bus haben.

Eine Alarmanlage

Das erste Beispiel demonstriert die Umsetzung einer Alarmanlage mit Hilfe von digitalen Ein-/Ausgangsmodulen. Die Alarmanlage lässt sich durch einen Handtaster aktivieren. Das Unterbrechen eines der sieben Alarmkontakte löst im aktiven Zustand einen Alarm aus.

Konkret beschaltet der Anwender sieben der acht digitalen Eingänge eines PCF8574-Eingangsmoduls mit jeweils einem Reed-Kontakt. Diese magnetisch bedienten Kontakte platzieren Alarmanlagen normalerweise in Fensterrahmen. Ist das Fenster geschlossen, betätigt ein im Fensterflügel montierter Magnet den Schalter und legt damit ein »true« an den zugeordneten digitalen Eingang. Bei geöffnetem Fenster ist auch der Schalter offen und das Eingangssignal hat den Wert »false« . Der achte digitale Eingang des Moduls führt zu einem Handtaster und dient der Aktivierung/Deaktivierung der Alarmanlage.

Da sich die Signale von Handtastern nur kurzzeitig ändern, ist zu überlegen, wie das System auf den Tastendruck reagieren soll. Soll die Betätigung des Tasters bereits ausgewertet werden, wenn der zwar gedrückt, aber noch nicht losgelassen wurde, so spricht man davon, dass der Aktor mit der steigenden Flanke schaltet. Die Auswertung erfolgt mit so genannten Rising Edge Triggern. Dieses Verhalten nutzen die hier gezeigten Beispiele für alle Taster.

Den Ausgang der Alarmanlage bildet ein PCF8574-basiertes Ausgangmodul mit Relais. An den ersten Ausgang des Ausgangmoduls lässt sich eine Signallampe oder alternativ auch eine Sirene anschließen. Das Beispiel der Alarmanlage ist mit Logi-CAD 3 umgesetzt, einer Eclipse-basierten Umgebung, die auch in einer Betaversion für Linux erhältlich ist [11].

SPS-Programmierung

Im Wesentlichen gibt die IEC61131-3-Norm, der Logi-CAD 3 folgt, Programmiersprachen und Sprachelemente vor, die die Erstellung von Steuerungslogik für Speicher-programmierbare Steuerungen (SPS) einfach gestalten. Logi-CAD 3 unterstützt die Steuerungsprogrammierung in einer textuellen Sprache, Structured Text (ST), und einer visuellen Programmiersprache, Function Block Diagram (FBD). Da der Raspberry Pi im Beispiel die Rolle einer Steuerung übernimmt, bietet sich der Einsatz einer derartigen Programmierumgebung hier geradezu an.

Mit Logi-CAD 3 lässt sich eine Vielzahl verschiedener Zielplattformen programmieren. Die Funktionen einer SPS übernimmt im Fall von Logi-CAD 3 die in Software realisierte Laufzeitumgebung »logi.RTS« . Die ist im Lieferumfang von Logi-CAD 3 enthalten, der Anwender muss sie aber noch vor der Inbetriebnahme manuell auf den Raspberry Pi übertragen und dort ausführen. Sobald die Laufzeitumgebung auf der Zielhardware in Betrieb ist, ist es möglich, die Steuerungslogik in Logi-CAD 3 zu erstellen.

Die kompilierte Steuerungslogik lässt sich dann direkt aus der Programmierumgebung zur Laufzeitumgebung auf dem Zielsystem übermitteln. Das Programm arbeitet Speicher-programmierbare Steuerungen zyklisch ab. Dabei ruft es die Steuerungslogik in einem festgelegten Zeitraster immer wieder auf. Die Werte lokaler Variablen des Programms bleiben von einem Durchlauf zum nächsten erhalten.

Listing 2 zeigt die Implementierung der Alarmanlage in der von ihrer Syntax her an Pascal angelehnten textuellen Sprache ST. Das zyklisch abgearbeitete Programm gliedert sich in fünf Teile: die Deklaration und Initialisierung der lokalen Variablen (Zeilen 2 bis 9), das Öffnen des I2C-Busses (Zeile 12), das Einlesen der Eingänge (Zeile 14), die eigentliche Steuerungslogik (Zeilen 16 bis 29) und das Schreiben der Ausgänge (Zeile 31).

Listing 2

i2calarm.iecst

01 PROGRAM AlarmSample
02  VAR
03    i2c_fd : DINT := -1;
04    count : DINT := 0;
05    inputs : ARRAY[0..7] OF BOOL;
06    outputs : ARRAY[0..7] OF BOOL;
07    alarm_activated : BOOL := TRUE;
08    activation_trigger : R_TRIG;
09  END_VAR
10
11  IF i2c_fd < 1 THEN
12    i2c_fd := I2C_open(bus_id := 1);
13  ELSIF i2c_fd >= 0 THEN
14    inputs := I2C_PCF8574_read(fd := i2c_fd,address := 16#38);
15
16    activation_trigger(CLK := inputs[7]);
17    IF activation_trigger.Q THEN
18      alarm_activated := NOT alarm_activated;
19      outputs[0] := FALSE;
20    END_IF;
21
22    IF alarm_activated THEN
23       FOR count := 0 TO 6 BY 1 DO
24      IF NOT inputs[count] THEN
25         outputs[0] := TRUE;
26         EXIT;
27      END_IF;
28    END_FOR;
29   END_IF;
30
31    I2C_PCF8574_write(fd := i2c_fd, address := 16#20, pin_values := outputs);
32  END_IF;
33 END_PROGRAM

Die Variable »i2c_fd« entspricht hier in etwa einem Datei-Handle, mit dessen Hilfe das Programm auf den Bus mit der ID 1 zugreift (Zeile 12). Beim ersten Durchlauf des Programms ist die Bedingung in Zeile 11 wahr. Ließ sich der I2C-Bus erfolgreich initialisieren, nimmt »i2c_fd« einen Wert größer gleich 0 an, was im zweiten und den darauf folgenden Durchläufen dazu führt, dass die Bedingung in Zeile 13 wahr ist und die Software die Zustände der Ein- und Ausgänge einlesen kann.

Der eigentliche Buszugriff in den Zeilen 14 beziehungsweise 31 erfolgt über die durch die Programmierumgebung bereitgestellten Funktionen »I2C_PCF8574_read()« respektive »I2C_PCF8574_write()« . Das Programm verknüpft die Ein- und Ausgänge zwischen den Zugriffen in den Zeilen 22 bis 29 derart, dass es bei Unterbrechung eines der Alarmkontakte oder bei einem »false« -Signal an einem der Eingänge den ersten digitalen Ausgang einschaltet.

Zudem berücksichtigt die Programmlogik einen De- und Aktivierungstaster (Zeilen 16 bis 20). Wird er betätigt, schaltet dies die Alarmanlage je nach aktuellem Zustand ein- beziehungsweise aus und setzt den Ausgang zurück.

Wetter- und Taster-gesteuerte Jalousie

Als weitere komplexe Aufgabenstellung dient nun die Umsetzung einer Jalousiensteuerung in Python 3. Ein Webservice ermittelt dabei die Windgeschwindigkeit. Ist sie zu groß, fährt eine Software die Jalousie hoch, um eine Beschädigung durch einen Sturm zu verhindern. Daneben soll es möglich sein, die Jalousie über zwei Taster zu bedienen. Die Herausforderung dieses Beispiels liegt einerseits in der Verknüpfung des Webservice mit den physikalischen Ausgängen der Steuerung, andererseits in der Verriegelung der Ausgänge, sodass eine Beschädigung des Jalousiemotors durch unsachgemäße Bedienung auszuschließen ist.

Hardware-seitig kommen bei diesem Beispiel wieder ein digitales Eingangs- und ein digitales Ausgangsmodul zum Einsatz. Dank PCF8574-Chip verfügen die Module jeweils über acht digitale Ein- und Ausgänge. Der ersten Eingang des Eingangsmoduls wird mit dem Handtaster für das Hochfahren der Jalousie verbunden, am zweiten Eingang hängt der Handtaster zum Herunterfahren. Analog dazu hängt am ersten Ausgang des Ausgangsmoduls die Steuerleitung für die Hochfahr-Drehrichtung des Jalousiemotors und am zweiten Ausgang die Steuerleitung für die entgegengesetzte Drehrichtung.

Der Zugriff auf den I2C-Bus erfolgt mit Python 3. Dazu braucht der Raspberry Pi das »smbus-cffi« -Paket [12]. Da im Hintergrund ein paar C-Sourcen übersetzt werden, kann die Installation des Pakets ein paar Minuten in Anspruch nehmen:

apt-get install python3-pip build-essential libi2c-dev i2c-tools python-dev libffi-dev
pip3 install smbus-cffi

Die Ermittlung der Windgeschwindigkeit erfolgt über die Open Weather Map [13]. Python 3 nutzt diesen Service über das »pyOWM« -Modul:

pip3 install pyowm

Listing 3 zeigt das Hauptprogramm der Wetter- und Taster-gesteuerten Jalousie. Mit einem Signal-Handler (Zeilen 19 und 10 bis 12) lässt sich die Abbruchbedingung der Hauptschleife des Programms (Zeilen 28 bis 39) über die globale Variable »running« (Zeile 8) setzen.

Listing 3

main.py

01 from signal import signal, SIGINT
02 from time import sleep, time
03 from pyowm import OWM
04 from pcf8574 import PCF8574
05 from digital import DigitalOut, DigitalIn, R_TRIG
06 from blind import WindowBlind
07
08 running = 1
09
10 def handler(signum, frame):
11    global running
12    running = 0
13
14 def getWindSpeed(city):
15    owm = OWM('your-API-key')
16    return owm.weather_at_place(city).get_weather().get_wind()['speed']
17
18 if __name__=="__main__":
19    signal(SIGINT, handler)
20    triggerUp, triggerDown = R_TRIG(), R_TRIG()
21    lastWeatherCheck = 0
22
23    with PCF8574(bus=1, addr=0x20) as outputs, PCF8574(bus=1, addr=0x38) as inputs:
24       outUp, outDown = DigitalOut(outputs, 0), DigitalOut(outputs, 1)
25       inUp, inDown = DigitalIn(inputs, 0), DigitalIn(inputs, 1)
26    blind = WindowBlind(20, 21, outUp, outDown)
27
28    while running:
29       if triggerUp(inUp.getValue()):
30          blind.up()
31       if triggerDown(inDown.getValue()):
32          blind.down()
33       if time() - lastWeatherCheck > 900:
34          if getWindSpeed("Vienna,at") > 10:
35              blind.up()
36  lastWeatherCheck = time()
37
38       blind.run()
39       sleep(.05)

Für jeden der beiden Handtaster legt Zeile 20 einen Trigger zur Erkennung der steigenden Signalflanke an. Die Trigger rufen Objekte der Klasse »DigitalIn« auf (Zeilen 29 und 31). Die Aufgabe dieser Klasse sowie der Klasse »DigitalOut« ist es, die Rohdaten digitaler Ein- und Ausgangsmodule aufzubereiten. Die Instanzierung erfolgt durch Angabe des die I2C-Buskomponente repräsentierenden Objekts (hier jeweils die Implementierung für einen PCF8574-Chip) sowie der zu behandelnden Pin-Nummer (Zeilen 24 und 25).

Die Implementierung der »DigitalOut« – und der »DigitalIn« -Klasse führt die notwendigen Bit-Operationen durch. Instanzen der »WindowBlind« -Klasse bilden die Jalousien ab (Zeile 26). Dem Konstruktor übergibt der Programmierer die Dauer für das vollständige Hoch- beziehungsweise Herunterfahren in Sekunden sowie die digitalen Ausgänge.

Auch hier handelt es sich um einen zyklischen Programmablauf mit einer Wartezeit von 0,05 Sekunden zwischen den einzelnen Zyklen (Zeile 39). Die Hauptschleife (Zeilen 28 bis 39) fährt die Jalousie in Abhängigkeit der Handtaster hoch oder herunter (Zeilen 29 bis 32). Auf die aktuelle Wetterlage reagieren die Zeilen 34 und 35, indem sie bei Windgeschwindigkeiten größer als 10 km/h die Jalousie hochfahren. Zeile 33 stellt sicher, dass das Wetter höchstens alle 900 Sekunden überprüft wird.

Zu häufigen Zugriff auf den Webservice der Open Weather Map ahndet dieser mit einer Zugriffssperre. Der zum Zugriff auf die Open-Weather-Map-Server nötige kostenlose API-Key lässt sich über die Homepage des Projekts [14] beziehen. Ihn bekommt der Konstruktor der OWM-Klasse übergeben (Zeile 15).

Die PCF8574-Klasse in Listing 4 kapselt den Zugriff auf PCF8574-Chips. Dabei erfolgen die Zugriffe auf den I2C-Bus über die »SMBus« -Klasse (Zeilen 6, 15 und 17). Die »__enter__()« – und »__exit__()« -Methoden sind notwendig, um mit dem »with« -Statement (Listing 3, Zeile 23) arbeiten zu können. Dies ermöglicht einen geregelten Buszugriff. Das Bus-Handle wird geschlossen, wenn kein weiterer Buszugriff mehr erfolgt (Zeile 12).

Listing 4

pcf8574.py

01 from smbus import SMBus
02
03 class PCF8574(object):
04     def __init__(self, bus, addr):
05        self.__addr = addr
06        self.__bus = SMBus(bus)
07
08     def __enter__(self):
09        return self
10
11     def __exit__(self, exc_type, exc_value, traceback):
12        self.__bus.close()
13
14     def read(self):
15        return ~self.__bus.read_byte(self.__addr)
16     def write(self, value):
17        self.__bus.write_byte(self.__addr, ~value)

Das Herzstück der Implementierung bilden die Zeilen 15 und 17. An dieser Stelle wird der Buszugriff initiiert. Die »SMBus« -Klasse stellt eine Vielzahl an Methoden bereit [15], um Anwendern den Aufruf der durch das I2C-Protokoll vorgegebenen Übertragunsmechanismen und damit letztlich das Ansprechen aller verfügbaren Hardware zu erlauben.

Die »DigitalIn« – beziehungsweise die »DigitalOut« -Klasse (Listing 5) ermöglichen es, bei der Steuerungsprogrammierung die Ein- und Ausgänge von digitalen I2C-Modulen einzeln zu betrachten. Im Wesentlichen sind dafür lediglich Bit-Operationen (Zeilen 7, 12 und 20) nötig. Die »R_TRIG« -Klasse (Zeilen 26 bis 29) vergleicht den im letzten Aufruf gespeicherten Zustand des Eingangssignals mit dem des aktuellen Aufrufs und retourniert »True« im Fall einer steigenden Signalflanke (Zeile 27 und 29) .

Listing 5

digital.py

01 class DigitalOut(object):
02     def __init__(self, module, pin):
03        self.module, self.pin = module, pin
04
05     def on(self):
06        outputs = self.module.read()
07        outputs |= (1 << self.pin)
08        self.module.write(outputs)
09
10     def off(self):
11        outputs = self.module.read()
12        outputs &= ~(1 << self.pin)
13        self.module.write(outputs)
14
15 class DigitalIn(object):
16     def __init__(self, module, pin):
17        self.module, self.pin = module, pin
18
19     def getValue(self):
20        return self.module.read() & (1 << self.pin) != 0
21
22 class R_TRIG(object):
23     def __init__(self):
24        self.__state = False
25
26        def __call__(self, cur_state):
27        retVal = cur_state and not self.__state
28        self.__state = cur_state
29        return retVal

Als Automat

Die Implementierung der Jalousiesteuerung selbst erfolgt in Form eines endlichen Automaten (Listing 6). Während des Betriebs kann sich die Jalousie in drei Zuständen befinden: dem inaktiven Zustand »idle« , dem Zustand des Hochfahrens sowie dem des Herunterfahrens (Zeilen 5, 6 und 7).

Listing 6

blind.py

01 from time import time
02
03 class WindowBlind(object):
04    def __init__(self, tu, td, outUp, outDown):
05       self.idle = BlindIdle(outUp, outDown)
06       self.moveUp = BlindMoving(tu, outUp, outDown)
07       self.moveDown = BlindMoving(td, outDown, outUp)
08
09       self.idle.enter()
10       self.__state = self.idle
11
12    def run(self):
13       nextstate = self.__state.next(self)
14       if nextstate != self.__state:
15          nextstate.enter()
16       self.__state = nextstate
17       self.__state.run()
18
19    def up(self):
20       self.__state.setNextState(self.moveUp)
21
22    def down(self):
23       self.__state.setNextState(self.moveDown)
24
25 class BlindState(object):
26    def enter(self):
27       self.lastChange = time()
28       self.nextState = self
29       self.running = True
30
31 class BlindMoving(BlindState):
32    def __init__(self, timeout, outOn, outOff):
33       self.timeout, self.outOn, self.outOff = timeout, outOn, outOff
34
35    def run(self):
36       self.outOn.on()
37       self.outOff.off()
38
39    def next(self, statemachine):
40       return self if self.running and time() - self.lastChange <= self.timeout else statemachine.idle
41
42    def setNextState(self, state):
43       self.running = False
44
45 class BlindIdle(BlindState):
46    def __init__(self, outUp, outDown):
47       self.outUp, self.outDown = outUp, outDown
48
49    def run(self):
50       self.outUp.off()
51       self.outDown.off()
52
53    def next(self, statemachine):
54       return self if time() - self.lastChange <= .5 else self.nextState
55
56    def setNextState(self, state):
57    self.nextState = state

Der inaktive Zustand ist jener zwischen den Bewegungszuständen, um die Ausgänge für die Drehrichtung des Jalousiemotors gegeneinander zu verriegeln. Die Wartezeit zwischen den Bewegungskommandos muss mindestens eine halbe Sekunde betragen (Zeile 54). Die Logik des Automaten wird in dessen »run()« -Methoden abgebildet (Zeilen 12 bis 17). Diese Methode darf nicht längere Zeit blockieren und ist zyklisch aufzurufen (siehe Listing 3, Zeile 38).

Jeder Zustand zeichnet sich durch eine »run()« – und eine »next()« -Methode aus. Erstere gibt an, was passieren soll, wenn der Zustand aktiv ist, letztere retourniert jenen Zustand, der als nächster einzunehmen ist. Die »enter()« -Methode ermöglicht das Initialisieren eines Zustands (Zeile 14 und 15 beziehungsweise Zeilen 26 bis 29).

Mit Hilfe der »setNextState()« -Methode lässt sich dem aktuellen Zustand jener Zustand mitteilen, der als nächster an die Reihe kommen soll. Im Falle des Bewegungszustands führt dies zum Abbruch beziehungsweise in weiterer Folge zur Rückkehr in den inaktiven Zustand. Im inaktiven Zustand führt der Aufruf der Methode nach Ablauf der Wartezeit von 0,5 Sekunden zu einer Überführung in einen der Bewegungszustände.

Der Start des Programms aus Listing 3 erfolgt durch

python3 main.py

also durch den Aufruf des Python-Interpreters inklusive der Angabe des Skriptnamens:

Fazit und Ausblick

Es lässt sich unschwer erkennen, dass der Programmierbarkeit der vorgestellten Lösung kaum Grenzen gesetzt sind. Dank der leistungsfähigen Hardware der neueren Raspberry-Pi-Modelle ist auch der Einsatz von Skriptsprachen wie Z-Python ein durchaus gangbarer Weg. Als Aternative lassen sich die Steuerungsaufgaben aus dem Bereich der Heimautomatisierung auch mit Unterstützung durch etablierte, aus dem industriellen Bereich stammende Programmiermittel umsetzen.

Die in diesem Artikel vorgestellte Heimautomatisierungslösung lässt sich relativ einfach um zusätzliche Protokolle und Hardware ergänzen. Das bereits angesprochene MQTT-Protokoll kann der Kommunikation zwischen den Stockwerksverteilern dienen.

Viele Ein- und Ausgangsgeräte der Automatisierungstechnik lassen sich auch über das Modbus-Protokoll ansprechen. Die Modbus-basierten Lösungen zeichnen sich bei geringen Mehrkosten gegenüber I2C-Lösungen durch eine größere zulässige Leitungslänge aus, was den Einsatz als Hausbus erleichtert oder überhaupt erst möglich macht.

Auch für diese Zweck gibt es dank der Libmodbus-Bibliothek [16] unter Linux hervorragende Unterstützung. Wenn das Eigenheim traditionell verkabelt wurde, gibt es bereits Funkmodule für den I2C-Bus, mit denen es auch möglich ist, günstige Funksteckdosen programmgesteuert zu schalten.

Ein weiterer offener Punkt ist die visuelle Darstellung der Steuerung während der Laufzeit. Hier sei beispielsweise wieder auf das Open-HAB-Projekt [17] verwiesen, das dank der Unterstützung für das MQTT-Protokoll die Visualisierung sowohl im Webbrowser als auch im mobilen Endgerät mit Hilfe einer App möglich macht.

Der Autor

Rainer Poisel arbeitet als Embedded Software Engineer bei der Logi.cals GmbH in Niederösterreich. Dort kümmert er sich um die Portierung des Laufzeitsystems Logi.RTS auf verschiedenste Hard- und Softwareplattformen: vom 8-Bit-Mikrocontroller bis zum Multicore-Industrie-PC.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 9 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben