Du siehst unsere Seite gerade im Ecomodus.

Raspberry Pi Web UI (Teil 2)

...oder wie ich eine Gegensprechanlage baute (Teil 2)

Im letzten Blog–Artikel habe ich kurz zusammengetragen, wie du eine LED und einen Button an einen Raspberry Pi anschließen kannst. In diesem Artikel geht es um zwei neue Fragen:

  • Wie kann ich mit einem Button eine LED steuern?
  • Wie kann ich per Web–Oberfläche (UI) das gleiche tun?

Die Code–Beispiele aus dem letzten Blog–Artikel werde ich in diesem einfach verwenden. Die Beispiele sind wieder in Python3.

Ansteuerung der Schaltung

Aus den Beispielen aus dem letzten Artikel lässt sich die LED schnell mit dem Button kombinieren. Der Code für die Hardware kommt in eine eigene Klasse, damit wir diese später für der Web–UI benutzen können.

Der Hardware–Controller lässt sich einzeln starten mit python3 CircuitController.test.main.py.

# content of CircuitController.py from BinaryOutput import BinaryOutput from Button import Button import RPi.GPIO as GPIO import time class CircuitController: """ Reads the input pins like buttons and sets the output pins like LEDs on the custom circuit of the board. """ def __init__(self, buttonPin: int, ledPin: int): GPIO.setmode(GPIO.BOARD) self._led = BinaryOutput(ledPin) self._button = Button(buttonPin) def run(self): """ Controller loop reading the inputs, setting the outputs and updating the shared state. """ while True: if self._button.read(): self.toggleLed() time.sleep(1) def toggleLed(self): """ Toggles the LED on the circuit. """ self._led.toggle() def isLedOn(self): """ Provides the current LED status. """ return self._led.isEnabled() def destroy(self): """ Shuts down the GPIO pins. """ self._led.destroy() self._button.destroy() GPIO.cleanup()
# content of CircuitController.test.main.py from CircuitController import CircuitController LedPin = 18 BtnPin = 10 if __name__ == '__main__': controller = CircuitController(BtnPin, LedPin) try: controller.run() except KeyboardInterrupt: controller.destroy()

Flask als Web–Server

Der Web–Server basiert auf Flask, das online ausführlich erklärt ist — zum Beispiel in diesem Tutorial. Die Routen lassen sich ganz einfach mit Decorators konfigurieren.

# content of WebServer.py from flask import Flask, render_template, redirect from CircuitController import CircuitController import time class WebServer: """ Listens to a port and allows access to the circuit controller via HTTP. """ def __init__(self, port: int, controller: CircuitController): self._port = port flask = self._flask = Flask(__name__) @flask.route('/') def index(): return render_template('ledControl.html', ledIsOn=controller.isLedOn()) @flask.route('/toggle') def toggle(): controller.toggleLed() return redirect("/", code=302) def listen(self): """ Uses the current thread to listen. """ self._flask.run(host='0.0.0.0', port=self._port, debug=True)

Wenn man den Web–Server und den Hardware–Controller parallel in zwei Threads startet, dann muss man sich eigentlich Gedanken um deren Synchronisation machen. Auch erfahrenen Programmierer kann Multithreading Kopfschmerzen bereiten und das Thema ist viel zu komplex für diesen Blog–Artikel. Wenn das Thema neu für dich ist und dich interessiert, empfehle ich dieses YouTube–Video und dieses interaktive Tutorial.

In diesem Projekt habe ich bewusst auf eine Synchronisation verzichtet, wissend, dass sich das System manchmal unerwartet verhalten könnte. Das bedeutet in diesem Fall allerdings bloß: die LED ist doch nicht an oder aus.

Da der Fehler umso seltener Auftritt, je weniger die LED umgeschaltet wird (und für den Raspberry Pi ist eine Sekunde eine Ewigkeit), sollte die Lösung ausreichen — meiner bescheidenen Meinung nach.

#!/usr/bin/env python from CircuitController import CircuitController from WebServer import WebServer import os import threading LedPin = 18 BtnPin = 10 if __name__ == '__main__': controller = CircuitController(BtnPin, LedPin) port = int(os.environ.get('PORT', '5000')) webServer = WebServer(port, controller) try: controllerThread = threading.Thread( target=controller.run, daemon=True) controllerThread.start() # listening only works in main thread webServer.listen() except KeyboardInterrupt: controller.destroy()

Die Web–UI ist ein wichtiger Schritt in Richtung smarter Gegensprechanlage. Der Code ist schon minimal darauf ausgelegt. Im nächsten Blog–Artikel wird es um die Übertragung von Kamerabildern gehen.

Anregungen und Feedback sind wie immer willkommen.

Bildquellen

Foto des Raspberry Pi 3 B+ von Gareth Halfacree from Bradford, UK - Raspberry Pi 3 B+, CC BY-SA 2.0