logging testing. logging works. e-fusing algo works right for jack. jack timing works.

This commit is contained in:
Thaddeus Hughes
2026-01-17 13:33:57 -06:00
parent 982ada9787
commit a1a8313525
20 changed files with 2376 additions and 651 deletions

View File

@@ -4,7 +4,7 @@
include(${CMAKE_CURRENT_LIST_DIR}/version.cmake) include(${CMAKE_CURRENT_LIST_DIR}/version.cmake)
idf_component_register( idf_component_register(
SRCS main.c i2c.c rtc.c storage.c uart_comms.c control_fsm.c power_mgmt.c rf_433.c rtc.c sensors.c solar.c webserver.c simple_dns_server.c comms.c # list the source files of this component SRCS main.c log_test.c i2c.c rtc.c storage.c uart_comms.c control_fsm.c power_mgmt.c rf_433.c rtc.c sensors.c solar.c webserver.c simple_dns_server.c comms.c # list the source files of this component
INCLUDE_DIRS "." "${CMAKE_BINARY_DIR}" INCLUDE_DIRS "." "${CMAKE_BINARY_DIR}"
PRIV_INCLUDE_DIRS # optional, add here private include directories PRIV_INCLUDE_DIRS # optional, add here private include directories

View File

@@ -19,7 +19,7 @@ static const char *TAG = "COMMS";
* Build a JSON object containing complete system status * Build a JSON object containing complete system status
*/ */
cJSON* comms_handle_get(void) { cJSON* comms_handle_get(void) {
ESP_LOGI(TAG, "GET request"); //ESP_LOGI(TAG, "GET request");
rtc_reset_shutdown_timer(); rtc_reset_shutdown_timer();
@@ -52,7 +52,6 @@ cJSON* comms_handle_get(void) {
case STATE_IDLE: case STATE_IDLE:
cJSON_AddItemToArray(msg_array, cJSON_CreateString("IDLE")); cJSON_AddItemToArray(msg_array, cJSON_CreateString("IDLE"));
break; break;
case STATE_UNDO_JACK:
case STATE_UNDO_JACK_START: case STATE_UNDO_JACK_START:
cJSON_AddItemToArray(msg_array, cJSON_CreateString("CANCELLING MOVE")); cJSON_AddItemToArray(msg_array, cJSON_CreateString("CANCELLING MOVE"));
break; break;
@@ -65,13 +64,13 @@ cJSON* comms_handle_get(void) {
if (fsm_get_remaining_distance() <= 0) { if (fsm_get_remaining_distance() <= 0) {
cJSON_AddItemToArray(msg_array, cJSON_CreateString("DISTANCE LIMIT HIT")); cJSON_AddItemToArray(msg_array, cJSON_CreateString("DISTANCE LIMIT HIT"));
} }
if (efuse_is_tripped(BRIDGE_AUX)) { if (efuse_get(BRIDGE_AUX)) {
cJSON_AddItemToArray(msg_array, cJSON_CreateString("AUX EFUSE TRIP")); cJSON_AddItemToArray(msg_array, cJSON_CreateString("AUX EFUSE TRIP"));
} }
if (efuse_is_tripped(BRIDGE_JACK)) { if (efuse_get(BRIDGE_JACK)) {
cJSON_AddItemToArray(msg_array, cJSON_CreateString("JACK EFUSE TRIP")); cJSON_AddItemToArray(msg_array, cJSON_CreateString("JACK EFUSE TRIP"));
} }
if (efuse_is_tripped(BRIDGE_DRIVE)) { if (efuse_get(BRIDGE_DRIVE)) {
cJSON_AddItemToArray(msg_array, cJSON_CreateString("DRIVE EFUSE TRIP")); cJSON_AddItemToArray(msg_array, cJSON_CreateString("DRIVE EFUSE TRIP"));
} }
if (!rtc_is_set()) { if (!rtc_is_set()) {
@@ -135,7 +134,7 @@ cJSON* comms_handle_get(void) {
* Process a POST request with JSON data * Process a POST request with JSON data
*/ */
esp_err_t comms_handle_post(cJSON *root, cJSON **response_json) { esp_err_t comms_handle_post(cJSON *root, cJSON **response_json) {
ESP_LOGI(TAG, "POST request"); //ESP_LOGI(TAG, "POST request");
if (root == NULL || response_json == NULL) { if (root == NULL || response_json == NULL) {
ESP_LOGE(TAG, "Invalid arguments to comms_handle_post"); ESP_LOGE(TAG, "Invalid arguments to comms_handle_post");
@@ -171,7 +170,7 @@ esp_err_t comms_handle_post(cJSON *root, cJSON **response_json) {
cJSON *cmd = cJSON_GetObjectItem(root, "cmd"); cJSON *cmd = cJSON_GetObjectItem(root, "cmd");
if (cJSON_IsString(cmd)) { if (cJSON_IsString(cmd)) {
const char *cmd_str = cmd->valuestring; const char *cmd_str = cmd->valuestring;
ESP_LOGI(TAG, "Executing command: %s", cmd_str); // ESP_LOGI(TAG, "Executing command: %s", cmd_str);
if (strcmp(cmd_str, "start") == 0) { if (strcmp(cmd_str, "start") == 0) {
fsm_request(FSM_CMD_START); fsm_request(FSM_CMD_START);
@@ -186,25 +185,23 @@ esp_err_t comms_handle_post(cJSON *root, cJSON **response_json) {
cmd_executed = true; cmd_executed = true;
} }
else if (strcmp(cmd_str, "fwd") == 0) { else if (strcmp(cmd_str, "fwd") == 0) {
pulseOverride(RELAY_A1); pulseOverride(FSM_OVERRIDE_DRIVE_FWD);
pulseOverride(RELAY_A3);
cmd_executed = true; cmd_executed = true;
} }
else if (strcmp(cmd_str, "rev") == 0) { else if (strcmp(cmd_str, "rev") == 0) {
pulseOverride(RELAY_B1); pulseOverride(FSM_OVERRIDE_DRIVE_REV);
pulseOverride(RELAY_A3);
cmd_executed = true; cmd_executed = true;
} }
else if (strcmp(cmd_str, "up") == 0) { else if (strcmp(cmd_str, "up") == 0) {
pulseOverride(RELAY_A2); pulseOverride(FSM_OVERRIDE_JACK_UP);
cmd_executed = true; cmd_executed = true;
} }
else if (strcmp(cmd_str, "down") == 0) { else if (strcmp(cmd_str, "down") == 0) {
pulseOverride(RELAY_B2); pulseOverride(FSM_OVERRIDE_JACK_DOWN);
cmd_executed = true; cmd_executed = true;
} }
else if (strcmp(cmd_str, "aux") == 0) { else if (strcmp(cmd_str, "aux") == 0) {
pulseOverride(RELAY_A3); pulseOverride(FSM_OVERRIDE_AUX);
cmd_executed = true; cmd_executed = true;
} }
else if (strcmp(cmd_str, "reboot") == 0) { else if (strcmp(cmd_str, "reboot") == 0) {

View File

@@ -17,6 +17,7 @@
#include "rtc.h" #include "rtc.h"
#include "sensors.h" #include "sensors.h"
#include "esp_log.h" #include "esp_log.h"
#include <string.h>
#include <sys/param.h> #include <sys/param.h>
#define TRANSITION_DELAY_US 1000000 #define TRANSITION_DELAY_US 1000000
@@ -28,24 +29,14 @@
static QueueHandle_t fsm_cmd_queue = NULL; static QueueHandle_t fsm_cmd_queue = NULL;
RTC_DATA_ATTR esp_err_t error = ESP_OK; RTC_DATA_ATTR esp_err_t fsm_error = ESP_OK;
esp_err_t fsm_get_error() { return error; } esp_err_t fsm_get_error() { return fsm_error; }
void fsm_clear_error() { error = ESP_OK; } void fsm_clear_error() { fsm_error = ESP_OK; }
// map from relay number to bridge
bridge_t bridge_map[] = {
BRIDGE_AUX,
BRIDGE_AUX,
BRIDGE_AUX,
BRIDGE_AUX,
BRIDGE_JACK,
BRIDGE_JACK,
BRIDGE_DRIVE,
BRIDGE_DRIVE };
bool relay_states[8] = {false}; int64_t override_time = -1;
int64_t override_times[8] = {-1}; fsm_override_t override_cmd;
int64_t override_cooldown[8] = {-1}; //int64_t override_cooldown[8] = {-1};
bool enabled = false; bool enabled = false;
float this_move_dist = 0.0f; float this_move_dist = 0.0f;
@@ -57,40 +48,14 @@ void fsm_set_remaining_distance(float x) { remaining_distance = x;}
static int32_t move_start_encoder = 0; static int32_t move_start_encoder = 0;
// Track total jack up time to use for jack down duration // Track total jack up time to use for jack down duration
static int64_t jack_up_total_time = 0; static int64_t jack_start_us = 0;
static int64_t jack_trans_us = 0;
static int64_t jack_finish_us = 0;
volatile fsm_state_t current_state = STATE_IDLE; volatile fsm_state_t current_state = STATE_IDLE;
volatile int64_t current_time = 0; volatile int64_t fsm_now = 0;
volatile bool start_running_request = false; volatile bool start_running_request = false;
void setRelay(int8_t relay, bool state) {
relay_states[relay] = state;
}
bool isRunning() {
for (int i=0;i<8;i++) {
if (relay_states[i]) return true;
}
return false;
}
void driveRelays() {
uint8_t state = 0x00;
//relay_states[0] = (current_time / 1000000) % 2; // for testing purposes
for (uint8_t i=0; i<8; i++) {
// if we command and efuse permits it set the relay
if (relay_states[i] && !efuse_is_tripped(bridge_map[i])) {
state |= 0x01<<i;
set_autozero(bridge_map[i]);
}
}
//ESP_LOGI(TAG, "RELAY STATE: %x", state);
i2c_set_relays(state);
}
fsm_state_t fsm_get_state() { fsm_state_t fsm_get_state() {
return current_state; return current_state;
@@ -99,28 +64,18 @@ fsm_state_t fsm_get_state() {
static int64_t timer_end = 0; static int64_t timer_end = 0;
static int64_t timer_start = 0; static int64_t timer_start = 0;
static inline void set_timer(uint64_t us) { static inline void set_timer(uint64_t us) {
timer_end = current_time + us; timer_end = fsm_now + us;
timer_start = current_time; timer_start = fsm_now;
} }
static inline bool timer_done() { return current_time >= timer_end; } static inline bool timer_done() { return fsm_now >= timer_end; }
void pulseOverride(relay_t relay) { void pulseOverride(fsm_override_t cmd) {
if (current_state == STATE_IDLE) { if (current_state == STATE_IDLE) {
// Check if this relay is in cooldown override_cmd = cmd;
if (override_cooldown[relay] > current_time) { override_time = fsm_now + get_param_value_t(PARAM_RF_PULSE_LENGTH).u32;
// Still cooling down, ignore the command
return;
}
override_times[relay] = current_time + get_param_value_t(PARAM_RF_PULSE_LENGTH).u32;
} }
} }
/*void fsm_begin_auto_move() {
if (current_state == STATE_IDLE)
current_state = STATE_MOVE_START_DELAY;
set_timer(TRANSITION_DELAY_US);
}*/
int64_t fsm_cal_t, fsm_cal_e; int64_t fsm_cal_t, fsm_cal_e;
float fsm_cal_val; float fsm_cal_val;
void fsm_set_cal_val(float v) {fsm_cal_val = v;} void fsm_set_cal_val(float v) {fsm_cal_val = v;}
@@ -143,9 +98,8 @@ int8_t fsm_get_current_progress(int8_t denominator) {
case STATE_MOVE_START_DELAY: case STATE_MOVE_START_DELAY:
case STATE_DRIVE_START_DELAY: case STATE_DRIVE_START_DELAY:
case STATE_DRIVE_END_DELAY: case STATE_DRIVE_END_DELAY:
case STATE_UNDO_JACK:
if (timer_end != timer_start) if (timer_end != timer_start)
x = (current_time-timer_start)*denominator/(timer_end-timer_start); x = (fsm_now-timer_start)*denominator/(timer_end-timer_start);
break; break;
case STATE_UNDO_JACK_START: case STATE_UNDO_JACK_START:
x = 0; x = 0;
@@ -160,9 +114,56 @@ int8_t fsm_get_current_progress(int8_t denominator) {
#define JACK_TIME get_param_value_t(PARAM_JACK_KT).f32 * get_param_value_t(PARAM_JACK_DIST ).f32 #define JACK_TIME get_param_value_t(PARAM_JACK_KT).f32 * get_param_value_t(PARAM_JACK_DIST ).f32
#define JACK_DOWN_TIME (jack_finish_us - jack_start_us) * 105/100
#define DRIVE_TIME get_param_value_t(PARAM_DRIVE_KT).f32 * this_move_dist #define DRIVE_TIME get_param_value_t(PARAM_DRIVE_KT).f32 * this_move_dist
#define DRIVE_DIST get_param_value_t(PARAM_DRIVE_KE).f32 * this_move_dist #define DRIVE_DIST get_param_value_t(PARAM_DRIVE_KE).f32 * this_move_dist
int64_t last_log_time = 0;
#define LOGSIZE 39
esp_err_t send_fsm_log() {
if(!rtc_is_set()) return ESP_OK;
uint8_t entry[LOGSIZE] = {};
// Pack 64-bit timestamp into bytes 1-8
uint64_t be_timestamp = rtc_get_ms();
memcpy(&entry[0], &be_timestamp, 8);
// Pack 32-bit voltages/currents into bytes 9-24
float be_voltage = get_battery_V();
memcpy(&entry[8], &be_voltage, 4);
float be_current1 = get_bridge_raw_A(BRIDGE_DRIVE);
memcpy(&entry[12], &be_current1, 4);
float be_current2 = get_bridge_raw_A(BRIDGE_JACK);
memcpy(&entry[16], &be_current2, 4);
float be_current3 = get_bridge_raw_A(BRIDGE_AUX);
memcpy(&entry[20], &be_current3, 4);
int16_t be_counter = get_sensor_counter(SENSOR_DRIVE);
memcpy(&entry[24], &be_counter, 2);
entry[26] = pack_sensors();
float heat1 = efuse_get_heat(BRIDGE_DRIVE);
memcpy(&entry[27], &heat1, 4);
float heat2 = efuse_get_heat(BRIDGE_JACK);
memcpy(&entry[31], &heat2, 4);
float heat3 = efuse_get_heat(BRIDGE_AUX);
memcpy(&entry[35], &heat3, 4);
last_log_time = esp_timer_get_time();
log_write(entry, LOGSIZE, fsm_get_state());
//ESP_LOGI(TAG, "WROTE LOG; %lld / %ld/%ld; %5.2f %5.2f %5.2f", (long long)rtc_get_ms(), (unsigned long)log_get_tail(), (unsigned long)log_get_head(), heat1, heat2, heat3);
return ESP_OK;
}
void control_task(void *param) { void control_task(void *param) {
esp_task_wdt_add(NULL); esp_task_wdt_add(NULL);
@@ -170,11 +171,22 @@ void control_task(void *param) {
const TickType_t xFrequency = pdMS_TO_TICKS(20); const TickType_t xFrequency = pdMS_TO_TICKS(20);
enabled = true; enabled = true;
sensors_init();
while (enabled) { while (enabled) {
vTaskDelayUntil(&xLastWakeTime, xFrequency); vTaskDelayUntil(&xLastWakeTime, xFrequency);
current_time = esp_timer_get_time(); fsm_now = esp_timer_get_time();
bool log = false;
/**** READ INPUTS ****/
for (uint8_t i = 0; i < N_BRIDGES; i++) {
process_bridge_current(i);
}
process_battery_voltage();
sensors_check();
/**** LISTEN TO COMMANDS ****/
fsm_cmd_t cmd; fsm_cmd_t cmd;
while (xQueueReceive(fsm_cmd_queue, &cmd, 0) == pdTRUE) { while (xQueueReceive(fsm_cmd_queue, &cmd, 0) == pdTRUE) {
// if (error != ESP_OK) continue; // don't do anything until error is cleared // if (error != ESP_OK) continue; // don't do anything until error is cleared
@@ -184,7 +196,8 @@ void control_task(void *param) {
// Check if we have remaining distance before starting // Check if we have remaining distance before starting
if (remaining_distance <= 0.0f) { if (remaining_distance <= 0.0f) {
ESP_LOGI(TAG, "FAILED TO START; NO REMAINING DISTANCE"); ESP_LOGI(TAG, "FAILED TO START; NO REMAINING DISTANCE");
error = SC_ERR_LEASH_HIT; fsm_error = SC_ERR_LEASH_HIT;
log = true;
continue; continue;
} }
this_move_dist = MIN(get_param_value_t(PARAM_DRIVE_DIST).f32, remaining_distance); this_move_dist = MIN(get_param_value_t(PARAM_DRIVE_DIST).f32, remaining_distance);
@@ -194,33 +207,34 @@ void control_task(void *param) {
if (get_battery_V() < get_param_value_t(PARAM_LOW_PROTECTION_V).f32) { if (get_battery_V() < get_param_value_t(PARAM_LOW_PROTECTION_V).f32) {
ESP_LOGI(TAG, "FAILED TO START; INSUFFICIENT VOLTAGE"); ESP_LOGI(TAG, "FAILED TO START; INSUFFICIENT VOLTAGE");
error = SC_ERR_LOW_BATTERY; fsm_error = SC_ERR_LOW_BATTERY;
continue; continue;
} }
if (!get_is_safe()) { if (!get_is_safe()) {
ESP_LOGI(TAG, "FAILED TO START; SAFETY NOT SET"); ESP_LOGI(TAG, "FAILED TO START; SAFETY NOT SET");
error = SC_ERR_SAFETY_TRIP; fsm_error = SC_ERR_SAFETY_TRIP;
continue; continue;
} }
if (efuse_is_tripped(BRIDGE_DRIVE)) { if (efuse_get(BRIDGE_DRIVE)) {
ESP_LOGI(TAG, "FAILED TO START; EFUSE 1 TRIP"); ESP_LOGI(TAG, "FAILED TO START; EFUSE 1 TRIP");
error = SC_ERR_EFUSE_TRIP_1; fsm_error = SC_ERR_EFUSE_TRIP_1;
continue; continue;
} }
if (efuse_is_tripped(BRIDGE_JACK)) { if (efuse_get(BRIDGE_JACK)) {
ESP_LOGI(TAG, "FAILED TO START; EFUSE 2 TRIP"); ESP_LOGI(TAG, "FAILED TO START; EFUSE 2 TRIP");
error = SC_ERR_EFUSE_TRIP_2; fsm_error = SC_ERR_EFUSE_TRIP_2;
continue; continue;
} }
if (efuse_is_tripped(BRIDGE_AUX)) { if (efuse_get(BRIDGE_AUX)) {
ESP_LOGI(TAG, "FAILED TO START; EFUSE 3 TRIP"); ESP_LOGI(TAG, "FAILED TO START; EFUSE 3 TRIP");
error = SC_ERR_EFUSE_TRIP_3; fsm_error = SC_ERR_EFUSE_TRIP_3;
continue; continue;
} }
ESP_LOGI(TAG, "STARTING"); ESP_LOGI(TAG, "STARTING");
error = ESP_OK; // if everything is OK now, we're OK. fsm_error = ESP_OK; // if everything is OK now, we're OK.
current_state = STATE_MOVE_START_DELAY; current_state = STATE_MOVE_START_DELAY;
log = true;
set_timer(TRANSITION_DELAY_US); set_timer(TRANSITION_DELAY_US);
} }
break; break;
@@ -229,9 +243,9 @@ void control_task(void *param) {
break; break;
case FSM_CMD_UNDO: case FSM_CMD_UNDO:
if (current_state != STATE_IDLE && if (current_state != STATE_IDLE &&
current_state != STATE_UNDO_JACK_START && current_state != STATE_UNDO_JACK_START) {
current_state != STATE_UNDO_JACK) {
current_state = STATE_UNDO_JACK_START; current_state = STATE_UNDO_JACK_START;
log = true;
} }
break; break;
case FSM_CMD_SHUTDOWN: case FSM_CMD_SHUTDOWN:
@@ -243,6 +257,7 @@ void control_task(void *param) {
if (current_state == STATE_IDLE if (current_state == STATE_IDLE
&& get_battery_V() > get_param_value_t(PARAM_LOW_PROTECTION_V).f32) { && get_battery_V() > get_param_value_t(PARAM_LOW_PROTECTION_V).f32) {
current_state = STATE_CALIBRATE_JACK_DELAY; current_state = STATE_CALIBRATE_JACK_DELAY;
log = true;
} }
break; break;
@@ -251,14 +266,16 @@ void control_task(void *param) {
if (current_state == STATE_CALIBRATE_JACK_DELAY if (current_state == STATE_CALIBRATE_JACK_DELAY
&& get_battery_V() > get_param_value_t(PARAM_LOW_PROTECTION_V).f32) { && get_battery_V() > get_param_value_t(PARAM_LOW_PROTECTION_V).f32) {
current_state = STATE_CALIBRATE_JACK_MOVE; current_state = STATE_CALIBRATE_JACK_MOVE;
log = true;
set_timer(CALIBRATE_JACK_MAX_TIME); set_timer(CALIBRATE_JACK_MAX_TIME);
} }
break; break;
case FSM_CMD_CALIBRATE_JACK_END: case FSM_CMD_CALIBRATE_JACK_END:
ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_JACK_END"); ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_JACK_END");
if (current_state == STATE_CALIBRATE_JACK_MOVE) { if (current_state == STATE_CALIBRATE_JACK_MOVE) {
fsm_cal_t = current_time - timer_start; fsm_cal_t = fsm_now - timer_start;
current_state = STATE_IDLE; current_state = STATE_IDLE;
log = true;
} }
break; break;
case FSM_CMD_CALIBRATE_JACK_FINISH: case FSM_CMD_CALIBRATE_JACK_FINISH:
@@ -274,6 +291,7 @@ void control_task(void *param) {
if (current_state == STATE_IDLE if (current_state == STATE_IDLE
&& get_battery_V() > get_param_value_t(PARAM_LOW_PROTECTION_V).f32) { && get_battery_V() > get_param_value_t(PARAM_LOW_PROTECTION_V).f32) {
current_state = STATE_CALIBRATE_DRIVE_DELAY; current_state = STATE_CALIBRATE_DRIVE_DELAY;
log = true;
} }
break; break;
@@ -282,6 +300,7 @@ void control_task(void *param) {
if (current_state == STATE_CALIBRATE_DRIVE_DELAY if (current_state == STATE_CALIBRATE_DRIVE_DELAY
&& get_battery_V() > get_param_value_t(PARAM_LOW_PROTECTION_V).f32) { && get_battery_V() > get_param_value_t(PARAM_LOW_PROTECTION_V).f32) {
current_state = STATE_CALIBRATE_DRIVE_MOVE; current_state = STATE_CALIBRATE_DRIVE_MOVE;
log = true;
set_timer(CALIBRATE_DRIVE_MAX_TIME); set_timer(CALIBRATE_DRIVE_MAX_TIME);
set_sensor_counter(SENSOR_DRIVE, 0); set_sensor_counter(SENSOR_DRIVE, 0);
} }
@@ -289,9 +308,10 @@ void control_task(void *param) {
case FSM_CMD_CALIBRATE_DRIVE_END: case FSM_CMD_CALIBRATE_DRIVE_END:
ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_DRIVE_END"); ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_DRIVE_END");
if (current_state == STATE_CALIBRATE_DRIVE_MOVE) { if (current_state == STATE_CALIBRATE_DRIVE_MOVE) {
fsm_cal_t = current_time - timer_start; fsm_cal_t = fsm_now - timer_start;
fsm_cal_e = get_sensor_counter(SENSOR_DRIVE); fsm_cal_e = get_sensor_counter(SENSOR_DRIVE);
current_state = STATE_IDLE; current_state = STATE_IDLE;
log = true;
} }
break; break;
case FSM_CMD_CALIBRATE_DRIVE_FINISH: case FSM_CMD_CALIBRATE_DRIVE_FINISH:
@@ -307,115 +327,81 @@ void control_task(void *param) {
} }
if (!enabled) break; if (!enabled) break;
// State transitions /**** STATE TRANSITIONS ****/
switch (current_state) { switch (current_state) {
case STATE_IDLE: case STATE_IDLE:
//ESP_LOGI("FSM", "IDLE @ %lld", current_time);
for (uint8_t i = 0; i < N_RELAYS; ++i) {
//ESP_LOGI("FSM", "t[%d] %lld", i, override_times[i]);
bool active = override_times[i] > current_time;
if (active) rtc_reset_shutdown_timer();
// Current limiting for manual jack down override (RELAY_B2)
if (i == RELAY_B2 && active) {
int64_t elapsed = current_time - (override_times[i] - get_param_value_t(PARAM_RF_PULSE_LENGTH).u32);
int64_t delay = get_param_value_t(PARAM_EFUSE_INRUSH_US).u32;
// After inrush delay, check for current spike
if (elapsed > delay) {
float current = get_bridge_A(BRIDGE_JACK);
float threshold = get_param_value_t(PARAM_JACK_I_DOWN).f32;
if (current > threshold) {
// Current spike detected - stop jacking down and start cooldown
override_times[i] = -1;
override_cooldown[i] = current_time + get_param_value_t(PARAM_EFUSE_TCOOL).u32;
active = false;
}
}
}
// prohibit movement past jack limit switch
//if (i == BRIDGE_JACK*2+(bridge_polarities[BRIDGE_JACK]>0?0:1) && get_sensor(SENSOR_JACK))
// setRelay(i, false);
//else
setRelay(i, active);
//if (active) ESP_LOGI("FSM", "RUN CHANNEL %d (%lld %c %lld)", i, (long long) override_times[i], active ? '>':'<', (long long) current_time);
}
break; break;
case STATE_MOVE_START_DELAY: case STATE_MOVE_START_DELAY:
if (!get_is_safe()) { if (!get_is_safe()) {
error = SC_ERR_SAFETY_TRIP; fsm_error = SC_ERR_SAFETY_TRIP;
current_state = STATE_IDLE; current_state = STATE_IDLE;
} log = true;
if (timer_done()) { } else if (timer_done()) {
current_state = STATE_JACK_UP_START; current_state = STATE_JACK_UP_START;
set_timer(JACK_TIME / 2); // First phase is half of total jack time set_timer(JACK_TIME / 2); // First phase is half of total jack time
jack_up_total_time = 0; // Reset jack up time tracker jack_start_us = fsm_now;
} }
break; break;
case STATE_JACK_UP_START: case STATE_JACK_UP_START:
if (!get_is_safe()) { if (!get_is_safe()) {
error = SC_ERR_SAFETY_TRIP; fsm_error = SC_ERR_SAFETY_TRIP;
current_state = STATE_UNDO_JACK_START; current_state = STATE_UNDO_JACK_START;
} jack_finish_us = fsm_now;
log = true;
{ } else {
// Track elapsed time
int64_t elapsed = current_time - timer_start; if (efuse_get(BRIDGE_JACK)) {
jack_up_total_time = elapsed; ESP_LOGI(TAG, "START->UP BY EFUSE");
current_state = STATE_JACK_UP;
jack_trans_us = fsm_now;
log = true;
set_timer(JACK_TIME);
}
// Get current sensing parameters if (get_bridge_overcurrent(BRIDGE_JACK, get_param_value_t(PARAM_JACK_I_UP).f32)) {
int64_t delay = get_param_value_t(PARAM_EFUSE_INRUSH_US).u32; ESP_LOGI(TAG, "START->UP BY CURRENT");
float current = get_bridge_A(BRIDGE_JACK); current_state = STATE_JACK_UP;
float threshold = get_param_value_t(PARAM_JACK_I_UP).f32; jack_trans_us = fsm_now;
log = true;
// After inrush delay, check for current spike OR half-time timeout set_timer(JACK_TIME);
if (elapsed > delay) {
if (current > threshold || timer_done()) {
ESP_LOGI(TAG, "START->UP BY CURRENT");
current_state = STATE_JACK_UP;
set_timer(JACK_TIME); // Second phase is also half of total jack time
}
} }
// E-fuse trip if (timer_done()) {
if (efuse_is_tripped(BRIDGE_JACK)) { ESP_LOGI(TAG, "START->UP BY TIME");
error = SC_ERR_EFUSE_TRIP_2; current_state = STATE_JACK_UP;
current_state = STATE_IDLE; jack_trans_us = fsm_now;
log = true;
set_timer(JACK_TIME);
} }
} }
break; break;
case STATE_JACK_UP: case STATE_JACK_UP:
if (!get_is_safe()) { if (!get_is_safe()) {
error = SC_ERR_SAFETY_TRIP; fsm_error = SC_ERR_SAFETY_TRIP;
current_state = STATE_UNDO_JACK_START; current_state = STATE_UNDO_JACK_START;
} jack_finish_us = fsm_now;
set_timer(JACK_DOWN_TIME);
{ log = true;
if (timer_done() || efuse_is_tripped(BRIDGE_JACK)) { } else {
if (timer_done() || efuse_get(BRIDGE_JACK)) {
// Track total time including first phase // Track total time including first phase
jack_up_total_time += current_time - timer_start; current_state = STATE_DRIVE_START_DELAY;
current_state = STATE_DRIVE_START_DELAY; jack_finish_us = fsm_now;
log = true;
set_timer(TRANSITION_DELAY_US); set_timer(TRANSITION_DELAY_US);
} }
if (efuse_is_tripped(BRIDGE_JACK)) {
error = SC_ERR_EFUSE_TRIP_2;
current_state = STATE_IDLE;
}
} }
break; break;
case STATE_DRIVE_START_DELAY: case STATE_DRIVE_START_DELAY:
if (!get_is_safe()) { if (!get_is_safe()) {
error = SC_ERR_SAFETY_TRIP; fsm_error = SC_ERR_SAFETY_TRIP;
current_state = STATE_UNDO_JACK_START; current_state = STATE_UNDO_JACK_START;
} set_timer(JACK_DOWN_TIME);
if (timer_done()) { log = true;
} else if (timer_done()) {
current_state = STATE_DRIVE; current_state = STATE_DRIVE;
log = true;
set_timer(DRIVE_TIME); set_timer(DRIVE_TIME);
// Set the encoder counter to track remaining distance in this move // Set the encoder counter to track remaining distance in this move
set_sensor_counter(SENSOR_DRIVE, -DRIVE_DIST); set_sensor_counter(SENSOR_DRIVE, -DRIVE_DIST);
@@ -425,10 +411,11 @@ void control_task(void *param) {
break; break;
case STATE_DRIVE: case STATE_DRIVE:
if (!get_is_safe()) { if (!get_is_safe()) {
error = SC_ERR_SAFETY_TRIP; fsm_error = SC_ERR_SAFETY_TRIP;
current_state = STATE_UNDO_JACK_START; current_state = STATE_UNDO_JACK_START;
} set_timer(JACK_DOWN_TIME);
{ log = true;
} else {
int32_t current_encoder = get_sensor_counter(SENSOR_DRIVE); int32_t current_encoder = get_sensor_counter(SENSOR_DRIVE);
int32_t ticks_traveled = current_encoder - move_start_encoder; int32_t ticks_traveled = current_encoder - move_start_encoder;
float ke = get_param_value_t(PARAM_DRIVE_KE).f32; float ke = get_param_value_t(PARAM_DRIVE_KE).f32;
@@ -443,81 +430,80 @@ void control_task(void *param) {
// remaining_distance -= distance_traveled; // remaining_distance -= distance_traveled;
current_state = STATE_DRIVE_END_DELAY; current_state = STATE_DRIVE_END_DELAY;
log = true;
set_timer(TRANSITION_DELAY_US); set_timer(TRANSITION_DELAY_US);
} }
if (efuse_is_tripped(BRIDGE_DRIVE)) { if (efuse_get(BRIDGE_DRIVE)) {
// Update remaining distance even on fault // Update remaining distance even on fault
remaining_distance -= distance_traveled; remaining_distance -= distance_traveled;
if (remaining_distance < 0.0f) remaining_distance = 0.0f; if (remaining_distance < 0.0f) remaining_distance = 0.0f;
error = SC_ERR_EFUSE_TRIP_1; fsm_error = SC_ERR_EFUSE_TRIP_1;
current_state = STATE_UNDO_JACK_START; current_state = STATE_UNDO_JACK_START;
set_timer(JACK_DOWN_TIME);
log = true;
} }
} }
break; break;
case STATE_DRIVE_END_DELAY: case STATE_DRIVE_END_DELAY:
if (!get_is_safe()) { if (!get_is_safe()) {
error = SC_ERR_SAFETY_TRIP; fsm_error = SC_ERR_SAFETY_TRIP;
current_state = STATE_UNDO_JACK_START; current_state = STATE_UNDO_JACK_START;
} log = true;
if (timer_done()) { } else if (timer_done()) {
current_state = STATE_JACK_DOWN; current_state = STATE_UNDO_JACK_START;
set_timer(jack_up_total_time); // Use the tracked jack up time log = true;
} }
break; break;
case STATE_JACK_DOWN: case STATE_JACK_DOWN:
if (!get_is_safe()) {
error = SC_ERR_SAFETY_TRIP; if (efuse_get(BRIDGE_JACK)) {
current_state = STATE_UNDO_JACK_START;
}
{
// Get current sensing parameters
int64_t delay = get_param_value_t(PARAM_EFUSE_INRUSH_US).u32;
int64_t elapsed = current_time - timer_start;
// After inrush delay, check for current spike
if (elapsed > delay) {
float current = get_bridge_A(BRIDGE_JACK);
float threshold = get_param_value_t(PARAM_JACK_I_DOWN).f32;
if (current > threshold) {
ESP_LOGI(TAG, "DOWN->IDLE BY CURRENT");
// Current spike detected - we've hit the ground
current_state = STATE_IDLE;
break;
}
}
// Timeout - finished jacking down ESP_LOGI(TAG, "DOWN->IDLE BY EFUSE");
if (timer_done()) { // Current spike detected
ESP_LOGI(TAG, "DOWN->IDLE BY TIME"); current_state = STATE_IDLE;
current_state = STATE_IDLE; log = true;
} break;
// E-fuse trip - assume we hit something hard
if (efuse_is_tripped(BRIDGE_JACK)) {
current_state = STATE_IDLE;
}
} }
if (get_bridge_overcurrent(BRIDGE_JACK, get_param_value_t(PARAM_JACK_I_DOWN).f32)) {
ESP_LOGI(TAG, "DOWN->IDLE BY OVERCURRENT");
// Current spike detected
current_state = STATE_IDLE;
log = true;
break;
}
if (get_bridge_spike(BRIDGE_JACK, get_param_value_t(PARAM_JACK_IS_DOWN).f32)) {
ESP_LOGI(TAG, "DOWN->IDLE BY SPIKE");
// Current spike detected
current_state = STATE_IDLE;
log = true;
break;
}
if (timer_done() ) {
ESP_LOGI(TAG, "DOWN->IDLE BY TIME");
current_state = STATE_IDLE;
log = true;
break;
}
break; break;
case STATE_UNDO_JACK_START: case STATE_UNDO_JACK_START:
// wait for e-fuse to un-trip // wait for e-fuse to un-trip
if (!efuse_is_tripped(BRIDGE_JACK)) { if (!efuse_get(BRIDGE_JACK)) {
current_state = STATE_UNDO_JACK; set_timer(JACK_DOWN_TIME);
set_timer(JACK_TIME); current_state = STATE_JACK_DOWN;
} log = true;
break;
case STATE_UNDO_JACK:
if (timer_done()){ // || get_sensor(SENSOR_JACK)) {
current_state = STATE_IDLE;
}
// assume we are jacked up all the way (e.g. sensor broke) and should stop
if (efuse_is_tripped(BRIDGE_JACK)) {
current_state = STATE_IDLE;
} }
break; break;
@@ -528,7 +514,7 @@ void control_task(void *param) {
case STATE_CALIBRATE_JACK_MOVE: case STATE_CALIBRATE_JACK_MOVE:
if (timer_done()) { if (timer_done()) {
current_state = STATE_IDLE; current_state = STATE_IDLE;
fsm_cal_t = current_time - timer_start; fsm_cal_t = fsm_now - timer_start;
} }
break; break;
@@ -539,7 +525,7 @@ void control_task(void *param) {
case STATE_CALIBRATE_DRIVE_MOVE: case STATE_CALIBRATE_DRIVE_MOVE:
if (!get_is_safe() || timer_done()) { if (!get_is_safe() || timer_done()) {
current_state = STATE_IDLE; current_state = STATE_IDLE;
fsm_cal_t = current_time - timer_start; fsm_cal_t = fsm_now - timer_start;
fsm_cal_e = get_sensor_counter(SENSOR_DRIVE); fsm_cal_e = get_sensor_counter(SENSOR_DRIVE);
} }
break; break;
@@ -547,110 +533,169 @@ void control_task(void *param) {
default: break; default: break;
} }
/**** SET OUTPUTS ****/
//int64_t elapsed_t = (current_time-timer_start);
//int64_t total_t = (timer_end-timer_start);
//int32_t ticks = get_sensor_counter(SENSOR_DRIVE);
//ESP_LOGI("FSM", "[%d] %lld / %lld ms, %ld ticks", current_state, (long long) elapsed_t, (long long) total_t, (long) ticks);
// Output control
switch (current_state) { switch (current_state) {
case STATE_IDLE: case STATE_IDLE:
//ESP_LOGI("FSM", "IDLE @ %lld", current_time); // In idle we still accept override commands
for (uint8_t i = 0; i < N_RELAYS; ++i) { if (override_time > fsm_now) {
//ESP_LOGI("FSM", "t[%d] %lld", i, override_times[i]); switch(override_cmd) {
bool active = override_times[i] > current_time; case FSM_OVERRIDE_DRIVE_FWD:
if (active) rtc_reset_shutdown_timer(); if (efuse_get(BRIDGE_DRIVE)){
driveRelays((relay_port_t){.bridges = {
// Current limiting for manual jack down override (RELAY_B2) .DRIVE=BRIDGE_OFF,
if (i == RELAY_B2 && active) { .JACK=BRIDGE_OFF,
int64_t elapsed = current_time - (override_times[i] - get_param_value_t(PARAM_RF_PULSE_LENGTH).u32); .AUX=BRIDGE_OFF
int64_t delay = get_param_value_t(PARAM_EFUSE_INRUSH_US).u32; }});
} else {
// After inrush delay, check for current spike driveRelays((relay_port_t){.bridges = {
if (elapsed > delay) { .DRIVE=BRIDGE_FWD,
float current = get_bridge_A(BRIDGE_JACK); .JACK=BRIDGE_OFF,
float threshold = get_param_value_t(PARAM_JACK_I_DOWN).f32; .AUX=BRIDGE_FWD
}});
if (current > threshold) { }
// Current spike detected - stop jacking down break;
override_times[i] = -1;
active = false; case FSM_OVERRIDE_DRIVE_REV:
} if (efuse_get(BRIDGE_DRIVE)){
} driveRelays((relay_port_t){.bridges = {
} .DRIVE=BRIDGE_OFF,
.JACK=BRIDGE_OFF,
// prohibit movement past jack limit switch .AUX=BRIDGE_OFF
//if (i == BRIDGE_JACK*2+(bridge_polarities[BRIDGE_JACK]>0?0:1) && get_sensor(SENSOR_JACK)) }});
// setRelay(i, false); } else {
//else driveRelays((relay_port_t){.bridges = {
setRelay(i, active); .DRIVE=BRIDGE_REV,
//if (active) ESP_LOGI("FSM", "RUN CHANNEL %d (%lld %c %lld)", i, (long long) override_times[i], active ? '>':'<', (long long) current_time); .JACK=BRIDGE_OFF,
.AUX=BRIDGE_OFF
} }});
}
break;
case FSM_OVERRIDE_JACK_UP:
if (efuse_get(BRIDGE_JACK)){
driveRelays((relay_port_t){.bridges = {
.DRIVE=BRIDGE_OFF,
.JACK=BRIDGE_OFF,
.AUX=BRIDGE_OFF
}});
} else {
driveRelays((relay_port_t){.bridges = {
.DRIVE=BRIDGE_OFF,
.JACK=BRIDGE_FWD,
.AUX=BRIDGE_OFF
}});
}
break;
case FSM_OVERRIDE_JACK_DOWN:
if (get_bridge_overcurrent(BRIDGE_JACK, get_param_value_t(PARAM_JACK_I_DOWN).f32) ||
get_bridge_spike(BRIDGE_JACK, get_param_value_t(PARAM_JACK_IS_DOWN).f32))
efuse_set(BRIDGE_JACK, EFUSE_OVERCURRENT);
if (efuse_get(BRIDGE_JACK)) {
driveRelays((relay_port_t){.bridges = {
.DRIVE=BRIDGE_OFF,
.JACK=BRIDGE_OFF,
.AUX=BRIDGE_OFF
}});
} else {
driveRelays((relay_port_t){.bridges = {
.DRIVE=BRIDGE_OFF,
.JACK=BRIDGE_REV,
.AUX=BRIDGE_OFF
}});
}
break;
case FSM_OVERRIDE_AUX:
if (efuse_get(BRIDGE_AUX)){
driveRelays((relay_port_t){.bridges = {
.DRIVE=BRIDGE_OFF,
.JACK=BRIDGE_OFF,
.AUX=BRIDGE_OFF
}});
} else {
driveRelays((relay_port_t){.bridges = {
.DRIVE=BRIDGE_OFF,
.JACK=BRIDGE_OFF,
.AUX=BRIDGE_FWD
}});
}
break;
default: // should never hit here but just in case...
driveRelays((relay_port_t){.bridges = {
.DRIVE=BRIDGE_OFF,
.JACK=BRIDGE_OFF,
.AUX=BRIDGE_OFF
}});
break;
}
rtc_reset_shutdown_timer();
log = true;
} else {
driveRelays((relay_port_t){.bridges = {
.DRIVE=BRIDGE_OFF,
.JACK=BRIDGE_OFF,
.AUX=BRIDGE_OFF
}});
}
break; break;
case STATE_CALIBRATE_JACK_MOVE: case STATE_CALIBRATE_JACK_MOVE:
case STATE_JACK_UP_START: case STATE_JACK_UP_START:
case STATE_JACK_UP: case STATE_JACK_UP:
// jack up and fluff // jack up and fluff
setRelay(RELAY_A1, false); driveRelays((relay_port_t){.bridges = {
setRelay(RELAY_B1, false); .DRIVE=BRIDGE_OFF,
.JACK=BRIDGE_FWD,
setRelay(RELAY_A2, true); .AUX=BRIDGE_FWD
setRelay(RELAY_B2, false); }});
setRelay(RELAY_A3, true);
rtc_reset_shutdown_timer(); rtc_reset_shutdown_timer();
log = true;
break; break;
case STATE_CALIBRATE_DRIVE_MOVE: case STATE_CALIBRATE_DRIVE_MOVE:
case STATE_DRIVE: case STATE_DRIVE:
// drive and fluff // drive and fluff
setRelay(RELAY_A1, true); driveRelays((relay_port_t){.bridges = {
setRelay(RELAY_B1, false); .DRIVE=BRIDGE_FWD,
.JACK=BRIDGE_OFF,
setRelay(RELAY_A2, false); .AUX=BRIDGE_FWD
setRelay(RELAY_B2, false); }});
setRelay(RELAY_A3, true);
rtc_reset_shutdown_timer(); rtc_reset_shutdown_timer();
log = true;
break; break;
case STATE_UNDO_JACK:
case STATE_JACK_DOWN: case STATE_JACK_DOWN:
// jack down and fluffer driveRelays((relay_port_t){.bridges = {
setRelay(RELAY_A1, false); .DRIVE=BRIDGE_OFF,
setRelay(RELAY_B1, false); .JACK=BRIDGE_REV,
.AUX=BRIDGE_OFF
setRelay(RELAY_A2, false); }});
setRelay(RELAY_B2, true);
setRelay(RELAY_A3, true);
rtc_reset_shutdown_timer(); rtc_reset_shutdown_timer();
log = true;
break; break;
case STATE_UNDO_JACK_START: case STATE_UNDO_JACK_START:
case STATE_DRIVE_START_DELAY: case STATE_DRIVE_START_DELAY:
case STATE_DRIVE_END_DELAY: case STATE_DRIVE_END_DELAY:
// only fluffer // only fluffer
setRelay(RELAY_A1, false); driveRelays((relay_port_t){.bridges = {
setRelay(RELAY_B1, false); .DRIVE=BRIDGE_OFF,
.JACK=BRIDGE_OFF,
setRelay(RELAY_A2, false); .AUX=BRIDGE_FWD
setRelay(RELAY_B2, false); }});
setRelay(RELAY_A3, true);
rtc_reset_shutdown_timer(); rtc_reset_shutdown_timer();
log = true;
break; break;
case STATE_CALIBRATE_JACK_DELAY: case STATE_CALIBRATE_JACK_DELAY:
default: default:
// invalid state; turn all relays off // invalid state; turn all relays off
setRelay(RELAY_A1, false); driveRelays((relay_port_t){.bridges = {
setRelay(RELAY_B1, false); .DRIVE=BRIDGE_OFF,
setRelay(RELAY_A2, false); .JACK=BRIDGE_OFF,
setRelay(RELAY_B2, false); .AUX=BRIDGE_OFF
setRelay(RELAY_A3, false); }});
break; break;
} }
/**** LOGGING ****/
if (log) send_fsm_log();
driveRelays();
esp_task_wdt_reset(); esp_task_wdt_reset();
} }
@@ -665,7 +710,7 @@ esp_err_t fsm_init() {
if (fsm_cmd_queue == NULL) { if (fsm_cmd_queue == NULL) {
fsm_cmd_queue = xQueueCreate(8, sizeof(fsm_cmd_t)); fsm_cmd_queue = xQueueCreate(8, sizeof(fsm_cmd_t));
} }
xTaskCreate(control_task, "FSM", 4096, NULL, 5, NULL); xTaskCreate(control_task, TAG, 4096, NULL, 10, NULL);
return ESP_OK; return ESP_OK;
} }

View File

@@ -34,7 +34,6 @@ typedef enum {
STATE_DRIVE, STATE_DRIVE,
STATE_DRIVE_END_DELAY, STATE_DRIVE_END_DELAY,
STATE_JACK_DOWN, STATE_JACK_DOWN,
STATE_UNDO_JACK,
STATE_UNDO_JACK_START, STATE_UNDO_JACK_START,
STATE_CALIBRATE_JACK_DELAY, STATE_CALIBRATE_JACK_DELAY,
@@ -43,6 +42,7 @@ typedef enum {
STATE_CALIBRATE_DRIVE_DELAY, STATE_CALIBRATE_DRIVE_DELAY,
STATE_CALIBRATE_DRIVE_MOVE STATE_CALIBRATE_DRIVE_MOVE
} fsm_state_t; } fsm_state_t;
#define LOG_TYPE_BAT 100
typedef enum { typedef enum {
RELAY_SENSORS = 0, RELAY_SENSORS = 0,
@@ -59,18 +59,32 @@ typedef enum {
BRIDGE_AUX = 2, BRIDGE_AUX = 2,
BRIDGE_JACK = 1, BRIDGE_JACK = 1,
BRIDGE_DRIVE = 0, BRIDGE_DRIVE = 0,
NUM_BRIDGES = 3,
} bridge_t; } bridge_t;
typedef enum {
BRIDGE_FWD = 0b10,
BRIDGE_REV = 0b01,
BRIDGE_OFF = 0b00,
BRIDGE_ON = 0b11
} bridge_dir_t;
typedef enum {
FSM_OVERRIDE_DRIVE_FWD,
FSM_OVERRIDE_DRIVE_REV,
FSM_OVERRIDE_JACK_UP,
FSM_OVERRIDE_JACK_DOWN,
FSM_OVERRIDE_AUX
} fsm_override_t;
#define N_RELAYS 8 #define N_RELAYS 8
#define N_BRIDGES 3 #define N_BRIDGES 3
void pulseOverride(relay_t relay/*, int64_t pulse*/); void pulseOverride(fsm_override_t cmd);
esp_err_t fsm_init(); esp_err_t fsm_init();
esp_err_t fsm_stop(); esp_err_t fsm_stop();
bool isRunning();
void fsm_set_cal_val(float v); void fsm_set_cal_val(float v);
int64_t fsm_get_cal_t(); int64_t fsm_get_cal_t();
int64_t fsm_get_cal_e(); int64_t fsm_get_cal_e();

View File

@@ -8,30 +8,6 @@
#include "esp_rom_sys.h" #include "esp_rom_sys.h"
#include "sensors.h" #include "sensors.h"
#define I2C_PORT I2C_NUM_0
#define TCA_ADDR_READ 0x21
#define TCA_ADDR_WRITE 0x21
#define I2C_PULLUP GPIO_PULLUP_DISABLE
#define I2C_FREQUENCY 400000
// TCA9555 Registers
#define TCA_REG_INPUT0 0x00
#define TCA_REG_INPUT1 0x01
#define TCA_REG_OUTPUT0 0x02
#define TCA_REG_OUTPUT1 0x03
#define TCA_REG_POLARITY0 0x04
#define TCA_REG_POLARITY1 0x05
#define TCA_REG_CONFIG0 0x06
#define TCA_REG_CONFIG1 0x07
// Debounce & Repeat Settings
#define DEBOUNCE_MS 50
#define REPEAT_MS 200
#define REPEAT_START_MS 700
#define SAFETY_MASK 0b00110001 // & permissible channels (jack and sensors)
#define SENSOR_EN_MASK 0b00000001 // | need forced high
// Static Variables // Static Variables
static bool i2c_initted = false; static bool i2c_initted = false;
//static bool safety_ok = false; // Safety interlock //static bool safety_ok = false; // Safety interlock
@@ -76,16 +52,8 @@ esp_err_t i2c_init(void) {
return ESP_OK; return ESP_OK;
} }
esp_err_t i2c_set_relays(uint8_t states) { esp_err_t i2c_set_relays(relay_port_t states) {
last_relay_request = states; // Always track the request return tca_write_word_8(TCA_REG_OUTPUT1, states.raw);
if (!get_is_safe()) {
// Safety interlock active - refuse to energize relays
if (states!=0) ESP_LOGW("I2C", "Main relay operation blocked by safety interlock");
return tca_write_word_8(TCA_REG_OUTPUT1, (states | SENSOR_EN_MASK) & SAFETY_MASK);
}
return tca_write_word_8(TCA_REG_OUTPUT1, states | SENSOR_EN_MASK);
} }
esp_err_t i2c_set_led1(uint8_t state) { esp_err_t i2c_set_led1(uint8_t state) {

View File

@@ -5,11 +5,53 @@
#include <stdbool.h> #include <stdbool.h>
#include "esp_err.h" #include "esp_err.h"
#define I2C_PORT I2C_NUM_0
#define TCA_ADDR_READ 0x21
#define TCA_ADDR_WRITE 0x21
#define I2C_PULLUP GPIO_PULLUP_DISABLE
#define I2C_FREQUENCY 400000
// TCA9555 Registers
#define TCA_REG_INPUT0 0x00
#define TCA_REG_INPUT1 0x01
#define TCA_REG_OUTPUT0 0x02
#define TCA_REG_OUTPUT1 0x03
#define TCA_REG_POLARITY0 0x04
#define TCA_REG_POLARITY1 0x05
#define TCA_REG_CONFIG0 0x06
#define TCA_REG_CONFIG1 0x07
// Debounce & Repeat Settings
#define DEBOUNCE_MS 50
#define REPEAT_MS 200
#define REPEAT_START_MS 700
typedef union {
uint8_t raw;
struct {
uint8_t SENSORS : 1; // [0]
uint8_t C3 : 1; // [1]
uint8_t B3 : 1; // [2]
uint8_t A3 : 1; // [3]
uint8_t B2 : 1; // [4]
uint8_t A2 : 1; // [5]
uint8_t B1 : 1; // [6]
uint8_t A1 : 1; // [7] MSB
} bits;
struct {
uint8_t SENSORS : 1;
uint8_t C3 : 1;
uint8_t AUX : 2;
uint8_t JACK : 2;
uint8_t DRIVE : 2;
} bridges;
} relay_port_t;
// Public Functions // Public Functions
esp_err_t i2c_init(void); esp_err_t i2c_init(void);
esp_err_t i2c_stop(void); esp_err_t i2c_stop(void);
esp_err_t i2c_set_relays(uint8_t states); esp_err_t i2c_set_relays(relay_port_t states);
esp_err_t i2c_set_led1(uint8_t state); esp_err_t i2c_set_led1(uint8_t state);
esp_err_t i2c_poll_buttons(); esp_err_t i2c_poll_buttons();

1180
main/log_test.c Normal file

File diff suppressed because it is too large Load Diff

47
main/log_test.h Normal file
View File

@@ -0,0 +1,47 @@
#ifndef LOG_TEST_H
#define LOG_TEST_H
#include "freertos/FreeRTOS.h" // Must be FIRST
#include "freertos/projdefs.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_err.h"
#include <stdint.h>
#include <stdbool.h>
// Test result structure
typedef struct {
const char* test_name;
bool passed;
const char* error_msg;
} test_result_t;
// Main test suite runner
esp_err_t run_all_log_tests(void);
// Individual test functions
bool test_log_integrity_basic(void);
bool test_log_integrity_wraparound(void);
bool test_log_head_tail_recovery_empty(void);
bool test_log_head_tail_recovery_partial_sector(void);
bool test_log_head_tail_recovery_full_sectors(void);
bool test_log_head_tail_recovery_wraparound(void);
bool test_log_head_equals_tail(void);
bool test_log_head_zero_tail_zero(void);
bool test_log_head_greater_than_tail(void);
bool test_log_head_less_than_tail(void);
bool test_log_wraparound_multiple_times(void);
bool test_log_sector_boundary_conditions(void);
bool test_log_maximum_payload(void);
bool test_log_minimum_payload(void);
bool test_log_corrupted_entry_recovery(void);
bool test_log_full_partition(void);
bool test_log_read_after_write(void);
bool test_log_multiple_types(void);
// Helper functions for testing
void print_test_results(test_result_t* results, int num_tests);
int count_passed_tests(test_result_t* results, int num_tests);
#endif // LOG_TEST_H

View File

@@ -1,5 +1,6 @@
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include "i2c.h" #include "i2c.h"
#include "log_test.h"
#include "storage.h" #include "storage.h"
#include "uart_comms.h" #include "uart_comms.h"
#include "esp_err.h" #include "esp_err.h"
@@ -17,49 +18,25 @@
#define TAG "MAIN" #define TAG "MAIN"
int64_t last_log_time = 0; int64_t last_bat_log_time = 0;
#define LOGSIZE 40 esp_err_t send_bat_log() {
esp_err_t send_log() { if(!rtc_is_set()) return ESP_OK;
uint8_t entry[LOGSIZE] = {};
uint8_t entry[12] = {};
entry[0] = fsm_get_state();
// Pack 64-bit timestamp into bytes 1-8 // Pack 64-bit timestamp into bytes 1-8
uint64_t be_timestamp = rtc_get_ms(); uint64_t be_timestamp = rtc_get_ms();
memcpy(&entry[1], &be_timestamp, 8); memcpy(&entry[0], &be_timestamp, 8);
// Pack 32-bit voltages/currents into bytes 9-24 // Pack 32-bit voltages/currents into bytes 9-24
float be_voltage = get_battery_V(); float be_voltage = get_battery_V();
memcpy(&entry[9], &be_voltage, 4); memcpy(&entry[8], &be_voltage, 4);
float be_current1 = get_bridge_A(BRIDGE_DRIVE);
memcpy(&entry[13], &be_current1, 4);
float be_current2 = get_bridge_A(BRIDGE_JACK);
memcpy(&entry[17], &be_current2, 4);
float be_current3 = get_bridge_A(BRIDGE_AUX);
memcpy(&entry[21], &be_current3, 4);
int16_t be_counter = get_sensor_counter(SENSOR_DRIVE); last_bat_log_time = esp_timer_get_time();
memcpy(&entry[25], &be_counter, 2);
entry[27] = pack_sensors(); log_write(entry, 12, LOG_TYPE_BAT);
float heat1 = get_bridge_heat(BRIDGE_DRIVE);
memcpy(&entry[28], &heat1, 4);
float heat2 = get_bridge_heat(BRIDGE_JACK);
memcpy(&entry[32], &heat2, 4);
float heat3 = get_bridge_heat(BRIDGE_AUX);
memcpy(&entry[36], &heat3, 4);
last_log_time = esp_timer_get_time();
log_write(entry, LOGSIZE);
ESP_LOGI(TAG, "WROTE LOG; %lld / %ld/%ld; %5.2f %5.2f %5.2f", (long long)rtc_get_ms(), (unsigned long)log_get_tail(), (unsigned long)log_get_head(), heat1, heat2, heat3);
return ESP_OK; return ESP_OK;
} }
@@ -126,15 +103,16 @@ void driveLEDs(led_state_t state) {
RTC_DATA_ATTR bool first_boot = true; RTC_DATA_ATTR bool first_boot = true;
void app_main(void) { void app_main(void) {esp_task_wdt_add(NULL);
esp_task_wdt_add(NULL);
//run_all_log_tests();
if (rtc_xtal_init() != ESP_OK) ESP_LOGE(TAG, "RTC FAILED"); if (rtc_xtal_init() != ESP_OK) ESP_LOGE(TAG, "RTC FAILED");
// Say hello; turn on the lights // Say hello; turn on the lights
esp_sleep_wakeup_cause_t cause = rtc_wakeup_cause(); esp_sleep_wakeup_cause_t cause = rtc_wakeup_cause();
if (i2c_init() != ESP_OK) ESP_LOGE(TAG, "I2C FAILED"); if (i2c_init() != ESP_OK) ESP_LOGE(TAG, "I2C FAILED");
i2c_set_relays(0); i2c_set_relays((relay_port_t){.raw=0});
driveLEDs(LED_STATE_BOOTING); driveLEDs(LED_STATE_BOOTING);
ESP_LOGI(TAG, "Firmware: %s", FIRMWARE_STRING); ESP_LOGI(TAG, "Firmware: %s", FIRMWARE_STRING);
@@ -188,8 +166,6 @@ void app_main(void) {
esp_restart(); esp_restart();
} }
first_boot = false;
// Every boot we load parameters and monitor solar, no matter what // Every boot we load parameters and monitor solar, no matter what
if (adc_init() != ESP_OK) ESP_LOGE(TAG, "ADC FAILED"); if (adc_init() != ESP_OK) ESP_LOGE(TAG, "ADC FAILED");
if (storage_init() != ESP_OK) ESP_LOGE(TAG, "STORAGE FAILED"); if (storage_init() != ESP_OK) ESP_LOGE(TAG, "STORAGE FAILED");
@@ -197,8 +173,10 @@ void app_main(void) {
if (solar_run_fsm() != ESP_OK) ESP_LOGE(TAG, "SOLAR FAILED"); if (solar_run_fsm() != ESP_OK) ESP_LOGE(TAG, "SOLAR FAILED");
// TODO: Do a 12V check and enter deep sleep if there's a problem // TODO: Do a 12V check and enter deep sleep if there's a problem
send_bat_log();
send_log();
//send_log();
//write_dummy_log_1(); //write_dummy_log_1();
@@ -214,17 +192,19 @@ void app_main(void) {
} else */if (cause == ESP_SLEEP_WAKEUP_EXT0) { } else */if (cause == ESP_SLEEP_WAKEUP_EXT0) {
ESP_LOGI("MAIN", "Woke from button press"); ESP_LOGI("MAIN", "Woke from button press");
} else { } else {
if (!rtc_alarm_tripped()) { if (!rtc_alarm_tripped() && !first_boot) {
//enter_deep_sleep(); rtc_enter_deep_sleep();
} }
} }
first_boot = false;
/*** FULL BOOT ***/ /*** FULL BOOT ***/
//if (uart_init() != ESP_OK) ESP_LOGE(TAG, "UART FAILED"); if (uart_init() != ESP_OK) ESP_LOGE(TAG, "UART FAILED");
if (power_init() != ESP_OK) ESP_LOGE(TAG, "POWER FAILED"); //if (power_init() != ESP_OK) ESP_LOGE(TAG, "POWER FAILED");
if (rf_433_init() != ESP_OK) ESP_LOGE(TAG, "RF FAILED"); if (rf_433_init() != ESP_OK) ESP_LOGE(TAG, "RF FAILED");
if (fsm_init() != ESP_OK) ESP_LOGE(TAG, "FSM FAILED"); if (fsm_init() != ESP_OK) ESP_LOGE(TAG, "FSM FAILED");
if (sensors_init() != ESP_OK) ESP_LOGE(TAG, "SENSORS FAILED"); //if (sensors_init() != ESP_OK) ESP_LOGE(TAG, "SENSORS FAILED");
if (webserver_init() != ESP_OK) ESP_LOGE(TAG, "WEBSERVER FAILED"); if (webserver_init() != ESP_OK) ESP_LOGE(TAG, "WEBSERVER FAILED");
/*** MAIN LOOP ***/ /*** MAIN LOOP ***/
@@ -276,16 +256,16 @@ void app_main(void) {
} }
// when not actively moving we log at a low frequency // when not actively moving we log at a low frequency
if (isRunning() || (esp_timer_get_time() > last_log_time + 3000000)) //DEEP_SLEEP_US)) if ((esp_timer_get_time() > last_bat_log_time + DEEP_SLEEP_US))
send_log(); send_bat_log();
if(i2c_get_button_ms(0) > 2100) if(i2c_get_button_ms(0) > 2100)
fsm_request(FSM_CMD_START); fsm_request(FSM_CMD_START);
break; break;
case STATE_UNDO_JACK: //case STATE_UNDO_JACK:
case STATE_UNDO_JACK_START: case STATE_UNDO_JACK_START:
// it's running the jack, but undoing // it's running the jack, but undoing
send_log(); //send_log();
driveLEDs(LED_STATE_CANCELLING); driveLEDs(LED_STATE_CANCELLING);
if (i2c_get_button_tripped(0)) { if (i2c_get_button_tripped(0)) {
ESP_LOGI(TAG, "AAAAH STOP!!!"); ESP_LOGI(TAG, "AAAAH STOP!!!");
@@ -294,31 +274,31 @@ void app_main(void) {
break; break;
case STATE_CALIBRATE_JACK_DELAY: case STATE_CALIBRATE_JACK_DELAY:
send_log(); //send_log();
if (i2c_get_button_tripped(0)) if (i2c_get_button_tripped(0))
fsm_request(FSM_CMD_CALIBRATE_JACK_START); fsm_request(FSM_CMD_CALIBRATE_JACK_START);
break; break;
case STATE_CALIBRATE_JACK_MOVE: case STATE_CALIBRATE_JACK_MOVE:
send_log(); //send_log();
if (i2c_get_button_tripped(0)) if (i2c_get_button_tripped(0))
fsm_request(FSM_CMD_CALIBRATE_JACK_END); fsm_request(FSM_CMD_CALIBRATE_JACK_END);
break; break;
case STATE_CALIBRATE_DRIVE_DELAY: case STATE_CALIBRATE_DRIVE_DELAY:
send_log(); //send_log();
if (i2c_get_button_tripped(0)) if (i2c_get_button_tripped(0))
fsm_request(FSM_CMD_CALIBRATE_DRIVE_START); fsm_request(FSM_CMD_CALIBRATE_DRIVE_START);
break; break;
case STATE_CALIBRATE_DRIVE_MOVE: case STATE_CALIBRATE_DRIVE_MOVE:
send_log(); //send_log();
if (i2c_get_button_tripped(0)) if (i2c_get_button_tripped(0))
fsm_request(FSM_CMD_CALIBRATE_DRIVE_END); fsm_request(FSM_CMD_CALIBRATE_DRIVE_END);
break; break;
default: default:
// it's running in every other case // it's running in every other case
send_log(); //send_log();
driveLEDs(LED_STATE_DRIVING); driveLEDs(LED_STATE_DRIVING);
if (i2c_get_button_tripped(0)) { if (i2c_get_button_tripped(0)) {
fsm_request(FSM_CMD_UNDO); fsm_request(FSM_CMD_UNDO);

View File

@@ -28,6 +28,8 @@
#include "esp_timer.h" #include "esp_timer.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "control_fsm.h" #include "control_fsm.h"
#include "i2c.h"
#include "sensors.h"
#include "soc/rtc_io_reg.h" #include "soc/rtc_io_reg.h"
#include "power_mgmt.h" #include "power_mgmt.h"
@@ -43,32 +45,104 @@
#define PIN_V_BATTERY ADC_CHANNEL_3 // GPIO39 / VN #define PIN_V_BATTERY ADC_CHANNEL_3 // GPIO39 / VN
#define PIN_V_SENS_BAT PIN_V_BATTERY #define PIN_V_SENS_BAT PIN_V_BATTERY
// map from relay number to bridge
/*bridge_t bridge_map[] = {
-1,
BRIDGE_AUX,
BRIDGE_AUX,
BRIDGE_AUX,
BRIDGE_JACK,
BRIDGE_JACK,
BRIDGE_DRIVE,
BRIDGE_DRIVE };*/
// update time // update time
#define UPDATE_MS 20 #define UPDATE_MS 20
#define UPDATE_S 0.02f #define UPDATE_S 0.02f
int64_t now; // us extern int64_t fsm_now; // us
// E-fuse data
typedef struct { typedef struct {
int64_t az_enable_time; // Timestamp to enable autozeroing at (negative to disable) int64_t az_enable_time; // Timestamp to enable autozeroing at (negative to disable)
float az_offset; // Accumulated zero offset float az_offset; // Accumulated zero offset
bool az_initialized; // First valid zero established bool az_initialized; // First valid zero established
float raw_current;
bool ema_init; bool ema_init;
float ema_current; float ema_current;
float current; // with all the corrections applied float current; // with all the corrections applied
float current_spike;
float heat; float heat;
bool tripped; efuse_trip_t tripped;
int64_t trip_time; int64_t trip_time;
// Inrush tolerance tracking int64_t on_us;
int64_t inrush_start_time; // When instantaneous overcurrent first detected (0 = not in overcurrent) int64_t off_us;
} isens_channel_t; } isens_channel_t;
static isens_channel_t isens[N_BRIDGES] = {0}; static isens_channel_t isens[N_BRIDGES] = {0};
/**** DRIVE RELAYS ****/
bool relay_states[8] = {false};
//int64_t bridge_transitions_on[NUM_BRIDGES] = {-1}; // last time relay turned on (used to ignore inrush)
//int64_t bridge_transitions_off[NUM_BRIDGES] = {-1}; // last time relay turned off (used to enable autozero)
relay_port_t last_relay_state;
// actually write relay states, taking note of transitions, and debouncing transitions to on.
#define BRIDGE_TRANSITION_LOGIC(BRIDGE_NAME) \
if (relay_state.bridges.BRIDGE_NAME == last_relay_state.bridges.BRIDGE_NAME) { \
/* no change; no need to do anything */ \
if(false) if (BRIDGE_##BRIDGE_NAME == BRIDGE_JACK) ESP_LOGI(TAG, "NO CHANGE"); \
} \
else if (last_relay_state.bridges.BRIDGE_NAME != BRIDGE_OFF && relay_state.bridges.BRIDGE_NAME == BRIDGE_OFF) { \
isens[BRIDGE_##BRIDGE_NAME].off_us = fsm_now; \
if(false) if (BRIDGE_##BRIDGE_NAME == BRIDGE_JACK) ESP_LOGI(TAG, "ON -> OFF"); \
} \
else if (last_relay_state.bridges.BRIDGE_NAME == BRIDGE_OFF && relay_state.bridges.BRIDGE_NAME != BRIDGE_OFF) { \
if (fsm_now > isens[BRIDGE_##BRIDGE_NAME].off_us + 2*get_param_value_t(PARAM_EFUSE_INRUSH_US).u32) { \
isens[BRIDGE_##BRIDGE_NAME].on_us = fsm_now; \
if(false) if (BRIDGE_##BRIDGE_NAME == BRIDGE_JACK) ESP_LOGI(TAG, "OFF -> ON"); \
} else { \
relay_state.bridges.BRIDGE_NAME = BRIDGE_OFF; \
if(false) if (BRIDGE_##BRIDGE_NAME == BRIDGE_JACK) ESP_LOGI(TAG, "NOT YET; -> OFF"); \
} \
} \
else { \
if(false) if (BRIDGE_##BRIDGE_NAME == BRIDGE_JACK) ESP_LOGE(TAG, "TOO FAST OF TRANSITION"); \
isens[BRIDGE_##BRIDGE_NAME].off_us = fsm_now; \
relay_state.bridges.BRIDGE_NAME = BRIDGE_OFF; \
}
esp_err_t driveRelays(relay_port_t relay_state) {
// Four types of transitions.
// Not a transition: this does nothing
// Anything -> off: always allowed. Record the transition time
// off -> anything: has debouncing; set & record transition if fsm_now > bridge_transitions_off + debounce, otherwise keep bridge off.
// fwd/rev/on -> fwd/rev/on: not allowed. Actually go to 0. Record the transition time.
BRIDGE_TRANSITION_LOGIC(DRIVE)
BRIDGE_TRANSITION_LOGIC(JACK)
BRIDGE_TRANSITION_LOGIC(AUX)
relay_state.bridges.SENSORS = 1;
if (!get_is_safe())
relay_state.bridges.DRIVE = 0;
last_relay_state = relay_state;
//ESP_LOGI(TAG, "RELAY STATE: %x", state);
return i2c_set_relays(relay_state);
}
/**** CURRENT / VOLTAGE MONITORING ****/
// === ADC Handles === // === ADC Handles ===
static adc_oneshot_unit_handle_t adc1_handle = NULL; static adc_oneshot_unit_handle_t adc1_handle = NULL;
static adc_cali_handle_t adc_cali_handle = NULL; static adc_cali_handle_t adc_cali_handle = NULL;
@@ -141,13 +215,28 @@ esp_err_t process_battery_voltage(void)
return ESP_OK; return ESP_OK;
} }
void set_autozero(bridge_t bridge) { void disable_autozero(bridge_t bridge) {
// enable autozeroing for this bridge 1 second from now // enable autozeroing for this bridge 1 second from now
isens[bridge].az_enable_time = now+1000000; isens[bridge].az_enable_time = fsm_now+1000000;
//ESP_LOGI(TAG, "KILLING BRIDGE %d; %lld -> %lld", bridge, (long long int) now, (long long int) isens[bridge].az_enable_time); //ESP_LOGI(TAG, "KILLING BRIDGE %d; %lld -> %lld", bridge, (long long int) now, (long long int) isens[bridge].az_enable_time);
} }
bool get_bridge_overcurrent(bridge_t bridge, float threshold) {
if (bridge < 0 || bridge>=NUM_BRIDGES) return true; // I GUESS?
if (fsm_now < isens[bridge].on_us + get_param_value_t(PARAM_EFUSE_INRUSH_US).u32) return false;
if (isens[bridge].raw_current < threshold) return false;
return true;
}
bool get_bridge_spike(bridge_t bridge, float threshold) {
if (bridge < 0 || bridge>=NUM_BRIDGES) return true; // I GUESS?
if (fsm_now < isens[bridge].on_us + get_param_value_t(PARAM_EFUSE_INRUSH_US).u32) return false;
if (isens[bridge].current_spike < threshold) return false;
return true;
}
esp_err_t process_bridge_current(bridge_t bridge) { esp_err_t process_bridge_current(bridge_t bridge) {
if (bridge < 0 || bridge >= NUM_BRIDGES) return ESP_ERR_INVALID_ARG;
int adc_raw = 0; int adc_raw = 0;
int voltage_mv = 0; int voltage_mv = 0;
@@ -158,7 +247,7 @@ esp_err_t process_bridge_current(bridge_t bridge) {
case BRIDGE_DRIVE: pin = PIN_V_ISENS1; break; case BRIDGE_DRIVE: pin = PIN_V_ISENS1; break;
case BRIDGE_JACK: pin = PIN_V_ISENS2; break; case BRIDGE_JACK: pin = PIN_V_ISENS2; break;
case BRIDGE_AUX: pin = PIN_V_ISENS3; break; case BRIDGE_AUX: pin = PIN_V_ISENS3; break;
default: return -42069; // lol default: return ESP_ERR_INVALID_ARG;
} }
if (adc_oneshot_read(adc1_handle, pin, &adc_raw) != ESP_OK) { if (adc_oneshot_read(adc1_handle, pin, &adc_raw) != ESP_OK) {
@@ -168,39 +257,41 @@ esp_err_t process_bridge_current(bridge_t bridge) {
return 0; return 0;
} }
float raw_a = NAN; float last_current = channel->raw_current;
channel->raw_current = NAN;
switch (bridge) { switch (bridge) {
case BRIDGE_JACK: case BRIDGE_JACK:
case BRIDGE_AUX: case BRIDGE_AUX:
// ACS37042KLHBLT-030B3 is 30A capable and 44 mV/A // ACS37042KLHBLT-030B3 is 30A capable and 44 mV/A
raw_a = (voltage_mv - 1650.0f) / 44.0f; channel->raw_current = (voltage_mv - 1650.0f) / 44.0f;
break; break;
case BRIDGE_DRIVE: case BRIDGE_DRIVE:
// ACS37220LEZATR-100B3 is 100A capable and 13.2 mV/A // ACS37220LEZATR-100B3 is 100A capable and 13.2 mV/A
raw_a = -(voltage_mv - 1650.0f) / 13.2f; channel->raw_current = -(voltage_mv - 1650.0f) / 13.2f;
break; break;
default: break;
} }
if (!channel->ema_init) { if (!channel->ema_init) {
channel->ema_current = (float)raw_a; channel->ema_current = channel->raw_current;
channel->ema_init = true; channel->ema_init = true;
} else { } else {
float alpha = get_param_value_t(PARAM_ADC_ALPHA_ISENS).f32; float alpha = get_param_value_t(PARAM_ADC_ALPHA_ISENS).f32;
if (isnan(raw_a)) { if (isnan(channel->raw_current)) {
//ESP_LOGI(TAG, "RAW BATTERY IS NAN"); //ESP_LOGI(TAG, "RAW BATTERY IS NAN");
channel->ema_current = NAN; channel->ema_current = NAN;
} else { } else {
if (isnan(ema_battery) || isnan(alpha)) { if (isnan(ema_battery) || isnan(alpha)) {
channel->ema_current = raw_a; channel->ema_current = channel->raw_current;
} else { } else {
channel->ema_current = alpha * raw_a + (1.0f - alpha) * channel->ema_current; channel->ema_current = alpha * channel->raw_current + (1.0f - alpha) * channel->ema_current;
} }
} }
} }
// === AUTO-ZERO LEARNING PHASE === // === AUTO-ZERO LEARNING PHASE ===
if (now > channel->az_enable_time) { if (fsm_now > channel->az_enable_time) {
//ESP_LOGI(TAG, "AZING %d", bridge); //ESP_LOGI(TAG, "AZING %d", bridge);
float db = get_param_value_t(PARAM_ADC_DB_IAZ).f32; float db = get_param_value_t(PARAM_ADC_DB_IAZ).f32;
if (isnan(db) || fabsf(channel->ema_current) <= db) { if (isnan(db) || fabsf(channel->ema_current) <= db) {
@@ -210,7 +301,7 @@ esp_err_t process_bridge_current(bridge_t bridge) {
channel->az_initialized = true; channel->az_initialized = true;
} else { } else {
float alpha = get_param_value_t(PARAM_ADC_ALPHA_IAZ).f32; float alpha = get_param_value_t(PARAM_ADC_ALPHA_IAZ).f32;
if (isnan(raw_a)) { if (isnan(channel->raw_current)) {
//ESP_LOGI(TAG, "RAW BATTERY IS NAN"); //ESP_LOGI(TAG, "RAW BATTERY IS NAN");
} else { } else {
if (isnan(ema_battery) || isnan(alpha)) { if (isnan(ema_battery) || isnan(alpha)) {
@@ -225,7 +316,9 @@ esp_err_t process_bridge_current(bridge_t bridge) {
} }
// Apply the offset // Apply the offset
channel->current = channel->ema_current - channel->az_offset; channel->current = channel->raw_current - channel->az_offset;
channel->raw_current = channel->raw_current - channel->az_offset;
channel->current_spike = channel->raw_current - last_current;
// PARAMETERS FOR E-FUSING ALGORITHM // PARAMETERS FOR E-FUSING ALGORITHM
@@ -245,31 +338,21 @@ esp_err_t process_bridge_current(bridge_t bridge) {
case BRIDGE_AUX: case BRIDGE_AUX:
I_nominal = get_param_value_t(PARAM_EFUSE_INOM_3).f32; I_nominal = get_param_value_t(PARAM_EFUSE_INOM_3).f32;
break; break;
} default: break;
}
// Normalize the current as a fraction of rated current
// Normalize the current as a fraction of rated current
float I_norm = fabsf(channel->current / I_nominal); float I_norm = fabsf(channel->current / I_nominal);
// Instant trip on extreme overcurrent - but with inrush tolerance // Instant trip on extreme overcurrent
if (I_norm >= get_param_value_t(PARAM_EFUSE_KINST).f32) { if (fsm_now > channel->on_us + get_param_value_t(PARAM_EFUSE_INRUSH_US).u32
// Start tracking if this is the first time we've seen overcurrent && I_norm >= get_param_value_t(PARAM_EFUSE_KINST).f32) {
if (channel->inrush_start_time == 0) {
channel->inrush_start_time = now;
}
// Check if overcurrent has persisted long enough // Check if overcurrent has persisted long enough
int64_t inrush_duration = now - channel->inrush_start_time; channel->tripped = true;
if (inrush_duration >= get_param_value_t(PARAM_EFUSE_INRUSH_US).u32) { channel->trip_time = fsm_now;
channel->tripped = true; //ESP_LOGI(TAG, "FUSE TRIP: Inom: %+.5f HEAT:%+2.5f", I_norm, channel->heat);
channel->trip_time = now; return ESP_OK; // no more processing, if we're over, we're over
channel->inrush_start_time = 0; // Reset for next time
//ESP_LOGI(TAG, "FUSE TRIP: Inom: %+.5f HEAT:%+2.5f", I_norm, channel->heat);
return ESP_OK; // no more processing, if we're over, we're over
}
// Still in overcurrent but within inrush tolerance window - don't trip yet // Still in overcurrent but within inrush tolerance window - don't trip yet
} else {
// Current dropped below threshold - reset inrush timer
channel->inrush_start_time = 0;
} }
// Accumulate heat // Accumulate heat
@@ -289,19 +372,20 @@ esp_err_t process_bridge_current(bridge_t bridge) {
// Ergo, heat is measured in seconds // Ergo, heat is measured in seconds
if (channel->heat > get_param_value_t(PARAM_EFUSE_HEAT_THRESH).f32) { if (channel->heat > get_param_value_t(PARAM_EFUSE_HEAT_THRESH).f32) {
channel->tripped = true; channel->tripped = true;
channel->trip_time = now; channel->trip_time = fsm_now;
// If we're not overheated // If we're not overheated
// And enough time has passed // And enough time has passed
// Go ahead and reset the e-fuse // Go ahead and reset the e-fuse
} else if (channel->tripped && } else if (channel->tripped &&
(now - channel->trip_time) > get_param_value_t(PARAM_EFUSE_TCOOL).u32) { (fsm_now - channel->trip_time) > get_param_value_t(PARAM_EFUSE_TCOOL).u32) {
channel->tripped = false; channel->tripped = false;
// channel.heat = 0.0f // I think we should wait for the e-fuse to catch up // channel.heat = 0.0f // I think we should wait for the e-fuse to catch up
} }
//if (bridge == BRIDGE_JACK) //if (bridge == BRIDGE_JACK) ESP_LOGI(TAG, "TIME: %lld", (long long) fsm_now);
//ESP_LOGI(TAG, "FUSE: raw_a: %+.4f cur: %+.4f Inorm: %+.5f HEAT:%+2.5f", raw_a, channel->current, I_norm, channel->heat);
//if (bridge == BRIDGE_JACK) ESP_LOGI(TAG, "FUSE: trip [%d] %lld, raw_a: %+.4f cur: %+.4f Inorm: %+.5f HEAT:%+2.5f", channel->tripped, channel->trip_time, channel->raw_current, channel->current, I_norm, channel->heat);
return ESP_OK; return ESP_OK;
} }
@@ -313,9 +397,13 @@ float get_bridge_A(bridge_t bridge)
if (bridge >= N_BRIDGES) return NAN; if (bridge >= N_BRIDGES) return NAN;
return isens[bridge].current; return isens[bridge].current;
} }
float get_bridge_raw_A(bridge_t bridge)
{
if (bridge >= N_BRIDGES) return NAN;
return isens[bridge].raw_current;
}
float efuse_get_heat(bridge_t bridge) {
float get_bridge_heat(bridge_t bridge) {
if (bridge >= N_BRIDGES) return NAN; if (bridge >= N_BRIDGES) return NAN;
return isens[bridge].heat; return isens[bridge].heat;
} }
@@ -327,57 +415,14 @@ float get_battery_V(void)
return get_raw_battery_voltage(); return get_raw_battery_voltage();
} }
// === Public E-Fuse Controls === efuse_trip_t efuse_get(bridge_t bridge)
/*void efuse_reset_all(void)
{
for (uint8_t i = 0; i < N_BRIDGES; i++) {
isens[i].heat = 0.0f;
isens[i].tripped = false;
}
}*/
bool efuse_is_tripped(bridge_t bridge)
{ {
if (bridge >= N_BRIDGES) return false; if (bridge >= N_BRIDGES) return false;
return isens[bridge].tripped; return isens[bridge].tripped;
} }
void efuse_set(bridge_t bridge, efuse_trip_t state)
// === Power Management Task === {
void power_mgmt_task(void *param) { if (bridge >= N_BRIDGES) return;
esp_task_wdt_add(NULL); isens[bridge].tripped = state;
isens[bridge].trip_time = fsm_now;
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = pdMS_TO_TICKS(UPDATE_MS);
while (1) {
vTaskDelayUntil(&xLastWakeTime, xFrequency);
now = esp_timer_get_time(); // us
/*if (now - last_wake_time < period) {
uint32_t delay_us = (period - (now - last_wake_time)) / 1000;
if (delay_us > 0) vTaskDelay(pdMS_TO_TICKS(delay_us));
continue;
}
last_wake_time = now;*/
// Sample currents
for (uint8_t i = 0; i < N_BRIDGES; i++) {
process_bridge_current(i);
}
process_battery_voltage();
esp_task_wdt_reset();
}
}
esp_err_t power_init() {
xTaskCreate(power_mgmt_task, "PWR", 4096, NULL, 5, NULL);
return ESP_OK;
}
esp_err_t power_stop() {
return ESP_OK;
} }

View File

@@ -12,19 +12,36 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include "esp_err.h" #include "esp_err.h"
#include "i2c.h"
typedef enum {
EFUSE_OK = 0,
EFUSE_OVERCURRENT = 1,
EFUSE_OVERHEAT = 2,
} efuse_trip_t;
//void efuse_reset_all(void); // Clear all trip states (manual/programmatic reset) //void efuse_reset_all(void); // Clear all trip states (manual/programmatic reset)
bool efuse_is_tripped(bridge_t bridge); // Query if bridge is currently faulted void efuse_clear (bridge_t bridge);
efuse_trip_t efuse_get (bridge_t bridge); // Query if bridge is currently faulted
float efuse_get_heat(bridge_t bridge);
void efuse_set(bridge_t bridge, efuse_trip_t state);
float get_bridge_A(bridge_t bridge); float get_bridge_A(bridge_t bridge);
float get_bridge_raw_A(bridge_t bridge);
float get_battery_V(); float get_battery_V();
float get_bridge_heat(bridge_t bridge);
void set_autozero(bridge_t bridge); void disable_autozero(bridge_t bridge);
bool get_bridge_overcurrent(bridge_t bridge, float threshold);
bool get_bridge_spike(bridge_t bridge, float threshold);
esp_err_t process_bridge_current(bridge_t bridge);
esp_err_t process_battery_voltage();
esp_err_t adc_init(); esp_err_t adc_init();
esp_err_t power_init(); esp_err_t power_init();
esp_err_t power_stop(); esp_err_t power_stop();
esp_err_t driveRelays(relay_port_t relay_state);
#endif /* MAIN_POWER_MGMT_H_ */ #endif /* MAIN_POWER_MGMT_H_ */

View File

@@ -155,10 +155,10 @@ static void rf_433_receiver_task(void* param) {
// Compare just the code (lower 32 bits) // Compare just the code (lower 32 bits)
if ((uint32_t)match == code && code!=0) { if ((uint32_t)match == code && code!=0) {
switch (i) { switch (i) {
case 0: pulseOverride(RELAY_A1); pulseOverride(RELAY_A3); break; case 0: pulseOverride(FSM_OVERRIDE_DRIVE_FWD); break;
case 1: pulseOverride(RELAY_B1); pulseOverride(RELAY_A3); break; case 1: pulseOverride(FSM_OVERRIDE_DRIVE_REV); break;
case 2: pulseOverride(RELAY_A2); break; case 2: pulseOverride(FSM_OVERRIDE_JACK_UP); break;
case 3: pulseOverride(RELAY_B2); break; case 3: pulseOverride(FSM_OVERRIDE_JACK_DOWN); break;
default: break; default: break;
} }
} }
@@ -210,7 +210,7 @@ static void rf_433_receiver_task(void* param) {
} }
esp_err_t rf_433_init() { esp_err_t rf_433_init() {
xTaskCreate(rf_433_receiver_task, TAG, 4096, NULL, 10, NULL); xTaskCreate(rf_433_receiver_task, TAG, 4096, NULL, 5, NULL);
return ESP_OK; return ESP_OK;
} }

View File

@@ -58,10 +58,31 @@ static void IRAM_ATTR sensor_isr_handler(void* arg) {
if (xHigherPriorityTaskWoken) portYIELD_FROM_ISR(); if (xHigherPriorityTaskWoken) portYIELD_FROM_ISR();
} }
// Debounce task: Processes queue, updates state & count esp_err_t sensors_init() {
static void sensor_debounce_task(void* param) {
esp_task_wdt_add(NULL); gpio_config_t io_conf = {
sensor_event_t evt; .pin_bit_mask = (1ULL << sensor_pins[0]) | (1ULL << sensor_pins[1]),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_ANYEDGE,
};
ESP_ERROR_CHECK(gpio_config(&io_conf));
sensor_event_queue = xQueueCreate(16, sizeof(sensor_event_t));
if (!sensor_event_queue) {
ESP_LOGE(TAG, "Failed to create sensor queue");
return ESP_FAIL;
}
// Install ISR service
ESP_ERROR_CHECK(gpio_install_isr_service(0));
for (uint8_t i = 0; i < N_SENSORS; i++) {
ESP_ERROR_CHECK(gpio_isr_handler_add(sensor_pins[i], sensor_isr_handler, INT2VOIDP(sensor_pins[i])));
sensor_stable_state[i] = !gpio_get_level(sensor_pins[i]);
}
//static uint64_t last_processed_time[N_SENSORS] = {0}; //static uint64_t last_processed_time[N_SENSORS] = {0};
// Initialize stable state // Initialize stable state
@@ -82,61 +103,65 @@ static void sensor_debounce_task(void* param) {
safety_high_start_time = esp_timer_get_time(); safety_high_start_time = esp_timer_get_time();
} }
return ESP_OK;
}
void sensors_check() {
sensor_event_t evt;
uint8_t i = 0; uint8_t i = 0;
if (xQueueReceive(sensor_event_queue, &evt, pdMS_TO_TICKS(10)) == pdTRUE) {
i = evt.sensor_id;
ESP_LOGI("SENS", "EVENT %d", i);
bool current_raw = !gpio_get_level(sensor_pins[i]);
while (1) { sensor_stable_state[i] = current_raw;
if (xQueueReceive(sensor_event_queue, &evt, pdMS_TO_TICKS(10)) == pdTRUE) {
i = evt.sensor_id; if (current_raw && !last_raw_state[i]){
ESP_LOGI("SENS", "FALLING");
ESP_LOGI("SENS", "EVENT %d", i); sensor_count[i]++;
}
bool current_raw = !gpio_get_level(sensor_pins[i]); if (!current_raw && last_raw_state[i]){
ESP_LOGI("SENS", "RISING");
sensor_stable_state[i] = current_raw; sensor_count[i]++;
if (current_raw && !last_raw_state[i]){
ESP_LOGI("SENS", "FALLING");
sensor_count[i]++;
}
if (!current_raw && last_raw_state[i]){
ESP_LOGI("SENS", "RISING");
sensor_count[i]++;
}
last_raw_state[i] = current_raw;
} }
// Handle safety sensor debouncing with asymmetric timing last_raw_state[i] = current_raw;
bool safety_current = !gpio_get_level(sensor_pins[SENSOR_SAFETY]);
uint64_t now = esp_timer_get_time();
if (safety_current) {
// Safety sensor is LOW (active)
if (safety_low_start_time == 0) {
// First time going low, start timing
safety_low_start_time = now;
safety_high_start_time = 0;
ESP_LOGI(TAG, "Safety sensor went LOW, starting make timer");
} else if (!is_safe && (now - safety_low_start_time >= SAFETY_MAKE_DEBOUNCE_US)) {
is_safe = true;
ESP_LOGW(TAG, "SAFETY MADE - Relays enabled");
}
} else {
// Safety sensor is HIGH (inactive)
if (safety_high_start_time == 0) {
// First time going high, start timing
safety_high_start_time = now;
safety_low_start_time = 0;
ESP_LOGI(TAG, "Safety sensor went HIGH, starting break timer");
} else if (is_safe && (now - safety_high_start_time >= SAFETY_BREAK_DEBOUNCE_US)) {
is_safe = false;
i2c_set_relays(0);
ESP_LOGI(TAG, "SAFETY BREAK - Relays disabled");
}
}
esp_task_wdt_reset();
} }
// Handle safety sensor debouncing with asymmetric timing
bool safety_current = !gpio_get_level(sensor_pins[SENSOR_SAFETY]);
uint64_t now = esp_timer_get_time();
if (safety_current) {
// Safety sensor is LOW (active)
if (safety_low_start_time == 0) {
// First time going low, start timing
safety_low_start_time = now;
safety_high_start_time = 0;
ESP_LOGI(TAG, "Safety sensor went LOW, starting make timer");
} else if (!is_safe && (now - safety_low_start_time >= SAFETY_MAKE_DEBOUNCE_US)) {
is_safe = true;
ESP_LOGW(TAG, "SAFETY MADE - Relays enabled");
}
} else {
// Safety sensor is HIGH (inactive)
if (safety_high_start_time == 0) {
// First time going high, start timing
safety_high_start_time = now;
safety_low_start_time = 0;
ESP_LOGI(TAG, "Safety sensor went HIGH, starting break timer");
} else if (is_safe && (now - safety_high_start_time >= SAFETY_BREAK_DEBOUNCE_US)) {
is_safe = false;
i2c_set_relays((relay_port_t){.raw=0});
ESP_LOGI(TAG, "SAFETY BREAK - Relays disabled");
}
}
esp_task_wdt_reset();
} }
int8_t pack_sensors() { int8_t pack_sensors() {
@@ -148,7 +173,7 @@ int8_t pack_sensors() {
return ret; return ret;
} }
esp_err_t sensors_init() { /*esp_err_t sensors_init() {
gpio_config_t io_conf = { gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << sensor_pins[0]) | (1ULL << sensor_pins[1]), .pin_bit_mask = (1ULL << sensor_pins[0]) | (1ULL << sensor_pins[1]),
.mode = GPIO_MODE_INPUT, .mode = GPIO_MODE_INPUT,
@@ -185,7 +210,7 @@ esp_err_t sensors_stop() {
vQueueDelete(sensor_event_queue); vQueueDelete(sensor_event_queue);
return ESP_OK; return ESP_OK;
} }*/
// Public API // Public API
bool get_sensor(sensor_t i) { bool get_sensor(sensor_t i) {

View File

@@ -33,6 +33,7 @@ bool get_is_safe(void);
int8_t pack_sensors(); int8_t pack_sensors();
esp_err_t sensors_init(); esp_err_t sensors_init();
esp_err_t sensors_stop(); void sensors_check();
//esp_err_t sensors_stop();
#endif /* MAIN_SENSORS_H_ */ #endif /* MAIN_SENSORS_H_ */

View File

@@ -29,8 +29,9 @@ static bool log_task_running = false;
// Log queue entry structure // Log queue entry structure
typedef struct { typedef struct {
uint8_t data[LOG_MAX_PAYLOAD + 1]; // +1 for length byte uint8_t data[LOG_MAX_PAYLOAD]; // +1 for length byte
uint8_t len; uint8_t len;
uint8_t type;
} log_queue_entry_t; } log_queue_entry_t;
// ============================================================================ // ============================================================================
@@ -48,9 +49,6 @@ typedef struct {
// Helper macro to check if a byte is a valid log type // Helper macro to check if a byte is a valid log type
#define IS_VALID_LOG_TYPE(x) ((x) >= 0xC0 && (x) <= 0xCF) #define IS_VALID_LOG_TYPE(x) ((x) >= 0xC0 && (x) <= 0xCF)
// Maximum payload size per log entry (255 max due to 1-byte size field)
#define LOG_MAX_PAYLOAD 255
// ============================================================================ // ============================================================================
// PARAMETER TABLE GENERATION // PARAMETER TABLE GENERATION
// ============================================================================ // ============================================================================
@@ -498,32 +496,50 @@ static inline uint32_t log_sector_end(uint32_t x) {
} }
// Helper function to check if a sector is erased (all 0xFF) // Helper function to check if a sector is erased (starts with 0xFF)
static bool is_sector_erased(uint32_t sector_offset) { static bool is_sector_erased(uint32_t x) {
uint8_t buf[256]; uint8_t buf; //[256];
esp_err_t err = esp_partition_read(storage_partition, sector_offset, buf, 256); esp_err_t err = esp_partition_read(storage_partition, LOG_START_OFFSET + x * FLASH_SECTOR_SIZE, &buf, 1);
if (err != ESP_OK) return false; if (err != ESP_OK) return false;
if (buf == 0xFF) return true;
for (int i = 0; i < 256; i++) { /*for (int i = 0; i < 256; i++) {
if (buf[i] != 0xFF) return false; if (buf[i] != 0xFF) return false;
} }*/
return false;
}
static bool is_sector_full(uint32_t x) {
uint8_t buf; //[256];
esp_err_t err = esp_partition_read(storage_partition, LOG_START_OFFSET + (x+1) * FLASH_SECTOR_SIZE - 1, &buf, 1);
if (err != ESP_OK) return false;
if (buf == 0xFF) return false;
/*for (int i = 0; i < 256; i++) {
if (buf[i] != 0xFF) return false;
}*/
return true; return true;
} }
static inline void find_head_tail(int32_t num_sectors, int32_t *head, int32_t *tail) {
}
// Helper function to check if a sector has data (contains non-0xFF, non-0x00 bytes) // Helper function to check if a sector has data (contains non-0xFF, non-0x00 bytes)
static bool sector_has_data(uint32_t sector_offset) { /*static bool sector_has_data(uint32_t sector_offset) {
uint8_t buf[256]; uint8_t buf; //[256];
esp_err_t err = esp_partition_read(storage_partition, sector_offset, buf, 256); esp_err_t err = esp_partition_read(storage_partition, sector_offset, &buf, 256);
if (err != ESP_OK) return false; if (err != ESP_OK) return false;
if (buf )
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
if (buf[i] != 0xFF && buf[i] != 0x00) return true; if (buf[i] != 0xFF && buf[i] != 0x00) return true;
} }
return false; return false;
} }*/
// Replace log_write with this non-blocking version: // Replace log_write with this non-blocking version:
esp_err_t log_write(uint8_t* buf, uint8_t len) { esp_err_t log_write(uint8_t* buf, uint8_t len, uint8_t type) {
if (!log_initialized || storage_partition == NULL) { if (!log_initialized || storage_partition == NULL) {
ESP_LOGE(TAG, "Logging not initialized"); ESP_LOGE(TAG, "Logging not initialized");
return ESP_FAIL; return ESP_FAIL;
@@ -539,9 +555,15 @@ esp_err_t log_write(uint8_t* buf, uint8_t len) {
return ESP_FAIL; return ESP_FAIL;
} }
if (type == 0xFF) {
ESP_LOGE(TAG, "Attempt to log with type=0xFF; not allowed");
return ESP_ERR_INVALID_ARG;
}
// Create queue entry // Create queue entry
log_queue_entry_t entry; log_queue_entry_t entry;
entry.len = len; entry.len = len;
entry.type = type;
memcpy(entry.data, buf, len); memcpy(entry.data, buf, len);
// Try to send to queue (non-blocking) // Try to send to queue (non-blocking)
@@ -554,7 +576,7 @@ esp_err_t log_write(uint8_t* buf, uint8_t len) {
} }
// The actual blocking write function (called by the task) // The actual blocking write function (called by the task)
static esp_err_t log_write_blocking(uint8_t* buf, uint8_t len) { static esp_err_t log_write_blocking(uint8_t* buf, uint8_t len, uint8_t type) {
if (!log_initialized || storage_partition == NULL) { if (!log_initialized || storage_partition == NULL) {
ESP_LOGE(TAG, "Logging not initialized"); ESP_LOGE(TAG, "Logging not initialized");
return ESP_FAIL; return ESP_FAIL;
@@ -568,8 +590,8 @@ static esp_err_t log_write_blocking(uint8_t* buf, uint8_t len) {
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY); if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
// check if we will overrun the sector // check if we will overrun the sector
if (log_head_offset + len+1 >= log_sector_end(log_head_offset)) { if (log_head_offset + len+2 >= log_sector_end(log_head_offset)) {
ESP_LOGI(TAG, "WILL OVERRUN (%ld >= %ld)", (long)log_head_offset + len+1, (long)log_sector_end(log_head_offset)); //ESP_LOGI(TAG, "WILL OVERRUN (%ld >= %ld)", (long)log_head_offset + len+2, (long)log_sector_end(log_head_offset));
// zero the rest of sector // zero the rest of sector
char zeros[256] = {0}; char zeros[256] = {0};
esp_partition_write(storage_partition, esp_partition_write(storage_partition,
@@ -588,7 +610,7 @@ static esp_err_t log_write_blocking(uint8_t* buf, uint8_t len) {
// Erase the next sector // Erase the next sector
if (err == ESP_OK && check_byte != 0xFF) { if (err == ESP_OK && check_byte != 0xFF) {
ESP_LOGI(TAG, "Erasing sector %lu for log", (unsigned long)log_head_offset); //ESP_LOGI(TAG, "Erasing sector %lu for log", (unsigned long)log_head_offset);
err = esp_partition_erase_range(storage_partition, err = esp_partition_erase_range(storage_partition,
log_head_offset, log_head_offset,
FLASH_SECTOR_SIZE); FLASH_SECTOR_SIZE);
@@ -606,13 +628,15 @@ static esp_err_t log_write_blocking(uint8_t* buf, uint8_t len) {
if (log_tail_offset >= storage_partition->size) if (log_tail_offset >= storage_partition->size)
log_tail_offset = LOG_START_OFFSET; log_tail_offset = LOG_START_OFFSET;
ESP_LOGI(TAG, "Tail/Head are now %lu/%lu", ESP_LOGI(TAG, "Erased; Tail/Head are now %lu/%lu",
(unsigned long)log_tail_offset, (unsigned long)log_head_offset); (unsigned long)log_tail_offset, (unsigned long)log_head_offset);
} }
len++; // account for type bit
esp_partition_write(storage_partition, log_head_offset, &len, 1); esp_partition_write(storage_partition, log_head_offset, &len, 1);
esp_partition_write(storage_partition, log_head_offset+1, buf, len); esp_partition_write(storage_partition, log_head_offset+1, buf, len-1);
esp_partition_write(storage_partition, log_head_offset+len, &type, 1);
log_head_offset+=len+1; log_head_offset+=len+1;
ESP_LOGI(TAG, "Wrote; Tail/Head are now %lu/%lu", ESP_LOGI(TAG, "Wrote; Tail/Head are now %lu/%lu",
@@ -629,17 +653,20 @@ static void log_writer_task(void *pvParameters) {
ESP_LOGI(TAG, "Log writer task started"); ESP_LOGI(TAG, "Log writer task started");
while (log_task_running) { while (log_task_running) {
// Feed watchdog
//esp_task_wdt_reset();
// Wait for log entry (with timeout to check running flag) // Wait for log entry (with timeout to check running flag)
if (xQueueReceive(log_queue, &entry, pdMS_TO_TICKS(100)) == pdTRUE) { if (xQueueReceive(log_queue, &entry, pdMS_TO_TICKS(100)) == pdTRUE) {
// Write the log entry (blocking) // Write the log entry (blocking)
esp_err_t err = log_write_blocking(entry.data, entry.len); esp_err_t err = log_write_blocking(entry.data, entry.len, entry.type);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write log entry: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to write log entry: %s", esp_err_to_name(err));
} }
//esp_task_wdt_reset();
} }
// Feed watchdog
esp_task_wdt_reset();
} }
ESP_LOGI(TAG, "Log writer task stopping"); ESP_LOGI(TAG, "Log writer task stopping");
@@ -664,28 +691,50 @@ esp_err_t log_init() {
log_head_offset = LOG_START_OFFSET; log_head_offset = LOG_START_OFFSET;
log_tail_offset = LOG_START_OFFSET; log_tail_offset = LOG_START_OFFSET;
// ... [INCLUDE THE BISECTION ALGORITHM FROM EARLIER HERE] ... // Binary search for the first non-full sector
// Binary search to find the first erased sector int32_t l = 0;
int32_t left = 0; int32_t r = num_sectors - 1;
int32_t right = num_sectors - 1; int32_t head_sector = 0;
int32_t head_sector = -1; int32_t tail_sector = 0;
while (left <= right) { while (l < r) {
int32_t mid = left + (right - left) / 2; int32_t m = (l + r) / 2;
uint32_t sector_offset = LOG_START_OFFSET + mid * FLASH_SECTOR_SIZE; if (is_sector_full(m)) {
l = m + 1;
esp_task_wdt_reset();
bool erased = is_sector_erased(sector_offset);
bool prev_has_data = (mid > 0) ? sector_has_data(LOG_START_OFFSET + (mid-1) * FLASH_SECTOR_SIZE) : false;
if (erased && (mid == 0 || prev_has_data)) {
head_sector = mid;
break;
} else if (erased) {
right = mid - 1;
} else { } else {
left = mid + 1; r = m;
}
}
int32_t first_non_full = l;
// Determine head based on what the first non-full sector is
if (first_non_full >= num_sectors) {
// All sectors are full (wraparound case)
head_sector = 0;
} else if (is_sector_erased(first_non_full)) {
// First non-full is erased -> head is this erased sector
head_sector = first_non_full;
} else {
// First non-full is partial -> head is this partial sector
head_sector = first_non_full;
}
// Binary search for tail: first full sector after head (with wraparound)
// Search from head+1 to head+num_sectors-1 (wrapping around)
l = 1;
r = num_sectors - 1;
while (l <= r) {
int32_t m = (l + r) / 2;
int32_t sector = (head_sector + m) % num_sectors;
if (is_sector_full(sector)) {
// Found a full sector, but need the FIRST one
tail_sector = sector;
r = m - 1;
} else {
l = m + 1;
} }
} }
@@ -737,31 +786,6 @@ esp_err_t log_init() {
log_head_offset = cursor; log_head_offset = cursor;
} }
// Find tail
int32_t tail_sector = -1;
for (int32_t i = head_sector + 1; i < num_sectors; i++) {
uint32_t sector_offset = LOG_START_OFFSET + i * FLASH_SECTOR_SIZE;
esp_task_wdt_reset();
if (sector_has_data(sector_offset)) {
tail_sector = i;
break;
}
}
if (tail_sector == -1 && head_sector > 0) {
for (int32_t i = 0; i < head_sector; i++) {
uint32_t sector_offset = LOG_START_OFFSET + i * FLASH_SECTOR_SIZE;
esp_task_wdt_reset();
if (sector_has_data(sector_offset)) {
tail_sector = i;
break;
}
}
}
if (tail_sector >= 0) { if (tail_sector >= 0) {
log_tail_offset = LOG_START_OFFSET + tail_sector * FLASH_SECTOR_SIZE; log_tail_offset = LOG_START_OFFSET + tail_sector * FLASH_SECTOR_SIZE;
} else { } else {
@@ -800,8 +824,10 @@ esp_err_t log_init() {
} }
// Add the task to watchdog // Add the task to watchdog
esp_task_wdt_add(log_task_handle); //if (esp_task_wdt_add(log_task_handle) != ESP_OK) {
// ESP_LOGW(TAG, "Log task already subscribed to watchdog or failed to add");
//}
ESP_LOGI(TAG, "Log writer task created successfully"); ESP_LOGI(TAG, "Log writer task created successfully");
if (log_mutex) xSemaphoreGive(log_mutex); if (log_mutex) xSemaphoreGive(log_mutex);
@@ -811,17 +837,17 @@ esp_err_t log_init() {
// Update storage_deinit to stop the task // Update storage_deinit to stop the task
void storage_deinit(void) { void storage_deinit(void) {
// Stop the log writer task // Stop the log writer task
if (log_task_running) { if (log_task_running) {
log_task_running = false; log_task_running = false;
vTaskDelay(pdMS_TO_TICKS(200));
// Wait a bit for task to finish
vTaskDelay(pdMS_TO_TICKS(200)); if (log_task_handle != NULL) {
// Don't try to delete from watchdog - task was never added
if (log_task_handle != NULL) { // esp_task_wdt_delete(log_task_handle); // <-- REMOVE THIS LINE
esp_task_wdt_delete(log_task_handle); vTaskDelay(pdMS_TO_TICKS(100));
log_task_handle = NULL; log_task_handle = NULL;
} }
} }
// Delete the queue // Delete the queue
if (log_queue != NULL) { if (log_queue != NULL) {
@@ -836,3 +862,324 @@ void storage_deinit(void) {
log_mutex = NULL; log_mutex = NULL;
} }
} }
/*
* ADDITIONS TO storage.c
*
* Add these functions to storage.c to support the test suite.
* These provide the ability to erase all log sectors, simulate power cycles,
* and read back log entries for verification.
*/
// Add these function declarations to storage.h:
/*
esp_err_t log_read(uint8_t* len, uint8_t* buf, uint8_t* type);
void log_read_reset(void);
esp_err_t log_erase_all_sectors(void);
esp_err_t log_simulate_power_cycle(void);
*/
// Add this static variable near the top of storage.c with other log static variables
// (around line 118, after log_tail_offset and log_initialized):
static uint32_t log_read_cursor = 0;
// Add these functions to storage.c (after the existing log functions):
/*
* ADDITIONS TO storage.c
*
* Add these functions to storage.c to support the test suite.
* These provide the ability to erase all log sectors, simulate power cycles,
* and read back log entries for verification.
*/
// Add these function declarations to storage.h:
/*
esp_err_t log_read(uint8_t* len, uint8_t* buf, uint8_t* type);
void log_read_reset(void);
esp_err_t log_erase_all_sectors(void);
esp_err_t log_simulate_power_cycle(void);
*/
// Add this static variable near the top of storage.c with other log static variables
// (around line 118, after log_tail_offset and log_initialized):
/*
static uint32_t log_read_cursor = 0;
*/
// Add these functions to storage.c (after the existing log functions):
/**
* @brief Read a log entry from the current read cursor position
*
* Reads a log entry from flash starting at the current read cursor and advances
* the cursor to the next entry. The read cursor is independent of tail - reading
* does NOT affect the tail pointer (which marks the boundary of valid data).
*
* The tail is only moved by the log write system when sectors need to be erased
* during wraparound. Reading is completely non-destructive.
*
* This is a test/debug function - the production logging system is write-only.
*
* Log entry format in flash:
* [1 byte: length] [length-1 bytes: data] [1 byte: type]
*
* @param len Pointer to store the entry length (data length, not including type byte)
* @param buf Buffer to store the entry data (must be at least LOG_MAX_PAYLOAD bytes)
* @param type Pointer to store the entry type
* @return ESP_OK on success, ESP_ERR_NOT_FOUND if no more entries, error code otherwise
*/
esp_err_t log_read(uint8_t* len, uint8_t* buf, uint8_t* type) {
// NOTE: log_read_cursor must be declared as a file-scope static variable
// Add this declaration near the other log static variables in storage.c:
// static uint32_t log_read_cursor = 0;
if (!log_initialized || storage_partition == NULL) {
ESP_LOGE(TAG, "Logging not initialized");
return ESP_ERR_INVALID_STATE;
}
if (len == NULL || buf == NULL || type == NULL) {
ESP_LOGE(TAG, "NULL pointer passed to log_read");
return ESP_ERR_INVALID_ARG;
}
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
// Initialize read cursor to tail on first read (when cursor is 0)
if (log_read_cursor == 0) {
log_read_cursor = log_tail_offset;
}
// Check if we've caught up to head (no more entries to read)
if (log_read_cursor == log_head_offset) {
if (log_mutex) xSemaphoreGive(log_mutex);
return ESP_ERR_NOT_FOUND;
}
// Read the length byte
uint8_t entry_len;
esp_err_t err = esp_partition_read(storage_partition, log_read_cursor, &entry_len, 1);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to read entry length at offset %lu", (unsigned long)log_read_cursor);
if (log_mutex) xSemaphoreGive(log_mutex);
return err;
}
// Check for erased flash (0xFF) - means we've reached the end
if (entry_len == 0xFF) {
if (log_mutex) xSemaphoreGive(log_mutex);
return ESP_ERR_NOT_FOUND;
}
// Check for sector padding (0x00) - skip to next sector
if (entry_len == 0x00) {
// Move to next sector
uint32_t next_sector = ((log_read_cursor / FLASH_SECTOR_SIZE) + 1) * FLASH_SECTOR_SIZE;
// Handle wraparound
if (next_sector >= storage_partition->size) {
log_read_cursor = LOG_START_OFFSET;
} else {
log_read_cursor = next_sector;
}
if (log_mutex) xSemaphoreGive(log_mutex);
return log_read(len, buf, type); // Recursive call to read from next sector
}
// Validate length
if (entry_len > LOG_MAX_PAYLOAD + 1) { // +1 for type byte included in length
ESP_LOGE(TAG, "Invalid entry length %d at offset %lu", entry_len, (unsigned long)log_read_cursor);
if (log_mutex) xSemaphoreGive(log_mutex);
return ESP_ERR_INVALID_SIZE;
}
// Read the data (length-1 bytes, since length includes the type byte)
uint8_t data_len = entry_len - 1;
err = esp_partition_read(storage_partition, log_read_cursor + 1, buf, data_len);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to read entry data at offset %lu", (unsigned long)(log_read_cursor + 1));
if (log_mutex) xSemaphoreGive(log_mutex);
return err;
}
// Read the type byte
uint8_t entry_type;
err = esp_partition_read(storage_partition, log_read_cursor + entry_len, &entry_type, 1);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to read entry type at offset %lu", (unsigned long)(log_read_cursor + entry_len));
if (log_mutex) xSemaphoreGive(log_mutex);
return err;
}
// Validate type
if (!IS_VALID_LOG_TYPE(entry_type)) {
ESP_LOGW(TAG, "Invalid log type 0x%02X at offset %lu", entry_type, (unsigned long)(log_read_cursor + entry_len));
// Continue anyway - might be valid data with unknown type
}
// Set output parameters
*len = data_len;
*type = entry_type;
// Advance read cursor (NOT tail - tail is only moved by writes during wraparound)
log_read_cursor += entry_len + 1; // +1 for the type byte after data
// Handle wraparound
if (log_read_cursor >= storage_partition->size) {
log_read_cursor = LOG_START_OFFSET;
}
if (log_mutex) xSemaphoreGive(log_mutex);
return ESP_OK;
}
/**
* @brief Reset the log read cursor to start reading from the beginning
*
* Resets the internal read cursor so that the next call to log_read()
* will start reading from the tail (oldest valid entry).
* This is useful for tests that need to re-read the log.
*
* IMPORTANT: This does NOT affect the tail pointer. The tail marks the
* boundary of valid data and is only moved by the write system.
*/
void log_read_reset(void) {
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
log_read_cursor = 0; // Reset to 0 so next read starts from tail
if (log_mutex) xSemaphoreGive(log_mutex);
}
/**
* @brief Erase all sectors in the log area
*
* This function erases the entire log area of the flash partition,
* resetting it to a clean state. Useful for testing.
*
* @return ESP_OK on success, error code otherwise
*/
esp_err_t log_erase_all_sectors(void) {
if (storage_partition == NULL) {
ESP_LOGE(TAG, "Storage partition not initialized");
return ESP_ERR_INVALID_STATE;
}
// Stop the log writer task
if (log_task_running) {
log_task_running = false;
vTaskDelay(pdMS_TO_TICKS(200));
if (log_task_handle != NULL) {
// Don't try to delete from watchdog - task was never added
// esp_task_wdt_delete(log_task_handle); // <-- REMOVE THIS LINE
vTaskDelay(pdMS_TO_TICKS(100));
log_task_handle = NULL;
}
}
// Clear the queue
if (log_queue != NULL) {
xQueueReset(log_queue);
}
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
uint32_t log_area_size = storage_partition->size - LOG_START_OFFSET;
ESP_LOGI(TAG, "Erasing all log sectors (%lu bytes)...", (unsigned long)log_area_size);
esp_err_t err = esp_partition_erase_range(storage_partition,
LOG_START_OFFSET,
log_area_size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to erase log area: %s", esp_err_to_name(err));
if (log_mutex) xSemaphoreGive(log_mutex);
return err;
}
ESP_LOGI(TAG, "All log sectors erased successfully");
if (log_mutex) xSemaphoreGive(log_mutex);
// Reinitialize
log_initialized = false;
log_read_cursor = 0;
err = log_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to reinitialize log after erase");
return err;
}
return ESP_OK;
}
/**
* @brief Simulate a power cycle by re-running log initialization
*
* This function simulates what happens when the device powers off and back on:
* - Clears the in-memory head/tail tracking (simulated by marking as uninitialized)
* - Re-runs log_init() to scan flash and recover head/tail positions
*
* This is crucial for testing that the log system can correctly recover its
* state from flash after a power loss.
*
* @return ESP_OK on success, error code otherwise
*/
esp_err_t log_simulate_power_cycle(void) {
ESP_LOGI(TAG, "Simulating power cycle...");
// Stop the log writer task
if (log_task_running) {
log_task_running = false;
vTaskDelay(pdMS_TO_TICKS(200));
if (log_task_handle != NULL) {
// Don't try to delete from watchdog - task was never added
// esp_task_wdt_delete(log_task_handle); // <-- REMOVE THIS LINE
vTaskDelay(pdMS_TO_TICKS(100));
log_task_handle = NULL;
}
}
// Clear the queue
if (log_queue != NULL) {
xQueueReset(log_queue);
}
// Mark log as uninitialized (simulates losing RAM state)
log_initialized = false;
// Reset read cursor (simulates losing RAM state)
log_read_cursor = 0;
// Re-run log initialization to scan flash and recover state
esp_err_t err = log_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to reinitialize log after simulated power cycle");
return err;
}
ESP_LOGI(TAG, "Power cycle simulation complete. Head=%lu, Tail=%lu",
(unsigned long)log_head_offset, (unsigned long)log_tail_offset);
return ESP_OK;
}
esp_err_t log_write_blocking_test(uint8_t* buf, uint8_t len, uint8_t type) {
// Check queue space - wait if nearly full
while (uxQueueSpacesAvailable(log_queue) < 2) {
vTaskDelay(pdMS_TO_TICKS(10));
esp_task_wdt_reset();
}
return log_write(buf, len, type);
}

View File

@@ -28,7 +28,7 @@
// 0xCA-0xCF reserved for future use // 0xCA-0xCF reserved for future use
// Maximum payload size per log entry (255 max due to 1-byte size field) // Maximum payload size per log entry (255 max due to 1-byte size field)
#define LOG_MAX_PAYLOAD 255 #define LOG_MAX_PAYLOAD 200
// Helper macro to check if a byte is a valid log type // Helper macro to check if a byte is a valid log type
#define IS_VALID_LOG_TYPE(x) ((x) >= 0xC0 && (x) <= 0xCF) #define IS_VALID_LOG_TYPE(x) ((x) >= 0xC0 && (x) <= 0xCF)
@@ -75,10 +75,10 @@ typedef struct {
PARAM_DEF(ADC_ALPHA_IAZ, f32, 0.005, "-") \ PARAM_DEF(ADC_ALPHA_IAZ, f32, 0.005, "-") \
PARAM_DEF(ADC_DB_IAZ, f32, 5.0, "A") \ PARAM_DEF(ADC_DB_IAZ, f32, 5.0, "A") \
PARAM_DEF(EFUSE_INOM_1, f32, 40.0, "A") \ PARAM_DEF(EFUSE_INOM_1, f32, 40.0, "A") \
PARAM_DEF(EFUSE_INOM_2, f32, 6.0, "A") \ PARAM_DEF(EFUSE_INOM_2, f32, 14.0, "A") \
PARAM_DEF(EFUSE_INOM_3, f32, 4.0, "A") \ PARAM_DEF(EFUSE_INOM_3, f32, 4.0, "A") \
PARAM_DEF(EFUSE_HEAT_THRESH, f32, 60.0, "i/i^2-s") \ PARAM_DEF(EFUSE_HEAT_THRESH, f32, 60.0, "i/i^2-s") \
PARAM_DEF(EFUSE_KINST, f32, 5.0, "i/i") \ PARAM_DEF(EFUSE_KINST, f32, 2.0, "i/i") \
PARAM_DEF(EFUSE_TAUCOOL, f32, 0.2, "i") \ PARAM_DEF(EFUSE_TAUCOOL, f32, 0.2, "i") \
PARAM_DEF(EFUSE_TCOOL, u32, 5000000, "us") \ PARAM_DEF(EFUSE_TCOOL, u32, 5000000, "us") \
PARAM_DEF(LOW_PROTECTION_V, f32, 10.0, "V") \ PARAM_DEF(LOW_PROTECTION_V, f32, 10.0, "V") \
@@ -91,13 +91,14 @@ typedef struct {
PARAM_DEF(WIFI_CHANNEL, u16, 6, "") \ PARAM_DEF(WIFI_CHANNEL, u16, 6, "") \
PARAM_DEF(WIFI_SSID, str, "sc.local", "") \ PARAM_DEF(WIFI_SSID, str, "sc.local", "") \
PARAM_DEF(WIFI_PASS, str, "password", "") \ PARAM_DEF(WIFI_PASS, str, "password", "") \
PARAM_DEF(EFUSE_INRUSH_US, u32, 300000, "us") \ PARAM_DEF(EFUSE_INRUSH_US, u32, 250000, "us") \
PARAM_DEF(JACK_I_UP, f32, 5.0, "A") \ PARAM_DEF(JACK_I_UP, f32, 8.0, "A") \
PARAM_DEF(JACK_I_DOWN, f32, 8.0, "A") \ PARAM_DEF(JACK_I_DOWN, f32, 15.0, "A") \
PARAM_DEF(V_SENS_K, f32, 0.00766666666, "V/mV") \ PARAM_DEF(V_SENS_K, f32, 0.00766666666, "V/mV") \
PARAM_DEF(BUILD_VERSION, str, "undefined", "") \ PARAM_DEF(BUILD_VERSION, str, "undefined", "") \
PARAM_DEF(SAFETY_BREAK_US, u32, 300000, "") \ PARAM_DEF(SAFETY_BREAK_US, u32, 300000, "") \
PARAM_DEF(SAFETY_MAKE_US, u32, 1000000, "") \ PARAM_DEF(SAFETY_MAKE_US, u32, 1000000, "") \
PARAM_DEF(JACK_IS_DOWN, f32, 8.0, "A") \
// Generate enum for parameter indices // Generate enum for parameter indices
#define PARAM_DEF(name, type, default_val, unit) PARAM_##name, #define PARAM_DEF(name, type, default_val, unit) PARAM_##name,
@@ -163,7 +164,7 @@ esp_err_t commit_params(void);
// Logging functions // Logging functions
esp_err_t log_init(void); esp_err_t log_init(void);
esp_err_t log_write(uint8_t* buf, uint8_t len); esp_err_t log_write(uint8_t* buf, uint8_t len, uint8_t type);
uint32_t log_get_head(void); uint32_t log_get_head(void);
uint32_t log_get_tail(void); uint32_t log_get_tail(void);
uint32_t log_get_offset(void); uint32_t log_get_offset(void);
@@ -176,4 +177,9 @@ esp_err_t write_dummy_log_1(void);
esp_err_t write_dummy_log_2(void); esp_err_t write_dummy_log_2(void);
esp_err_t write_dummy_log_3(void); esp_err_t write_dummy_log_3(void);
esp_err_t log_read(uint8_t* len, uint8_t* buf, uint8_t* type);
esp_err_t log_erase_all_sectors(void);
esp_err_t log_simulate_power_cycle(void);
void log_read_reset(void);
esp_err_t log_write_blocking_test(uint8_t* buf, uint8_t len, uint8_t type);
#endif // STORAGE_H #endif // STORAGE_H

File diff suppressed because one or more lines are too long

View File

@@ -4,7 +4,7 @@
#include <Arduino.h> #include <Arduino.h>
const unsigned char PROGMEM html_content_gz[] = { const unsigned char PROGMEM html_content_gz[] = {
0x1f, 0x8b, 0x08, 0x00, 0x68, 0x76, 0x5c, 0x69, 0x02, 0xff, 0xed, 0x3d, 0x6b, 0x57, 0xdb, 0xc6, 0x1f, 0x8b, 0x08, 0x00, 0x11, 0xd0, 0x6b, 0x69, 0x02, 0xff, 0xed, 0x3d, 0x6b, 0x57, 0xdb, 0xc6,
0xb6, 0x9f, 0x4f, 0x7f, 0xc5, 0x40, 0x52, 0x2a, 0x25, 0x42, 0xb6, 0x81, 0xf4, 0x61, 0x23, 0x73, 0xb6, 0x9f, 0x4f, 0x7f, 0xc5, 0x40, 0x52, 0x2a, 0x25, 0x42, 0xb6, 0x81, 0xf4, 0x61, 0x23, 0x73,
0x08, 0x38, 0x6d, 0x9a, 0xf0, 0x58, 0x3c, 0xd2, 0x9e, 0xcb, 0x61, 0x21, 0xd9, 0x1a, 0x63, 0x15, 0x08, 0x38, 0x6d, 0x9a, 0xf0, 0x58, 0x3c, 0xd2, 0x9e, 0xcb, 0x61, 0x21, 0xd9, 0x1a, 0x63, 0x15,
0x59, 0x72, 0x25, 0x19, 0x42, 0x85, 0xff, 0xfb, 0xd9, 0x7b, 0x1e, 0xd2, 0xe8, 0x61, 0x63, 0x92, 0x59, 0x72, 0x25, 0x19, 0x42, 0x85, 0xff, 0xfb, 0xd9, 0x7b, 0x1e, 0xd2, 0xe8, 0x61, 0x63, 0x92,

View File

@@ -61,7 +61,7 @@ char httpBuffer[4096];
/* Handler to serve the HTML page */ /* Handler to serve the HTML page */
static esp_err_t root_get_handler(httpd_req_t *req) { static esp_err_t root_get_handler(httpd_req_t *req) {
ESP_LOGI(TAG, "root_get_handler"); //ESP_LOGI(TAG, "root_get_handler");
if (req == NULL) { if (req == NULL) {
ESP_LOGE(TAG, "Null request pointer"); ESP_LOGE(TAG, "Null request pointer");
@@ -199,9 +199,9 @@ static esp_err_t log_handler(httpd_req_t *req) {
// Total size: 4 (json length) + json + 8 (head/tail) + log_data // Total size: 4 (json length) + json + 8 (head/tail) + log_data
uint32_t total_size = 4 + json_len + 8 + log_data_size; uint32_t total_size = 4 + json_len + 8 + log_data_size;
ESP_LOGI(TAG, "Log request: tail=%ld, head=%ld, json_len=%lu, log_size=%ld, total=%lu", //ESP_LOGI(TAG, "Log request: tail=%ld, head=%ld, json_len=%lu, log_size=%ld, total=%lu",
(long)tail, (long)head, (unsigned long)json_len, (long)log_data_size, // (long)tail, (long)head, (unsigned long)json_len, (long)log_data_size,
(unsigned long)total_size); // (unsigned long)total_size);
// Send HTTP headers // Send HTTP headers
char len_str[16]; char len_str[16];
@@ -368,7 +368,7 @@ static esp_err_t log_handler(httpd_req_t *req) {
* Unified GET handler - returns complete system status * Unified GET handler - returns complete system status
*/ */
static esp_err_t get_handler(httpd_req_t *req) { static esp_err_t get_handler(httpd_req_t *req) {
ESP_LOGI(TAG, "get_handler"); //ESP_LOGI(TAG, "get_handler");
if (req == NULL) { if (req == NULL) {
ESP_LOGE(TAG, "Null request pointer"); ESP_LOGE(TAG, "Null request pointer");
@@ -692,7 +692,7 @@ static esp_err_t ota_post_handler(httpd_req_t *req) {
static esp_err_t catchall_handler(httpd_req_t *req) { static esp_err_t catchall_handler(httpd_req_t *req) {
ESP_LOGI(TAG, "catchall_handler; %s", req->uri); //ESP_LOGI(TAG, "catchall_handler; %s", req->uri);
const char *uri = req->uri; const char *uri = req->uri;
// Windows NCSI // Windows NCSI
@@ -868,6 +868,9 @@ static void wifi_event_handler(void* arg, esp_event_base_t event_base,
static esp_err_t launchSoftAp(void) { static esp_err_t launchSoftAp(void) {
esp_err_t err; esp_err_t err;
ESP_LOGI(TAG, "AP LAUNCHING");
err = nvs_flash_init(); err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
// NVS partition was truncated and needs to be erased // NVS partition was truncated and needs to be erased
@@ -880,11 +883,17 @@ static esp_err_t launchSoftAp(void) {
// Retry init after erase // Retry init after erase
err = nvs_flash_init(); err = nvs_flash_init();
} }
ESP_LOGI(TAG, "AP LAUNCHING...");
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize NVS: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to initialize NVS: %s", esp_err_to_name(err));
return err; return err;
} }
ESP_LOGI(TAG, "HI THERE");
err = esp_netif_init(); err = esp_netif_init();
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize netif: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to initialize netif: %s", esp_err_to_name(err));
@@ -1032,6 +1041,8 @@ esp_err_t webserver_init(void) {
return err; return err;
} }
ESP_LOGI(TAG, "AP LAUNCHED");
err = startHttpServer(); err = startHttpServer();
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to start HTTP server: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to start HTTP server: %s", esp_err_to_name(err));

View File

@@ -1,8 +1,8 @@
# ESP32 Partition Table - 8MB (0x800000) Flash with OTA Support # ESP32 Partition Table - 8MB (0x800000) Flash with OTA Support #5568K,
# Name, Type, SubType, Offset, Size, Flags # Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000, nvs, data, nvs, 0x9000, 0x4000,
otadata, data, ota, 0xd000, 0x2000, otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, 0xf000, 0x1000,
ota_0, app, ota_0, 0x10000, 1280K, ota_0, app, ota_0, 0x10000, 1280K,
ota_1, app, ota_1, 0x150000, 1280K, ota_1, app, ota_1, 0x150000, 1280K,
storage, data, 0x40, 0x290000, 5568K, storage, data, 0x40, 0x290000, 128K,
1 # ESP32 Partition Table - 8MB (0x800000) Flash with OTA Support # ESP32 Partition Table - 8MB (0x800000) Flash with OTA Support #5568K,
2 # Name, Type, SubType, Offset, Size, Flags # Name, Type, SubType, Offset, Size, Flags
3 nvs, data, nvs, 0x9000, 0x4000, nvs, data, nvs, 0x9000, 0x4000,
4 otadata, data, ota, 0xd000, 0x2000, otadata, data, ota, 0xd000, 0x2000,
5 phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, 0xf000, 0x1000,
6 ota_0, app, ota_0, 0x10000, 1280K, ota_0, app, ota_0, 0x10000, 1280K,
7 ota_1, app, ota_1, 0x150000, 1280K, ota_1, app, ota_1, 0x150000, 1280K,
8 storage, data, 0x40, 0x290000, 5568K, storage, data, 0x40, 0x290000, 128K,