projekt:python_fastapi
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
| projekt:python_fastapi [2026/02/20 18:51] – torsten.roehl | projekt:python_fastapi [2026/02/23 07:26] (aktuell) – [Systemd] torsten.roehl | ||
|---|---|---|---|
| Zeile 1: | Zeile 1: | ||
| - | ====== | + | ====== Python FASTAPI |
| [[raspberry_pi: | [[raspberry_pi: | ||
| - | * Klare Trennung: Hardware | + | //In diesem Projekt wird auf dem Raspberry Pi eine Weboberfläche mit FastAPI erstellt, über die eine LED-Ampel geschaltet und die Temperatur eines DS18B20 |
| - | * Keine Template-Engine | + | |
| - | * HTML als eigene Datei | + | |
| - | * LED + DS18B20 | + | |
| - | * Apache bleibt Hauptserver | + | |
| - | * Reboot-fest | + | |
| - | * VENV: ~/devel/projects/course_env | + | |
| + | |{{ : | ||
| + | |Die LED-Ampel kann nun über den Webbrowser gesteuert und die Temperatur ausgelesen werden.| | ||
| + | ====== Überblick ====== | ||
| + | * Voraussetzungen | ||
| + | * Software | ||
| + | * Konfiguration | ||
| - | ===== 1. Projektordner | + | ====== |
| + | |||
| + | ===== Voraussetzungen ===== | ||
| + | |||
| + | ==== ENV ==== | ||
| + | |||
| + | <note important> | ||
| + | **Aktivierung der Python-Environment: | ||
| + | |||
| + | Alle weiteren Schritte erfolgen mit der aktivierten Python-Umgebung. | ||
| + | |||
| + | < | ||
| + | source ~/ | ||
| + | </ | ||
| + | |||
| + | </ | ||
| + | Anschließend werden FastAPI und Uvicorn installiert: | ||
| <code bash> | <code bash> | ||
| - | cd / | + | pip install fastapi uvicorn |
| - | mkdir -p python_web/ | + | |
| - | mkdir -p python_web/ | + | |
| - | cd python_web/ | + | |
| - | touch __init__.py | + | |
| </ | </ | ||
| + | < | ||
| + | **FastAPI / Uvicorn** | ||
| + | * **FastAPI** | ||
| + | * stellt das Web-Framework bereit, mit dem die Webseiten und Routen programmiert werden. | ||
| + | * **Uvicorn** | ||
| + | * startet die Anwendung und sorgt dafür, dass sie im Browser erreichbar ist. | ||
| + | </ | ||
| - | ===== 2. VENV ===== | + | ==== Projektstruktur |
| + | '' | ||
| + | < | ||
| + | course_web/ | ||
| + | └── src/ | ||
| + | ├── app.py | ||
| + | ├── core/ | ||
| + | │ | ||
| + | │ | ||
| + | └── html/ | ||
| + | ├── led.html | ||
| + | └── temp.html | ||
| + | |||
| + | </ | ||
| + | ==== Apache2 Startseite ==== | ||
| + | Im Verzeichnis des Apache2-Webservers (''/ | ||
| + | Beim Aufruf der **IP-Adresse des Raspberry Pi im Browser** wird diese Startseite geladen, über die anschließend das gewünschte Projekt ausgewählt werden kann. | ||
| + | |||
| + | <note tip> **Tip** | ||
| + | |||
| + | Bevor die neue '' | ||
| <code bash> | <code bash> | ||
| - | source ~/devel/projects/course_env/bin/activate | + | sudo mv /var/www/html/index.html / |
| - | pip install fastapi uvicorn RPi.GPIO | + | |
| - | which uvicorn | + | |
| </ | </ | ||
| + | </ | ||
| - | Erwartet: | + | <code html /var/www/html/index.html> |
| - | | + | < |
| + | < | ||
| + | < | ||
| + | < | ||
| + | </head> | ||
| + | < | ||
| + | < | ||
| - | ===== 3. Hardware-Schicht | + | < |
| + | < | ||
| + | < | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | ==== Hardware | ||
| + | Die Hardware, also die LED-Ampel und der Temperatursensor, | ||
| - | Datei: | + | ===== Software ===== |
| - | / | + | Im folgenden Abschnitt werden die für die Webanwendung benötigten Python- und HTML-Dateien vorgestellt. Dazu gehören die Hardware-Anbindung über GPIO und den Temperatursensor, |
| + | ==== API ==== | ||
| - | <code python> | + | <code python |
| import RPi.GPIO as GPIO | import RPi.GPIO as GPIO | ||
| import glob | import glob | ||
| import time | import time | ||
| - | # ========================= | + | # ----------------------------- |
| - | # GPIO LED | + | # API-Funktionen |
| - | # ========================= | + | # ----------------------------- |
| PIN_R = 17 | PIN_R = 17 | ||
| Zeile 55: | Zeile 110: | ||
| _initialized = False | _initialized = False | ||
| - | |||
| def init(): | def init(): | ||
| Zeile 72: | Zeile 126: | ||
| - | def setRedLED(v): | + | def setLED(pin, value): |
| - | GPIO.output(PIN_R, GPIO.HIGH if v == 1 else GPIO.LOW) | + | GPIO.output(pin, GPIO.HIGH if value == 1 else GPIO.LOW) |
| + | |||
| + | |||
| + | def setRedLED(value): | ||
| + | setLED(PIN_R, | ||
| - | def setYellowLED(v): | + | def setYellowLED(value): |
| - | | + | |
| - | def setGreenLED(v): | + | def setGreenLED(value): |
| - | | + | |
| - | def led_status(): | + | def status(): |
| return ( | return ( | ||
| int(GPIO.input(PIN_R)), | int(GPIO.input(PIN_R)), | ||
| Zeile 92: | Zeile 150: | ||
| - | # ========================= | + | # ----------------------------- |
| - | # DS18B20 Temperatur | + | # API-Funktionen ds18b20 |
| - | # ========================= | + | # ----------------------------- |
| SENSOR_TIMEOUT = 1 | SENSOR_TIMEOUT = 1 | ||
| - | + | def get_sensor(): | |
| - | def get_sensor_file(): | + | |
| sensors = glob.glob("/ | sensors = glob.glob("/ | ||
| if not sensors: | if not sensors: | ||
| Zeile 107: | Zeile 164: | ||
| def get_temperature(): | def get_temperature(): | ||
| - | sensor_file = get_sensor_file() | + | sensor_file = get_sensor() |
| if sensor_file is None: | if sensor_file is None: | ||
| return None | return None | ||
| Zeile 131: | Zeile 188: | ||
| - | ===== 4. HTML-Datei ===== | + | ==== HTML ==== |
| - | Datei: | + | === LED === |
| - | / | + | |
| - | <code html> | + | |
| + | < | ||
| < | < | ||
| < | < | ||
| < | < | ||
| - | < | + | < |
| </ | </ | ||
| < | < | ||
| - | < | + | < |
| - | < | + | < |
| - | <a href="/web/ | + | <p>Status: {{R}}</p> |
| - | <a href="/web/ | + | <a href="/ |
| + | <a href="/ | ||
| - | < | + | < |
| - | <a href="/web/ | + | <p>Status: {{Y}}</p> |
| - | <a href="/web/ | + | <a href="/ |
| + | <a href="/ | ||
| - | < | + | < |
| - | <a href="/web/ | + | <p>Status: {{G}}</p> |
| - | <a href="/web/ | + | <a href="/ |
| + | <a href="/ | ||
| - | <hr> | + | <br>< |
| + | <a href="/"> | ||
| + | |||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | |||
| + | === Temperature === | ||
| + | |||
| + | |||
| + | <code html / | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | </ | ||
| + | <body> | ||
| < | < | ||
| + | |||
| < | < | ||
| + | |||
| + | <br> | ||
| + | <a href="/"> | ||
| </ | </ | ||
| Zeile 168: | Zeile 249: | ||
| - | ===== 5. Web-Schicht ===== | + | ==== FASTAPI APP ==== |
| + | |||
| + | |||
| + | <code python / | ||
| - | Datei: | ||
| - | / | ||
| - | <code python> | ||
| from fastapi import FastAPI, HTTPException | from fastapi import FastAPI, HTTPException | ||
| from fastapi.responses import HTMLResponse, | from fastapi.responses import HTMLResponse, | ||
| from core import hardware | from core import hardware | ||
| - | app = FastAPI(root_path="/ | + | app = FastAPI() |
| Zeile 186: | Zeile 267: | ||
| - | def load_html(r, y, g, t): | + | def load_template(name, replacements): |
| - | with open("static/index.html", " | + | |
| - | html = f.read() | + | |
| + | html = f.read() | ||
| + | except FileNotFoundError: | ||
| + | raise HTTPException(status_code=500, | ||
| - | | + | |
| - | html = html.replace(" | + | html = html.replace(key, str(value)) |
| - | html = html.replace(" | + | |
| - | + | ||
| - | if t is None: | + | |
| - | html = html.replace(" | + | |
| - | else: | + | |
| - | html = html.replace(" | + | |
| return html | return html | ||
| + | @app.get("/ | ||
| + | def led_page(): | ||
| + | r, y, g = hardware.status() | ||
| - | @app.get("/", | + | return HTMLResponse( |
| - | def index(): | + | load_template("led.html", |
| - | | + | " |
| - | t = hardware.get_temperature() | + | " |
| - | return HTMLResponse(load_html(r, y, g, t)) | + | " |
| + | " | ||
| + | " | ||
| + | " | ||
| + | }) | ||
| + | ) | ||
| @app.get("/ | @app.get("/ | ||
| Zeile 224: | Zeile 309: | ||
| raise HTTPException(status_code=400) | raise HTTPException(status_code=400) | ||
| - | return RedirectResponse(url="/ | + | return RedirectResponse(url="/ |
| + | |||
| + | |||
| + | @app.get("/ | ||
| + | def temp_page(): | ||
| + | t = hardware.get_temperature() | ||
| + | |||
| + | if t is None: | ||
| + | value = " | ||
| + | else: | ||
| + | value = f" | ||
| + | |||
| + | return HTMLResponse( | ||
| + | load_template(" | ||
| + | " | ||
| + | }) | ||
| + | | ||
| </ | </ | ||
| - | ===== 6. Apache ===== | ||
| - | In: | ||
| - | / | ||
| - | < | + | ===== Konfiguration ===== |
| + | In diesem Abschnitt wird die Einbindung der **FastAPI-Anwendung** in den Apache-Webserver sowie die Einrichtung als '' | ||
| + | ==== Apache Proxy ==== | ||
| + | |||
| + | === Konfiguration === | ||
| + | |||
| + | In der Datei '' | ||
| + | innerhalb von ''< | ||
| + | |||
| + | |||
| + | <note tip> **Tip** | ||
| + | |||
| + | Bevor die Datei ''/ | ||
| + | sollte die vorhandene Konfiguration gesichert werden. | ||
| + | |||
| + | <code bash> | ||
| + | cd / | ||
| + | sudo cp 000-default.conf 000-default.conf.course_backup | ||
| + | </ | ||
| + | |||
| + | </ | ||
| + | |||
| + | |||
| + | < | ||
| ProxyPreserveHost On | ProxyPreserveHost On | ||
| - | ProxyPass | + | |
| - | ProxyPassReverse /web | + | ProxyPass |
| + | ProxyPassReverse /led | ||
| + | |||
| + | ProxyPass | ||
| + | ProxyPassReverse /temp | ||
| </ | </ | ||
| + | |||
| + | === Aktivieren === | ||
| + | Damit die Weiterleitung an die FastAPI-Anwendung funktioniert, | ||
| <code bash> | <code bash> | ||
| sudo a2enmod proxy | sudo a2enmod proxy | ||
| sudo a2enmod proxy_http | sudo a2enmod proxy_http | ||
| - | sudo systemctl | + | sudo systemctl |
| </ | </ | ||
| + | ==== Systemd ==== | ||
| - | ===== 7. systemd ===== | + | Die Service-Datei muss unter ''/ |
| + | === Service | ||
| - | Datei: | ||
| - | / | ||
| - | < | + | < |
| [Unit] | [Unit] | ||
| Description=Python Web FastAPI | Description=Python Web FastAPI | ||
| Zeile 259: | Zeile 387: | ||
| [Service] | [Service] | ||
| User=pi | User=pi | ||
| - | WorkingDirectory=/ | + | WorkingDirectory=/ |
| ExecStart=/ | ExecStart=/ | ||
| Restart=always | Restart=always | ||
| Zeile 266: | Zeile 394: | ||
| WantedBy=multi-user.target | WantedBy=multi-user.target | ||
| </ | </ | ||
| + | |||
| + | === Registrierung === | ||
| + | Damit die neu erstellte Service-Datei von '' | ||
| <code bash> | <code bash> | ||
| sudo systemctl daemon-reload | sudo systemctl daemon-reload | ||
| - | sudo systemctl enable | + | sudo systemctl enable |
| - | sudo systemctl start python_web | + | sudo systemctl start course_web |
| </ | </ | ||
| - | + | Nützlich: | |
| - | ===== 8. Test ===== | + | |
| - | + | ||
| - | * http:// | + | |
| - | * http:// | + | |
| - | + | ||
| - | ===== 9. Reboot ===== | + | |
| <code bash> | <code bash> | ||
| - | sudo reboot | + | sudo systemctl restart course_web |
| + | sudo systemctl status course_web | ||
| </ | </ | ||
| - | |||
| - | System startet automatisch. | ||
| - | Hardware wird einmal initialisiert. | ||
| - | Reboot-fest. | ||
| - | |||
| - | |||
projekt/python_fastapi.1771613477.txt.gz · Zuletzt geändert: von torsten.roehl
