ArduRPC per HTTP Rest API

Mit Hilfe von ArduRPC lassen sich Befehle an einen Arduino oder kompatiblen Mikrocontroller senden. In Python lässt sich in wenigen Schritten ein Rest-Schnittstelle programmieren.

Das in diesem Beitrag gezeigte Beispiel ist sehr minimalistisch gehalten und soll in erster Linie als Anregung für eigene Entwicklungen dienen.

Der Webserver wird mit Hilfe des in Python geschriebene Micro-Frameworks Flask umgesetzt. 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 flask
Collecting flask
  Downloading Flask-0.10.1.tar.gz (544kB)
    100% |████████████████████████████████| 544kB 508kB/s
Collecting Werkzeug>=0.7 (from flask)
  Downloading Werkzeug-0.10.4-py2.py3-none-any.whl (293kB)
    100% |████████████████████████████████| 294kB 981kB/s
Collecting Jinja2>=2.4 (from flask)
  Downloading Jinja2-2.7.3.tar.gz (378kB)
    100% |████████████████████████████████| 380kB 1.1MB/s
Collecting itsdangerous>=0.21 (from flask)
  Downloading itsdangerous-0.24.tar.gz (46kB)
    100% |████████████████████████████████| 49kB 2.8MB/s
Collecting markupsafe (from Jinja2>=2.4->flask)
  Downloading MarkupSafe-0.23.tar.gz
Installing collected packages: Werkzeug, markupsafe, Jinja2, itsdangerous, flask
  Running setup.py install for markupsafe
  Running setup.py install for Jinja2
  Running setup.py install for itsdangerous
  Running setup.py install for flask
Successfully installed Jinja2-2.7.3 Werkzeug-0.10.4 flask-0.10.1 itsdangerous-0.24 markupsafe-0.23
$ pip install ardurpc
Collecting ardurpc
  Downloading 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 Webservers zu sehen, welcher auch über server.py heruntergeladen werden kann.

#!/usr/bin/env python
import argparse
import json

from flask import Flask
from flask import request

from ardurpc import ArduRPC
from ardurpc.connector.serial_con import Serial

app = Flask(__name__)
con = None
rpc = None


@app.route("/<handler>/<command>", methods=["POST", "GET"])
def do(handler, command):
    # Try to find the handle with the given name
    h = rpc.get_handler_by_name(handler)
    if h is None:
        return "Unable to find handler '%s'" % handler

    # Check for internal commands
    if command.startswith("_") or command.endswith("_"):
        return "Command might be an internal Python command"

    # Parse data
    data = []
    if request.method == "POST":
        tmp = request.get_data()
        try:
            data = json.loads(tmp.decode("utf-8"))
        except ValueError as e:
            return "Unable to parse json data: '%s'" % str(e)
    elif request.method == "GET":
        data = []

    # Get command function from handler object
    try:
        func = getattr(h, command)
    except AttributeError:
        func = None

    if func is None:
        return "Unable to find command '%s'" % command

    # Call function and ...
    try:
        result = func(*data)
    except TypeError as e:
        return "Unable to call function: '%s'" % str(e)

    # ... return result
    try:
        return json.dumps({"result": result})
    except TypeError as e:
        return "Unable to serialize result"


def main():
    global con, rpc
    parser = argparse.ArgumentParser(description="ArduRPC HTTP Rest API")
    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(
        "--debug",
        action="store_true",
        default=False,
        dest="debug",
        help="Enable debug mode",
    )

    # Parse commandline arguments
    args = parser.parse_args()

    # Connect to Arduino and initialize ArduRPC interface
    con = Serial(args.device, args.baud)
    rpc = ArduRPC(connector=con)

    # Start HTTP Server
    app.run(debug=args.debug)

if __name__ == "__main__":
    main()

In der Funktion main() werden übergebene Kommandozeilenparameter ausgewertet, eine Verbindung zum Arduino aufgebaut und die Webanwendung gestartet. Die Funktion do() ist für die Verarbeitung der Anfragen notwendig. Sie nimmt die Anfragen entgegen, wertet eventuell per Post-Methode übergebene Parameter aus, ermittelt die für den angegeben Befehl benötigte Funktion, führt den Befehl aus und gibt das Ergebnis zurück.

Der Webserver kann mit dem folgenden Befehl gestartet werden.

$ python server.py

Mit Hilfe der Tastenkombination Strg. + C kann der Server wieder beendet werden.

Je nach verwendetem Arduino müssen eventuell die Angabe für den seriellen Port oder die Baudrate angepasst werden. Welche Optionen zur Verfügung stehen kann wie folgt in Erfahrung gebracht werden.

$ python server.py --help
usage: server.py [-h] [-b--baud N] [-D--device DEVICE] [--debug]

ArduRPC HTTP Rest API

optional arguments:
  -h, --help         show this help message and exit
  -b--baud N         Baud
  -D--device DEVICE  Serial Device
  --debug            Enable debug mode

Anschließend kann der Arduino zum Beispiel per curl gesteuert werden.

Die Temperatur eines angeschlossenen Sensors lässt sich zum Beispiel wie folgt abfragen.

$ curl http://localhost:5000/sensor/getTemperature
{"result": 20.0}

Ist zum Beispiel eine LED-Matrix angeschlossen lässt sich mit dem folgenden Befehl ein rote Linie zeichnen.

$ curl -X POST -d '[0,0,0,4,[255,0,0]]' http://localhost:5000/matrix/drawLine
{"result": 0}

Verwandte Artikel