optimize AP mode
Some checks failed
ESP-IDF Build / build (esp32c6, release-v5.4) (push) Failing after 6m44s
ESP-IDF Build / build (esp32c6, release-v5.5) (push) Failing after 3m59s
ESP-IDF Build / build (esp32s3, release-v5.4) (push) Failing after 3m51s
ESP-IDF Build / build (esp32s3, release-v5.5) (push) Failing after 3m52s
Some checks failed
ESP-IDF Build / build (esp32c6, release-v5.4) (push) Failing after 6m44s
ESP-IDF Build / build (esp32c6, release-v5.5) (push) Failing after 3m59s
ESP-IDF Build / build (esp32s3, release-v5.4) (push) Failing after 3m51s
ESP-IDF Build / build (esp32s3, release-v5.5) (push) Failing after 3m52s
- save wifi data - show status led Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
@@ -12,4 +12,5 @@ idf_component_register(SRCS
|
|||||||
esp_event
|
esp_event
|
||||||
json
|
json
|
||||||
simulator
|
simulator
|
||||||
|
persistence-manager
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <esp_http_server.h>
|
#include <esp_http_server.h>
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
#include <esp_wifi.h>
|
#include <esp_wifi.h>
|
||||||
|
#include <persistence_manager.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
@@ -116,6 +117,17 @@ esp_err_t api_wifi_scan_handler(httpd_req_t *req)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void reboot_task(void *param)
|
||||||
|
{
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_valid(const cJSON *string)
|
||||||
|
{
|
||||||
|
return string && cJSON_IsString(string) && string->valuestring && strlen(string->valuestring) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t api_wifi_config_handler(httpd_req_t *req)
|
esp_err_t api_wifi_config_handler(httpd_req_t *req)
|
||||||
{
|
{
|
||||||
ESP_LOGI(TAG, "POST /api/wifi/config");
|
ESP_LOGI(TAG, "POST /api/wifi/config");
|
||||||
@@ -128,12 +140,22 @@ esp_err_t api_wifi_config_handler(httpd_req_t *req)
|
|||||||
}
|
}
|
||||||
buf[ret] = '\0';
|
buf[ret] = '\0';
|
||||||
|
|
||||||
// Passwort maskieren
|
|
||||||
cJSON *json = cJSON_Parse(buf);
|
cJSON *json = cJSON_Parse(buf);
|
||||||
if (json)
|
if (json)
|
||||||
{
|
{
|
||||||
|
cJSON *ssid = cJSON_GetObjectItem(json, "ssid");
|
||||||
cJSON *pw = cJSON_GetObjectItem(json, "password");
|
cJSON *pw = cJSON_GetObjectItem(json, "password");
|
||||||
if (pw && cJSON_IsString(pw) && pw->valuestring)
|
if (is_valid(ssid) && is_valid(pw))
|
||||||
|
{
|
||||||
|
persistence_manager_t pm;
|
||||||
|
if (persistence_manager_init(&pm, "wifi_config") == ESP_OK)
|
||||||
|
{
|
||||||
|
persistence_manager_set_string(&pm, "ssid", ssid->valuestring);
|
||||||
|
persistence_manager_set_string(&pm, "password", pw->valuestring);
|
||||||
|
persistence_manager_deinit(&pm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_valid(pw))
|
||||||
{
|
{
|
||||||
size_t pwlen = strlen(pw->valuestring);
|
size_t pwlen = strlen(pw->valuestring);
|
||||||
char *masked = malloc(pwlen + 1);
|
char *masked = malloc(pwlen + 1);
|
||||||
@@ -163,7 +185,9 @@ esp_err_t api_wifi_config_handler(httpd_req_t *req)
|
|||||||
ESP_LOGI(TAG, "Received WiFi config: %s", buf);
|
ESP_LOGI(TAG, "Received WiFi config: %s", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Parse JSON and connect to WiFi
|
// Define a reboot task function
|
||||||
|
xTaskCreate(reboot_task, "reboot_task", 2048, NULL, 5, NULL);
|
||||||
|
|
||||||
set_cors_headers(req);
|
set_cors_headers(req);
|
||||||
httpd_resp_set_status(req, "200 OK");
|
httpd_resp_set_status(req, "200 OK");
|
||||||
return httpd_resp_sendstr(req, "{\"status\":\"ok\"}");
|
return httpd_resp_sendstr(req, "{\"status\":\"ok\"}");
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ static void ws_clients_init(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add a client to the list
|
// Add a client to the list
|
||||||
static void add_client(int fd)
|
static bool add_client(int fd)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < WS_MAX_CLIENTS; i++)
|
for (int i = 0; i < WS_MAX_CLIENTS; i++)
|
||||||
{
|
{
|
||||||
@@ -27,10 +27,11 @@ static void add_client(int fd)
|
|||||||
ws_clients[i] = fd;
|
ws_clients[i] = fd;
|
||||||
ws_client_count++;
|
ws_client_count++;
|
||||||
ESP_LOGI(TAG, "WebSocket client connected: fd=%d (total: %d)", fd, ws_client_count);
|
ESP_LOGI(TAG, "WebSocket client connected: fd=%d (total: %d)", fd, ws_client_count);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ESP_LOGW(TAG, "Max WebSocket clients reached, cannot add fd=%d", fd);
|
ESP_LOGW(TAG, "Max WebSocket clients reached, cannot add fd=%d", fd);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove a client from the list
|
// Remove a client from the list
|
||||||
@@ -83,6 +84,13 @@ esp_err_t websocket_handler(httpd_req_t *req)
|
|||||||
{
|
{
|
||||||
// This is the handshake
|
// This is the handshake
|
||||||
ESP_LOGI(TAG, "WebSocket handshake");
|
ESP_LOGI(TAG, "WebSocket handshake");
|
||||||
|
int fd = httpd_req_to_sockfd(req);
|
||||||
|
if (!add_client(fd))
|
||||||
|
{
|
||||||
|
// Zu viele Clients, Verbindung schließen
|
||||||
|
httpd_sess_trigger_close(req->handle, fd);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ static void dns_server_task(void *pvParameters)
|
|||||||
// Fragen: 1, Antworten: 1
|
// Fragen: 1, Antworten: 1
|
||||||
buf[7] = 1;
|
buf[7] = 1;
|
||||||
// Antwort anhängen (Name Pointer auf Frage)
|
// Antwort anhängen (Name Pointer auf Frage)
|
||||||
int qlen = len - 12;
|
|
||||||
int pos = len;
|
int pos = len;
|
||||||
buf[pos++] = 0xC0;
|
buf[pos++] = 0xC0;
|
||||||
buf[pos++] = 0x0C; // Name pointer
|
buf[pos++] = 0x0C; // Name pointer
|
||||||
|
|||||||
@@ -16,204 +16,120 @@
|
|||||||
#include <lwip/sys.h>
|
#include <lwip/sys.h>
|
||||||
#include <mdns.h>
|
#include <mdns.h>
|
||||||
#include <nvs_flash.h>
|
#include <nvs_flash.h>
|
||||||
|
#include <persistence_manager.h>
|
||||||
#include <sdkconfig.h>
|
#include <sdkconfig.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// Event group to signal when we are connected
|
// Event group to signal WiFi connection status
|
||||||
static EventGroupHandle_t s_wifi_event_group;
|
static EventGroupHandle_t s_wifi_event_group;
|
||||||
|
|
||||||
// The bits for the event group
|
// Event group bits
|
||||||
#define WIFI_CONNECTED_BIT BIT0
|
#define WIFI_CONNECTED_BIT BIT0
|
||||||
#define WIFI_FAIL_BIT BIT1
|
#define WIFI_FAIL_BIT BIT1
|
||||||
|
|
||||||
static const char *TAG = "wifi_manager";
|
static const char *TAG = "wifi_manager";
|
||||||
|
|
||||||
static int s_retry_num = 0;
|
static void wifi_create_ap()
|
||||||
static int s_current_network_index = 0;
|
|
||||||
|
|
||||||
// WiFi network configuration structure
|
|
||||||
typedef struct
|
|
||||||
{
|
{
|
||||||
const char *ssid;
|
ESP_ERROR_CHECK(esp_wifi_stop());
|
||||||
const char *password;
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
|
||||||
} wifi_network_config_t;
|
|
||||||
|
|
||||||
// Array of configured WiFi networks
|
|
||||||
static const wifi_network_config_t s_wifi_networks[] = {
|
|
||||||
#if CONFIG_WIFI_ENABLED
|
|
||||||
{CONFIG_WIFI_SSID_1, CONFIG_WIFI_PASSWORD_1},
|
|
||||||
#if CONFIG_WIFI_NETWORK_COUNT >= 2
|
|
||||||
{CONFIG_WIFI_SSID_2, CONFIG_WIFI_PASSWORD_2},
|
|
||||||
#endif
|
|
||||||
#if CONFIG_WIFI_NETWORK_COUNT >= 3
|
|
||||||
{CONFIG_WIFI_SSID_3, CONFIG_WIFI_PASSWORD_3},
|
|
||||||
#endif
|
|
||||||
#if CONFIG_WIFI_NETWORK_COUNT >= 4
|
|
||||||
{CONFIG_WIFI_SSID_4, CONFIG_WIFI_PASSWORD_4},
|
|
||||||
#endif
|
|
||||||
#if CONFIG_WIFI_NETWORK_COUNT >= 5
|
|
||||||
{CONFIG_WIFI_SSID_5, CONFIG_WIFI_PASSWORD_5},
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int s_wifi_network_count = sizeof(s_wifi_networks) / sizeof(s_wifi_networks[0]);
|
|
||||||
|
|
||||||
static void try_next_network(void);
|
|
||||||
|
|
||||||
static void connect_to_network(int index)
|
|
||||||
{
|
|
||||||
#if CONFIG_WIFI_ENABLED
|
|
||||||
if (index >= s_wifi_network_count)
|
|
||||||
{
|
|
||||||
ESP_LOGE(TAG, "No more networks to try");
|
|
||||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const wifi_network_config_t *network = &s_wifi_networks[index];
|
|
||||||
|
|
||||||
// Skip empty SSIDs
|
|
||||||
if (network->ssid == NULL || strlen(network->ssid) == 0)
|
|
||||||
{
|
|
||||||
ESP_LOGW(TAG, "Skipping empty SSID at index %d", index);
|
|
||||||
s_current_network_index++;
|
|
||||||
s_retry_num = 0;
|
|
||||||
try_next_network();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_DIAG_EVENT(TAG, "Trying to connect to network %d: %s", index + 1, network->ssid);
|
|
||||||
|
|
||||||
wifi_config_t wifi_config = {
|
|
||||||
.sta =
|
|
||||||
{
|
|
||||||
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
strncpy((char *)wifi_config.sta.ssid, network->ssid, sizeof(wifi_config.sta.ssid) - 1);
|
|
||||||
strncpy((char *)wifi_config.sta.password, network->password, sizeof(wifi_config.sta.password) - 1);
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
|
||||||
esp_wifi_connect();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void try_next_network(void)
|
|
||||||
{
|
|
||||||
#if CONFIG_WIFI_ENABLED
|
|
||||||
s_current_network_index++;
|
|
||||||
s_retry_num = 0;
|
|
||||||
|
|
||||||
if (s_current_network_index < s_wifi_network_count)
|
|
||||||
{
|
|
||||||
connect_to_network(s_current_network_index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ESP_LOGE(TAG, "Failed to connect to any configured network");
|
|
||||||
led_behavior_t led0_behavior = {
|
|
||||||
.index = 0,
|
|
||||||
.mode = LED_MODE_BLINK,
|
|
||||||
.color = {.red = 50, .green = 0, .blue = 0},
|
|
||||||
.on_time_ms = 1000,
|
|
||||||
.off_time_ms = 500,
|
|
||||||
};
|
|
||||||
led_status_set_behavior(led0_behavior);
|
|
||||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
|
||||||
{
|
|
||||||
#if CONFIG_WIFI_ENABLED
|
|
||||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
|
|
||||||
{
|
|
||||||
led_behavior_t led0_behavior = {
|
|
||||||
.index = 0,
|
|
||||||
.mode = LED_MODE_BLINK,
|
|
||||||
.color = {.red = 50, .green = 50, .blue = 0},
|
|
||||||
.on_time_ms = 200,
|
|
||||||
.off_time_ms = 200,
|
|
||||||
};
|
|
||||||
led_status_set_behavior(led0_behavior);
|
|
||||||
|
|
||||||
connect_to_network(s_current_network_index);
|
|
||||||
}
|
|
||||||
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
|
|
||||||
{
|
|
||||||
if (s_retry_num < CONFIG_WIFI_CONNECT_RETRIES)
|
|
||||||
{
|
|
||||||
led_behavior_t led0_behavior = {
|
|
||||||
.index = 0,
|
|
||||||
.mode = LED_MODE_BLINK,
|
|
||||||
.color = {.red = 50, .green = 50, .blue = 0},
|
|
||||||
.on_time_ms = 200,
|
|
||||||
.off_time_ms = 200,
|
|
||||||
};
|
|
||||||
led_status_set_behavior(led0_behavior);
|
|
||||||
|
|
||||||
s_retry_num++;
|
|
||||||
ESP_DIAG_EVENT(TAG, "Retrying network %d (%d/%d)", s_current_network_index + 1, s_retry_num,
|
|
||||||
CONFIG_WIFI_CONNECT_RETRIES);
|
|
||||||
esp_wifi_connect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retries exhausted for current network, try next one
|
|
||||||
ESP_LOGW(TAG, "Failed to connect to network %d after %d retries, trying next...", s_current_network_index + 1,
|
|
||||||
CONFIG_WIFI_CONNECT_RETRIES);
|
|
||||||
try_next_network();
|
|
||||||
}
|
|
||||||
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
|
|
||||||
{
|
|
||||||
led_behavior_t led0_behavior = {
|
|
||||||
.index = 0,
|
|
||||||
.mode = LED_MODE_SOLID,
|
|
||||||
.color = {.red = 0, .green = 50, .blue = 0},
|
|
||||||
};
|
|
||||||
led_status_set_behavior(led0_behavior);
|
|
||||||
|
|
||||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
|
||||||
ESP_DIAG_EVENT(TAG, "Got IP address:" IPSTR " (network %d: %s)", IP2STR(&event->ip_info.ip),
|
|
||||||
s_current_network_index + 1, s_wifi_networks[s_current_network_index].ssid);
|
|
||||||
s_retry_num = 0;
|
|
||||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void wifi_manager_init()
|
|
||||||
{
|
|
||||||
s_wifi_event_group = xEventGroupCreate();
|
|
||||||
s_retry_num = 0;
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(esp_netif_init());
|
|
||||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
|
||||||
|
|
||||||
// Access Point erstellen
|
|
||||||
esp_netif_create_default_wifi_ap();
|
|
||||||
|
|
||||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
|
||||||
|
|
||||||
wifi_config_t ap_config = {.ap = {.ssid = "system-control",
|
wifi_config_t ap_config = {.ap = {.ssid = "system-control",
|
||||||
.ssid_len = strlen("system-control"),
|
.ssid_len = strlen("system-control"),
|
||||||
.password = "",
|
.password = "",
|
||||||
.max_connection = 4,
|
.max_connection = 4,
|
||||||
.authmode = WIFI_AUTH_OPEN}};
|
.authmode = WIFI_AUTH_OPEN}};
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config));
|
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config));
|
||||||
ESP_ERROR_CHECK(esp_wifi_start());
|
ESP_ERROR_CHECK(esp_wifi_start());
|
||||||
|
ESP_LOGI(TAG, "Access Point 'system-control' started");
|
||||||
|
dns_server_start("192.168.4.1");
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Access Point 'system-control' gestartet");
|
led_behavior_t led_behavior = {
|
||||||
|
.color = {.red = 50, .green = 0, .blue = 0},
|
||||||
|
.index = 0,
|
||||||
|
.mode = LED_MODE_SOLID,
|
||||||
|
};
|
||||||
|
led_status_set_behavior(led_behavior);
|
||||||
|
}
|
||||||
|
|
||||||
// DNS Hijack Server starten (alle DNS-Anfragen auf AP-IP umleiten)
|
void wifi_manager_init()
|
||||||
dns_server_start("192.168.4.1"); // ggf. dynamisch ermitteln
|
{
|
||||||
|
s_wifi_event_group = xEventGroupCreate();
|
||||||
|
|
||||||
// API-Server starten
|
ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
|
||||||
|
// Try to load stored WiFi configuration
|
||||||
|
persistence_manager_t pm;
|
||||||
|
char ssid[33] = {0};
|
||||||
|
char password[65] = {0};
|
||||||
|
bool have_ssid = false, have_password = false;
|
||||||
|
if (persistence_manager_init(&pm, "wifi_config") == ESP_OK)
|
||||||
|
{
|
||||||
|
persistence_manager_get_string(&pm, "ssid", ssid, sizeof(ssid), "");
|
||||||
|
persistence_manager_get_string(&pm, "password", password, sizeof(password), "");
|
||||||
|
have_ssid = strlen(ssid) > 0;
|
||||||
|
have_password = strlen(password) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_ssid && have_password)
|
||||||
|
{
|
||||||
|
led_behavior_t led_behavior = {
|
||||||
|
.on_time_ms = 250,
|
||||||
|
.off_time_ms = 100,
|
||||||
|
.color = {.red = 50, .green = 50, .blue = 0},
|
||||||
|
.index = 0,
|
||||||
|
.mode = LED_MODE_BLINK,
|
||||||
|
};
|
||||||
|
led_status_set_behavior(led_behavior);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Found WiFi configuration: SSID='%s'", ssid);
|
||||||
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||||
|
wifi_config_t wifi_config = {0};
|
||||||
|
strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid) - 1);
|
||||||
|
strncpy((char *)wifi_config.sta.password, password, sizeof(wifi_config.sta.password) - 1);
|
||||||
|
wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_start());
|
||||||
|
|
||||||
|
int retries = 0;
|
||||||
|
EventBits_t bits;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
esp_wifi_connect();
|
||||||
|
bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE,
|
||||||
|
5000 / portTICK_PERIOD_MS);
|
||||||
|
if (bits & WIFI_CONNECTED_BIT)
|
||||||
|
{
|
||||||
|
led_behavior_t led_behavior = {
|
||||||
|
.index = 0,
|
||||||
|
.color = {.red = 0, .green = 50, .blue = 0},
|
||||||
|
.mode = LED_MODE_SOLID,
|
||||||
|
};
|
||||||
|
led_status_set_behavior(led_behavior);
|
||||||
|
ESP_LOGI(TAG, "WiFi connection established successfully");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
retries++;
|
||||||
|
} while (!(bits & WIFI_CONNECTED_BIT) && retries < CONFIG_WIFI_CONNECT_RETRIES);
|
||||||
|
|
||||||
|
if (!(bits & WIFI_CONNECTED_BIT))
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "WiFi connection failed, switching to Access Point mode");
|
||||||
|
wifi_create_ap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Create Access Point
|
||||||
|
esp_netif_create_default_wifi_ap();
|
||||||
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||||
|
wifi_create_ap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// API server start
|
||||||
api_server_config_t s_config = API_SERVER_CONFIG_DEFAULT();
|
api_server_config_t s_config = API_SERVER_CONFIG_DEFAULT();
|
||||||
ESP_ERROR_CHECK(api_server_start(&s_config));
|
ESP_ERROR_CHECK(api_server_start(&s_config));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint8_t h;
|
float h;
|
||||||
uint8_t s;
|
float s;
|
||||||
uint8_t v;
|
float v;
|
||||||
} hsv_t;
|
} hsv_t;
|
||||||
|
|
||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ rgb_t interpolate_color_hsv(rgb_t start, rgb_t end, float factor)
|
|||||||
|
|
||||||
// Interpolate HSV values
|
// Interpolate HSV values
|
||||||
hsv_t interpolated_hsv;
|
hsv_t interpolated_hsv;
|
||||||
interpolated_hsv.h = fmod(h1 + (h2 - h1) * factor, 360.0);
|
interpolated_hsv.h = fmodf(h1 + (h2 - h1) * factor, 360.0f);
|
||||||
if (interpolated_hsv.h < 0)
|
if (interpolated_hsv.h < 0.0f)
|
||||||
{
|
{
|
||||||
interpolated_hsv.h += 360.0;
|
interpolated_hsv.h += 360.0f;
|
||||||
}
|
}
|
||||||
interpolated_hsv.s = start_hsv.s + (end_hsv.s - start_hsv.s) * factor;
|
interpolated_hsv.s = start_hsv.s + (end_hsv.s - start_hsv.s) * factor;
|
||||||
interpolated_hsv.v = start_hsv.v + (end_hsv.v - start_hsv.v) * factor;
|
interpolated_hsv.v = start_hsv.v + (end_hsv.v - start_hsv.v) * factor;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <esp_err.h>
|
||||||
#include <nvs.h>
|
#include <nvs.h>
|
||||||
#include <nvs_flash.h>
|
#include <nvs_flash.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@@ -33,14 +34,14 @@ extern "C"
|
|||||||
* @param pm Pointer to the persistence manager structure.
|
* @param pm Pointer to the persistence manager structure.
|
||||||
* @param nvs_namespace Namespace to use for NVS operations.
|
* @param nvs_namespace Namespace to use for NVS operations.
|
||||||
*/
|
*/
|
||||||
void persistence_manager_init(persistence_manager_t *pm, const char *nvs_namespace);
|
esp_err_t persistence_manager_init(persistence_manager_t *pm, const char *nvs_namespace);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Deinitialize the persistence manager and release resources.
|
* @brief Deinitialize the persistence manager and release resources.
|
||||||
*
|
*
|
||||||
* @param pm Pointer to the persistence manager structure.
|
* @param pm Pointer to the persistence manager structure.
|
||||||
*/
|
*/
|
||||||
void persistence_manager_deinit(persistence_manager_t *pm);
|
esp_err_t persistence_manager_deinit(persistence_manager_t *pm);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check if the persistence manager is initialized.
|
* @brief Check if the persistence manager is initialized.
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
#define TAG "persistence_manager"
|
#define TAG "persistence_manager"
|
||||||
|
|
||||||
void persistence_manager_init(persistence_manager_t *pm, const char *nvs_namespace)
|
esp_err_t persistence_manager_init(persistence_manager_t *pm, const char *nvs_namespace)
|
||||||
{
|
{
|
||||||
if (!pm)
|
if (!pm)
|
||||||
return;
|
return ESP_ERR_INVALID_ARG;
|
||||||
strncpy(pm->nvs_namespace, nvs_namespace ? nvs_namespace : "config", sizeof(pm->nvs_namespace) - 1);
|
strncpy(pm->nvs_namespace, nvs_namespace ? nvs_namespace : "config", sizeof(pm->nvs_namespace) - 1);
|
||||||
pm->nvs_namespace[sizeof(pm->nvs_namespace) - 1] = '\0';
|
pm->nvs_namespace[sizeof(pm->nvs_namespace) - 1] = '\0';
|
||||||
pm->initialized = false;
|
pm->initialized = false;
|
||||||
@@ -16,20 +16,20 @@ void persistence_manager_init(persistence_manager_t *pm, const char *nvs_namespa
|
|||||||
{
|
{
|
||||||
pm->initialized = true;
|
pm->initialized = true;
|
||||||
ESP_LOGI(TAG, "Initialized with namespace: %s", pm->nvs_namespace);
|
ESP_LOGI(TAG, "Initialized with namespace: %s", pm->nvs_namespace);
|
||||||
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
else
|
ESP_LOGE(TAG, "Failed to open NVS handle: %s", esp_err_to_name(err));
|
||||||
{
|
return err;
|
||||||
ESP_LOGE(TAG, "Failed to open NVS handle: %s", esp_err_to_name(err));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void persistence_manager_deinit(persistence_manager_t *pm)
|
esp_err_t persistence_manager_deinit(persistence_manager_t *pm)
|
||||||
{
|
{
|
||||||
if (pm && pm->initialized)
|
if (pm && pm->initialized)
|
||||||
{
|
{
|
||||||
nvs_close(pm->nvs_handle);
|
nvs_close(pm->nvs_handle);
|
||||||
pm->initialized = false;
|
pm->initialized = false;
|
||||||
}
|
}
|
||||||
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool persistence_manager_is_initialized(const persistence_manager_t *pm)
|
bool persistence_manager_is_initialized(const persistence_manager_t *pm)
|
||||||
|
|||||||
@@ -186,10 +186,7 @@ void app_task(void *args)
|
|||||||
setup_buttons();
|
setup_buttons();
|
||||||
init_ui();
|
init_ui();
|
||||||
|
|
||||||
#if CONFIG_WIFI_ENABLED
|
|
||||||
wifi_manager_init();
|
wifi_manager_init();
|
||||||
analytics_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
start_simulation();
|
start_simulation();
|
||||||
|
|
||||||
|
|||||||
@@ -27,25 +27,9 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h1>🚂 System Control</h1>
|
<h1>🚂 System Control</h1>
|
||||||
<p data-i18n="captive.subtitle">WLAN-Einrichtung</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div id="scan-section">
|
|
||||||
<button class="btn btn-secondary" onclick="scanNetworks()" data-i18n="captive.scan">
|
|
||||||
📡 Netzwerke suchen
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div id="loading" class="loading">
|
|
||||||
<div class="spinner"></div>
|
|
||||||
<p data-i18n="captive.scanning">Suche nach Netzwerken...</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="network-list" class="network-list" style="display: none;"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="divider"><span data-i18n="captive.or.manual">oder manuell eingeben</span></div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="ssid" data-i18n="wifi.ssid">WLAN-Name (SSID)</label>
|
<label for="ssid" data-i18n="wifi.ssid">WLAN-Name (SSID)</label>
|
||||||
<input type="text" id="ssid" data-i18n-placeholder="wifi.ssid.placeholder"
|
<input type="text" id="ssid" data-i18n-placeholder="wifi.ssid.placeholder"
|
||||||
@@ -62,7 +46,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-primary" onclick="saveWifi()" data-i18n="captive.connect">
|
<button class="btn btn-primary" id="connect-btn" onclick="saveWifi()" data-i18n="captive.connect"
|
||||||
|
disabled>
|
||||||
💾 Verbinden
|
💾 Verbinden
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,12 +97,21 @@
|
|||||||
setTheme(current === 'dark' ? 'light' : 'dark');
|
setTheme(current === 'dark' ? 'light' : 'dark');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Button aktivieren/deaktivieren
|
||||||
|
function updateConnectBtn() {
|
||||||
|
const ssid = document.getElementById('ssid').value;
|
||||||
|
const pw = document.getElementById('password').value;
|
||||||
|
const btn = document.getElementById('connect-btn');
|
||||||
|
btn.disabled = !(ssid.length > 0 && pw.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize on load
|
// Initialize on load
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
initTheme();
|
initTheme();
|
||||||
initI18n();
|
initI18n();
|
||||||
// Auto-scan on load
|
document.getElementById('ssid').addEventListener('input', updateConnectBtn);
|
||||||
setTimeout(scanNetworks, 500);
|
document.getElementById('password').addEventListener('input', updateConnectBtn);
|
||||||
|
updateConnectBtn();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Toggle password visibility
|
// Toggle password visibility
|
||||||
|
|||||||
@@ -39,6 +39,15 @@ body {
|
|||||||
box-shadow: 0 4px 20px var(--shadow);
|
box-shadow: 0 4px 20px var(--shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#connect-btn:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: #888 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Form Group - More spacing */
|
/* Form Group - More spacing */
|
||||||
.form-group {
|
.form-group {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1>🚂 System Control</h1>
|
|
||||||
<div class="header-controls">
|
<div class="header-controls">
|
||||||
<button class="lang-toggle" onclick="toggleLanguage()" aria-label="Sprache wechseln">
|
<button class="lang-toggle" onclick="toggleLanguage()" aria-label="Sprache wechseln">
|
||||||
<span class="lang-flag" id="lang-flag">🇩🇪</span>
|
<span class="lang-flag" id="lang-flag">🇩🇪</span>
|
||||||
@@ -27,6 +26,7 @@
|
|||||||
<span class="theme-toggle-label" id="theme-label">Dark</span>
|
<span class="theme-toggle-label" id="theme-label">Dark</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<h1>🚂 System Control</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
|
|||||||
@@ -43,6 +43,13 @@ async function scanNetworks() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/wifi/scan');
|
const response = await fetch('/api/wifi/scan');
|
||||||
|
if (!response.ok) {
|
||||||
|
// Fehlerhafte API-Antwort, aber ESP32 ist erreichbar
|
||||||
|
const errorText = await response.text();
|
||||||
|
showStatus('wifi-status', t('wifi.error.scan') + ': ' + errorText, 'error');
|
||||||
|
if (loading) loading.classList.remove('active');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const networks = await response.json();
|
const networks = await response.json();
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@@ -92,43 +99,43 @@ async function scanNetworks() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
loading.classList.remove('active');
|
loading.classList.remove('active');
|
||||||
}
|
}
|
||||||
|
// Nur bei Netzwerkfehlern Demo-Daten anzeigen
|
||||||
// Demo mode for local testing
|
if (error instanceof TypeError) {
|
||||||
const demoNetworks = [
|
const demoNetworks = [
|
||||||
{ ssid: 'Demo-Netzwerk', rssi: -45 },
|
{ ssid: 'Demo-Netzwerk', rssi: -45 },
|
||||||
{ ssid: 'Gast-WLAN', rssi: -67 },
|
{ ssid: 'Gast-WLAN', rssi: -67 },
|
||||||
{ ssid: 'Nachbar-WiFi', rssi: -82 }
|
{ ssid: 'Nachbar-WiFi', rssi: -82 }
|
||||||
];
|
];
|
||||||
|
if (networkList) {
|
||||||
if (networkList) {
|
demoNetworks.forEach(network => {
|
||||||
demoNetworks.forEach(network => {
|
const signalIcon = getSignalIcon(network.rssi);
|
||||||
const signalIcon = getSignalIcon(network.rssi);
|
const item = document.createElement('div');
|
||||||
const item = document.createElement('div');
|
item.className = 'network-item';
|
||||||
item.className = 'network-item';
|
item.onclick = () => selectNetwork(network.ssid, item);
|
||||||
item.onclick = () => selectNetwork(network.ssid, item);
|
item.innerHTML = `
|
||||||
item.innerHTML = `
|
<span class="network-name">
|
||||||
<span class="network-name">
|
<span class="signal-icon">${signalIcon}</span>
|
||||||
<span class="signal-icon">${signalIcon}</span>
|
${escapeHtml(network.ssid)}
|
||||||
${escapeHtml(network.ssid)}
|
</span>
|
||||||
</span>
|
<span class="network-signal">${network.rssi} dBm</span>
|
||||||
<span class="network-signal">${network.rssi} dBm</span>
|
`;
|
||||||
`;
|
networkList.appendChild(item);
|
||||||
networkList.appendChild(item);
|
});
|
||||||
});
|
networkList.style.display = 'block';
|
||||||
networkList.style.display = 'block';
|
}
|
||||||
|
if (select) {
|
||||||
|
select.innerHTML = `<option value="">${t('wifi.scan.hint')}</option>`;
|
||||||
|
demoNetworks.forEach(network => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = network.ssid;
|
||||||
|
option.textContent = `${network.ssid} (${network.rssi} dBm)`;
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
showStatus('wifi-status', 'Demo: ' + t('wifi.networks.found', { count: demoNetworks.length }), 'info');
|
||||||
|
} else {
|
||||||
|
showStatus('wifi-status', t('wifi.error.scan') + ': ' + error.message, 'error');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (select) {
|
|
||||||
select.innerHTML = `<option value="">${t('wifi.scan.hint')}</option>`;
|
|
||||||
demoNetworks.forEach(network => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = network.ssid;
|
|
||||||
option.textContent = `${network.ssid} (${network.rssi} dBm)`;
|
|
||||||
select.appendChild(option);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
showStatus('wifi-status', 'Demo: ' + t('wifi.networks.found', { count: demoNetworks.length }), 'info');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user