Dies ist eine alte Version des Dokuments!
Inhaltsverzeichnis
Python FASTAPI
☚ zurück - Einstiegskurs Raspberry Pi
In diesem Projekt wird eine kleine Weboberfläche mit FastAPI auf dem Raspberry Pi bereitgestellt, um eine LED-Ampel per GPIO zu schalten und die Temperatur eines DS18B20 über das Sysfs-Interface auszulesen. Die FastAPI-App läuft als systemd-Service (uvicorn) und wird über einen Apache-Reverse-Proxy unter /led und /temp erreichbar gemacht.
Überblick
- Voraussetzungen
- Software
- Konfiguration
Details
Voraussetzungen
ENV
source ~/devel/projects/course_env/bin/activate pip install fastapi uvicorn RPi.GPIO
Projektstruktur
cd ~/devel/projects/
course_web/
└── src/
├── app.py
├── core/
│ └── hardware.py
└── html/
├── led.html
└── temp.html
Apache2 Startseite
Hardware
Die Hardware, also die LED-Ampel und der Temperatursensor, wurden in den vorausgegangenen Projekten ausführlich behandelt und werden exakt so verwendet wie dort beschrieben.
Software
API
- /home/pi/devel/projects/course_web/src/core/hardware.py
import RPi.GPIO as GPIO import glob import time # ----------------------------- # API-Funktionen GPIO LED Ampel # ----------------------------- PIN_R = 17 PIN_Y = 27 PIN_G = 22 _initialized = False def init(): global _initialized if _initialized: return GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(PIN_R, GPIO.OUT) GPIO.setup(PIN_Y, GPIO.OUT) GPIO.setup(PIN_G, GPIO.OUT) _initialized = True def setLED(pin, value): GPIO.output(pin, GPIO.HIGH if value == 1 else GPIO.LOW) def setRedLED(value): setLED(PIN_R, value) def setYellowLED(value): setLED(PIN_Y, value) def setGreenLED(value): setLED(PIN_G, value) def status(): return ( int(GPIO.input(PIN_R)), int(GPIO.input(PIN_Y)), int(GPIO.input(PIN_G)), ) # ----------------------------- # API-Funktionen ds18b20 # ----------------------------- SENSOR_TIMEOUT = 1 def get_sensor(): sensors = glob.glob("/sys/bus/w1/devices/28-*") if not sensors: return None return sensors[0] + "/w1_slave" def get_temperature(): sensor_file = get_sensor() if sensor_file is None: return None start_time = time.time() while True: with open(sensor_file, "r") as f: lines = f.readlines() if lines[0].strip().endswith("YES"): break if time.time() - start_time > SENSOR_TIMEOUT: return None time.sleep(0.1) temp_line = lines[1] temp_str = temp_line.split("t=")[1] return float(temp_str) / 1000.0
HTML
LED
- /home/pi/devel/projects/course_web/src/html/led.html
<!DOCTYPE html> <html> <head> <title>LED Ampel</title> </head> <body> <h1>LED Ampel</h1> <h2>Rot (Status: {{R}})</h2> <a href="/led/r/1">ON</a> <a href="/led/r/0">OFF</a> <h2>Gelb (Status: {{Y}})</h2> <a href="/led/y/1">ON</a> <a href="/led/y/0">OFF</a> <h2>Grün (Status: {{G}})</h2> <a href="/led/g/1">ON</a> <a href="/led/g/0">OFF</a> <br><br> <a href="/">Zurück</a> </body> </html>
Temperature
FASTAPI APP
- /home/pi/devel/projects/course_web/src/app.py
from fastapi import FastAPI, HTTPException from fastapi.responses import HTMLResponse, RedirectResponse from core import hardware app = FastAPI() @app.on_event("startup") def startup(): hardware.init() def load_template(name, replacements): try: with open(f"html/{name}", "r") as f: html = f.read() except FileNotFoundError: raise HTTPException(status_code=500, detail="Template nicht gefunden") for key, value in replacements.items(): html = html.replace(key, str(value)) return html @app.get("/led", response_class=HTMLResponse) def led_page(): r, y, g = hardware.status() return HTMLResponse( load_template("led.html", { "{{R}}": r, "{{Y}}": y, "{{G}}": g }) ) @app.get("/led/{color}/{value}") def set_led(color: str, value: int): if value not in (0, 1): raise HTTPException(status_code=400) if color == "r": hardware.setRedLED(value) elif color == "y": hardware.setYellowLED(value) elif color == "g": hardware.setGreenLED(value) else: raise HTTPException(status_code=400) return RedirectResponse(url="/led", status_code=303) @app.get("/temp", response_class=HTMLResponse) def temp_page(): t = hardware.get_temperature() if t is None: value = "Sensorfehler" else: value = f"{t:.2f}" return HTMLResponse( load_template("temp.html", { "{{T}}": value }) )
Konfiguration
Apache Proxy
Konfiguration
In der Datei /etc/apache2/sites-available/000-default.conf, innerhalb von <VirtualHost *:80> folgendes einfügen:
- /etc/apache2/sites-available/000-default.conf
ProxyPreserveHost On ProxyPass /led http://127.0.0.1:8000/led ProxyPassReverse /led http://127.0.0.1:8000/led ProxyPass /temp http://127.0.0.1:8000/temp ProxyPassReverse /temp http://127.0.0.1:8000/temp
Aktivieren
sudo a2enmod proxy sudo a2enmod proxy_http sudo systemctl restart apache2
Systemd
Die Service-Datei muss unter /etc/systemd/system/course_web.service neu erstellt und anschließend bei systemd registriert werden.
Service
- /etc/systemd/system/course_web.service
[Unit] Description=Python Web FastAPI After=network-online.target Wants=network-online.target [Service] User=pi WorkingDirectory=/home/pi/devel/projects/course_web/src ExecStart=/home/pi/devel/projects/course_env/bin/uvicorn app:app --host 127.0.0.1 --port 8000 Restart=always [Install] WantedBy=multi-user.target
Registrierung
sudo systemctl daemon-reload sudo systemctl enable course_web sudo systemctl start course_web
