latest website and FW changes
Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
@@ -86,38 +86,47 @@ esp_err_t api_wifi_scan_handler(httpd_req_t *req)
|
||||
{
|
||||
ESP_LOGI(TAG, "GET /api/wifi/scan");
|
||||
|
||||
// Start WiFi scan non-blocking (async) to avoid blocking HTTP server
|
||||
// The scan will complete in background, results available on next request
|
||||
wifi_scan_config_t scan_config = {.ssid = NULL, .bssid = NULL, .channel = 0, .show_hidden = true};
|
||||
esp_err_t err = esp_wifi_scan_start(&scan_config, true);
|
||||
esp_err_t err = esp_wifi_scan_start(&scan_config, false);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
return send_error_response(req, 500, "WiFi scan failed");
|
||||
ESP_LOGD(TAG, "WiFi scan start: %s (may already be scanning)", esp_err_to_name(err));
|
||||
// Continue and return cached results - don't block on error
|
||||
}
|
||||
|
||||
// Get cached scan results (from previous scan if available)
|
||||
uint16_t ap_num = 0;
|
||||
esp_wifi_scan_get_ap_num(&ap_num);
|
||||
wifi_ap_record_t *ap_list = heap_caps_calloc(ap_num, sizeof(wifi_ap_record_t), MALLOC_CAP_DEFAULT);
|
||||
if (!ap_list)
|
||||
{
|
||||
return send_error_response(req, 500, "Memory allocation failed");
|
||||
}
|
||||
esp_wifi_scan_get_ap_records(&ap_num, ap_list);
|
||||
|
||||
cJSON *json = cJSON_CreateArray();
|
||||
for (int i = 0; i < ap_num; i++)
|
||||
|
||||
if (ap_num > 0)
|
||||
{
|
||||
if (ap_list[i].ssid[0] != '\0')
|
||||
wifi_ap_record_t *ap_list = heap_caps_calloc(ap_num, sizeof(wifi_ap_record_t), MALLOC_CAP_DEFAULT);
|
||||
if (ap_list)
|
||||
{
|
||||
cJSON *entry = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(entry, "ssid", (const char *)ap_list[i].ssid);
|
||||
cJSON_AddNumberToObject(entry, "rssi", ap_list[i].rssi);
|
||||
bool secure = ap_list[i].authmode != WIFI_AUTH_OPEN;
|
||||
cJSON_AddBoolToObject(entry, "secure", secure);
|
||||
cJSON_AddItemToArray(json, entry);
|
||||
esp_wifi_scan_get_ap_records(&ap_num, ap_list);
|
||||
|
||||
for (int i = 0; i < ap_num; i++)
|
||||
{
|
||||
if (ap_list[i].ssid[0] != '\0')
|
||||
{
|
||||
cJSON *entry = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(entry, "ssid", (const char *)ap_list[i].ssid);
|
||||
cJSON_AddNumberToObject(entry, "rssi", ap_list[i].rssi);
|
||||
bool secure = ap_list[i].authmode != WIFI_AUTH_OPEN;
|
||||
cJSON_AddBoolToObject(entry, "secure", secure);
|
||||
cJSON_AddItemToArray(json, entry);
|
||||
}
|
||||
}
|
||||
free(ap_list);
|
||||
}
|
||||
}
|
||||
|
||||
char *response = cJSON_PrintUnformatted(json);
|
||||
cJSON_Delete(json);
|
||||
free(ap_list);
|
||||
esp_err_t res = send_json_response(req, response);
|
||||
free(response);
|
||||
return res;
|
||||
@@ -898,6 +907,7 @@ static const char *get_mime_type(const char *path)
|
||||
esp_err_t api_static_file_handler(httpd_req_t *req)
|
||||
{
|
||||
char filepath[CONFIG_HTTPD_MAX_URI_LEN + 16];
|
||||
char gz_filepath[CONFIG_HTTPD_MAX_URI_LEN + 20];
|
||||
|
||||
const char *uri = req->uri;
|
||||
wifi_mode_t mode = 0;
|
||||
@@ -927,26 +937,44 @@ esp_err_t api_static_file_handler(httpd_req_t *req)
|
||||
return send_error_response(req, 400, "URI too long");
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Serving static file: %s", filepath);
|
||||
bool use_gzip = false;
|
||||
const char *served_path = filepath;
|
||||
|
||||
written = snprintf(gz_filepath, sizeof(gz_filepath), "%s.gz", filepath);
|
||||
if (written >= 0 && (size_t)written < sizeof(gz_filepath))
|
||||
{
|
||||
struct stat gz_st;
|
||||
if (stat(gz_filepath, &gz_st) == 0)
|
||||
{
|
||||
use_gzip = true;
|
||||
served_path = gz_filepath;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Serving static file: %s%s", served_path, use_gzip ? " (gzip)" : "");
|
||||
|
||||
// Check if file exists
|
||||
struct stat st;
|
||||
if (stat(filepath, &st) != 0)
|
||||
if (stat(served_path, &st) != 0)
|
||||
{
|
||||
ESP_LOGW(TAG, "File not found: %s", filepath);
|
||||
ESP_LOGW(TAG, "File not found: %s", served_path);
|
||||
return send_error_response(req, 404, "File not found");
|
||||
}
|
||||
|
||||
// Open and serve file
|
||||
FILE *f = fopen(filepath, "r");
|
||||
FILE *f = fopen(served_path, "rb");
|
||||
if (f == NULL)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to open file: %s", filepath);
|
||||
ESP_LOGE(TAG, "Failed to open file: %s", served_path);
|
||||
return send_error_response(req, 500, "Failed to open file");
|
||||
}
|
||||
|
||||
set_cors_headers(req);
|
||||
httpd_resp_set_type(req, get_mime_type(filepath));
|
||||
if (use_gzip)
|
||||
{
|
||||
httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
|
||||
}
|
||||
|
||||
char buf[512];
|
||||
size_t read_bytes;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <sdkconfig.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MESSAGE_QUEUE_LENGTH 16
|
||||
#define MESSAGE_QUEUE_LENGTH 32
|
||||
#define MESSAGE_QUEUE_ITEM_SIZE sizeof(message_t)
|
||||
|
||||
static const char *TAG = "message_manager";
|
||||
|
||||
@@ -3,6 +3,54 @@
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "persistence_manager"
|
||||
#define PM_MAX_CACHED_HANDLES 8
|
||||
|
||||
// NVS handle cache to avoid repeated nvs_open/close operations (performance optimization)
|
||||
typedef struct
|
||||
{
|
||||
char namespace[16];
|
||||
nvs_handle_t handle;
|
||||
bool in_use;
|
||||
} nvs_handle_cache_entry_t;
|
||||
|
||||
static nvs_handle_cache_entry_t nvs_cache[PM_MAX_CACHED_HANDLES] = {0};
|
||||
|
||||
// Get or create a cached NVS handle for the given namespace
|
||||
static esp_err_t _get_cached_nvs_handle(const char *nvs_namespace, nvs_handle_t *out_handle)
|
||||
{
|
||||
// Search for existing handle in cache
|
||||
for (int i = 0; i < PM_MAX_CACHED_HANDLES; i++)
|
||||
{
|
||||
if (nvs_cache[i].in_use && strcmp(nvs_cache[i].namespace, nvs_namespace) == 0)
|
||||
{
|
||||
*out_handle = nvs_cache[i].handle;
|
||||
ESP_LOGD(TAG, "Using cached NVS handle for namespace: %s", nvs_namespace);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found, try to create a new one
|
||||
for (int i = 0; i < PM_MAX_CACHED_HANDLES; i++)
|
||||
{
|
||||
if (!nvs_cache[i].in_use)
|
||||
{
|
||||
esp_err_t err = nvs_open(nvs_namespace, NVS_READWRITE, &nvs_cache[i].handle);
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
nvs_cache[i].in_use = true;
|
||||
strncpy(nvs_cache[i].namespace, nvs_namespace, sizeof(nvs_cache[i].namespace) - 1);
|
||||
nvs_cache[i].namespace[sizeof(nvs_cache[i].namespace) - 1] = '\0';
|
||||
*out_handle = nvs_cache[i].handle;
|
||||
ESP_LOGD(TAG, "Opened and cached NVS handle for namespace: %s", nvs_namespace);
|
||||
return ESP_OK;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "NVS handle cache full (max %d handles)", PM_MAX_CACHED_HANDLES);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_err_t persistence_manager_factory_reset(void)
|
||||
{
|
||||
@@ -19,26 +67,32 @@ esp_err_t persistence_manager_init(persistence_manager_t *pm, const char *nvs_na
|
||||
{
|
||||
if (!pm)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
|
||||
strncpy(pm->nvs_namespace, nvs_namespace ? nvs_namespace : "config", sizeof(pm->nvs_namespace) - 1);
|
||||
pm->nvs_namespace[sizeof(pm->nvs_namespace) - 1] = '\0';
|
||||
pm->initialized = false;
|
||||
esp_err_t err = nvs_open(pm->nvs_namespace, NVS_READWRITE, &pm->nvs_handle);
|
||||
|
||||
// Get cached NVS handle instead of opening a new one each time
|
||||
esp_err_t err = _get_cached_nvs_handle(pm->nvs_namespace, &pm->nvs_handle);
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
pm->initialized = true;
|
||||
ESP_LOGD(TAG, "Initialized with namespace: %s", pm->nvs_namespace);
|
||||
ESP_LOGD(TAG, "Initialized with namespace: %s (cached)", pm->nvs_namespace);
|
||||
return ESP_OK;
|
||||
}
|
||||
ESP_LOGE(TAG, "Failed to open NVS handle: %s", esp_err_to_name(err));
|
||||
|
||||
ESP_LOGE(TAG, "Failed to get NVS handle: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t persistence_manager_deinit(persistence_manager_t *pm)
|
||||
{
|
||||
if (pm && pm->initialized)
|
||||
// Handles are now cached and kept open for performance
|
||||
// Only mark as uninitialized, don't close the handle
|
||||
if (pm)
|
||||
{
|
||||
nvs_close(pm->nvs_handle);
|
||||
pm->initialized = false;
|
||||
ESP_LOGD(TAG, "Deinitialized (handle remains cached for reuse)");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ extern "C"
|
||||
void start_simulate_night(void);
|
||||
void start_simulation_task(void);
|
||||
void stop_simulation_task(void);
|
||||
void start_simulation_with_reload(bool force_reload);
|
||||
void start_simulation(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ static char *time = NULL;
|
||||
static TaskHandle_t simulation_task_handle = NULL;
|
||||
static SemaphoreHandle_t simulation_mutex = NULL;
|
||||
static light_item_node_t *head = NULL;
|
||||
static bool schema_loaded = false;
|
||||
static int loaded_variant = -1;
|
||||
static const interpolation_mode_t interpolation_mode = INTERPOLATION_RGB;
|
||||
|
||||
// Helper function: converts hhmm format to minutes of the day
|
||||
@@ -140,22 +142,38 @@ void cleanup_light_items(void)
|
||||
}
|
||||
|
||||
head = NULL;
|
||||
schema_loaded = false;
|
||||
loaded_variant = -1;
|
||||
ESP_LOGI(TAG, "Cleaned up all light items.");
|
||||
}
|
||||
|
||||
static void initialize_light_items(void)
|
||||
static void initialize_light_items(bool force_reload)
|
||||
{
|
||||
cleanup_light_items();
|
||||
initialize_storage();
|
||||
|
||||
static char filename[30];
|
||||
persistence_manager_t persistence;
|
||||
persistence_manager_init(&persistence, "config");
|
||||
int variant = persistence_manager_get_int(&persistence, "light_variant", 1);
|
||||
snprintf(filename, sizeof(filename), "schema_%02d.csv", variant);
|
||||
load_file(filename);
|
||||
persistence_manager_deinit(&persistence);
|
||||
|
||||
bool variant_changed = (loaded_variant != variant);
|
||||
bool needs_reload = force_reload || !schema_loaded || variant_changed;
|
||||
|
||||
if (needs_reload)
|
||||
{
|
||||
cleanup_light_items();
|
||||
initialize_storage();
|
||||
|
||||
snprintf(filename, sizeof(filename), "schema_%02d.csv", variant);
|
||||
load_file(filename);
|
||||
schema_loaded = true;
|
||||
loaded_variant = variant;
|
||||
ESP_LOGI(TAG, "Schema loaded (variant=%d, force_reload=%s)", variant, force_reload ? "true" : "false");
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGD(TAG, "Schema reload skipped (variant=%d unchanged)", variant);
|
||||
}
|
||||
|
||||
// The list is now sorted because add_light_item inserts sorted
|
||||
|
||||
if (head == NULL)
|
||||
@@ -225,7 +243,7 @@ char *get_time(void)
|
||||
|
||||
void start_simulate_day(void)
|
||||
{
|
||||
initialize_light_items();
|
||||
initialize_light_items(false);
|
||||
|
||||
light_item_node_t *current_item = find_best_light_item_for_time(1200);
|
||||
if (current_item != NULL)
|
||||
@@ -238,7 +256,7 @@ void start_simulate_day(void)
|
||||
|
||||
void start_simulate_night(void)
|
||||
{
|
||||
initialize_light_items();
|
||||
initialize_light_items(false);
|
||||
|
||||
light_item_node_t *current_item = find_best_light_item_for_time(0);
|
||||
if (current_item != NULL)
|
||||
@@ -267,7 +285,7 @@ void simulate_cycle(void *args)
|
||||
return;
|
||||
}
|
||||
|
||||
initialize_light_items();
|
||||
initialize_light_items(false);
|
||||
|
||||
const int total_minutes_in_day = 24 * 60;
|
||||
long delay_ms = (long)cycle_duration_minutes * 60 * 1000 / total_minutes_in_day;
|
||||
@@ -398,7 +416,7 @@ void stop_simulation_task(void)
|
||||
}
|
||||
}
|
||||
|
||||
void start_simulation(void)
|
||||
void start_simulation_with_reload(bool force_reload)
|
||||
{
|
||||
stop_simulation_task();
|
||||
|
||||
@@ -410,6 +428,10 @@ void start_simulation(void)
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // Simulation mode
|
||||
if (force_reload)
|
||||
{
|
||||
initialize_light_items(true);
|
||||
}
|
||||
start_simulation_task();
|
||||
break;
|
||||
case 1: // Day mode
|
||||
@@ -429,3 +451,8 @@ void start_simulation(void)
|
||||
}
|
||||
persistence_manager_deinit(&persistence);
|
||||
}
|
||||
|
||||
void start_simulation(void)
|
||||
{
|
||||
start_simulation_with_reload(true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user