Refactor, state manager

This commit is contained in:
2025-07-09 22:55:35 -06:00
parent f3fb4f4ac8
commit c3bbba4a24
4 changed files with 592 additions and 184 deletions

View File

@ -9,13 +9,13 @@
#include "esp_log.h"
#include "esp_http_server.h"
#include "esp_task_wdt.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "cJSON.h"
// Project modules
#include "config.h"
#include "motor_control.h"
#include "state_manager.h"
// WiFi event group
static EventGroupHandle_t s_wifi_event_group;
@ -84,11 +84,6 @@ static const char* html_page =
"function startUpdates(){if(updateInterval)clearInterval(updateInterval);updateInterval=setInterval(getStatus,1000)}"
"document.addEventListener('DOMContentLoaded',function(){getStatus();startUpdates()})</script></body></html>";
// Forward declarations
static esp_err_t save_motor_state_to_nvs(void);
static esp_err_t load_motor_state_from_nvs(void);
static bool is_watchdog_reset(void);
// Initialize watchdog timer
void init_watchdog(void) {
ESP_LOGI(SYSTEM_TAG, "Setting up watchdog monitoring...");
@ -117,165 +112,6 @@ void feed_watchdog(void) {
}
}
// Check if this was a watchdog reset
static bool is_watchdog_reset(void) {
esp_reset_reason_t reset_reason = esp_reset_reason();
// Only consider TASK_WDT and INT_WDT as true watchdog resets
// ESP_RST_WDT can be triggered by power disconnection, so we exclude it
return (reset_reason == ESP_RST_TASK_WDT ||
reset_reason == ESP_RST_INT_WDT);
}
// Save motor state to NVS
static esp_err_t save_motor_state_to_nvs(void) {
nvs_handle_t nvs_handle;
esp_err_t err;
err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
if (err != ESP_OK) {
ESP_LOGE(SYSTEM_TAG, "Error opening NVS handle: %s", esp_err_to_name(err));
return err;
}
// Get current motor state
const motor_state_t* state = motor_get_state();
motor_mode_t last_on_mode;
int last_on_speed;
motor_get_last_on_state(&last_on_mode, &last_on_speed);
bool user_turned_off = motor_get_user_turned_off();
ESP_LOGI(SYSTEM_TAG, "=== SAVING STATE TO NVS ===");
ESP_LOGI(SYSTEM_TAG, "Mode: %d, Speed: %d%%, Last ON: %d@%d%%, User OFF: %s",
state->mode, state->target_speed,
last_on_mode, last_on_speed,
user_turned_off ? "YES" : "NO");
// Save current motor state
err = nvs_set_u8(nvs_handle, NVS_KEY_MODE, (uint8_t)state->mode);
if (err == ESP_OK) {
err = nvs_set_u8(nvs_handle, NVS_KEY_SPEED, (uint8_t)state->target_speed);
}
// Save last ON state
if (err == ESP_OK) {
err = nvs_set_u8(nvs_handle, NVS_KEY_LAST_ON_MODE, (uint8_t)last_on_mode);
}
if (err == ESP_OK) {
err = nvs_set_u8(nvs_handle, NVS_KEY_LAST_ON_SPEED, (uint8_t)last_on_speed);
}
// Save power state (whether user turned off manually)
if (err == ESP_OK) {
err = nvs_set_u8(nvs_handle, NVS_KEY_POWER_STATE, user_turned_off ? 1 : 0);
}
if (err == ESP_OK) {
err = nvs_commit(nvs_handle);
if (err == ESP_OK) {
ESP_LOGI(SYSTEM_TAG, "✓ Motor state successfully saved to NVS");
} else {
ESP_LOGE(SYSTEM_TAG, "✗ NVS commit failed: %s", esp_err_to_name(err));
}
} else {
ESP_LOGE(SYSTEM_TAG, "✗ Error saving to NVS: %s", esp_err_to_name(err));
}
ESP_LOGI(SYSTEM_TAG, "===========================");
nvs_close(nvs_handle);
return err;
}
// Load motor state from NVS
static esp_err_t load_motor_state_from_nvs(void) {
nvs_handle_t nvs_handle;
esp_err_t err;
err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
if (err != ESP_OK) {
ESP_LOGI(SYSTEM_TAG, "NVS not found, using default state");
return ESP_ERR_NVS_NOT_FOUND;
}
uint8_t stored_mode = 0;
uint8_t stored_speed = 0;
uint8_t stored_last_mode = 1; // Default to MOTOR_EXHAUST
uint8_t stored_last_speed = 50;
uint8_t stored_power_state = 0;
// Load current motor state
err = nvs_get_u8(nvs_handle, NVS_KEY_MODE, &stored_mode);
if (err == ESP_OK) {
nvs_get_u8(nvs_handle, NVS_KEY_SPEED, &stored_speed);
nvs_get_u8(nvs_handle, NVS_KEY_LAST_ON_MODE, &stored_last_mode);
nvs_get_u8(nvs_handle, NVS_KEY_LAST_ON_SPEED, &stored_last_speed);
nvs_get_u8(nvs_handle, NVS_KEY_POWER_STATE, &stored_power_state);
// Validate ranges using config macros
if (stored_mode > MOTOR_INTAKE) stored_mode = MOTOR_OFF;
if (!IS_VALID_SPEED(stored_speed)) stored_speed = 0;
if (stored_last_mode < MOTOR_EXHAUST || stored_last_mode > MOTOR_INTAKE) stored_last_mode = MOTOR_EXHAUST;
if (!IS_VALID_SPEED(stored_last_speed)) stored_last_speed = 50;
// Set the last ON state in motor control module
motor_set_last_on_state((motor_mode_t)stored_last_mode, stored_last_speed);
motor_set_user_turned_off(stored_power_state == 1);
ESP_LOGI(SYSTEM_TAG, "Loaded state from NVS - Mode: %d, Speed: %d%%, Last ON: %d@%d%%, User OFF: %s",
stored_mode, stored_speed, stored_last_mode, stored_last_speed,
stored_power_state ? "YES" : "NO");
// Check reset reason to decide whether to restore state
bool was_watchdog_reset = is_watchdog_reset();
esp_reset_reason_t reset_reason = esp_reset_reason();
ESP_LOGI(SYSTEM_TAG, "=== RESET ANALYSIS ===");
ESP_LOGI(SYSTEM_TAG, "Reset reason: %d", reset_reason);
ESP_LOGI(SYSTEM_TAG, "Reset reason name: %s",
reset_reason == ESP_RST_POWERON ? "POWERON" :
reset_reason == ESP_RST_EXT ? "EXTERNAL" :
reset_reason == ESP_RST_SW ? "SOFTWARE" :
reset_reason == ESP_RST_PANIC ? "PANIC" :
reset_reason == ESP_RST_INT_WDT ? "INT_WDT" :
reset_reason == ESP_RST_TASK_WDT ? "TASK_WDT" :
reset_reason == ESP_RST_WDT ? "WDT" :
reset_reason == ESP_RST_DEEPSLEEP ? "DEEPSLEEP" :
reset_reason == ESP_RST_BROWNOUT ? "BROWNOUT" :
reset_reason == ESP_RST_SDIO ? "SDIO" : "UNKNOWN");
ESP_LOGI(SYSTEM_TAG, "Watchdog reset: %s", was_watchdog_reset ? "YES" : "NO");
ESP_LOGI(SYSTEM_TAG, "Stored mode: %d, speed: %d", stored_mode, stored_speed);
ESP_LOGI(SYSTEM_TAG, "User turned off: %s", stored_power_state ? "YES" : "NO");
ESP_LOGI(SYSTEM_TAG, "====================");
// Store the restored state for potential motor restoration
if (was_watchdog_reset) {
// True watchdog reset (TASK_WDT or INT_WDT) - don't restore state, start fresh
ESP_LOGI(SYSTEM_TAG, "⚠️ TRUE watchdog reset detected - starting in OFF state for safety");
// Motor module is already initialized in OFF state, no action needed
} else if (stored_power_state) {
// User manually turned off - stay off
ESP_LOGI(SYSTEM_TAG, "🔒 User had turned off manually - staying OFF");
// Motor module is already initialized in OFF state, no action needed
} else if (stored_mode != MOTOR_OFF && stored_speed > 0) {
// Normal power loss or general WDT (which can be power-related) - restore previous state
ESP_LOGI(SYSTEM_TAG, "🔋 Power restored - will resume previous state: %s @ %d%%",
stored_mode == MOTOR_EXHAUST ? "EXHAUST" : "INTAKE", stored_speed);
// Set the motor to the restored state (will be applied after initialization)
motor_set_speed((motor_mode_t)stored_mode, stored_speed);
} else {
ESP_LOGI(SYSTEM_TAG, "❌ No valid state to restore (mode=%d, speed=%d)", stored_mode, stored_speed);
// Motor module is already initialized in OFF state, no action needed
}
} else {
ESP_LOGI(SYSTEM_TAG, "No saved state found, using defaults");
err = ESP_ERR_NVS_NOT_FOUND;
}
nvs_close(nvs_handle);
return err;
}
// WiFi event handler
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
@ -444,8 +280,11 @@ static esp_err_t fan_post_handler(httpd_req_t *req)
ESP_LOGI(SYSTEM_TAG, "ON button pressed - resuming last state");
motor_resume_last_state();
// Save state after ON button
save_motor_state_to_nvs();
// Save state after ON button using state manager
esp_err_t save_result = state_manager_save();
if (save_result != ESP_OK) {
ESP_LOGW(SYSTEM_TAG, "Failed to save state after ON button: %s", esp_err_to_name(save_result));
}
cJSON_Delete(json);
return status_get_handler(req);
@ -458,8 +297,11 @@ static esp_err_t fan_post_handler(httpd_req_t *req)
ESP_LOGI(SYSTEM_TAG, "HTTP Request: mode=%s, speed=%d", mode_str, speed);
motor_set_speed(mode, speed);
// Save state after any motor command
save_motor_state_to_nvs();
// Save state after any motor command using state manager
esp_err_t save_result = state_manager_save();
if (save_result != ESP_OK) {
ESP_LOGW(SYSTEM_TAG, "Failed to save state after motor command: %s", esp_err_to_name(save_result));
}
cJSON_Delete(json);
@ -600,13 +442,13 @@ void app_main(void)
{
ESP_LOGI(SYSTEM_TAG, "Starting Maxxfan HTTP Controller with State Preservation!");
// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
// Initialize state manager (includes NVS initialization)
ESP_LOGI(SYSTEM_TAG, "Initializing state manager...");
esp_err_t ret = state_manager_init();
if (ret != ESP_OK) {
ESP_LOGE(SYSTEM_TAG, "Failed to initialize state manager: %s", esp_err_to_name(ret));
return;
}
ESP_ERROR_CHECK(ret);
// Initialize watchdog timer
init_watchdog();
@ -619,9 +461,16 @@ void app_main(void)
return;
}
// Load saved state from NVS and potentially restore motor state
ESP_LOGI(SYSTEM_TAG, "Loading saved state...");
load_motor_state_from_nvs();
// Load saved state and restore motor if appropriate
ESP_LOGI(SYSTEM_TAG, "Loading saved state and applying restoration logic...");
esp_err_t load_result = state_manager_load_and_restore();
if (load_result == ESP_OK) {
ESP_LOGI(SYSTEM_TAG, "✓ State loaded and restoration logic applied");
} else if (load_result == ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGI(SYSTEM_TAG, " No saved state found, using defaults");
} else {
ESP_LOGW(SYSTEM_TAG, "⚠️ Failed to load state: %s", esp_err_to_name(load_result));
}
ESP_LOGI(SYSTEM_TAG, "Connecting to WiFi network: %s", WIFI_SSID);
wifi_init_sta();
@ -629,25 +478,36 @@ void app_main(void)
// Start HTTP server
start_webserver();
// Report final motor state after initialization
// Report final system state after initialization
const motor_state_t* final_state = motor_get_state();
ESP_LOGI(SYSTEM_TAG, "=== MOTOR STATE AFTER INITIALIZATION ===");
ESP_LOGI(SYSTEM_TAG, "=== SYSTEM INITIALIZATION COMPLETE ===");
ESP_LOGI(SYSTEM_TAG, "Reset Reason: %s", state_manager_get_reset_reason_string());
ESP_LOGI(SYSTEM_TAG, "Watchdog Reset: %s", state_manager_was_watchdog_reset() ? "YES" : "NO");
ESP_LOGI(SYSTEM_TAG, "Final motor state: mode=%s, target=%d%%, current=%d%%, state=%s",
motor_mode_to_string(final_state->mode), final_state->target_speed,
final_state->current_speed, motor_state_to_string(final_state->state));
if (final_state->mode != MOTOR_OFF && final_state->target_speed > 0) {
ESP_LOGI(SYSTEM_TAG, "Motor restored to: %s @ %d%%",
ESP_LOGI(SYSTEM_TAG, "🔄 Motor restored to: %s @ %d%%",
motor_mode_to_string(final_state->mode), final_state->target_speed);
} else {
ESP_LOGI(SYSTEM_TAG, "Motor remains OFF");
ESP_LOGI(SYSTEM_TAG, "⏸️ Motor remains OFF");
}
ESP_LOGI(SYSTEM_TAG, "=======================================");
motor_mode_t last_on_mode;
int last_on_speed;
motor_get_last_on_state(&last_on_mode, &last_on_speed);
ESP_LOGI(SYSTEM_TAG, "Last ON state: %s @ %d%%",
motor_mode_to_string(last_on_mode), last_on_speed);
ESP_LOGI(SYSTEM_TAG, "User turned off: %s", motor_get_user_turned_off() ? "YES" : "NO");
ESP_LOGI(SYSTEM_TAG, "Saved state exists: %s", state_manager_has_saved_state() ? "YES" : "NO");
ESP_LOGI(SYSTEM_TAG, "=====================================");
ESP_LOGI(SYSTEM_TAG, "=== Enhanced Maxxfan Controller Ready! ===");
ESP_LOGI(SYSTEM_TAG, "Features: State Preservation, Direction Safety, Motor Ramping, ON Button");
ESP_LOGI(SYSTEM_TAG, "Safety: %d-second cooldown for direction changes", DIRECTION_CHANGE_COOLDOWN_MS / 1000);
ESP_LOGI(SYSTEM_TAG, "Memory: Remembers settings after power loss (except watchdog resets)");
ESP_LOGI(SYSTEM_TAG, "State Manager: Enhanced NVS operations with validation and recovery");
ESP_LOGI(SYSTEM_TAG, "Open your browser and go to: http://[ESP32_IP_ADDRESS]");
ESP_LOGI(SYSTEM_TAG, "Check the monitor output above for your IP address");