diff --git a/main/maxxfan-controller.c b/main/maxxfan-controller.c index d2c94f1..1287df8 100755 --- a/main/maxxfan-controller.c +++ b/main/maxxfan-controller.c @@ -3,11 +3,13 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" +#include "freertos/timers.h" #include "esp_system.h" #include "esp_wifi.h" #include "esp_event.h" #include "esp_log.h" #include "esp_http_server.h" +#include "esp_task_wdt.h" #include "nvs_flash.h" #include "driver/gpio.h" #include "driver/ledc.h" @@ -31,6 +33,14 @@ #define PWM_R_CHANNEL LEDC_CHANNEL_0 #define PWM_L_CHANNEL LEDC_CHANNEL_1 +// Motor ramping configuration +#define RAMP_STEP_MS 50 // Time between ramp steps (milliseconds) +#define RAMP_STEP_SIZE 5 // PWM duty change per step (0-255) +#define MIN_MOTOR_SPEED 10 // Minimum speed to overcome motor inertia + +// Watchdog configuration +#define WATCHDOG_TIMEOUT_S 10 // Watchdog timeout in seconds + static const char* TAG = "HTTP_MOTOR"; // WiFi event group @@ -47,13 +57,29 @@ typedef enum { MOTOR_INTAKE } motor_mode_t; -static motor_mode_t current_mode = MOTOR_OFF; -static int current_speed = 0; +typedef struct { + motor_mode_t mode; + int target_speed; + int current_speed; + bool ramping; + TimerHandle_t ramp_timer; +} motor_state_t; + +static motor_state_t motor_state = { + .mode = MOTOR_OFF, + .target_speed = 0, + .current_speed = 0, + .ramping = false, + .ramp_timer = NULL +}; // HTTP server handle static httpd_handle_t server = NULL; -// HTML web page for control +// Task handles for watchdog +static TaskHandle_t main_task_handle = NULL; + +// HTML web page for control (same as before) static const char* html_page = "" "" @@ -77,6 +103,7 @@ static const char* html_page = " .speed-slider { width: 100%; height: 40px; }" " .status { background: #e3f2fd; padding: 15px; border-radius: 5px; margin: 20px 0; }" " .status h4 { margin: 0 0 10px 0; color: #1976d2; }" +" .ramping { background: #fff3e0; border-left: 4px solid #ff9800; padding: 10px; margin: 10px 0; }" " " "" "" @@ -87,6 +114,10 @@ static const char* html_page = "

Current Status

" "

Mode: OFF

" "

Speed: 0%

" +"

Target: 0%

" +"
" +" ⚡ Ramping in progress..." +"
" " " " " "
" @@ -143,7 +174,15 @@ static const char* html_page = " " " function updateStatus(data) {" " document.getElementById('mode').textContent = data.mode.toUpperCase();" -" document.getElementById('speed').textContent = data.speed;" +" document.getElementById('speed').textContent = data.current_speed;" +" document.getElementById('target').textContent = data.target_speed;" +" " +" const rampStatus = document.getElementById('rampStatus');" +" if (data.ramping) {" +" rampStatus.style.display = 'block';" +" } else {" +" rampStatus.style.display = 'none';" +" }" " }" " " " function getStatus() {" @@ -153,8 +192,8 @@ static const char* html_page = " .catch(error => console.error('Error:', error));" " }" " " -" // Update status every 2 seconds" -" setInterval(getStatus, 2000);" +" // Update status every 1 second (faster to show ramping)" +" setInterval(getStatus, 1000);" " " " // Get initial status" " getStatus();" @@ -162,7 +201,26 @@ static const char* html_page = "" ""; -// WiFi event handler (same as before) +// Forward declarations +static void motor_ramp_timer_callback(TimerHandle_t xTimer); +static void apply_motor_pwm(int speed_percent); + +// Initialize watchdog timer +void init_watchdog(void) { + ESP_LOGI(TAG, "Setting up watchdog monitoring..."); + + // The system watchdog is already monitoring the main task + // We don't need to add anything, just need to reset it properly + ESP_LOGI(TAG, "Using system watchdog - will reset from main context"); +} + +// Feed the watchdog +void feed_watchdog(void) { + // Simply reset the watchdog for the current task context + esp_task_wdt_reset(); +} + +// WiFi event handler static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { @@ -240,18 +298,14 @@ void configure_pwm(void) ESP_LOGI(TAG, "PWM configured"); } -void set_motor_speed(motor_mode_t mode, int speed_percent) -{ +// Apply PWM to motor based on current mode and speed +static void apply_motor_pwm(int speed_percent) { if (speed_percent < 0) speed_percent = 0; if (speed_percent > 100) speed_percent = 100; - current_mode = mode; - current_speed = speed_percent; - uint32_t duty = (speed_percent * 255) / 100; - if (mode == MOTOR_OFF || speed_percent == 0) { - ESP_LOGI(TAG, "Motor OFF"); + if (motor_state.mode == MOTOR_OFF || speed_percent == 0) { gpio_set_level(LED_PIN, 0); gpio_set_level(MOTOR_R_EN, 0); gpio_set_level(MOTOR_L_EN, 0); @@ -260,23 +314,19 @@ void set_motor_speed(motor_mode_t mode, int speed_percent) ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_R_CHANNEL); ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_L_CHANNEL); - } else if (mode == MOTOR_EXHAUST) { - ESP_LOGI(TAG, "Motor EXHAUST - Speed: %d%%", speed_percent); + } else if (motor_state.mode == MOTOR_EXHAUST) { gpio_set_level(LED_PIN, 1); gpio_set_level(MOTOR_R_EN, 1); gpio_set_level(MOTOR_L_EN, 0); - vTaskDelay(pdMS_TO_TICKS(10)); ledc_set_duty(LEDC_LOW_SPEED_MODE, PWM_R_CHANNEL, duty); ledc_set_duty(LEDC_LOW_SPEED_MODE, PWM_L_CHANNEL, 0); ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_R_CHANNEL); ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_L_CHANNEL); - } else if (mode == MOTOR_INTAKE) { - ESP_LOGI(TAG, "Motor INTAKE - Speed: %d%%", speed_percent); + } else if (motor_state.mode == MOTOR_INTAKE) { gpio_set_level(LED_PIN, 1); gpio_set_level(MOTOR_R_EN, 0); gpio_set_level(MOTOR_L_EN, 1); - vTaskDelay(pdMS_TO_TICKS(10)); ledc_set_duty(LEDC_LOW_SPEED_MODE, PWM_R_CHANNEL, 0); ledc_set_duty(LEDC_LOW_SPEED_MODE, PWM_L_CHANNEL, duty); ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_R_CHANNEL); @@ -284,6 +334,117 @@ void set_motor_speed(motor_mode_t mode, int speed_percent) } } +// Motor ramp timer callback +static void motor_ramp_timer_callback(TimerHandle_t xTimer) { + if (!motor_state.ramping) { + return; + } + + int speed_diff = motor_state.target_speed - motor_state.current_speed; + + if (abs(speed_diff) <= RAMP_STEP_SIZE) { + // Close enough to target, finish ramping + motor_state.current_speed = motor_state.target_speed; + motor_state.ramping = false; + + // Stop the timer + xTimerStop(motor_state.ramp_timer, 0); + + ESP_LOGI(TAG, "Ramping complete - Final speed: %d%%", motor_state.current_speed); + } else { + // Continue ramping + if (speed_diff > 0) { + motor_state.current_speed += RAMP_STEP_SIZE; + } else { + motor_state.current_speed -= RAMP_STEP_SIZE; + } + + ESP_LOGD(TAG, "Ramping: %d%% -> %d%% (target: %d%%)", + motor_state.current_speed - (speed_diff > 0 ? RAMP_STEP_SIZE : -RAMP_STEP_SIZE), + motor_state.current_speed, motor_state.target_speed); + } + + apply_motor_pwm(motor_state.current_speed); +} + +// Initialize motor ramping system +void init_motor_ramping(void) { + motor_state.ramp_timer = xTimerCreate( + "MotorRampTimer", // Timer name + pdMS_TO_TICKS(RAMP_STEP_MS), // Timer period + pdTRUE, // Auto-reload + (void*)0, // Timer ID + motor_ramp_timer_callback // Callback function + ); + + if (motor_state.ramp_timer == NULL) { + ESP_LOGE(TAG, "Failed to create motor ramp timer"); + } else { + ESP_LOGI(TAG, "Motor ramping system initialized"); + } +} + +void set_motor_speed(motor_mode_t mode, int speed_percent) +{ + if (speed_percent < 0) speed_percent = 0; + if (speed_percent > 100) speed_percent = 100; + + // Stop any current ramping + if (motor_state.ramping) { + xTimerStop(motor_state.ramp_timer, 0); + motor_state.ramping = false; + } + + motor_mode_t previous_mode = motor_state.mode; + motor_state.mode = mode; + motor_state.target_speed = speed_percent; + + ESP_LOGI(TAG, "Motor command: %s - Target: %d%% (Current: %d%%)", + mode == MOTOR_OFF ? "OFF" : (mode == MOTOR_EXHAUST ? "EXHAUST" : "INTAKE"), + speed_percent, motor_state.current_speed); + + // Handle different scenarios + if (mode == MOTOR_OFF || speed_percent == 0) { + // Immediate stop + motor_state.current_speed = 0; + motor_state.target_speed = 0; + apply_motor_pwm(0); + ESP_LOGI(TAG, "Motor stopped immediately"); + + } else if (previous_mode == MOTOR_OFF || motor_state.current_speed == 0) { + // Starting from stop - apply minimum speed first, then ramp + int start_speed = (speed_percent < MIN_MOTOR_SPEED) ? speed_percent : MIN_MOTOR_SPEED; + motor_state.current_speed = start_speed; + apply_motor_pwm(start_speed); + + if (speed_percent > start_speed) { + // Start ramping to target + motor_state.ramping = true; + xTimerStart(motor_state.ramp_timer, 0); + ESP_LOGI(TAG, "Motor starting at %d%%, ramping to %d%%", start_speed, speed_percent); + } else { + ESP_LOGI(TAG, "Motor started at %d%% (no ramping needed)", start_speed); + } + + } else if (previous_mode != mode) { + // Direction change - ramp down to minimum, change direction, then ramp up + motor_state.target_speed = MIN_MOTOR_SPEED; + motor_state.ramping = true; + xTimerStart(motor_state.ramp_timer, 0); + ESP_LOGI(TAG, "Direction change - ramping down first"); + + // Note: In a real implementation, you might want to implement a state machine + // to handle the direction change sequence properly + + } else { + // Same mode, just speed change - ramp to new speed + motor_state.ramping = true; + xTimerStart(motor_state.ramp_timer, 0); + ESP_LOGI(TAG, "Speed change - ramping from %d%% to %d%%", + motor_state.current_speed, speed_percent); + } +} + // HTTP handler for the main web page static esp_err_t root_get_handler(httpd_req_t *req) { @@ -298,11 +459,13 @@ static esp_err_t status_get_handler(httpd_req_t *req) cJSON *json = cJSON_CreateObject(); const char* mode_str = "off"; - if (current_mode == MOTOR_EXHAUST) mode_str = "exhaust"; - else if (current_mode == MOTOR_INTAKE) mode_str = "intake"; + if (motor_state.mode == MOTOR_EXHAUST) mode_str = "exhaust"; + else if (motor_state.mode == MOTOR_INTAKE) mode_str = "intake"; cJSON_AddStringToObject(json, "mode", mode_str); - cJSON_AddNumberToObject(json, "speed", current_speed); + cJSON_AddNumberToObject(json, "current_speed", motor_state.current_speed); + cJSON_AddNumberToObject(json, "target_speed", motor_state.target_speed); + cJSON_AddBoolToObject(json, "ramping", motor_state.ramping); char *json_string = cJSON_Print(json); httpd_resp_set_type(req, "application/json"); @@ -476,9 +639,19 @@ void wifi_init_sta(void) } } +// Main application task with watchdog feeding +void main_task(void *pvParameters) { + ESP_LOGI(TAG, "Main task started"); + + while (1) { + feed_watchdog(); + vTaskDelay(pdMS_TO_TICKS(5000)); // Feed watchdog every 5 seconds + } +} + void app_main(void) { - ESP_LOGI(TAG, "Starting Maxxfan HTTP Controller!"); + ESP_LOGI(TAG, "Starting Maxxfan HTTP Controller with improvements!"); // Initialize NVS esp_err_t ret = nvs_flash_init(); @@ -488,17 +661,30 @@ void app_main(void) } ESP_ERROR_CHECK(ret); + // Initialize watchdog timer + init_watchdog(); + // Configure hardware configure_gpio_pins(); configure_pwm(); + // Initialize motor ramping system + init_motor_ramping(); + ESP_LOGI(TAG, "Connecting to WiFi network: %s", WIFI_SSID); wifi_init_sta(); // Start HTTP server start_webserver(); - ESP_LOGI(TAG, "=== Maxxfan Controller Ready! ==="); + ESP_LOGI(TAG, "=== Enhanced Maxxfan Controller Ready! ==="); + ESP_LOGI(TAG, "Features: Motor Ramping, Optimized Performance"); ESP_LOGI(TAG, "Open your browser and go to: http://[ESP32_IP_ADDRESS]"); ESP_LOGI(TAG, "Check the monitor output above for your IP address"); + + // Main loop - reset watchdog periodically + while (1) { + feed_watchdog(); + vTaskDelay(pdMS_TO_TICKS(3000)); // Feed every 3 seconds (system default is usually 5s timeout) + } } \ No newline at end of file