Kontakt

Raspberry Pi Web UI

by Christoph Dähne on 01.07.2019

...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()

Wir haben einen Lichtschalter gebaut.

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 LED lässt sich nun bequem mit dem Smartphone vom Sofa aus steuern.

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