435 lines
17 KiB
Markdown
435 lines
17 KiB
Markdown
# Raspberry Pi Pico W – Sensor Projekt
|
||
|
||
## Regeln
|
||
- Claude erklärt Konzepte und gibt Hinweise, aber **kein fertiger Code**
|
||
- Ich schreibe den Code selbst
|
||
- Bei Fehlern analysieren wir gemeinsam
|
||
- Ausnahme: Setup und Boilerplate darf vorgegeben werden
|
||
|
||
---
|
||
|
||
## Hardware
|
||
|
||
| Komponente | Protokoll | Misst |
|
||
|---|---|---|
|
||
| Raspberry Pi Pico W | – | Mikrocontroller mit WLAN |
|
||
| BME280 (GY-BME280) | I2C (Adresse 0x76) | Temperatur + Luftdruck + Luftfeuchtigkeit |
|
||
|
||
> AM2302 und DS18B20 werden nicht verwendet – BME280 liefert alle benötigten Messwerte.
|
||
|
||
---
|
||
|
||
## Entwicklungsumgebung
|
||
|
||
### System
|
||
- CachyOS (Arch-basiert)
|
||
- VS Code
|
||
|
||
### Installierte Tools
|
||
```bash
|
||
sudo pacman -S arm-none-eabi-gcc arm-none-eabi-newlib cmake ninja git python
|
||
sudo pacman -S minicom
|
||
```
|
||
|
||
### VS Code Extensions
|
||
- C/C++ (ms-vscode.cpptools)
|
||
- CMake Tools (ms-vscode.cmake-tools)
|
||
- CMake (twxs.cmake)
|
||
- Serial Monitor (ms-vscode.vscode-serial-monitor)
|
||
- Raspberry Pi Pico (raspberry-pi.raspberry-pi-pico)
|
||
|
||
### Serieller Monitor
|
||
- Port: `/dev/ttyACM0`
|
||
- Baud Rate: `115200`
|
||
- Gruppe für Zugriff: `uucp`
|
||
```bash
|
||
sudo usermod -aG uucp $USER
|
||
# Dann neu einloggen!
|
||
```
|
||
|
||
---
|
||
|
||
## Projekt Struktur
|
||
|
||
Das Repo heißt `sensor-pico` und ist gleichzeitig der Root – keine extra Ebene darüber.
|
||
|
||
```
|
||
sensor-pico/ ← Git Repo Root
|
||
├── pico-sdk/ ← Git Submodule (Raspberry Pi Pico SDK)
|
||
├── lib/
|
||
│ ├── bme280/ ← Git Submodule (lafftale1999/bme280_driver)
|
||
│ └── dhcp_server/ ← DHCP Server (von pico-examples, kein Submodule)
|
||
├── src/
|
||
│ ├── main.cpp ← Hauptprogramm (WiFi + MQTT Verbindungslogik)
|
||
│ ├── webserver.c ← lwIP POST-Handler + Formular-Parsing
|
||
│ ├── webserver.h ← Exports: saved_ssid, saved_mqtt_* etc.
|
||
│ └── fsdata.c ← Generiert von makefsdata, nicht im Git!
|
||
├── fs/
|
||
│ ├── index.html ← Navigations-Hub (Links zu den Unterseiten)
|
||
│ ├── wlan_config.html ← WLAN SSID + Passwort Formular (POST /config)
|
||
│ ├── mqtt_config.html ← MQTT + Frequenz Formular (POST /mqtt-config)
|
||
│ └── live_stats.html ← (geplant) Live-Sensordaten
|
||
├── build/ ← Von cmake generiert, nicht im Git
|
||
├── CMakeLists.txt
|
||
├── lwipopts.h ← lwIP Konfiguration
|
||
└── prepend_include.cmake ← Hilfsskript für makefsdata-Build-Schritt
|
||
```
|
||
|
||
### Git Submodule
|
||
```
|
||
[submodule "lib/bme280"]
|
||
path = lib/bme280
|
||
url = git@github.com:lafftale1999/bme280_driver.git
|
||
|
||
[submodule "pico-sdk"]
|
||
path = pico-sdk
|
||
url = https://github.com/raspberrypi/pico-sdk.git
|
||
```
|
||
|
||
### .gitignore
|
||
```
|
||
build/
|
||
.vscode/
|
||
CMakeCache.txt
|
||
CMakeFiles/
|
||
cmake_install.cmake
|
||
Makefile
|
||
*.uf2
|
||
*.elf
|
||
*.bin
|
||
*.hex
|
||
*.map
|
||
*.dis
|
||
__pycache__/
|
||
*.pyc
|
||
src/fsdata.c
|
||
```
|
||
|
||
> `src/fsdata.c` wird beim Build automatisch generiert und gehört nicht ins Repo.
|
||
|
||
---
|
||
|
||
## CMakeLists.txt (aktueller Stand)
|
||
|
||
```cmake
|
||
cmake_minimum_required(VERSION 3.13)
|
||
set(PICO_BOARD pico_w)
|
||
|
||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||
set(PICO_SDK_PATH ${CMAKE_SOURCE_DIR}/pico-sdk)
|
||
|
||
include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
|
||
|
||
project(sensor-pico C CXX ASM)
|
||
set(CMAKE_C_STANDARD 11)
|
||
set(CMAKE_CXX_STANDARD 17)
|
||
|
||
pico_sdk_init()
|
||
|
||
add_subdirectory(lib/bme280 bme280_build)
|
||
add_subdirectory(lib/dhcp_server dhcp_server_build)
|
||
|
||
# makefsdata: fs/ → src/fsdata.c (wird von fs.c via #include eingebunden)
|
||
add_custom_command(
|
||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/fsdata.c
|
||
COMMAND perl ${PICO_SDK_PATH}/lib/lwip/src/apps/http/makefsdata/makefsdata
|
||
COMMAND ${CMAKE_COMMAND} -E rename fsdata.c src/fsdata.c
|
||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/prepend_include.cmake
|
||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||
DEPENDS ${CMAKE_SOURCE_DIR}/fs/index.html
|
||
${CMAKE_SOURCE_DIR}/fs/wlan_config.html
|
||
${CMAKE_SOURCE_DIR}/fs/mqtt_config.html
|
||
)
|
||
|
||
# fsdata.c wird NICHT in add_executable gelistet – fs.c inkludiert sie via
|
||
# #include HTTPD_FSDATA_FILE (definiert in lwipopts.h als "src/fsdata.c")
|
||
add_executable(sensor-pico
|
||
src/main.cpp
|
||
src/webserver.c
|
||
)
|
||
|
||
pico_enable_stdio_usb(sensor-pico 1)
|
||
pico_enable_stdio_uart(sensor-pico 0)
|
||
|
||
target_include_directories(sensor-pico PRIVATE
|
||
${CMAKE_CURRENT_SOURCE_DIR}
|
||
)
|
||
|
||
target_link_libraries(sensor-pico
|
||
pico_stdlib
|
||
pico_cyw43_arch_lwip_threadsafe_background
|
||
pico_lwip
|
||
pico_lwip_http
|
||
pico_lwip_mqtt
|
||
bme280
|
||
dhcp_server
|
||
)
|
||
|
||
pico_add_extra_outputs(sensor-pico)
|
||
```
|
||
|
||
---
|
||
|
||
## prepend_include.cmake
|
||
|
||
Dieses Script wird von CMake als Build-Schritt ausgeführt und fügt den fehlenden `#include` an den Anfang der generierten `fsdata.c` ein. Nötig weil das alte Perl-Script von lwIP keinen Header einfügt.
|
||
|
||
```cmake
|
||
file(READ "src/fsdata.c" CONTENT)
|
||
file(WRITE "src/fsdata.c" "#include \"lwip/apps/fs.h\"\n${CONTENT}")
|
||
```
|
||
|
||
> Warum nicht direkt Perl dafür? CMake-Scripts haben kein Shell-Quoting-Problem – `file(READ/WRITE)` arbeitet direkt mit dem Dateisystem, ohne Shell-Umweg.
|
||
|
||
---
|
||
|
||
## Build & Flash Workflow
|
||
|
||
```bash
|
||
# Erstes Setup (nur einmal)
|
||
mkdir build
|
||
|
||
# Bauen
|
||
cd build
|
||
cmake .. -DPICO_BOARD=pico_w # PICO_BOARD muss explizit angegeben werden!
|
||
make -j$(nproc)
|
||
|
||
# Was passiert beim Build automatisch:
|
||
# 1. makefsdata liest fs/index.html
|
||
# 2. Generiert fsdata.c mit HTML-Inhalt als Byte-Array
|
||
# 3. Verschiebt fsdata.c nach src/fsdata.c
|
||
# 4. prepend_include.cmake fügt #include "lwip/apps/fs.h" ein
|
||
# 5. fsdata.c wird mitkompiliert
|
||
|
||
# Flashen
|
||
# 1. BOOTSEL gedrückt halten
|
||
# 2. USB einstecken
|
||
# 3. Taste loslassen
|
||
# 4. RPI-RP2 erscheint als Laufwerk
|
||
cp sensor-pico.uf2 /run/media/$USER/RPI-RP2/
|
||
```
|
||
|
||
---
|
||
|
||
## Verkabelung
|
||
|
||
**I2C (BME280):**
|
||
```
|
||
VCC → 3.3V (Pin 36)
|
||
GND → GND
|
||
SDA → GPIO 14 (Pin 19) ← Vorgabe der bme280-Bibliothek (i2c1)
|
||
SCL → GPIO 15 (Pin 20) ← Vorgabe der bme280-Bibliothek (i2c1)
|
||
```
|
||
|
||
---
|
||
|
||
## BME280 Messwerte
|
||
|
||
Die Bibliothek gibt skalierte Integer zurück – kein Floating Point, üblich auf Embedded-Systemen wegen Speicher und Geschwindigkeit.
|
||
|
||
| Wert | Skalierung | Beispiel | Umrechnung |
|
||
|---|---|---|---|
|
||
| Temperatur | × 100 | 2494 | `/ 100` → 24.94°C |
|
||
| Luftfeuchtigkeit | × 1024 | 30759 | `/ 1024` → 30.0% |
|
||
| Luftdruck | × 256 (Pa) | 26074767 | `/ 256 / 100` → 1018.54 hPa |
|
||
|
||
JSON-Ausgabe per `bme280_get_json(handle)`:
|
||
```json
|
||
{"temperature":2494,"humidity":30759,"pressure":26074767}
|
||
```
|
||
|
||
Die Umrechnung kann auf dem Empfänger (z.B. Home Assistant) gemacht werden – der Pico sendet die Rohwerte.
|
||
|
||
Lesbare Ausgabe per printf möglich:
|
||
```c
|
||
printf("Temp: %d.%02d C\n", temperature / 100, temperature % 100);
|
||
```
|
||
|
||
---
|
||
|
||
## Netzwerk / Webserver
|
||
|
||
### Architektur
|
||
|
||
Der Pico hat zwei Betriebsmodi:
|
||
|
||
**Modus 1 – Erststart / Konfiguration (Access Point)**
|
||
- Pico öffnet einen eigenen Access Point (`SensorAP`, WPA2)
|
||
- DHCP-Server läuft – Clients bekommen automatisch eine IP (`192.168.4.x`)
|
||
- Pico ist erreichbar unter `192.168.4.1`
|
||
- Weboberfläche zeigt Konfigurationsformular
|
||
|
||
**Modus 2 – Produktionsbetrieb (Station Mode)**
|
||
- Pico verbindet sich mit bestehendem WLAN (gespeicherte SSID + Passwort)
|
||
- Publisht Sensordaten per MQTT an Broker
|
||
- Webserver läuft weiterhin: Live-Daten + Einstellungen änderbar
|
||
|
||
### Aktueller Stand
|
||
- ✅ Access Point (`SensorAP`, WPA2) öffnet sich
|
||
- ✅ DHCP-Server läuft – Clients bekommen IP
|
||
- ✅ Pico erreichbar unter `192.168.4.1`
|
||
- ✅ lwIP httpd läuft – Seiten werden ausgeliefert
|
||
- ✅ `makefsdata` automatisch im Build-Prozess eingebunden
|
||
- ✅ Alle HTML-Seiten aus `fs/` werden als Byte-Array in Firmware eingebaut
|
||
- ✅ WLAN-Konfiguration über Captive Portal (POST Handler)
|
||
- ✅ Pico verbindet sich mit bestehendem WLAN (Station Mode)
|
||
- ✅ MQTT-Konfiguration über Webformular (Adresse, Port, User, Passwort, Frequenzen)
|
||
- ✅ MQTT-Verbindung zum Broker steht
|
||
- ✅ BME280 Sensor liest Messwerte aus und gibt JSON per printf aus
|
||
- ⬜ Sensordaten per MQTT publishen
|
||
- ⬜ Live-Daten Seite (`live_stats.html`)
|
||
|
||
### Programm-Ablauf (main.cpp)
|
||
```
|
||
1. cyw43_arch_init() + httpd_init()
|
||
2. WiFi-Schleife:
|
||
└─ ap_init() → warte auf SSID+Passwort → verbinde mit WLAN
|
||
└─ bei Erfolg: weiter; bei Fehler: neu versuchen
|
||
3. MQTT-Config-Schleife:
|
||
└─ warte bis saved_mqtt_address gesetzt ist (Formular abgeschickt)
|
||
└─ connect_to_mqtt() → bei Fehler: Config zurücksetzen + neu warten
|
||
4. Sensor-Loop (TODO):
|
||
└─ BME280 auslesen → per MQTT publishen
|
||
```
|
||
|
||
### Geplante Anforderungen
|
||
1. ✅ Beim Erststart: Access Point für Erstkonfiguration
|
||
2. ✅ Weboberfläche zum Einstellen von:
|
||
- ✅ WLAN SSID + Passwort
|
||
- ✅ MQTT Broker (Adresse, Port, User, Passwort)
|
||
- ✅ Messfrequenz + Pushfrequenz
|
||
3. Im Produktionsbetrieb: Live-Daten + Einstellungen änderbar
|
||
|
||
### makefsdata – wie es funktioniert
|
||
|
||
`makefsdata` ist ein Perl-Script im lwIP-Quellcode:
|
||
```
|
||
pico-sdk/lib/lwip/src/apps/http/makefsdata/makefsdata
|
||
```
|
||
|
||
Es liest alle Dateien aus dem `fs/`-Ordner und erzeugt daraus eine `fsdata.c` mit dem Dateiinhalt als statische Byte-Arrays. lwIP httpd schaut zur Laufzeit in dieser Datei nach statt auf einem echten Dateisystem.
|
||
|
||
**Problem:** Das Script generiert `fsdata.c` ohne den nötigen `#include "lwip/apps/fs.h"` – der Compiler kennt dann weder `struct fsdata_file` noch `FS_FILE_FLAGS_*`.
|
||
**Lösung:** `prepend_include.cmake` fügt den Include als CMake-Build-Schritt nach der Generierung ein.
|
||
|
||
### lwipopts.h
|
||
```c
|
||
// fsdata.c wird von lwIP's fs.c via #include eingebunden – Pfad relativ zum Include-Root
|
||
#define HTTPD_FSDATA_FILE "src/fsdata.c"
|
||
|
||
#define NO_SYS 1
|
||
#define LWIP_SOCKET 0
|
||
#define LWIP_NETCONN 0
|
||
#define LWIP_IPV4 1
|
||
#define LWIP_TCP 1
|
||
#define LWIP_UDP 1
|
||
#define LWIP_DHCP 1
|
||
#define LWIP_DNS 1
|
||
#define LWIP_RAW 1
|
||
#define LWIP_ARP 1
|
||
#define LWIP_ETHERNET 1
|
||
#define LWIP_ICMP 1
|
||
#define LWIP_NETIF_HOSTNAME 1
|
||
#define LWIP_NETIF_STATUS_CALLBACK 1
|
||
#define MEM_ALIGNMENT 4
|
||
#define MEM_SIZE 4000
|
||
#define MEMP_NUM_TCP_SEG 32
|
||
#define PBUF_POOL_SIZE 24
|
||
#define TCP_MSS 1460
|
||
#define TCP_WND (8 * TCP_MSS)
|
||
#define TCP_SND_BUF (8 * TCP_MSS)
|
||
#define LWIP_HTTPD 1
|
||
#define LWIP_HTTPD_CGI 1
|
||
#define LWIP_HTTPD_SSI 1
|
||
#define LWIP_HTTPD_SUPPORT_POST 1 // POST-Body Callbacks aktivieren
|
||
```
|
||
|
||
---
|
||
|
||
## Nächste Schritte
|
||
|
||
- [x] `makefsdata` automatisch in cmake einbinden
|
||
- [x] WLAN Konfigurationsformular + POST-Handler
|
||
- [x] WLAN Station Mode
|
||
- [x] MQTT Konfigurationsformular + POST-Handler
|
||
- [x] MQTT Verbindung zum Broker
|
||
- [x] BME280 Sensor auslesen
|
||
- [ ] BME280 Sensordaten per MQTT publishen
|
||
- [ ] Live-Daten Seite (`live_stats.html`)
|
||
- [ ] Einstellungen im Flash speichern (Neustart ohne Neukonfiguration)
|
||
|
||
---
|
||
|
||
## Gelerntes
|
||
|
||
### Konzepte
|
||
|
||
- **I2C** – Bus-Protokoll, alle Geräte an 2 Drähten (SDA + SCL), jedes Gerät hat eine Adresse
|
||
- **Hexadezimal** – `0x` Prefix, 16 Ziffern (0-9, A-F)
|
||
- **ACK/NACK** – Antwort/keine Antwort bei I2C Kommunikation
|
||
- **Handle** – "Ticket" auf ein intern verwaltetes Objekt (Garderobe-Analogie)
|
||
- **Pull-up Widerstand** – nötig bei I2C und OneWire
|
||
- **printf** – Formatstring mit Platzhaltern (`%d`, `%s`, `%02X`), Zeilenumbruch mit `\n`
|
||
- **&variable** – Adressoperator, gibt Speicheradresse zurück
|
||
- **uint8_t** – 8-bit unsigned integer, genau 1 Byte
|
||
- **void** – Rückgabetyp wenn Funktion nichts zurückgibt
|
||
- **Embedded** – kein `std::cout`, `printf` bevorzugen wegen Speicher
|
||
- **IP-Adresse** – eindeutige Adresse im Netzwerk, 4 Zahlen (0-255)
|
||
- **Netzmaske** – definiert welcher Teil der IP das Netzwerk ist (`255.255.255.0`)
|
||
- **DHCP** – verteilt automatisch IP-Adressen an Geräte im Netzwerk
|
||
- **lwIP** – schlanker TCP/IP Stack für Embedded-Systeme
|
||
- **fsdata** – HTML-Dateien werden beim Build als C-Bytes in die Firmware eingebaut
|
||
- **makefsdata** – Perl-Script das `fs/`-Ordner in `fsdata.c` Byte-Array konvertiert
|
||
- **extern "C"** – verhindert C++ Name Mangling bei C-Bibliotheken
|
||
- **Name Mangling** – C++ kodiert Funktionsnamen mit Typinfo im Symbol (für Overloading); C nicht – das verursacht Linker-Fehler wenn man C-Bibliotheken aus C++ nutzt ohne `extern "C"`
|
||
- **-j$(nproc)** – paralleles Bauen mit allen CPU-Kernen
|
||
- **add_custom_command** – CMake-Befehl um externe Programme als Build-Schritt auszuführen; mit `OUTPUT` weiß CMake welche Datei erzeugt wird und wann neu gebaut werden muss
|
||
- **${CMAKE_COMMAND} -E** – plattformunabhängige CMake-Dateibefehle (rename, copy, remove) ohne Shell-Abhängigkeit
|
||
- **${CMAKE_COMMAND} -P** – führt ein CMake-Script direkt aus, ohne Shell-Umweg und ohne Quoting-Probleme
|
||
- **GET vs POST** – GET-Parameter landen in der URL (sichtbar, in Logs), POST-Daten im Body – für Passwörter immer POST
|
||
- **Buffer** – Array als temporärer Zwischenspeicher; in C immer manuell null-terminieren (`buf[len] = '\0'`)
|
||
- **pbuf** – lwIPs internes Paket-Buffer-Format; Inhalt mit `pbuf_copy_partial()` extrahieren, danach `pbuf_free()` aufrufen
|
||
- **strstr** – sucht Substring in String, gibt Zeiger auf Fundstelle zurück oder NULL
|
||
- **strchr** – sucht einzelnes Zeichen in String, gibt Zeiger darauf zurück
|
||
- **Zeigerarithmetik** – zwei Zeiger auf denselben String subtrahieren gibt die Länge zwischen ihnen
|
||
- **strlen** – Länge eines C-Strings ohne Null-Terminator
|
||
- **atoi** – wandelt C-String (`"30"`) in Integer (`30`) um; aus `<stdlib.h>`
|
||
- **memset** – setzt alle Bytes eines Arrays auf einen Wert; `memset(arr, 0, sizeof(arr))` zum Nullen
|
||
- **static (lokale Variable)** – lebt für die gesamte Programmlaufzeit, wird nicht bei jedem Aufruf neu initialisiert
|
||
- **Asynchrone Callbacks** – Funktion wird nicht direkt aufgerufen sondern von lwIP registriert und später automatisch aufgerufen (z.B. bei MQTT-Verbindung)
|
||
- **void *arg** – generischer Kontext-Zeiger in C-Callbacks; übergib `&deine_variable`, caste in Callback zurück mit `static_cast<typ*>(arg)`
|
||
- **static_cast<>** – C++ Cast-Operator; typsicherer als C-Style-Cast `(typ)`
|
||
- **Ternary Operator** – `bedingung ? wert_true : wert_false`; Kurzform für einfache if/else-Zuweisungen
|
||
- **mqtt_client_t** – lwIP MQTT Client-Objekt; erstellt mit `mqtt_client_new()`
|
||
- **mqtt_connect_client_info_t** – Struct mit Client-ID, Username, Passwort für MQTT-Verbindung
|
||
- **MQTT Verbindungsflow** – `mqtt_client_connect()` kehrt sofort zurück; Ergebnis kommt asynchron im Callback
|
||
|
||
### CMake-Konzepte
|
||
|
||
- **add_subdirectory** – bindet einen Unterordner mit eigenem CMakeLists.txt ein; zweiter Parameter ist der Build-Unterordner
|
||
- **target_include_directories** – sagt dem Compiler wo er Header-Dateien suchen soll; `PRIVATE` = nur für dieses Target
|
||
- **target_link_libraries** – verknüpft Bibliotheken mit dem Executable
|
||
- **add_custom_command OUTPUT** – definiert wie eine generierte Datei erzeugt wird; CMake führt den Befehl aus wenn die Datei fehlt oder Abhängigkeiten neuer sind
|
||
- **DEPENDS in add_custom_command** – wenn diese Dateien sich ändern, wird der Befehl beim nächsten Build neu ausgeführt
|
||
|
||
### Pico W Besonderheiten
|
||
|
||
- Interne LED hängt am WLAN-Chip (CYW43), nicht an GPIO 25
|
||
- Braucht `pico_cyw43_arch_none` (ohne Netzwerk) oder `pico_cyw43_arch_lwip_threadsafe_background` (mit lwIP)
|
||
- `PICO_BOARD=pico_w` muss **vor** `pico_sdk_import.cmake` gesetzt werden
|
||
- `MEM_LIBC_MALLOC` ist inkompatibel mit `threadsafe_background`
|
||
- `lwipopts.h` muss selbst erstellt werden – lwIP hat keine Defaults
|
||
- lwIP httpd CGI erwartet C-Funktionen – in `.cpp`-Dateien mit `extern "C"` wrappen
|
||
|
||
### Bekannte Fallstricke
|
||
|
||
- `makefsdata` generiert `fsdata.c` ohne `#include "lwip/apps/fs.h"` → Compiler kennt `struct fsdata_file` nicht → `prepend_include.cmake` als Workaround
|
||
- Shell-Quoting in CMake `COMMAND` ist fehleranfällig bei komplexen Perl-Ausdrücken → CMake-Scripts (`-P`) verwenden statt Shell-Befehle
|
||
- `add_custom_command OUTPUT` mit relativem Pfad legt die Datei im Build-Verzeichnis ab, nicht im Source-Verzeichnis → immer absoluten Pfad mit `${CMAKE_CURRENT_SOURCE_DIR}` angeben
|
||
- `pico_lwip_http` ist ein INTERFACE-Target – `target_include_directories` mit `PRIVATE` schlägt fehl; stattdessen `HTTPD_FSDATA_FILE` in `lwipopts.h` überschreiben
|
||
- lwIP `fs.c` inkludiert `fsdata.c` direkt via `#include HTTPD_FSDATA_FILE` – ohne Override wird die Default-Seite aus dem pico-sdk verwendet statt der eigenen
|
||
- `strcmp` gibt `0` zurück wenn Strings gleich sind, nicht `true` – `== true` prüft auf Verschiedenheit
|
||
- lwIP CGI-Handler parst nur GET-Parameter aus der URL, nicht POST-Body → für POST-Formulare `LWIP_HTTPD_SUPPORT_POST` + die drei Callbacks (`httpd_post_begin`, `httpd_post_receive_data`, `httpd_post_finished`) verwenden
|
||
- `dhcp_server_t` in `ap_init()` muss `static` sein – sonst wird der Stack-Speicher nach Funktionsrückkehr freigegeben, der DHCP-Server läuft aber weiter und greift auf ungültigen Speicher zu
|
||
- `strncpy` fügt **kein** Null-Byte hinzu wenn exakte Länge übergeben wird → immer `dest[len] = '\0'` danach setzen
|