Mit Hilfe von ArduRPC lassen sich Befehle an einen Arduino oder kompatiblen Mikrocontroller senden. Mit Python lässt sich in wenigen Schritten ein MQTT zu ArduRPC Gateway erstellen.
Das in diesem Beitrag gezeigte Beispiel ist sehr minimalistisch gehalten und soll in erster Linie als Anregung für eigene Entwicklungen dienen. Weitere Informationen zu MQTT können den Artikeln "Mosquitto als MQTT Broker" und "MQTT mit Python nutzen entnommen werden.
Der MQTT Client wird mit Hilfe des paho-mqtt Python Modul realisiert. Zusätzlich kommt das im Rahmen des ArduRPC Projekts bereitgestellte Python Modul ArduRPC zum Einsatz. Da der Arduino über eine serielle Verbindung angesprochen werden soll, wird zusätzlich noch pySerial benötigt. Diese Pakete müssen zunächst installiert werden. Das folgende Beispiel zeigt wie dies umgesetzt werden kann.
$ pip install paho-mqtt
Collecting paho-mqtt
Using cached paho-mqtt-1.1.tar.gz
Installing collected packages: paho-mqtt
Running setup.py install for paho-mqtt
Successfully installed paho-mqtt-1.1
$ pip install ardurpc
Collecting ardurpc
Using cached ardurpc-0.3.tar.gz
Installing collected packages: ardurpc
Running setup.py install for ardurpc
Successfully installed ardurpc-0.3
$ pip install pyserial
Collecting pyserial
Using cached pyserial-2.7.tar.gz
Installing collected packages: pyserial
Running setup.py install for pyserial
Successfully installed pyserial-2.7
Im folgenden Beispiel ist die eigentliche Umsetzung des MQTT Gateway zu sehen, welcher auch über gateway.py heruntergeladen werden kann.
#!/usr/bin/env python
import argparse
import json
import logging
import paho.mqtt.client as mqtt
from ardurpc import ArduRPC
from ardurpc.connector.serial_con import Serial
con = None
rpc = None
base_topic = None
def on_connect(client, userdata, flags, rc):
print("Connected with result code " + str(rc))
# Subscribe to base topic and all sub topics
client.subscribe(base_topic + "#")
def on_message(client, userdata, msg):
if not msg.topic.startswith(base_topic):
logging.warning("Topic not allowed: %s" % msg.topic)
return
# Strip base topic
tmp = msg.topic[len(base_topic):]
# Split handler and command
handler, sep, command = tmp.partition("/")
# Try to find the handle with the given name
h = rpc.get_handler_by_name(handler)
if h is None:
logging.warning("Unable to find handler '%s'" % handler)
return
# Check for internal commands
if command.startswith("_") or command.endswith("_"):
logging.warning("Command might be an internal Python command")
return
# Parse data
data = []
if len(msg.payload) > 0:
try:
data = json.loads(msg.payload.decode("utf-8"))
except ValueError as e:
logging.warning("Unable to parse json data: '%s'" % str(e))
return
# Get command function from handler object
try:
func = getattr(h, command)
except AttributeError:
func = None
if func is None:
logging.warning("Unable to find command '%s'" % command)
return
# Call function
try:
func(*data)
except TypeError as e:
logging.warning("Unable to call function: '%s'" % str(e))
return
def main():
global base_topic, con, rpc
# Parse args
parser = argparse.ArgumentParser(description="ArduRPC MQTT Gateway")
parser.add_argument(
"-b",
"--baud",
default=9600,
dest="baud",
help="Baud",
metavar="N",
type=int,
)
parser.add_argument(
"-D",
"--device",
default="/dev/ttyUSB0",
dest="device",
help="Serial Device",
type=str,
)
parser.add_argument(
"--host",
default="localhost",
dest="hostname",
help="Hostname of the MQTT Broker",
type=str,
)
parser.add_argument(
"--port",
default=1883,
dest="port",
help="Port of the MQTT Broker",
type=int,
)
parser.add_argument(
"--base-topic",
default="arduino/ardurpc",
dest="base_topic",
help="Base topic (Default: arduino/ardurpc)",
type=str,
)
# Parse commandline arguments
args = parser.parse_args()
# Connect to Arduino and initialize ArduRPC interface
con = Serial(args.device, args.baud)
rpc = ArduRPC(connector=con)
base_topic = args.base_topic.strip()
if not base_topic.endswith("/"):
base_topic += "/"
# Initialize new MQTT client and connect to broker
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect(args.hostname, args.port, 60)
client.loop_forever()
if __name__ == "__main__":
main()
In der Funktion main()
werden die übergebene Kommandozeilenparameter ausgewertet, eine Verbindung zum Arduino und zum MQTT Broker aufgebaut. Die Funktion on_connect()
wird direkt nach dem Verbindungsaufbau aufgerufen und teilt dem Broker mit, über welche Topics der Gateway informiert werden möchte. In der Funktion on_message()
findet die Verarbeitung der Nachrichten statt. Sie nimmt die Nachrichten entgegen, wertet eventuell Parameter aus, ermittelt die für den angegeben Befehl benötigte Funktion und führt den Befehl aus. Zu beachten ist, dass die Kommunikation bei MQTT prinzipiell nur in eine Richtung stattfindet und es somit nicht möglich ist, Ergebnisse direkt zurückzusenden.
Der MQTT Gateway kann mit folgendem Befehl gestartet werden.
$ python gateway.py
Mit Hilfe der Tastenkombination Strg. + C kann er wieder beendet werden.
Je nach verwendetem Arduino müssen eventuell die Angabe für den seriellen Port oder die Baudrate angepasst werden. Welche Optionen zusätzlich zur Verfügung stehen kann wie folgt in Erfahrung gebracht werden.
$ python gateway.py --help
usage: gateway.py [-h] [-b N] [-D DEVICE] [--host HOSTNAME] [--port PORT]
[--base-topic BASE_TOPIC]
ArduRPC MQTT Gatway
optional arguments:
-h, --help show this help message and exit
-b N, --baud N Baud
-D DEVICE, --device DEVICE
Serial Device
--host HOSTNAME Hostname of the MQTT Broker
--port PORT Port of the MQTT Broker
--base-topic BASE_TOPIC
Base topic (Default: arduino/ardurpc)
Anschließend kann der Arduino zum Beispiel per mosquitto_pub
gesteuert werden.
Ist eine LED-Matrix angeschlossen lässt sich mit dem folgenden Befehl ein rote Linie zeichnen.
$ mosquitto_pub -t 'arduino/ardurpc/matrix/drawLine' -m '[0,0,4,4,[255, 0, 0]]'