MCP23017

Auf dieser Seite beschreibe ich, wie MCP23017 mit fhem beispielhaft genutzt werden kann. Mein Fokus liegt auf Anwendung im Bereich Hausautomatisierung.

Datenblatt: mcp23017.pdf

Achtung: i2c muss konfiguriert sein.

Verkabelung

i2c Device erzeugen

define i2c RPII2C 1
attr i2c alias Onboard I2C Controller 1
attr i2c room ops

Falls folgende Fehlermeldung erscheint: „i2c: Error! I2C device not readable: /dev/i2c-1. Please install wiringpi or change access rights for fhem user“ muss eventuell der User „fhem“ der Gruppe „i2c“ hinzugefügt werden und anschließend „fhem“ neu gestartet werden. Dazu folgende Kommandos als root in der Shell ausführen:

usermod -a -G i2c fhem
service fhem restart

Tip: Für alle hier aufgeführten Kommandos verwende ich die fhem Telnet-Schnittstelle mit rlwrap.

Beispiel um alle Ports als Input z.B. für Taster nutzen zu können. In diesem Beispiel ist die Adresse des MCP23017 0x20. Zunächst erzeuge ich das Device und setze alle Ports auf „high“. Damit liegt an jedem der 16 Ports „high“-Pegel (3,3 Volt) an. Ein angeschlossener Taster muss nun bei Tastendruck eine Verbindung zu GND (0 Volt) herstellen. Das Betätigen des Tasters wird mit „on“ (aufgrund von „invert_input“) gemeldet.

MCP23017 Device konfigurieren

define i2c20 I2C_MCP23017 0x20
attr i2c20 IODev i2c
attr i2c20 room ops
attr i2c20 Interrupt A0,A1,A2,A3,A4,A5,A6,A7,B0,B1,B2,B3,B4,B5,B6,B7
attr i2c20 InterruptOut connected_active-low
attr i2c20 Pullup A0,A1,A2,A3,A4,A5,A6,A7,B0,B1,B2,B3,B4,B5,B6,B7
attr i2c20 invert_input A0,A1,A2,A3,A4,A5,A6,A7,B0,B1,B2,B3,B4,B5,B6,B7

Interrupt einrichten

Achtung: Es müssen zuvor entsprechende Berechtigungen vergeben werden. siehe Rasbian fhem Konfiguration

Um eine Änderung der Ports detektieren zu können muss ein Interrupt verwendet werden. Ich verwende hierzu in diesem Beispiel Pin 38 bzw. GPIO 20. Der Port wird als Input konfiguriert und der intere Widerstand gesetzt, so dass am Port „high“ (3,3 Volt) anliegt. Der MCP23017 legt bei jeder Änderung einer der 16 Ports seine beiden Interrupt-Ausgänge auf GND (0 Volt). Sobald ein Interrupt auslöst, wird mit dem „userReadings“-Kommando get i2c20 der Status aller Ports aktualisiert.

define int2 RPI_GPIO 20
attr int2 direction input
attr int2 active_low yes
attr int2 interrupt both
attr int2 pud_resistor up
attr int2 userReadings get_i2c20 {fhem ("get i2c20")}

Alternativ kann auch, um GPIO-Ports zu sparen, ein Interrupt gemeinsam genutzt werden (z.B. mit pcf8574). Dazu müssen einfach beide Interrupt-Ausgänge mit dem GPIO-Port verbunden werden und das „userReadings“-Kommando entsprechen gesetzt sein. Beispiel:

attr int userReadings get_int {fhem ("get i2c20,i2c21")}

Testen

Mit inform kann einfach getestet werden, ob alles wie erwartet funktioniert. Wenn man anschließend Taster betätigt, sieht man wann welcher Taster gedrückt (on) und wieder los gelassen wurde (off).

inform timer i2c20:Port.*
beispieloutput.txt
2015-04-26 04:44:23 I2C_MCP23017 i2c20 PortB0: on
2015-04-26 04:44:24 I2C_MCP23017 i2c20 PortA7: on
2015-04-26 04:44:24 I2C_MCP23017 i2c20 PortB0: off
2015-04-26 04:44:25 I2C_MCP23017 i2c20 PortA7: off

readingsProxy einrichten

Taster Dummy für einen Port

define i2c20pA6 readingsProxy i2c20:PortA6
attr i2c20pA6 room ops
attr i2c20pA6 alias Taster A6

Taster Dummy für alle Ports

{ foreach my $i ('A', 'B') { \
foreach my $j (0..7) { \
  fhem(" \
    define i2c20p$i$j readingsProxy i2c20:Port$i$j;; \
    attr i2c20p$i$j room ops;; \
    attr i2c20p$i$j alias Taster $i$j;; \
");; } } }

Alle löschen:

{ foreach my $i ('A', 'B') { \
foreach my $j (0..7) { \
  fhem(" \
    delete i2c20p$i$j;; \
");; } } }

Licht mit Taster

Für die einzelnen Ports können jetzt z.B. einzelne readingsProxys eingerichtet werden. Beispiel für Port B0:

define i2c20pB0 readingsProxy i2c20:PortB0
attr i2c20pB0 room ops
attr i2c20pB0 alias Taster B0

Wie man nun eine einzelne Lampe mit dem Taster schalten kann soll folgendes Beispiel mit einem Dummy zeigen:

define lampeB0 dummy
attr lampeB0 room ops
attr lampeB0 devStateIcon on:on:off off:off:on
attr lampeB0 alias Lampe B0
set lampeB0 off
 
define lampeB0_toggle notify i2c20pB0:on { \
if   (Value("lampeB0") eq "off") {fhem "set lampeB0 on"} \
else {fhem "set lampeB0 off"} \
}

Jalousie mit einem Taster

Hier ein Beispiel um eine Jalousie mit einem Taster zu steuern:

define i2c20pA7 readingsProxy i2c20:PortA7
attr i2c20pA7 room ops
attr i2c20pA7 alias Taster A7
define jalousie1 dummy
attr jalousie1 room ops
attr jalousie1 devStateIcon hoch:fts_shutter_up@yellow:stop_h runter:fts_shutter_down@yellow:stop_r stop_r:fts_shutter_up@white:hoch stop_h:fts_shutter_down@white:runter
attr jalousie1 alias Jalousie 1
set jalousie1 stop_r
 
# Cycle: hoch -> stop_h -> runter -> stop_r -> ... Automatisch stoppen nach Wartezeit (Nachteil: Wenn zwischenzeitlich noch ein Tastendruck kam werden trotzdem die Kommandos nach dem sleep ausgeführt)
define jalousie1_toggle notify i2c20pA7:on { \
if    (Value("jalousie1") eq "stop_r") {fhem "set jalousie1 hoch;;sleep 5;;set jalousie1 stop_h"} \
elsif (Value("jalousie1") eq "hoch")   {fhem "set jalousie1 stop_h"} \
elsif (Value("jalousie1") eq "stop_h") {fhem "set jalousie1 runter;;sleep 5;;set jalousie1 stop_r"} \
else  {fhem "set jalousie1 stop_r"} \
}