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