Added the webserver and the wifi handling
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -21,3 +21,6 @@ Makefile
|
|||||||
# Python cache (pico sdk tools)
|
# Python cache (pico sdk tools)
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
|
# FS
|
||||||
|
fsdata.c
|
||||||
|
|||||||
57
CMakeLists.txt
Normal file
57
CMakeLists.txt
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
# Pico SDK einbinden – MUSS vor project() stehen
|
||||||
|
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)
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
add_executable(sensor-pico
|
||||||
|
src/main.cpp
|
||||||
|
src/webserver.c
|
||||||
|
src/fsdata.c
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Standard Ein-/Ausgabe über USB (du siehst printf im Terminal)
|
||||||
|
pico_enable_stdio_usb(sensor-pico 1)
|
||||||
|
pico_enable_stdio_uart(sensor-pico 0)
|
||||||
|
|
||||||
|
target_include_directories(sensor-pico PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Benötigte Bibliotheken
|
||||||
|
target_link_libraries(sensor-pico
|
||||||
|
pico_stdlib
|
||||||
|
pico_cyw43_arch_lwip_threadsafe_background
|
||||||
|
bme280
|
||||||
|
dhcp_server
|
||||||
|
pico_lwip
|
||||||
|
pico_lwip_http
|
||||||
|
)
|
||||||
|
|
||||||
|
# Erzeugt .uf2 Datei zum Flashen
|
||||||
|
pico_add_extra_outputs(sensor-pico)
|
||||||
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13)
|
|||||||
set(PICO_BOARD pico_w)
|
set(PICO_BOARD pico_w)
|
||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
set(PICO_SDK_PATH ${CMAKE_SOURCE_DIR}/../pico-sdk)
|
set(PICO_SDK_PATH ${CMAKE_SOURCE_DIR}/pico-sdk)
|
||||||
|
|
||||||
# Pico SDK einbinden – MUSS vor project() stehen
|
# Pico SDK einbinden – MUSS vor project() stehen
|
||||||
include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
|
include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
|
||||||
@@ -14,14 +14,16 @@ set(CMAKE_CXX_STANDARD 17)
|
|||||||
|
|
||||||
pico_sdk_init()
|
pico_sdk_init()
|
||||||
|
|
||||||
add_subdirectory(../lib/bme280 bme280_build)
|
add_subdirectory(lib/bme280 bme280_build)
|
||||||
add_subdirectory(../lib/dhcp_server dhcp_server_build)
|
add_subdirectory(lib/dhcp_server dhcp_server_build)
|
||||||
|
|
||||||
add_executable(sensor-pico
|
add_executable(sensor-pico
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/webserver.c
|
src/webserver.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Standard Ein-/Ausgabe über USB (du siehst printf im Terminal)
|
# Standard Ein-/Ausgabe über USB (du siehst printf im Terminal)
|
||||||
pico_enable_stdio_usb(sensor-pico 1)
|
pico_enable_stdio_usb(sensor-pico 1)
|
||||||
pico_enable_stdio_uart(sensor-pico 0)
|
pico_enable_stdio_uart(sensor-pico 0)
|
||||||
377
PROJEKT.md
Normal file
377
PROJEKT.md
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
# 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
|
||||||
|
│ ├── webserver.c ← lwIP httpd Initialisierung + CGI-Handler
|
||||||
|
│ └── fsdata.c ← Generiert von makefsdata, nicht im Git!
|
||||||
|
├── fs/
|
||||||
|
│ └── index.html ← Weboberfläche, wird beim Build eingebaut
|
||||||
|
├── 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/index.html → src/fsdata.c
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(sensor-pico
|
||||||
|
src/main.cpp
|
||||||
|
src/webserver.c
|
||||||
|
src/fsdata.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
|
||||||
|
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 – Index-Seite wird ausgeliefert
|
||||||
|
- ✅ `makefsdata` automatisch im Build-Prozess eingebunden
|
||||||
|
- ✅ `fs/index.html` wird beim Build als Byte-Array in Firmware eingebaut
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
- Sendefrequenz
|
||||||
|
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
|
||||||
|
#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
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Nächste Schritte
|
||||||
|
|
||||||
|
- [x] `makefsdata` automatisch in cmake einbinden damit `fs/index.html` beim Build in die Firmware eingebaut wird
|
||||||
|
- [ ] Konfigurationsformular in `index.html` (WLAN + MQTT + Sendefrequenz)
|
||||||
|
- [ ] CGI-Handler in `webserver.c` für POST-Request vom Formular
|
||||||
|
- [ ] Einstellungen im Flash speichern
|
||||||
|
- [ ] WLAN Station Mode (Verbindung mit bestehendem Netz)
|
||||||
|
- [ ] MQTT einbinden
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
### 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
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<title>Konfiguration</title>
|
<title>Konfiguration</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>WLAN Einstellungen</h1>
|
<h1>WLAN Einstellungen</h1>
|
||||||
@@ -24,13 +24,13 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H
|
||||||
|
#define MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H
|
|
||||||
#define MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H
|
|
||||||
|
|
||||||
#include "lwip/ip_addr.h"
|
#include "lwip/ip_addr.h"
|
||||||
|
|
||||||
#define DHCPS_BASE_IP (16)
|
#define DHCPS_BASE_IP (16)
|
||||||
@@ -51,8 +51,8 @@ typedef struct _dhcp_server_t {
|
|||||||
void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm);
|
void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm);
|
||||||
void dhcp_server_deinit(dhcp_server_t *d);
|
void dhcp_server_deinit(dhcp_server_t *d);
|
||||||
|
|
||||||
#endif // MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H
|
||||||
|
|||||||
2
prepend_include.cmake
Normal file
2
prepend_include.cmake
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
file(READ "src/fsdata.c" CONTENT)
|
||||||
|
file(WRITE "src/fsdata.c" "#include \"lwip/apps/fs.h\"\n${CONTENT}")
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#include "bme280.h"
|
|
||||||
#include "dhcp_server.h"
|
|
||||||
#include "hardware/i2c.h"
|
|
||||||
#include "lwip/apps/httpd.h"
|
|
||||||
#include "pico/cyw43_arch.h"
|
|
||||||
#include "pico/stdlib.h"
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
stdio_init_all();
|
|
||||||
sleep_ms(3000);
|
|
||||||
cyw43_arch_init();
|
|
||||||
cyw43_arch_enable_ap_mode("SensorAP", "passwort123", CYW43_AUTH_WPA2_AES_PSK);
|
|
||||||
|
|
||||||
ip_addr_t gw{};
|
|
||||||
ip_addr_t mask{};
|
|
||||||
IP4_ADDR(&gw, 192, 168, 4, 1);
|
|
||||||
IP4_ADDR(&mask, 255, 255, 255, 0);
|
|
||||||
|
|
||||||
dhcp_server_t dhcp_server;
|
|
||||||
dhcp_server_init(&dhcp_server, &gw, &mask);
|
|
||||||
|
|
||||||
httpd_init();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
cyw43_arch_poll();
|
|
||||||
sleep_ms(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
// to be continued
|
|
||||||
55
src/main.cpp
Normal file
55
src/main.cpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#include "bme280.h"
|
||||||
|
#include "dhcp_server.h"
|
||||||
|
#include "hardware/i2c.h"
|
||||||
|
#include "lwip/apps/httpd.h"
|
||||||
|
#include "pico/cyw43_arch.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "webserver.h"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
void ap_init() {
|
||||||
|
cyw43_arch_enable_ap_mode("SensorAP", "passwort123", CYW43_AUTH_WPA2_AES_PSK);
|
||||||
|
|
||||||
|
ip_addr_t gw{};
|
||||||
|
ip_addr_t mask{};
|
||||||
|
IP4_ADDR(&gw, 192, 168, 4, 1);
|
||||||
|
IP4_ADDR(&mask, 255, 255, 255, 0);
|
||||||
|
|
||||||
|
static dhcp_server_t dhcp_server{};
|
||||||
|
dhcp_server_init(&dhcp_server, &gw, &mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
stdio_init_all();
|
||||||
|
sleep_ms(3000);
|
||||||
|
cyw43_arch_init();
|
||||||
|
httpd_init();
|
||||||
|
webserver_init();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
memset(saved_ssid, 0, sizeof(saved_ssid));
|
||||||
|
memset(saved_password, 0, sizeof(saved_password));
|
||||||
|
ap_init();
|
||||||
|
|
||||||
|
while (saved_ssid[0] == '\0') {
|
||||||
|
cyw43_arch_poll();
|
||||||
|
sleep_ms(100);
|
||||||
|
}
|
||||||
|
cyw43_arch_disable_ap_mode();
|
||||||
|
cyw43_arch_enable_sta_mode();
|
||||||
|
|
||||||
|
if (!(cyw43_arch_wifi_connect_timeout_ms(saved_ssid, saved_password,
|
||||||
|
CYW43_AUTH_WPA2_AES_PSK, 20000))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
|
||||||
|
sleep_ms(200);
|
||||||
|
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
|
||||||
|
sleep_ms(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/webserver.c
Normal file
22
src/webserver.c
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#include "lwip/apps/httpd.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
char saved_ssid[33];
|
||||||
|
char saved_password[65];
|
||||||
|
|
||||||
|
static const char *config_handler(int iIndex, int iNumParams, char *pcParam[],
|
||||||
|
char *pcValue[]) {
|
||||||
|
for (int i = 0; i < iNumParams; i++) {
|
||||||
|
if (strcmp(pcParam[i], "ssid") == 0) {
|
||||||
|
strncpy(saved_ssid, pcValue[i], 32);
|
||||||
|
}
|
||||||
|
if (strcmp(pcParam[i], "password") == 0) {
|
||||||
|
strncpy(saved_password, pcValue[i], 64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "/index.html";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const tCGI cgi_handlers[] = {{"/config", config_handler}};
|
||||||
|
|
||||||
|
void webserver_init(void) { http_set_cgi_handlers(cgi_handlers, 1); }
|
||||||
17
src/webserver.h
Normal file
17
src/webserver.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef WEBSERVER_H
|
||||||
|
#define WEBSERVER_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern char saved_ssid[33];
|
||||||
|
extern char saved_password[65];
|
||||||
|
|
||||||
|
void webserver_init(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user