ArduRPC per MQTT

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]]'

Verwandte Artikel