Benutzer-Werkzeuge

Webseiten-Werkzeuge


projekt:python_fastapi

Dies ist eine alte Version des Dokuments!


GPIO mit FastAPI (Websteuerung)

Ziel

  • Klare Trennung: Hardware / Web / HTML
  • Keine Template-Engine
  • HTML als eigene Datei
  • LED + DS18B20 Temperatur
  • Apache bleibt Hauptserver
  • Reboot-fest über systemd
  • VENV: ~/devel/projects/course_env

1. Projektordner

cd /home/pi/devel/projects
mkdir -p python_web/src/core
mkdir -p python_web/src/static
cd python_web/src/core
touch __init__.py

2. VENV

source ~/devel/projects/course_env/bin/activate
pip install fastapi uvicorn RPi.GPIO
which uvicorn

Erwartet:

/home/pi/devel/projects/course_env/bin/uvicorn

3. Hardware-Schicht

Datei:

/home/pi/devel/projects/python_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):
    if value == 1:
        GPIO.output(pin, GPIO.HIGH)
    else:
        GPIO.output(pin, 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():
    r = GPIO.input(PIN_R)
    y = GPIO.input(PIN_Y)
    g = GPIO.input(PIN_G)
    return r, y, g
 
 
def cleanup():
    GPIO.cleanup()
 
 
# -----------------------------
# API-Funktionen ds18b20
# -----------------------------
 
SENSOR_TIMEOUT = 1  # Sekunden
 
 
def is_sensor():
    sensors = glob.glob("/sys/bus/w1/devices/28-*")
    return len(sensors) > 0
 
 
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

4. HTML

Datei:

/home/pi/devel/projects/python_web/src/static/index.html
<!DOCTYPE html>
<html>
<head>
    <title>Python Web</title>
</head>
<body>
 
<h1>LED Steuerung</h1>
 
<h2>Rot (Status: {{R}})</h2>
<a href="/web/led/r/1">ON</a>
<a href="/web/led/r/0">OFF</a>
 
<h2>Gelb (Status: {{Y}})</h2>
<a href="/web/led/y/1">ON</a>
<a href="/web/led/y/0">OFF</a>
 
<h2>Grün (Status: {{G}})</h2>
<a href="/web/led/g/1">ON</a>
<a href="/web/led/g/0">OFF</a>
 
<hr>
 
<h1>Temperatur</h1>
<p>Temperatur: {{T}} °C</p>
 
</body>
</html>

5. Web-Schicht

Datei:

/home/pi/devel/projects/python_web/src/app.py
from fastapi import FastAPI, HTTPException
from fastapi.responses import HTMLResponse, RedirectResponse
from core import hardware
 
app = FastAPI(root_path="/web")
 
 
@app.on_event("startup")
def startup():
    hardware.init()
 
 
def load_html(r, y, g, t):
    with open("static/index.html", "r") as f:
        html = f.read()
 
    html = html.replace("{{R}}", str(r))
    html = html.replace("{{Y}}", str(y))
    html = html.replace("{{G}}", str(g))
 
    if t is None:
        html = html.replace("{{T}}", "Sensorfehler")
    else:
        html = html.replace("{{T}}", f"{t:.2f}")
 
    return html
 
 
@app.get("/", response_class=HTMLResponse)
def index():
    r, y, g = hardware.status()
    t = hardware.get_temperature()
    return HTMLResponse(load_html(r, y, g, t))
 
 
@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="/web", status_code=303)

6. Apache

In:

/etc/apache2/sites-available/000-default.conf
ProxyPreserveHost On
ProxyPass        /web  http://127.0.0.1:8000/
ProxyPassReverse /web  http://127.0.0.1:8000/
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo systemctl reload apache2

7. systemd

Datei:

/etc/systemd/system/python_web.service
[Unit]
Description=Python Web FastAPI
After=network-online.target
Wants=network-online.target

[Service]
User=pi
WorkingDirectory=/home/pi/devel/projects/python_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
sudo systemctl daemon-reload
sudo systemctl enable python_web
sudo systemctl start python_web

8. Test

9. Reboot

sudo reboot

Reboot-fest. Hardware wird einmal initialisiert. Struktur entspricht den ursprünglichen Prinzipien.

projekt/python_fastapi.1771613595.txt.gz · Zuletzt geändert: von torsten.roehl