From e8f90661d34be606131e718b372a28101b9db2c5 Mon Sep 17 00:00:00 2001 From: rasmus Date: Fri, 27 Mar 2026 22:27:32 +0100 Subject: [PATCH] AI update --- CMakeLists.txt | 3 + fs/index.html | 292 +++++++++++++++++++++++++++++++++++++-- fs/live_stats.html | 4 + fs/mqtt_config.html | 217 +++++++++++++++++++++++++---- fs/wlan_config.html | 157 ++++++++++++++++++--- lwipopts.h | 1 + src/main.cpp | 327 +++++++++++++++++++++++++------------------- src/webserver.c | 166 ++++++++++++++++++---- src/webserver.h | 20 ++- 9 files changed, 963 insertions(+), 224 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e5964a5..f95d1d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ add_custom_command( DEPENDS ${CMAKE_SOURCE_DIR}/fs/index.html DEPENDS ${CMAKE_SOURCE_DIR}/fs/mqtt_config.html DEPENDS ${CMAKE_SOURCE_DIR}/fs/wlan_config.html + DEPENDS ${CMAKE_SOURCE_DIR}/fs/live_stats.html ) @@ -60,6 +61,8 @@ target_link_libraries(sensor-pico pico_lwip_http pico_lwip_mqtt pico_lwip_sntp + hardware_flash + hardware_sync ) # Erzeugt .uf2 Datei zum Flashen diff --git a/fs/index.html b/fs/index.html index 8a85291..5b6e590 100644 --- a/fs/index.html +++ b/fs/index.html @@ -1,16 +1,290 @@ - + - Config + + Sensor Pico + -

Sensor Pico

-
- Live Daten -
- WLAN Config -
- MQTT und Messungs Config + +
+
+
Sensor Pico
+
BME280
+
+
+
+ +
+
+
+
Temperatur
+
+ -- + °C +
+
+ 🌡 +
+
+
+
Luftfeuchtigkeit
+
+ -- + % +
+
+ 💧 +
+
+
+
Luftdruck
+
+ -- + hPa +
+
+ 🔆 +
+
+ +
+
+ Verbinde… + +
+ + +
+
+
+
Einstellungen
+ + WLAN konfigurieren + + + + MQTT & Messung + + +
+ + diff --git a/fs/live_stats.html b/fs/live_stats.html index e69de29..bd13a4f 100644 --- a/fs/live_stats.html +++ b/fs/live_stats.html @@ -0,0 +1,4 @@ + + + + diff --git a/fs/mqtt_config.html b/fs/mqtt_config.html index 0a80464..e6a3c7b 100644 --- a/fs/mqtt_config.html +++ b/fs/mqtt_config.html @@ -1,32 +1,189 @@ - - - - - Konfiguration - - -

MQTT Konfiguration

-
- - -
- - -
- - -
- - -
- - -
- - -
- -
- + + + + + MQTT Konfiguration + + + +
+ ← Zurück +
+
+
+
+

MQTT & Messung

+

Broker und Intervalle konfigurieren

+
+
+
+ +
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +

in Millisekunden

+
+
+ + +

in Sekunden

+
+
+
+ +
+
+
+ diff --git a/fs/wlan_config.html b/fs/wlan_config.html index e310c89..69eef78 100644 --- a/fs/wlan_config.html +++ b/fs/wlan_config.html @@ -1,20 +1,141 @@ - - - - - Konfiguration - - -

WLAN Einstellungen

-
- - -
- - -
- -
- + + + + + WLAN Konfiguration + + + +
+ ← Zurück +
+
+
📶
+
+

WLAN

+

Netzwerkverbindung konfigurieren

+
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+ diff --git a/lwipopts.h b/lwipopts.h index c7734fb..22a84b9 100644 --- a/lwipopts.h +++ b/lwipopts.h @@ -23,6 +23,7 @@ #define LWIP_HTTPD_CGI 1 #define LWIP_HTTPD_SSI 1 #define LWIP_HTTPD_SUPPORT_POST 1 +#define LWIP_HTTPD_CUSTOM_FILES 1 #define LWIP_NETIF_HOSTNAME 1 #define LWIP_NETIF_STATUS_CALLBACK 1 diff --git a/src/main.cpp b/src/main.cpp index 8bd94e6..6f7a077 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,28 +15,21 @@ static dhcp_server_t dhcp_server{}; -void ap_init() { - cyw43_arch_enable_ap_mode("SensorAP", "passwort123", CYW43_AUTH_WPA2_AES_PSK); +/* ── WiFi ─────────────────────────────────────────────────────────────────── + */ - ip_addr_t gw{}; - ip_addr_t mask{}; +static void ap_init() { + cyw43_arch_enable_ap_mode("SensorAP", "passwort123", CYW43_AUTH_WPA2_AES_PSK); + ip_addr_t gw{}, mask{}; IP4_ADDR(&gw, 192, 168, 4, 1); IP4_ADDR(&mask, 255, 255, 255, 0); - dhcp_server_deinit(&dhcp_server); dhcp_server_init(&dhcp_server, &gw, &mask); } -void reset_mqtt_config() { - memset(saved_mqtt_address, 0, sizeof(saved_mqtt_address)); - memset(saved_mqtt_user, 0, sizeof(saved_mqtt_user)); - memset(saved_mqtt_password, 0, sizeof(saved_mqtt_password)); - saved_measure_frequency = 0; - saved_post_frequency = 0; -} - -int connect_to_wifi() { - int ret{}; +/* Open AP, wait for user to POST credentials, then connect STA. + Returns 0 on success. */ +static int connect_via_ap() { memset(saved_ssid, 0, sizeof(saved_ssid)); memset(saved_password, 0, sizeof(saved_password)); ap_init(); @@ -49,33 +42,41 @@ int connect_to_wifi() { cyw43_arch_disable_sta_mode(); sleep_ms(500); cyw43_arch_enable_sta_mode(); - - ret = cyw43_arch_wifi_connect_timeout_ms(saved_ssid, saved_password, - CYW43_AUTH_WPA2_MIXED_PSK, 30000); - if (ret == 0) { - return 0; - } - return -1; + return cyw43_arch_wifi_connect_timeout_ms(saved_ssid, saved_password, + CYW43_AUTH_WPA2_MIXED_PSK, 30000); } -void mqtt_cb(mqtt_client_t *client, void *arg, - mqtt_connection_status_t status) { +/* Connect STA with current saved credentials (no AP fallback). */ +static int connect_sta() { + cyw43_arch_disable_sta_mode(); + sleep_ms(500); + cyw43_arch_enable_sta_mode(); + return cyw43_arch_wifi_connect_timeout_ms(saved_ssid, saved_password, + CYW43_AUTH_WPA2_MIXED_PSK, 30000); +} + +/* ── MQTT ─────────────────────────────────────────────────────────────────── + */ + +static void mqtt_cb(mqtt_client_t *client, void *arg, + mqtt_connection_status_t status) { + (void)client; int *mqtt_status{static_cast(arg)}; *mqtt_status = (status == MQTT_CONNECT_ACCEPTED) ? 0 : 1; } -mqtt_client_t *connect_to_mqtt() { +static mqtt_client_t *connect_to_mqtt() { static int mqtt_status{-1}; - ip_addr_t broker_ip; static mqtt_client_t *client{}; static mqtt_connect_client_info_t info{}; + ip_addr_t broker_ip; - if (!ipaddr_aton(saved_mqtt_address, &broker_ip)) { + if (!ipaddr_aton(saved_mqtt_address, &broker_ip)) return nullptr; - } - if (!client) { + + if (!client) client = mqtt_client_new(); - } + info.client_id = "sensor-pico"; info.client_user = saved_mqtt_user; info.client_pass = saved_mqtt_password; @@ -87,70 +88,62 @@ mqtt_client_t *connect_to_mqtt() { cyw43_arch_poll(); sleep_ms(100); } - if (mqtt_status == 0) { - return client; - } - return nullptr; + return (mqtt_status == 0) ? client : nullptr; } -BME280_READING_INTERVALS_MS convert_Interval(int interval) { - if (interval < 1) { - return INTERVAL_0_5MS; - } else if (interval < 20 && interval > 1) { - return INTERVAL_10MS; - } else if (interval < 30 && interval > 10) { - return INTERVAL_20MS; - } else if (interval < 125 && interval > 20) { - return INTERVAL_62_5MS; - } else if (interval < 250 && interval > 62.5) { - return INTERVAL_125MS; - } else if (interval < 500 && interval > 125) { - return INTERVAL_250MS; - } else if (interval < 1000 && interval > 250) { - return INTERVAL_500MS; - } else if (interval > 1000) { - return INTERVAL_1000MS; - } else { - return INTERVAL_500MS; - } +static void publish_cb(void *arg, err_t err) { + (void)arg; + (void)err; + printf("Publish successful!\n"); } -void publish_cb(void *arg, err_t err) { printf("Publish succesfull!\n"); } - -void publish_mqtt(mqtt_client_t *client, const char *payload) { +static void publish_mqtt(mqtt_client_t *client, const char *payload) { uint payload_len{strlen(payload)}; mqtt_publish(client, "tele/sensor-pico/SENSOR", payload, payload_len, 1, 1, publish_cb, nullptr); } -bool parse_json(const char *raw, float &temperature, float &humidity, - float &pressure) { +/* ── BME280 ───────────────────────────────────────────────────────────────── + */ + +static BME280_READING_INTERVALS_MS convert_interval(int ms) { + if (ms < 1) + return INTERVAL_0_5MS; + if (ms < 20) + return INTERVAL_10MS; + if (ms < 30) + return INTERVAL_20MS; + if (ms < 125) + return INTERVAL_62_5MS; + if (ms < 250) + return INTERVAL_125MS; + if (ms < 500) + return INTERVAL_250MS; + if (ms < 1000) + return INTERVAL_500MS; + return INTERVAL_1000MS; +} + +static bool parse_json(const char *raw, float &temperature, float &humidity, + float &pressure) { if (!raw) return false; - const char *t_ptr = std::strstr(raw, "\"temperature\":"); const char *h_ptr = std::strstr(raw, "\"humidity\":"); const char *p_ptr = std::strstr(raw, "\"pressure\":"); - if (!t_ptr || !h_ptr || !p_ptr) return false; - - long t_raw = std::strtol(t_ptr + 14, nullptr, 10); - long h_raw = std::strtol(h_ptr + 11, nullptr, 10); - long p_raw = std::strtol(p_ptr + 11, nullptr, 10); - - temperature = t_raw / 100.0f; - humidity = h_raw / 1024.0f; - pressure = p_raw / 25600.0f; - + temperature = std::strtol(t_ptr + 14, nullptr, 10) / 100.0f; + humidity = std::strtol(h_ptr + 11, nullptr, 10) / 1024.0f; + pressure = std::strtol(p_ptr + 11, nullptr, 10) / 25600.0f; return true; } -void build_sensor_payload(char *buffer, size_t size, bme280_handle_t handle, - const char *sensor_name) { +static void build_sensor_payload(char *buffer, size_t size, + bme280_handle_t handle, + const char *sensor_name) { bme280_read_data(handle); const char *raw = bme280_get_json(handle); - if (!raw) { snprintf(buffer, size, "{\"error\":\"no data\"}"); return; @@ -158,37 +151,32 @@ void build_sensor_payload(char *buffer, size_t size, bme280_handle_t handle, float temperature, humidity, pressure; parse_json(raw, temperature, humidity, pressure); + webserver_update_sensor(temperature, humidity, pressure); time_t now; time(&now); - struct tm *timeinfo = localtime(&now); - char time_str[32]; - strftime(time_str, sizeof(time_str), "%Y-%m-%dT%H:%M:%S", timeinfo); + strftime(time_str, sizeof(time_str), "%Y-%m-%dT%H:%M:%S", localtime(&now)); snprintf(buffer, size, - "{" - "\"Time\":\"%s\"," - "\"%s\":{" - "\"Temperature\":%.2f," - "\"Humidity\":%.2f," - "\"Pressure\":%.2f" - "}," - "\"TempUnit\":\"C\"" - "}", + "{\"Time\":\"%s\",\"%s\":{" + "\"Temperature\":%.2f,\"Humidity\":%.2f,\"Pressure\":%.2f" + "},\"TempUnit\":\"C\"}", time_str, sensor_name, temperature, humidity, pressure); } -bool is_time_synced() { - time_t now = time(NULL); - struct tm *timeinfo = localtime(&now); - return timeinfo->tm_year > 70; + +/* ── SNTP ─────────────────────────────────────────────────────────────────── + */ + +static bool is_time_synced() { + time_t now = time(nullptr); + return localtime(&now)->tm_year > 70; } -void start_sntp() { +static void start_sntp() { cyw43_arch_lwip_begin(); - if (sntp_enabled()) { + if (sntp_enabled()) sntp_stop(); - } sntp_setoperatingmode(SNTP_OPMODE_POLL); #if SNTP_SERVER_DNS sntp_setservername(0, "de.pool.ntp.org"); @@ -197,15 +185,10 @@ void start_sntp() { cyw43_arch_lwip_end(); } +/* ── Main ─────────────────────────────────────────────────────────────────── + */ + int main() { - uint32_t last_publish{}; - uint32_t last_sntp_retry_ms{}; - int mqtt_ret{-1}; - int wifi_status{1}; - int bme_status{-1}; - char payload[256]; - mqtt_client_t *client{}; - bme280_handle_t handle{}; stdio_init_all(); setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0", 1); tzset(); @@ -213,55 +196,117 @@ int main() { cyw43_arch_init(); httpd_init(); - while (wifi_status != 0) { - wifi_status = (connect_to_wifi() == 0) ? 0 : 1; - printf("Wifi status: %d\n", wifi_status); - } - printf("Connected to wifi!\n"); - printf("IP: %s\n", ip4addr_ntoa(netif_ip4_addr(netif_default))); - start_sntp(); - last_sntp_retry_ms = to_ms_since_boot(get_absolute_time()); + /* ── 1. Load config from flash ── */ + bool has_saved_config = config_load(); + /* ── 2. Connect to WiFi ── */ + if (has_saved_config && saved_ssid[0] != '\0') { + printf("Trying saved WiFi: %s\n", saved_ssid); + if (connect_sta() != 0) { + printf("Saved WiFi failed, opening AP...\n"); + while (connect_via_ap() != 0) { + printf("AP connect failed, retrying...\n"); + } + } + } else { + while (connect_via_ap() != 0) { + printf("AP connect failed, retrying...\n"); + } + } + printf("WiFi connected! IP: %s\n", + ip4addr_ntoa(netif_ip4_addr(netif_default))); + + start_sntp(); + uint32_t last_sntp_retry_ms = to_ms_since_boot(get_absolute_time()); + + /* ── 3. Wait for MQTT config if not saved ── */ + while (saved_mqtt_address[0] == '\0') { + cyw43_arch_poll(); + sleep_ms(200); + } + + /* ── 4. Connect to MQTT ── */ + mqtt_client_t *client{}; while (true) { + client = connect_to_mqtt(); + if (client) + break; + printf("MQTT connect failed, waiting for new config...\n"); + memset(saved_mqtt_address, 0, sizeof(saved_mqtt_address)); while (saved_mqtt_address[0] == '\0') { cyw43_arch_poll(); sleep_ms(200); } - client = connect_to_mqtt(); - if (client == nullptr) { - printf("Mqtt Status: %d\n", mqtt_ret); - reset_mqtt_config(); - } else { - printf("Connected to mqtt!\n"); - break; - } } - bme_status = - bme280_init(&handle, 0x76, convert_Interval(saved_measure_frequency)); - if (!bme_status) { - last_publish = to_ms_since_boot(get_absolute_time()); - while (true) { - if ((to_ms_since_boot(get_absolute_time()) - last_publish) >= - saved_post_frequency * 1000) { - if (is_time_synced()) { - build_sensor_payload(payload, sizeof(payload), handle, "BME280"); - publish_mqtt(client, payload); - printf("Payload: %s\n", payload); - last_publish = to_ms_since_boot(get_absolute_time()); - } else { - printf("Waiting for SNTP sync (Current year: 1970)...\n"); - uint32_t now_ms = to_ms_since_boot(get_absolute_time()); - if (now_ms - last_sntp_retry_ms > 30000) { - printf("Retrying SNTP...\n"); - start_sntp(); - last_sntp_retry_ms = now_ms; - } - last_publish = to_ms_since_boot(get_absolute_time()) - - (saved_post_frequency * 1000) + 5000; - } + printf("MQTT connected!\n"); + + /* ── 5. Init BME280 ── */ + bme280_handle_t handle{}; + bool bme_ok = (bme280_init(&handle, 0x76, + convert_interval(saved_measure_frequency)) == 0); + + char payload[256]; + uint32_t last_publish = to_ms_since_boot(get_absolute_time()); + + /* ── 6. Main loop ── */ + while (true) { + + /* ── Handle WiFi config change ── */ + if (wlan_config_updated) { + wlan_config_updated = false; + config_save(); + printf("WiFi config updated, reconnecting...\n"); + if (mqtt_client_is_connected(client)) + mqtt_disconnect(client); + if (connect_sta() == 0) { + printf("WiFi reconnected!\n"); + start_sntp(); + last_sntp_retry_ms = to_ms_since_boot(get_absolute_time()); + } else { + printf("WiFi reconnect failed!\n"); } - cyw43_arch_poll(); - sleep_ms(100); + /* Force MQTT reconnect after WiFi change */ + mqtt_config_updated = true; } + + /* ── Handle MQTT / interval config change ── */ + if (mqtt_config_updated) { + mqtt_config_updated = false; + config_save(); + printf("MQTT config updated, reconnecting...\n"); + if (mqtt_client_is_connected(client)) + mqtt_disconnect(client); + client = connect_to_mqtt(); + if (client) { + printf("MQTT reconnected!\n"); + bme_ok = (bme280_init(&handle, 0x76, + convert_interval(saved_measure_frequency)) == 0); + last_publish = to_ms_since_boot(get_absolute_time()); + } else { + printf("MQTT reconnect failed!\n"); + } + } + + /* ── Periodic publish ── */ + uint32_t now_ms = to_ms_since_boot(get_absolute_time()); + if (client && bme_ok && + (now_ms - last_publish) >= uint32_t(saved_post_frequency) * 1000) { + if (is_time_synced()) { + build_sensor_payload(payload, sizeof(payload), handle, "BME280"); + publish_mqtt(client, payload); + printf("Payload: %s\n", payload); + last_publish = now_ms; + } else { + printf("Waiting for SNTP sync...\n"); + if (now_ms - last_sntp_retry_ms > 30000) { + start_sntp(); + last_sntp_retry_ms = now_ms; + } + last_publish = now_ms - uint32_t(saved_post_frequency) * 1000 + 5000; + } + } + + cyw43_arch_poll(); + sleep_ms(100); } } diff --git a/src/webserver.c b/src/webserver.c index a22664a..88c1f49 100644 --- a/src/webserver.c +++ b/src/webserver.c @@ -1,20 +1,141 @@ #include "lwip/apps/httpd.h" +#include "lwip/apps/fs.h" +#include "hardware/flash.h" +#include "hardware/sync.h" +#include "pico/platform.h" #include "string.h" +#include #include #include +#include + +/* ── Sensor data endpoint ─────────────────────────────────────────────────── */ + +static char sensor_response[256]; +static int sensor_response_len = 0; + +void webserver_update_sensor(float temperature, float humidity, float pressure) { + const char *hdr = "HTTP/1.0 200 OK\r\n" + "Content-Type: application/json\r\n" + "\r\n"; + char body[96]; + int blen = snprintf(body, sizeof(body), + "{\"temperature\":%.2f,\"humidity\":%.2f,\"pressure\":%.2f}", + temperature, humidity, pressure); + int hlen = strlen(hdr); + if (hlen + blen < (int)sizeof(sensor_response)) { + memcpy(sensor_response, hdr, hlen); + memcpy(sensor_response + hlen, body, blen); + sensor_response_len = hlen + blen; + } +} + +int fs_open_custom(struct fs_file *file, const char *name) { + if (strcmp(name, "/sensor-data.json") == 0) { + memset(file, 0, sizeof(struct fs_file)); + file->data = sensor_response; + file->len = sensor_response_len; + file->index = sensor_response_len; + file->flags = FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT; + return 1; + } + return 0; +} + +void fs_close_custom(struct fs_file *file) { + (void)file; +} + +/* ── Saved config variables ───────────────────────────────────────────────── */ char saved_ssid[33]; char saved_password[65]; char saved_mqtt_address[65]; char saved_mqtt_user[33]; char saved_mqtt_password[65]; -int saved_measure_frequency; -int saved_post_frequency; -int saved_mqtt_port; +int saved_measure_frequency; +int saved_post_frequency; +int saved_mqtt_port; -static int post_state; +/* ── Config update flags (set by POST handlers, read in main loop) ────────── */ -static char post_buffer[400]; +volatile bool wlan_config_updated = false; +volatile bool mqtt_config_updated = false; + +/* ── Flash persistence ────────────────────────────────────────────────────── */ + +#define FLASH_CONFIG_MAGIC 0xC0DECAFEu +#define FLASH_CONFIG_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE) + +typedef struct { + uint32_t magic; + char ssid[33]; + char password[65]; + char mqtt_address[65]; + char mqtt_user[33]; + char mqtt_password[65]; + int32_t mqtt_port; + int32_t measure_frequency; + int32_t post_frequency; +} config_t; + +_Static_assert(sizeof(config_t) <= FLASH_PAGE_SIZE * 2, + "config_t does not fit in 2 flash pages"); + +void config_save(void) { + static uint8_t buf[FLASH_PAGE_SIZE * 2]; + memset(buf, 0xff, sizeof(buf)); + + config_t *cfg = (config_t *)buf; + cfg->magic = FLASH_CONFIG_MAGIC; + strncpy(cfg->ssid, saved_ssid, sizeof(cfg->ssid) - 1); + strncpy(cfg->password, saved_password, sizeof(cfg->password) - 1); + strncpy(cfg->mqtt_address, saved_mqtt_address, sizeof(cfg->mqtt_address) - 1); + strncpy(cfg->mqtt_user, saved_mqtt_user, sizeof(cfg->mqtt_user) - 1); + strncpy(cfg->mqtt_password, saved_mqtt_password, sizeof(cfg->mqtt_password) - 1); + cfg->mqtt_port = (int32_t)saved_mqtt_port; + cfg->measure_frequency = (int32_t)saved_measure_frequency; + cfg->post_frequency = (int32_t)saved_post_frequency; + + uint32_t ints = save_and_disable_interrupts(); + flash_range_erase(FLASH_CONFIG_OFFSET, FLASH_SECTOR_SIZE); + flash_range_program(FLASH_CONFIG_OFFSET, buf, sizeof(buf)); + restore_interrupts(ints); + + printf("Config saved to flash\n"); +} + +bool config_load(void) { + const config_t *cfg = + (const config_t *)(uintptr_t)(XIP_BASE + FLASH_CONFIG_OFFSET); + + if (cfg->magic != FLASH_CONFIG_MAGIC) { + printf("No valid config in flash\n"); + return false; + } + + strncpy(saved_ssid, cfg->ssid, sizeof(saved_ssid) - 1); + strncpy(saved_password, cfg->password, sizeof(saved_password) - 1); + strncpy(saved_mqtt_address, cfg->mqtt_address, sizeof(saved_mqtt_address) - 1); + strncpy(saved_mqtt_user, cfg->mqtt_user, sizeof(saved_mqtt_user) - 1); + strncpy(saved_mqtt_password, cfg->mqtt_password, sizeof(saved_mqtt_password) - 1); + saved_ssid[sizeof(saved_ssid) - 1] = '\0'; + saved_password[sizeof(saved_password) - 1] = '\0'; + saved_mqtt_address[sizeof(saved_mqtt_address) - 1] = '\0'; + saved_mqtt_user[sizeof(saved_mqtt_user) - 1] = '\0'; + saved_mqtt_password[sizeof(saved_mqtt_password) - 1] = '\0'; + saved_mqtt_port = (int)cfg->mqtt_port; + saved_measure_frequency = (int)cfg->measure_frequency; + saved_post_frequency = (int)cfg->post_frequency; + + printf("Config loaded from flash\n"); + return true; +} + +/* ── POST parsing helper ──────────────────────────────────────────────────── */ + +static int post_state; +static char post_buffer[400]; static uint16_t post_buffer_len = 0; void parse_post(const char *key, char delimiter, char *dest, int max_len) { @@ -27,24 +148,24 @@ void parse_post(const char *key, char delimiter, char *dest, int max_len) { char *pos = start + strlen(key); char *end = (delimiter == '\0') ? (post_buffer + strlen(post_buffer)) : strchr(pos, delimiter); - - if (!end) { + if (!end) end = post_buffer + strlen(post_buffer); - } int len = end - pos; - if (len > max_len) { + if (len > max_len) len = max_len; - } strncpy(dest, pos, len); dest[len] = '\0'; } +/* ── lwip httpd POST callbacks ────────────────────────────────────────────── */ + err_t httpd_post_begin(void *connection, const char *uri, const char *http_request, u16_t http_request_len, int content_len, char *response_uri, u16_t response_uri_len, u8_t *post_auto_wnd) { + (void)connection; (void)http_request; (void)http_request_len; (void)content_len; if (strcmp(uri, "/wlan-config") == 0) { strncpy(response_uri, "/wlan_config.html", response_uri_len); *post_auto_wnd = 1; @@ -52,15 +173,16 @@ err_t httpd_post_begin(void *connection, const char *uri, return ERR_OK; } if (strcmp(uri, "/mqtt-config") == 0) { - post_state = 1; strncpy(response_uri, "/mqtt_config.html", response_uri_len); *post_auto_wnd = 1; + post_state = 1; return ERR_OK; } return ERR_VAL; } err_t httpd_post_receive_data(void *connection, struct pbuf *p) { + (void)connection; post_buffer_len = pbuf_copy_partial(p, post_buffer, sizeof(post_buffer) - 1, 0); post_buffer[post_buffer_len] = '\0'; @@ -70,27 +192,25 @@ err_t httpd_post_receive_data(void *connection, struct pbuf *p) { void httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len) { + (void)connection; if (post_state == 0) { strncpy(response_uri, "/wlan_config.html", response_uri_len); - - parse_post("ssid=", '&', saved_ssid, 32); - parse_post("password=", '\0', saved_password, 64); + parse_post("ssid=", '&', saved_ssid, 32); + parse_post("password=", '\0', saved_password, 64); + wlan_config_updated = true; } else if (post_state == 1) { char temp[16]; strncpy(response_uri, "/mqtt_config.html", response_uri_len); - - parse_post("mqtt-address=", '&', saved_mqtt_address, 64); - parse_post("mqtt-user=", '&', saved_mqtt_user, 32); - parse_post("mqtt-password=", '&', saved_mqtt_password, 64); - - parse_post("mqtt-port=", '&', temp, 15); + parse_post("mqtt-address=", '&', saved_mqtt_address, 64); + parse_post("mqtt-user=", '&', saved_mqtt_user, 32); + parse_post("mqtt-password=", '&', saved_mqtt_password, 64); + parse_post("mqtt-port=", '&', temp, 15); saved_mqtt_port = atoi(temp); - parse_post("measure-frequency=", '&', temp, 15); saved_measure_frequency = atoi(temp); - - parse_post("push-frequency=", '\0', temp, 15); + parse_post("push-frequency=", '\0', temp, 15); saved_post_frequency = atoi(temp); + mqtt_config_updated = true; } } diff --git a/src/webserver.h b/src/webserver.h index cd09fde..cc35156 100644 --- a/src/webserver.h +++ b/src/webserver.h @@ -1,18 +1,32 @@ #ifndef WEBSERVER_H #define WEBSERVER_H +#include + #ifdef __cplusplus extern "C" { #endif +/* Saved configuration (written by POST handlers, read by main) */ extern char saved_ssid[33]; extern char saved_password[65]; extern char saved_mqtt_address[65]; extern char saved_mqtt_user[33]; extern char saved_mqtt_password[65]; -extern int saved_measure_frequency; -extern int saved_post_frequency; -extern int saved_mqtt_port; +extern int saved_measure_frequency; +extern int saved_post_frequency; +extern int saved_mqtt_port; + +/* Set by POST handlers when new config arrives; cleared by main after handling */ +extern volatile bool wlan_config_updated; +extern volatile bool mqtt_config_updated; + +/* Flash persistence */ +void config_save(void); +bool config_load(void); + +/* Sensor data for live dashboard */ +void webserver_update_sensor(float temperature, float humidity, float pressure); #ifdef __cplusplus }