4. Raspberry, Python und I2C

Mit Python habe ich bereits verschiedene Erfahrungen sammeln können, so dass ich nun „nur noch“ verstehen musste, wie I2C überhaupt funktioniert und wie man das in Python umsetzt. Als Bibliothek kommt smbus und für ein HD44780-LCD-Display noch eine kleine Treiber-Bibliothek aus einem Tutorial zum Einsatz.  

Levelshifter zwischen Pi und Arduino

Bevor wir nun gedankenlos den I2C-Bus des Raspberry und des Arduino verbinden und uns wundern, dass der Pi zwei GPIO-Ports weniger hat, müssen wir einen Pegelwandler für 3,3 und 5 Volt einsetzen, denn der Pi mag keine Spannungen über 3,3V am GPIO und der Arduino arbeitet regulär mit 5V. Der Pegelwandler wird einfach in die Busleitungen SCL und SDA eingefügt und mit Masse und Vin aus beiden Richtungen versorgt. Wenn das erledigt ist, kann man mit dem Kommandozeilentool i2cdetec schauen, ob die Slaves am Bus gefunden werden. Ist dies der Fall, geht es an die Programmierung.

I2C im Python ansprechen

Als erstes ein paar Experimente mit dem Display. Auf dem HD44780 ist, dank des kleinen Treibers, schnell ein erster Text angezeigt. Der Code für die Ansteuerung ist fast selbsterklärend und man hat mit dem darunterliegenden I2C-Bus eigentlich noch keinen richtigen Kontakt. Wenn man es genau betrachtet ist der Treiber auch eher ein Wrapper, der die Strings für das Display in die passenden Bytes und Adressierungen umsetzt und die Kommunikation mit dem I2C-Bus über smbus organisiert.

Pi spricht mit dem Arduino

Aufgabe 1: Vom Pi einen Befehl senden, der auf dem Arduino eine LED ein oder aus schaltet.

Das gelingt Mithilfe einiger Anleitungen aus dem Netz und der Wire-Bibliothek auf dem Arduino recht schnell. Den Arduino als I2C-Slave initialisieren, ihm eine Adresse geben und dann auf die ankommenden Bytes reagieren … 0x00 LED aus, 0x01 LED an … aber Moment … das muss doch auch eleganter gehen, indem ich nicht nur ein Byte als Adresse sondern je Adresse auch noch weitere Bytes als Parameter übertrage und auswerte.

Nach etwas Knoten im Kopf und umdenken (bisher habe ich in Projekten immer über Sockets, APIs und MQTT mit Strings kommuniziert) habe ich dann herausgefunden wie die smbus-Funktionen und die C++Befehle dafür aussehen und die Daten aufbereitet und ausgewertet werden müssen. Nach ein paar frustranen Versuchen ist der Knoten geplatzt und es funktioniert.

Aufgabe 2: Vom Pi einen Eingang am Arduino abfragen.

In meinem Leichtsinn dachte ich, dass ich einfach auf einen Befehl antworten kann, aber bei I2C wird die Übertragung ausschließlich vom Master gesteuert.

Wenn man nochmal genau nachließt und dann auch verstanden hat, dass der Master entweder nur Daten sendet oder einen Request mit eigenen Daten an den Slave sendet und dieser dann direkt antwortet, kommt man auf den richtigen Weg. Also im Arduino den Code umgebaut und siehe da, ich kann nun auch den aktuellen Schaltzustand meiner LED und eines Einganges abfragen.

Aufgabe 3: eine sinnvolle Adressierung auf dem Arduino für IN/OUT

Ich mache es kurz … ich habe einfach die wesentlichen Adressen für die io-Ports aus dem Datenblatt des MCP23017 übernommen. Für das Verständnis des Datenblattes und der Abläufe auf dem I2C-Bus hat mir ein unten verlinktes Tutorial zum MCP sehr geholfen.

Zu meiner Schande musste ich mich nach jahrelanger Erfahrung mit selbst beigebrachten Hobby-Programmierkenntnissen nun endlich mal mit binärer Logik beschäftigen … also wie setze ich 8 Bits zu einem Byte zusammen und wie prüfe ich effektiv und einfach auf der anderen Seite welche Bits 1 bzw. 0 sind. Grundsätzlich war der Zusammenhang klar, aber wie es in Python und C++ praktisch funktioniert war dann eine Abendstudie und ein bisschen Try und Error.

Dabei hat mich die Funktion pow() im C++ fast zur Verzweifelung getrieben, bis ich verstanden habe, dass sie als float-Funktion eben ein „krummes“ Ergebnis liefert, was dann beim Vergleich mit einem Integer zu falschen Ergebnissen führt. Nach weiter Forschung habe ich dann mit bit() die für diesen Zweck richtig Funktion gefunden.

Nun konnte ich also meinen Arduino als dummen I2C-Portexpander missbrauchen und ihn für Ein- und Ausgänge an meiner Brandmeldeanlage nutzen.

„Warum haste nicht gleich nen MCP23017 genommen?“ … das ist schnell erklärt: ich wollte ja verstehen, wie I2C funktioniert und wie solche Bausteine kommunizieren. Das geht natürlich leichter, wenn man auf beiden Seiten des Busses mitlesen kann. Mission erfüllt 😉

Jetzt müssen irgendwie die Taster und LEDs am Arduino mit dem MQTT verbunden werden …

Hier die erwähnten Quellen aus dem Text

Tutorial: HD44780 per I2C ansteuern | Webseite | Python-Bibiothek

Tutorial: Raspberry Pi GPIOs mittels I2C Port Expander erweitern | Webseite