Refining logging
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include "power_mgmt.h"
|
||||
#include "rf_433.h"
|
||||
#include "rtc.h"
|
||||
#include "sensors.h"
|
||||
#include "storage.h"
|
||||
#include "version.h"
|
||||
#include <string.h>
|
||||
@@ -77,6 +78,11 @@ cJSON* comms_handle_get(void) {
|
||||
cJSON_AddItemToArray(msg_array, cJSON_CreateString("CLOCK NOT SET"));
|
||||
}
|
||||
|
||||
if (!get_is_safe()) {
|
||||
cJSON_AddItemToArray(msg_array, cJSON_CreateString("SAFETY SENSOR BREAK"));
|
||||
}
|
||||
|
||||
|
||||
cJSON_AddItemToObject(root, "msg", msg_array);
|
||||
|
||||
// Add parameters object
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "rtc.h"
|
||||
#include "sensors.h"
|
||||
#include "esp_log.h"
|
||||
#include <sys/param.h>
|
||||
|
||||
#define TRANSITION_DELAY_US 1000000
|
||||
|
||||
@@ -47,7 +48,7 @@ int64_t override_times[8] = {-1};
|
||||
int64_t override_cooldown[8] = {-1};
|
||||
bool enabled = false;
|
||||
|
||||
|
||||
float this_move_dist = 0.0f;
|
||||
RTC_DATA_ATTR float remaining_distance = 0.0f;
|
||||
float fsm_get_remaining_distance(void) { return remaining_distance; }
|
||||
void fsm_set_remaining_distance(float x) { remaining_distance = x;}
|
||||
@@ -159,8 +160,8 @@ 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 DRIVE_TIME get_param_value_t(PARAM_DRIVE_KT).f32 * get_param_value_t(PARAM_DRIVE_DIST).f32
|
||||
#define DRIVE_DIST get_param_value_t(PARAM_DRIVE_KE).f32 * get_param_value_t(PARAM_DRIVE_DIST).f32
|
||||
#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
|
||||
|
||||
void control_task(void *param) {
|
||||
esp_task_wdt_add(NULL);
|
||||
@@ -180,34 +181,44 @@ void control_task(void *param) {
|
||||
|
||||
switch (cmd) {
|
||||
case FSM_CMD_START:
|
||||
// Check if we have remaining distance before starting
|
||||
if (remaining_distance <= 0.0f) {
|
||||
ESP_LOGI(TAG, "FAILED TO START; NO REMAINING DISTANCE");
|
||||
error = SC_ERR_LEASH_HIT;
|
||||
continue;
|
||||
}
|
||||
this_move_dist = MIN(get_param_value_t(PARAM_DRIVE_DIST).f32, remaining_distance);
|
||||
case FSM_CMD_START_IGNORE_OVERTRAVEL:
|
||||
this_move_dist = get_param_value_t(PARAM_DRIVE_DIST).f32;
|
||||
if (current_state == STATE_IDLE) {
|
||||
// Check if we have remaining distance before starting
|
||||
if (remaining_distance <= 0.0f) {
|
||||
error = SC_ERR_LEASH_HIT;
|
||||
continue;
|
||||
}
|
||||
|
||||
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");
|
||||
error = SC_ERR_LOW_BATTERY;
|
||||
continue;
|
||||
}
|
||||
if (get_safety_sensor()) {
|
||||
if (!get_is_safe()) {
|
||||
ESP_LOGI(TAG, "FAILED TO START; SAFETY NOT SET");
|
||||
error = SC_ERR_SAFETY_TRIP;
|
||||
continue;
|
||||
}
|
||||
if (efuse_is_tripped(BRIDGE_DRIVE)) {
|
||||
ESP_LOGI(TAG, "FAILED TO START; EFUSE 1 TRIP");
|
||||
error = SC_ERR_EFUSE_TRIP_1;
|
||||
continue;
|
||||
}
|
||||
if (efuse_is_tripped(BRIDGE_JACK)) {
|
||||
ESP_LOGI(TAG, "FAILED TO START; EFUSE 2 TRIP");
|
||||
error = SC_ERR_EFUSE_TRIP_2;
|
||||
continue;
|
||||
}
|
||||
if (efuse_is_tripped(BRIDGE_AUX)) {
|
||||
ESP_LOGI(TAG, "FAILED TO START; EFUSE 3 TRIP");
|
||||
error = SC_ERR_EFUSE_TRIP_3;
|
||||
continue;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "STARTING");
|
||||
error = ESP_OK; // if everything is OK now, we're OK.
|
||||
current_state = STATE_MOVE_START_DELAY;
|
||||
set_timer(TRANSITION_DELAY_US);
|
||||
@@ -337,7 +348,7 @@ void control_task(void *param) {
|
||||
}
|
||||
break;
|
||||
case STATE_MOVE_START_DELAY:
|
||||
if (!get_safety_sensor()) {
|
||||
if (!get_is_safe()) {
|
||||
error = SC_ERR_SAFETY_TRIP;
|
||||
current_state = STATE_IDLE;
|
||||
}
|
||||
@@ -348,7 +359,7 @@ void control_task(void *param) {
|
||||
}
|
||||
break;
|
||||
case STATE_JACK_UP_START:
|
||||
if (!get_safety_sensor()) {
|
||||
if (!get_is_safe()) {
|
||||
error = SC_ERR_SAFETY_TRIP;
|
||||
current_state = STATE_UNDO_JACK_START;
|
||||
}
|
||||
@@ -380,7 +391,7 @@ void control_task(void *param) {
|
||||
}
|
||||
break;
|
||||
case STATE_JACK_UP:
|
||||
if (!get_safety_sensor()) {
|
||||
if (!get_is_safe()) {
|
||||
error = SC_ERR_SAFETY_TRIP;
|
||||
current_state = STATE_UNDO_JACK_START;
|
||||
}
|
||||
@@ -399,7 +410,7 @@ void control_task(void *param) {
|
||||
}
|
||||
break;
|
||||
case STATE_DRIVE_START_DELAY:
|
||||
if (!get_safety_sensor()) {
|
||||
if (!get_is_safe()) {
|
||||
error = SC_ERR_SAFETY_TRIP;
|
||||
current_state = STATE_UNDO_JACK_START;
|
||||
}
|
||||
@@ -410,12 +421,10 @@ void control_task(void *param) {
|
||||
set_sensor_counter(SENSOR_DRIVE, -DRIVE_DIST);
|
||||
// Record starting encoder position AFTER setting it
|
||||
move_start_encoder = get_sensor_counter(SENSOR_DRIVE);
|
||||
ESP_LOGI(TAG, "STATE_DRIVE starting: encoder=%ld, remaining_distance=%.2f, DRIVE_DIST=%.2f",
|
||||
(long)move_start_encoder, remaining_distance, DRIVE_DIST);
|
||||
}
|
||||
break;
|
||||
case STATE_DRIVE:
|
||||
if (!get_safety_sensor()) {
|
||||
if (!get_is_safe()) {
|
||||
error = SC_ERR_SAFETY_TRIP;
|
||||
current_state = STATE_UNDO_JACK_START;
|
||||
}
|
||||
@@ -425,41 +434,22 @@ void control_task(void *param) {
|
||||
float ke = get_param_value_t(PARAM_DRIVE_KE).f32;
|
||||
float distance_traveled = ticks_traveled / ke;
|
||||
|
||||
ESP_LOGI(TAG, "STATE_DRIVE: current_encoder=%ld, move_start=%ld, ticks=%ld, ke=%.2f, dist_traveled=%.2f, remaining=%.2f",
|
||||
(long)current_encoder, (long)move_start_encoder, (long)ticks_traveled,
|
||||
ke, distance_traveled, remaining_distance);
|
||||
|
||||
// Check if we'll exceed remaining distance with a full move
|
||||
bool will_exceed = distance_traveled >= remaining_distance;
|
||||
|
||||
// Stop if timer expires OR encoder target reached OR we've used up remaining distance
|
||||
if (timer_done() || current_encoder > 0 || will_exceed) {
|
||||
ESP_LOGI(TAG, "Drive stopping: timer_done=%d, encoder>0=%d, will_exceed=%d",
|
||||
timer_done(), current_encoder > 0, will_exceed);
|
||||
|
||||
if (timer_done() || current_encoder > 0) {
|
||||
// Update remaining distance based on actual travel
|
||||
float old_remaining = remaining_distance;
|
||||
if (will_exceed) {
|
||||
ESP_LOGI(TAG, "Move stopped early - reached remaining distance limit (%.2f)", remaining_distance);
|
||||
remaining_distance = 0.0f;
|
||||
} else {
|
||||
remaining_distance -= distance_traveled;
|
||||
if (remaining_distance < 0.0f) remaining_distance = 0.0f;
|
||||
}
|
||||
ESP_LOGI(TAG, "Drive complete: traveled %.2f, old_remaining %.2f, new_remaining %.2f",
|
||||
distance_traveled, old_remaining, remaining_distance);
|
||||
//if (current_encoder < 0)
|
||||
remaining_distance -= this_move_dist;
|
||||
//else
|
||||
// remaining_distance -= distance_traveled;
|
||||
|
||||
current_state = STATE_DRIVE_END_DELAY;
|
||||
set_timer(TRANSITION_DELAY_US);
|
||||
}
|
||||
|
||||
if (efuse_is_tripped(BRIDGE_DRIVE)) {
|
||||
float old_remaining = remaining_distance;
|
||||
// Update remaining distance even on fault
|
||||
remaining_distance -= distance_traveled;
|
||||
if (remaining_distance < 0.0f) remaining_distance = 0.0f;
|
||||
ESP_LOGW(TAG, "Drive fault: traveled %.2f, old_remaining %.2f, new_remaining %.2f",
|
||||
distance_traveled, old_remaining, remaining_distance);
|
||||
|
||||
error = SC_ERR_EFUSE_TRIP_1;
|
||||
current_state = STATE_UNDO_JACK_START;
|
||||
@@ -467,7 +457,7 @@ void control_task(void *param) {
|
||||
}
|
||||
break;
|
||||
case STATE_DRIVE_END_DELAY:
|
||||
if (!get_safety_sensor()) {
|
||||
if (!get_is_safe()) {
|
||||
error = SC_ERR_SAFETY_TRIP;
|
||||
current_state = STATE_UNDO_JACK_START;
|
||||
}
|
||||
@@ -477,7 +467,7 @@ void control_task(void *param) {
|
||||
}
|
||||
break;
|
||||
case STATE_JACK_DOWN:
|
||||
if (!get_safety_sensor()) {
|
||||
if (!get_is_safe()) {
|
||||
error = SC_ERR_SAFETY_TRIP;
|
||||
current_state = STATE_UNDO_JACK_START;
|
||||
}
|
||||
@@ -547,7 +537,7 @@ void control_task(void *param) {
|
||||
// no way out of this except a command
|
||||
break;
|
||||
case STATE_CALIBRATE_DRIVE_MOVE:
|
||||
if (!get_safety_sensor() || timer_done()) {
|
||||
if (!get_is_safe() || timer_done()) {
|
||||
current_state = STATE_IDLE;
|
||||
fsm_cal_t = current_time - timer_start;
|
||||
fsm_cal_e = get_sensor_counter(SENSOR_DRIVE);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
typedef enum {
|
||||
FSM_CMD_START,
|
||||
FSM_CMD_START_IGNORE_OVERTRAVEL,
|
||||
FSM_CMD_STOP,
|
||||
FSM_CMD_UNDO,
|
||||
FSM_CMD_SHUTDOWN,
|
||||
|
||||
33
main/i2c.c
33
main/i2c.c
@@ -6,6 +6,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "sensors.h"
|
||||
|
||||
#define I2C_PORT I2C_NUM_0
|
||||
#define TCA_ADDR_READ 0x21
|
||||
@@ -28,11 +29,12 @@
|
||||
#define REPEAT_MS 200
|
||||
#define REPEAT_START_MS 700
|
||||
|
||||
#define SAFETY_MASK 0b00111111
|
||||
#define SAFETY_MASK 0b00110001 // & permissible channels (jack and sensors)
|
||||
#define SENSOR_EN_MASK 0b00000001 // | need forced high
|
||||
|
||||
// Static Variables
|
||||
static bool i2c_initted = false;
|
||||
static bool safety_ok = false; // Safety interlock
|
||||
//static bool safety_ok = false; // Safety interlock
|
||||
static uint8_t last_relay_request = 0; // Track last relay request
|
||||
|
||||
// === I2C LOW-LEVEL ===
|
||||
@@ -68,35 +70,22 @@ esp_err_t i2c_init(void) {
|
||||
ESP_ERROR_CHECK(tca_write_word_8(TCA_REG_CONFIG1, 0b00000000));
|
||||
|
||||
i2c_initted = true;
|
||||
safety_ok = false; // Start with safety not OK
|
||||
//safety_ok = false; // Start with safety not OK
|
||||
last_relay_request = 0;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void i2c_set_safety_status(bool safe) {
|
||||
safety_ok = safe;
|
||||
|
||||
if (!safe) {
|
||||
// Safety tripped - immediately turn off all relays
|
||||
ESP_LOGW("I2C", "Safety interlock activated");
|
||||
tca_write_word_8(TCA_REG_OUTPUT1, last_relay_request & SAFETY_MASK);
|
||||
} else {
|
||||
// Safety cleared - restore last requested relay state
|
||||
ESP_LOGI("I2C", "Safety interlock cleared");
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t i2c_set_relays(uint8_t states) {
|
||||
last_relay_request = states; // Always track the request
|
||||
|
||||
if (!safety_ok) {
|
||||
if (!get_is_safe()) {
|
||||
// Safety interlock active - refuse to energize relays
|
||||
ESP_LOGW("I2C", "Main relay operation blocked by safety interlock");
|
||||
return tca_write_word_8(TCA_REG_OUTPUT1, states & SAFETY_MASK);
|
||||
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);
|
||||
return tca_write_word_8(TCA_REG_OUTPUT1, states | SENSOR_EN_MASK);
|
||||
}
|
||||
|
||||
esp_err_t i2c_set_led1(uint8_t state) {
|
||||
@@ -106,8 +95,8 @@ esp_err_t i2c_set_led1(uint8_t state) {
|
||||
|
||||
esp_err_t i2c_stop() {
|
||||
if (!i2c_initted) return ESP_OK;
|
||||
i2c_set_relays(0);
|
||||
i2c_set_led1(0);
|
||||
tca_write_word_8(TCA_REG_OUTPUT0, 0);
|
||||
tca_write_word_8(TCA_REG_OUTPUT1, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ esp_err_t i2c_stop(void);
|
||||
|
||||
esp_err_t i2c_set_relays(uint8_t states);
|
||||
esp_err_t i2c_set_led1(uint8_t state);
|
||||
void i2c_set_safety_status(bool safe);
|
||||
|
||||
esp_err_t i2c_poll_buttons();
|
||||
|
||||
|
||||
44
main/main.c
44
main/main.c
@@ -18,10 +18,12 @@
|
||||
#define TAG "MAIN"
|
||||
|
||||
int64_t last_log_time = 0;
|
||||
#define LOGSIZE 40
|
||||
esp_err_t send_log() {
|
||||
// >Hqfffflccc
|
||||
uint8_t entry[32] = {0};
|
||||
entry[0] = 32;
|
||||
uint8_t entry[LOGSIZE] = {};
|
||||
|
||||
|
||||
entry[0] = fsm_get_state();
|
||||
|
||||
// Pack 64-bit timestamp into bytes 1-8
|
||||
uint64_t be_timestamp = rtc_get_ms();
|
||||
@@ -37,16 +39,29 @@ esp_err_t send_log() {
|
||||
float be_current3 = get_bridge_A(BRIDGE_AUX);
|
||||
memcpy(&entry[21], &be_current3, 4);
|
||||
|
||||
int32_t be_counter = get_sensor_counter(SENSOR_DRIVE);
|
||||
memcpy(&entry[25], &be_counter, 4);
|
||||
int16_t be_counter = get_sensor_counter(SENSOR_DRIVE);
|
||||
memcpy(&entry[25], &be_counter, 2);
|
||||
|
||||
entry[29] = get_sensor(SENSOR_SAFETY);
|
||||
entry[30] = get_sensor(SENSOR_DRIVE);
|
||||
entry[31] = fsm_get_state();
|
||||
entry[27] = pack_sensors();
|
||||
|
||||
|
||||
|
||||
|
||||
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();
|
||||
|
||||
return write_log(LOG_TYPE_DATA, entry, 32);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -242,7 +257,7 @@ void app_main(void) {
|
||||
} else if (i2c_get_button_ms(0) > 100){
|
||||
driveLEDs(LED_STATE_START1);
|
||||
} else {
|
||||
if (
|
||||
/*if (
|
||||
rtc_is_set() &&
|
||||
!efuse_is_tripped(BRIDGE_JACK) &&
|
||||
!efuse_is_tripped(BRIDGE_AUX) &&
|
||||
@@ -252,11 +267,16 @@ void app_main(void) {
|
||||
driveLEDs(LED_STATE_AWAKE);
|
||||
} else {
|
||||
driveLEDs(LED_STATE_ERROR);
|
||||
}
|
||||
}*/
|
||||
|
||||
int8_t state = 0b001;
|
||||
if (get_is_safe()) state |= 0b010;
|
||||
if (get_sensor(SENSOR_SAFETY)) state |= 0b100;
|
||||
i2c_set_led1(state);
|
||||
}
|
||||
|
||||
// when not actively moving we log at a low frequency
|
||||
if (isRunning() || (esp_timer_get_time() > last_log_time + DEEP_SLEEP_US))
|
||||
if (isRunning() || (esp_timer_get_time() > last_log_time + 3000000)) //DEEP_SLEEP_US))
|
||||
send_log();
|
||||
|
||||
if(i2c_get_button_ms(0) > 2100)
|
||||
|
||||
@@ -314,6 +314,12 @@ float get_bridge_A(bridge_t bridge)
|
||||
return isens[bridge].current;
|
||||
}
|
||||
|
||||
|
||||
float get_bridge_heat(bridge_t bridge) {
|
||||
if (bridge >= N_BRIDGES) return NAN;
|
||||
return isens[bridge].heat;
|
||||
}
|
||||
|
||||
float get_battery_V(void)
|
||||
{
|
||||
if (ema_battery_init)
|
||||
|
||||
@@ -19,6 +19,7 @@ bool efuse_is_tripped(bridge_t bridge); // Query if bridge is currently fault
|
||||
|
||||
float get_bridge_A(bridge_t bridge);
|
||||
float get_battery_V();
|
||||
float get_bridge_heat(bridge_t bridge);
|
||||
|
||||
void set_autozero(bridge_t bridge);
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#include "sensors.h"
|
||||
#include "i2c.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "i2c.h"
|
||||
#include "storage.h"
|
||||
#include <sys/param.h>
|
||||
|
||||
// make the compiler shut up about casting an int to a void
|
||||
#define INT2VOIDP(i) (void*)(uintptr_t)(i)
|
||||
@@ -14,19 +15,22 @@
|
||||
|
||||
static const char* TAG = "SENS";
|
||||
|
||||
uint8_t sensor_pins[N_SENSORS] = {GPIO_NUM_27, GPIO_NUM_14};
|
||||
uint8_t sensor_pins[N_SENSORS] = {GPIO_NUM_27, GPIO_NUM_14, GPIO_NUM_16, GPIO_NUM_19};
|
||||
|
||||
volatile int32_t sensor_count[N_SENSORS] = {0};
|
||||
volatile int16_t sensor_count[N_SENSORS] = {0};
|
||||
static volatile uint64_t sensor_last_isr_time[N_SENSORS] = {0};
|
||||
static volatile bool sensor_stable_state[N_SENSORS] = {false};
|
||||
static QueueHandle_t sensor_event_queue = NULL;
|
||||
|
||||
|
||||
static volatile bool last_raw_state[N_SENSORS] = {false};
|
||||
|
||||
// Safety sensor debouncing
|
||||
static volatile bool safety_tripped = false;
|
||||
static volatile bool is_safe = true;
|
||||
static volatile uint64_t safety_low_start_time = 0;
|
||||
static volatile uint64_t safety_high_start_time = 0;
|
||||
#define SAFETY_TRIP_DEBOUNCE_US get_param_value_t(PARAM_SAFETY_BREAK_US).u32
|
||||
#define SAFETY_UNTRIP_DEBOUNCE_US get_param_value_t(PARAM_SAFETY_BREAK_US).u32
|
||||
#define SAFETY_BREAK_DEBOUNCE_US get_param_value_t(PARAM_SAFETY_BREAK_US).u32
|
||||
#define SAFETY_MAKE_DEBOUNCE_US get_param_value_t(PARAM_SAFETY_MAKE_US).u32
|
||||
|
||||
#define DEBOUNCE_TIME_US 2000 // 2 ms debounce (adjust per switch)
|
||||
#define DEBOUNCE_TICKS pdMS_TO_TICKS(DEBOUNCE_TIME_MS)
|
||||
@@ -59,7 +63,6 @@ static void sensor_debounce_task(void* param) {
|
||||
esp_task_wdt_add(NULL);
|
||||
sensor_event_t evt;
|
||||
//static uint64_t last_processed_time[N_SENSORS] = {0};
|
||||
static bool last_raw_state[N_SENSORS] = {false};
|
||||
|
||||
// Initialize stable state
|
||||
for (uint8_t i = 0; i < N_SENSORS; i++) {
|
||||
@@ -113,12 +116,10 @@ static void sensor_debounce_task(void* param) {
|
||||
// First time going low, start timing
|
||||
safety_low_start_time = now;
|
||||
safety_high_start_time = 0;
|
||||
ESP_LOGI(TAG, "Safety sensor went LOW, starting trip timer");
|
||||
} else if (!safety_tripped && (now - safety_low_start_time >= SAFETY_TRIP_DEBOUNCE_US)) {
|
||||
// Been low for 200ms, trip the safety
|
||||
safety_tripped = true;
|
||||
i2c_set_safety_status(false);
|
||||
ESP_LOGW(TAG, "SAFETY TRIPPED - Relays disabled");
|
||||
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)
|
||||
@@ -126,12 +127,11 @@ static void sensor_debounce_task(void* param) {
|
||||
// First time going high, start timing
|
||||
safety_high_start_time = now;
|
||||
safety_low_start_time = 0;
|
||||
ESP_LOGI(TAG, "Safety sensor went HIGH, starting un-trip timer");
|
||||
} else if (safety_tripped && (now - safety_high_start_time >= SAFETY_UNTRIP_DEBOUNCE_US)) {
|
||||
// Been high for 300ms, un-trip the safety
|
||||
safety_tripped = false;
|
||||
i2c_set_safety_status(true);
|
||||
ESP_LOGI(TAG, "SAFETY CLEARED - Relays enabled");
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,15 @@ static void sensor_debounce_task(void* param) {
|
||||
}
|
||||
}
|
||||
|
||||
int8_t pack_sensors() {
|
||||
int8_t ret = 0;
|
||||
for(uint8_t i=0; i<MIN(4, N_SENSORS); i++) {
|
||||
if(sensor_stable_state[i]) ret |= 0x01<<i;
|
||||
if(last_raw_state[i]) ret |= 0x01<<(i+4);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t sensors_init() {
|
||||
gpio_config_t io_conf = {
|
||||
.pin_bit_mask = (1ULL << sensor_pins[0]) | (1ULL << sensor_pins[1]),
|
||||
@@ -183,14 +192,14 @@ bool get_sensor(sensor_t i) {
|
||||
return sensor_stable_state[i];
|
||||
}
|
||||
|
||||
bool get_safety_sensor(void) {
|
||||
return !safety_tripped; // Returns true if safe, false if tripped
|
||||
bool get_is_safe(void) {
|
||||
return is_safe;
|
||||
}
|
||||
|
||||
int32_t get_sensor_counter(sensor_t i) {
|
||||
int16_t get_sensor_counter(sensor_t i) {
|
||||
return sensor_count[i];
|
||||
}
|
||||
|
||||
void set_sensor_counter(sensor_t i, int32_t to) {
|
||||
void set_sensor_counter(sensor_t i, int16_t to) {
|
||||
sensor_count[i] = to;
|
||||
}
|
||||
@@ -16,17 +16,21 @@
|
||||
#define SENSOR_DEBOUNCE_US 500 // Reduced to 0.5 ms for responsiveness
|
||||
|
||||
typedef enum {
|
||||
SENSOR_SAFETY = 0,
|
||||
SENSOR_DRIVE = 1,
|
||||
N_SENSORS = 2
|
||||
SENSOR_SAFETY = 0, // IO27
|
||||
SENSOR_DRIVE = 1, // IO14
|
||||
SENSOR_AUX1 = 2, // IO16 on V4
|
||||
SENSOR_AUX2 = 3, // IO19 on V4
|
||||
N_SENSORS = 4
|
||||
} sensor_t;
|
||||
|
||||
void reset_sensor_counter(sensor_t i);
|
||||
void set_sensor_counter(sensor_t i, int32_t to);
|
||||
int32_t get_sensor_counter(sensor_t i);
|
||||
void set_sensor_counter(sensor_t i, int16_t to);
|
||||
int16_t get_sensor_counter(sensor_t i);
|
||||
|
||||
bool get_sensor(sensor_t i);
|
||||
bool get_safety_sensor(void);
|
||||
bool get_is_safe(void);
|
||||
|
||||
int8_t pack_sensors();
|
||||
|
||||
esp_err_t sensors_init();
|
||||
esp_err_t sensors_stop();
|
||||
|
||||
654
main/storage.c
654
main/storage.c
@@ -1,9 +1,11 @@
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include "esp_partition.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_crc.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "storage.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
@@ -11,6 +13,26 @@
|
||||
|
||||
#define TAG "STORAGE"
|
||||
|
||||
// Add these includes at the top
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
// Add these defines near the top
|
||||
#define LOG_QUEUE_SIZE 8 // Number of log entries that can be queued
|
||||
#define LOG_TASK_STACK_SIZE 4096
|
||||
#define LOG_TASK_PRIORITY 5
|
||||
|
||||
// Add these static variables after the existing log variables (around line 100)
|
||||
static QueueHandle_t log_queue = NULL;
|
||||
static TaskHandle_t log_task_handle = NULL;
|
||||
static bool log_task_running = false;
|
||||
|
||||
// Log queue entry structure
|
||||
typedef struct {
|
||||
uint8_t data[LOG_MAX_PAYLOAD + 1]; // +1 for length byte
|
||||
uint8_t len;
|
||||
} log_queue_entry_t;
|
||||
|
||||
// ============================================================================
|
||||
// LOG TYPE DEFINITIONS (Magic values 0xC0-0xCF)
|
||||
// ============================================================================
|
||||
@@ -21,7 +43,6 @@
|
||||
#define LOG_TYPE_SENSOR 0xC4 // Sensor reading
|
||||
#define LOG_TYPE_COMMAND 0xC5 // Command executed
|
||||
#define LOG_TYPE_STATUS 0xC6 // Status update
|
||||
#define LOG_TYPE_PADDING 0xCE // Padding to sector boundary
|
||||
#define LOG_TYPE_CUSTOM 0xCF // Custom/user-defined
|
||||
|
||||
// Helper macro to check if a byte is a valid log type
|
||||
@@ -92,28 +113,29 @@ static const esp_partition_t *storage_partition = NULL;
|
||||
|
||||
// Log head/tail tracking with mutex protection
|
||||
// These now track byte offsets within the log area, not entry indices
|
||||
static uint32_t log_head_offset = 0; // Offset from LOG_START_OFFSET
|
||||
static uint32_t log_tail_offset = 0; // Offset from LOG_START_OFFSET
|
||||
static SemaphoreHandle_t log_mutex = NULL;
|
||||
static bool log_initialized = false;
|
||||
RTC_DATA_ATTR static uint32_t log_head_offset = 0;
|
||||
RTC_DATA_ATTR static uint32_t log_tail_offset = 0;
|
||||
RTC_DATA_ATTR static bool log_initialized = false;
|
||||
|
||||
uint32_t get_log_head(void) {
|
||||
static SemaphoreHandle_t log_mutex = NULL;
|
||||
|
||||
uint32_t log_get_head(void) {
|
||||
uint32_t head;
|
||||
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
|
||||
head = LOG_START_OFFSET + log_head_offset;
|
||||
head = log_head_offset;
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
return head;
|
||||
}
|
||||
|
||||
uint32_t get_log_tail(void) {
|
||||
uint32_t log_get_tail(void) {
|
||||
uint32_t tail;
|
||||
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
|
||||
tail = LOG_START_OFFSET + log_tail_offset;
|
||||
tail = log_tail_offset;
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
return tail;
|
||||
}
|
||||
|
||||
uint32_t get_log_offset(void) {
|
||||
uint32_t log_get_offset(void) {
|
||||
return LOG_START_OFFSET;
|
||||
}
|
||||
|
||||
@@ -385,6 +407,8 @@ esp_err_t factory_reset(void) {
|
||||
memcpy(¶meter_table[i], ¶meter_defaults[i], sizeof(param_value_t));
|
||||
}
|
||||
|
||||
// TODO: WIPE ENTIRE PARTITION
|
||||
|
||||
// Commit defaults to flash
|
||||
esp_err_t err = commit_params();
|
||||
if (err != ESP_OK) {
|
||||
@@ -402,11 +426,21 @@ esp_err_t factory_reset(void) {
|
||||
esp_err_t storage_init(void) {
|
||||
ESP_LOGI(TAG, "Initializing storage system...");
|
||||
|
||||
|
||||
|
||||
log_mutex = xSemaphoreCreateMutex();
|
||||
if (log_mutex == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to create log mutex");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
|
||||
|
||||
storage_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_ANY,
|
||||
"storage");
|
||||
if (storage_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Storage partition not found");
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
@@ -415,7 +449,7 @@ esp_err_t storage_init(void) {
|
||||
|
||||
// Load parameters from flash
|
||||
uint32_t flash_offset = PARAMS_OFFSET;
|
||||
bool all_valid = true;
|
||||
//bool all_valid = true;
|
||||
|
||||
for (int i = 0; i < NUM_PARAMS; i++) {
|
||||
param_stored_t stored;
|
||||
@@ -427,7 +461,7 @@ esp_err_t storage_init(void) {
|
||||
ESP_LOGW(TAG, "Failed to read parameter %d (%s), using default",
|
||||
i, parameter_names[i]);
|
||||
memcpy(¶meter_table[i], ¶meter_defaults[i], sizeof(param_value_t));
|
||||
all_valid = false;
|
||||
//all_valid = false;
|
||||
flash_offset += sizeof(param_stored_t);
|
||||
continue;
|
||||
}
|
||||
@@ -443,244 +477,120 @@ esp_err_t storage_init(void) {
|
||||
ESP_LOGW(TAG, "Parameter %d (%s) failed CRC check, using default",
|
||||
i, parameter_names[i]);
|
||||
memcpy(¶meter_table[i], ¶meter_defaults[i], sizeof(param_value_t));
|
||||
all_valid = false;
|
||||
//all_valid = false;
|
||||
}
|
||||
|
||||
flash_offset += sizeof(param_stored_t);
|
||||
}
|
||||
|
||||
if (all_valid) {
|
||||
/*if (all_valid) {
|
||||
ESP_LOGI(TAG, "All parameters loaded successfully from flash");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Some parameters failed validation, using defaults");
|
||||
}
|
||||
}*/
|
||||
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// VARIABLE-LENGTH LOGGING FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Find the first valid log entry by scanning for magic bytes.
|
||||
* Returns absolute flash offset, or -1 if no valid entry found.
|
||||
*/
|
||||
/*static int32_t find_first_valid_entry(uint32_t start_offset, uint32_t end_offset) {
|
||||
if (storage_partition == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t buffer[256];
|
||||
uint32_t scan_pos = start_offset;
|
||||
|
||||
while (scan_pos < end_offset) {
|
||||
size_t chunk_size = (end_offset - scan_pos) < sizeof(buffer) ?
|
||||
(end_offset - scan_pos) : sizeof(buffer);
|
||||
|
||||
esp_err_t err = esp_partition_read(storage_partition, scan_pos, buffer, chunk_size);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read during scan at offset %lu", (unsigned long)scan_pos);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Scan for valid type byte
|
||||
for (size_t i = 0; i < chunk_size; i++) {
|
||||
if (IS_VALID_LOG_TYPE(buffer[i])) {
|
||||
// Found potential entry - verify we can read size byte
|
||||
if (i + 1 < chunk_size) {
|
||||
// Size byte is in buffer
|
||||
return scan_pos + i;
|
||||
} else if (scan_pos + i + 1 < end_offset) {
|
||||
// Size byte is in next read
|
||||
return scan_pos + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move to next chunk, with 1-byte overlap to catch split entries
|
||||
scan_pos += chunk_size - 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Initialize the log system by finding head position
|
||||
*/
|
||||
esp_err_t log_init(void) {
|
||||
if (storage_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Storage partition not initialized, call storage_init() first");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
log_mutex = xSemaphoreCreateMutex();
|
||||
if (log_mutex == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to create log mutex");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
//uint32_t log_area_size = storage_partition->size - LOG_START_OFFSET;
|
||||
uint32_t log_area_end = storage_partition->size;
|
||||
|
||||
// Scan for first empty (0xFF) byte to find head
|
||||
uint8_t buffer[256];
|
||||
bool found_head = false;
|
||||
|
||||
for (uint32_t offset = LOG_START_OFFSET; offset < log_area_end; offset += sizeof(buffer)) {
|
||||
size_t to_read = (log_area_end - offset) < sizeof(buffer) ?
|
||||
(log_area_end - offset) : sizeof(buffer);
|
||||
|
||||
esp_err_t err = esp_partition_read(storage_partition, offset, buffer, to_read);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read during log init at offset %lu", (unsigned long)offset);
|
||||
vSemaphoreDelete(log_mutex);
|
||||
log_mutex = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
// Look for first 0xFF byte (empty flash)
|
||||
for (size_t i = 0; i < to_read; i++) {
|
||||
if (buffer[i] == 0xFF) {
|
||||
log_head_offset = (offset + i) - LOG_START_OFFSET;
|
||||
found_head = true;
|
||||
ESP_LOGI(TAG, "Log head found at offset %lu (absolute: %lu)",
|
||||
(unsigned long)log_head_offset,
|
||||
(unsigned long)(LOG_START_OFFSET + log_head_offset));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_head) break;
|
||||
}
|
||||
|
||||
if (!found_head) {
|
||||
// Log is completely full, wrap to beginning
|
||||
log_head_offset = 0;
|
||||
ESP_LOGI(TAG, "Log is full, wrapping to beginning");
|
||||
|
||||
// Erase first sector
|
||||
esp_err_t err = esp_partition_erase_range(storage_partition, LOG_START_OFFSET,
|
||||
FLASH_SECTOR_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to erase first log sector");
|
||||
vSemaphoreDelete(log_mutex);
|
||||
log_mutex = NULL;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// Set tail to start of log area initially (will be updated as sectors are erased)
|
||||
log_tail_offset = 0;
|
||||
|
||||
log_initialized = true;
|
||||
ESP_LOGI(TAG, "Log system initialized. Head offset: %lu, Tail offset: %lu",
|
||||
(unsigned long)log_head_offset, (unsigned long)log_tail_offset);
|
||||
|
||||
return ESP_OK;
|
||||
static inline uint32_t log_sector_end(uint32_t x) {
|
||||
return (x/FLASH_SECTOR_SIZE+1)*FLASH_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a variable-length log entry
|
||||
* @param type Log entry type (0xC0-0xCF range)
|
||||
* @param data Payload data pointer
|
||||
* @param size Payload size in bytes (0-255)
|
||||
*/
|
||||
/*esp_err_t write_log(char* entry) {
|
||||
// Legacy interface for compatibility - treat as raw 32-byte entry
|
||||
// Extract type and size from first two bytes if they look valid
|
||||
uint8_t type = (uint8_t)entry[0];
|
||||
uint8_t size = (uint8_t)entry[1];
|
||||
|
||||
if (!IS_VALID_LOG_TYPE(type)) {
|
||||
// Old format - use as LOG_TYPE_DATA with 30 bytes
|
||||
type = LOG_TYPE_DATA;
|
||||
size = 30; // Assume old 32-byte format minus 2-byte header
|
||||
// Helper function to check if a sector is erased (all 0xFF)
|
||||
static bool is_sector_erased(uint32_t sector_offset) {
|
||||
uint8_t buf[256];
|
||||
esp_err_t err = esp_partition_read(storage_partition, sector_offset, buf, 256);
|
||||
if (err != ESP_OK) return false;
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (buf[i] != 0xFF) return false;
|
||||
}
|
||||
|
||||
return write_log(type, (const uint8_t*)&entry[2], size);
|
||||
}*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a variable-length log entry (new interface)
|
||||
*/
|
||||
esp_err_t write_log(uint8_t type, const uint8_t* data, uint8_t size) {
|
||||
// Helper function to check if a sector has data (contains non-0xFF, non-0x00 bytes)
|
||||
static bool sector_has_data(uint32_t sector_offset) {
|
||||
uint8_t buf[256];
|
||||
esp_err_t err = esp_partition_read(storage_partition, sector_offset, buf, 256);
|
||||
if (err != ESP_OK) return false;
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (buf[i] != 0xFF && buf[i] != 0x00) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Replace log_write with this non-blocking version:
|
||||
esp_err_t log_write(uint8_t* buf, uint8_t len) {
|
||||
if (!log_initialized || storage_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Logging not initialized");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (!IS_VALID_LOG_TYPE(type)) {
|
||||
ESP_LOGE(TAG, "Invalid log type: 0x%02X", type);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
if (len > LOG_MAX_PAYLOAD) {
|
||||
ESP_LOGE(TAG, "Log payload too large: %d bytes (max %d)", len, LOG_MAX_PAYLOAD);
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
/*if (size > LOG_MAX_PAYLOAD) {
|
||||
ESP_LOGE(TAG, "Log payload too large: %d bytes (max %d)", size, LOG_MAX_PAYLOAD);
|
||||
if (log_queue == NULL) {
|
||||
ESP_LOGE(TAG, "Log queue not initialized");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Create queue entry
|
||||
log_queue_entry_t entry;
|
||||
entry.len = len;
|
||||
memcpy(entry.data, buf, len);
|
||||
|
||||
// Try to send to queue (non-blocking)
|
||||
if (xQueueSend(log_queue, &entry, 0) != pdTRUE) {
|
||||
ESP_LOGW(TAG, "Log queue full, dropping entry");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// The actual blocking write function (called by the task)
|
||||
static esp_err_t log_write_blocking(uint8_t* buf, uint8_t len) {
|
||||
if (!log_initialized || storage_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Logging not initialized");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (len > LOG_MAX_PAYLOAD) {
|
||||
ESP_LOGE(TAG, "Log payload too large: %d bytes (max %d)", len, LOG_MAX_PAYLOAD);
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}*/
|
||||
}
|
||||
|
||||
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
|
||||
|
||||
uint32_t log_area_size = storage_partition->size - LOG_START_OFFSET;
|
||||
uint32_t log_area_end = storage_partition->size;
|
||||
uint32_t entry_total_size = LOG_HEADER_SIZE + size; // 2 + payload
|
||||
|
||||
// Calculate absolute offsets
|
||||
uint32_t abs_head = LOG_START_OFFSET + log_head_offset;
|
||||
|
||||
// Check if entry would cross sector boundary
|
||||
uint32_t current_sector = abs_head / FLASH_SECTOR_SIZE;
|
||||
uint32_t entry_end = abs_head + entry_total_size;
|
||||
uint32_t end_sector = entry_end / FLASH_SECTOR_SIZE;
|
||||
|
||||
if (end_sector != current_sector) {
|
||||
// Entry would cross sector boundary - write padding
|
||||
uint32_t bytes_to_sector_end = FLASH_SECTOR_SIZE - (abs_head % FLASH_SECTOR_SIZE);
|
||||
// check if we will overrun the sector
|
||||
if (log_head_offset + len+1 >= 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));
|
||||
// zero the rest of sector
|
||||
char zeros[256] = {0};
|
||||
esp_partition_write(storage_partition,
|
||||
log_head_offset, &zeros,
|
||||
log_sector_end(log_head_offset)-log_head_offset);
|
||||
|
||||
ESP_LOGI(TAG, "Entry would cross sector boundary, padding %lu bytes",
|
||||
(unsigned long)bytes_to_sector_end);
|
||||
|
||||
// Write padding entry (type + size = 0)
|
||||
uint8_t pad_entry[2] = {LOG_TYPE_PADDING, 0x00};
|
||||
esp_err_t err = esp_partition_write(storage_partition, abs_head, pad_entry, 2);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to write padding: %s", esp_err_to_name(err));
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Advance head to next sector boundary
|
||||
log_head_offset += bytes_to_sector_end;
|
||||
|
||||
// Handle wrap-around
|
||||
if (log_head_offset >= log_area_size) {
|
||||
log_head_offset = 0;
|
||||
}
|
||||
|
||||
// Recalculate abs_head for actual entry write
|
||||
abs_head = LOG_START_OFFSET + log_head_offset;
|
||||
current_sector = abs_head / FLASH_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
// Check if we need to erase the next sector before writing
|
||||
uint32_t next_offset = abs_head + entry_total_size;
|
||||
if (next_offset >= log_area_end) {
|
||||
next_offset = LOG_START_OFFSET; // Wrap
|
||||
}
|
||||
|
||||
uint32_t next_sector = next_offset / FLASH_SECTOR_SIZE;
|
||||
|
||||
if (next_sector != current_sector) {
|
||||
// set head to next sector, and check for wrap
|
||||
log_head_offset = log_sector_end(log_head_offset);
|
||||
if (log_head_offset >= storage_partition->size)
|
||||
log_head_offset = LOG_START_OFFSET;
|
||||
|
||||
// Next write will be in a new sector - check if it needs erasing
|
||||
uint8_t check_byte;
|
||||
esp_err_t err = esp_partition_read(storage_partition, next_sector * FLASH_SECTOR_SIZE,
|
||||
esp_err_t err = esp_partition_read(storage_partition, log_head_offset,
|
||||
&check_byte, 1);
|
||||
|
||||
// Erase the next sector
|
||||
if (err == ESP_OK && check_byte != 0xFF) {
|
||||
ESP_LOGI(TAG, "Erasing sector %lu for log", (unsigned long)next_sector);
|
||||
ESP_LOGI(TAG, "Erasing sector %lu for log", (unsigned long)log_head_offset);
|
||||
err = esp_partition_erase_range(storage_partition,
|
||||
next_sector * FLASH_SECTOR_SIZE,
|
||||
log_head_offset,
|
||||
FLASH_SECTOR_SIZE);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
@@ -689,128 +599,240 @@ esp_err_t write_log(uint8_t type, const uint8_t* data, uint8_t size) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Update tail - it's now at the start of the sector we just erased
|
||||
uint32_t new_tail_abs = next_sector * FLASH_SECTOR_SIZE;
|
||||
if (new_tail_abs < LOG_START_OFFSET) {
|
||||
new_tail_abs = LOG_START_OFFSET;
|
||||
}
|
||||
|
||||
// update the tail, if needed
|
||||
if (log_tail_offset >= log_head_offset + FLASH_SECTOR_SIZE) log_tail_offset = log_head_offset + FLASH_SECTOR_SIZE;
|
||||
if (log_tail_offset >= storage_partition->size)
|
||||
log_tail_offset = LOG_START_OFFSET;
|
||||
|
||||
ESP_LOGI(TAG, "Tail/Head are now %lu/%lu",
|
||||
(unsigned long)log_tail_offset, (unsigned long)log_head_offset);
|
||||
}
|
||||
|
||||
|
||||
esp_partition_write(storage_partition, log_head_offset, &len, 1);
|
||||
esp_partition_write(storage_partition, log_head_offset+1, buf, len);
|
||||
|
||||
log_head_offset+=len+1;
|
||||
ESP_LOGI(TAG, "Wrote; Tail/Head are now %lu/%lu",
|
||||
(unsigned long)log_tail_offset, (unsigned long)log_head_offset);
|
||||
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Log writer task
|
||||
static void log_writer_task(void *pvParameters) {
|
||||
log_queue_entry_t entry;
|
||||
|
||||
ESP_LOGI(TAG, "Log writer task started");
|
||||
|
||||
while (log_task_running) {
|
||||
// Wait for log entry (with timeout to check running flag)
|
||||
if (xQueueReceive(log_queue, &entry, pdMS_TO_TICKS(100)) == pdTRUE) {
|
||||
// Write the log entry (blocking)
|
||||
esp_err_t err = log_write_blocking(entry.data, entry.len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to write log entry: %s", esp_err_to_name(err));
|
||||
}
|
||||
log_tail_offset = new_tail_abs - LOG_START_OFFSET;
|
||||
|
||||
ESP_LOGI(TAG, "Tail/Head are now %lu/%lu",
|
||||
(unsigned long)log_tail_offset, (unsigned long)log_head_offset);
|
||||
}
|
||||
|
||||
// Feed watchdog
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Log writer task stopping");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
// Modified log_init to create queue and task
|
||||
esp_err_t log_init() {
|
||||
if (storage_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Storage partition not initialized, call storage_init() first");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
|
||||
|
||||
uint32_t log_area_size = storage_partition->size - LOG_START_OFFSET;
|
||||
uint32_t num_sectors = log_area_size / FLASH_SECTOR_SIZE;
|
||||
|
||||
ESP_LOGI(TAG, "Log init: scanning %lu sectors with bisection", (unsigned long)num_sectors);
|
||||
|
||||
// Default to empty log
|
||||
log_head_offset = LOG_START_OFFSET;
|
||||
log_tail_offset = LOG_START_OFFSET;
|
||||
|
||||
// ... [INCLUDE THE BISECTION ALGORITHM FROM EARLIER HERE] ...
|
||||
// Binary search to find the first erased sector
|
||||
int32_t left = 0;
|
||||
int32_t right = num_sectors - 1;
|
||||
int32_t head_sector = -1;
|
||||
|
||||
while (left <= right) {
|
||||
int32_t mid = left + (right - left) / 2;
|
||||
uint32_t sector_offset = LOG_START_OFFSET + mid * FLASH_SECTOR_SIZE;
|
||||
|
||||
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 {
|
||||
left = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the actual log entry header + payload
|
||||
uint8_t header[LOG_HEADER_SIZE] = {type, size};
|
||||
|
||||
esp_err_t err = esp_partition_write(storage_partition, abs_head, header, LOG_HEADER_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to write log header: %s", esp_err_to_name(err));
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
return ESP_FAIL;
|
||||
if (head_sector == -1) {
|
||||
if (is_sector_erased(LOG_START_OFFSET)) {
|
||||
ESP_LOGI(TAG, "Log is empty");
|
||||
log_head_offset = LOG_START_OFFSET;
|
||||
log_tail_offset = LOG_START_OFFSET;
|
||||
} else {
|
||||
head_sector = 0;
|
||||
ESP_LOGW(TAG, "Log appears full, searching from start");
|
||||
}
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
err = esp_partition_write(storage_partition, abs_head + LOG_HEADER_SIZE, data, size);
|
||||
// Walk the data structure to find exact head
|
||||
uint32_t cursor;
|
||||
if (head_sector > 0) {
|
||||
cursor = LOG_START_OFFSET + (head_sector - 1) * FLASH_SECTOR_SIZE;
|
||||
} else {
|
||||
cursor = LOG_START_OFFSET;
|
||||
}
|
||||
|
||||
uint32_t head_sector_start = LOG_START_OFFSET + head_sector * FLASH_SECTOR_SIZE;
|
||||
|
||||
bool found_head = false;
|
||||
while (cursor < head_sector_start + FLASH_SECTOR_SIZE && cursor < storage_partition->size) {
|
||||
uint8_t buf;
|
||||
esp_err_t err = esp_partition_read(storage_partition, cursor, &buf, 1);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to write log payload: %s", esp_err_to_name(err));
|
||||
ESP_LOGE(TAG, "Failed to read during log init at offset %lu", (unsigned long)cursor);
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
return ESP_FAIL;
|
||||
return err;
|
||||
}
|
||||
|
||||
if (buf == 0xFF) {
|
||||
log_head_offset = cursor;
|
||||
found_head = true;
|
||||
break;
|
||||
} else if (buf == 0x00) {
|
||||
cursor = log_sector_end(cursor);
|
||||
} else {
|
||||
cursor += buf + 1;
|
||||
}
|
||||
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
|
||||
if (!found_head) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Advance head
|
||||
log_head_offset += entry_total_size;
|
||||
if (log_head_offset >= log_area_size) {
|
||||
log_head_offset -= log_area_size; // Wrap around
|
||||
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) {
|
||||
log_tail_offset = LOG_START_OFFSET + tail_sector * FLASH_SECTOR_SIZE;
|
||||
} else {
|
||||
log_tail_offset = LOG_START_OFFSET;
|
||||
}
|
||||
|
||||
log_initialized = true;
|
||||
ESP_LOGI(TAG, "Log system initialized (bisection). Head: %lu, Tail: %lu",
|
||||
(unsigned long)log_head_offset, (unsigned long)log_tail_offset);
|
||||
|
||||
// Create the log queue
|
||||
log_queue = xQueueCreate(LOG_QUEUE_SIZE, sizeof(log_queue_entry_t));
|
||||
if (log_queue == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to create log queue");
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
// Create the log writer task
|
||||
log_task_running = true;
|
||||
BaseType_t task_created = xTaskCreate(
|
||||
log_writer_task,
|
||||
"log_writer",
|
||||
LOG_TASK_STACK_SIZE,
|
||||
NULL,
|
||||
LOG_TASK_PRIORITY,
|
||||
&log_task_handle
|
||||
);
|
||||
|
||||
if (task_created != pdPASS) {
|
||||
ESP_LOGE(TAG, "Failed to create log writer task");
|
||||
vQueueDelete(log_queue);
|
||||
log_queue = NULL;
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
// Add the task to watchdog
|
||||
esp_task_wdt_add(log_task_handle);
|
||||
|
||||
ESP_LOGI(TAG, "Log writer task created successfully");
|
||||
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TEST FUNCTIONS FOR VARIABLE-LENGTH LOGS
|
||||
// ============================================================================
|
||||
|
||||
esp_err_t write_dummy_log_1(void) {
|
||||
ESP_LOGI(TAG, "Writing dummy variable-length log pattern 1");
|
||||
|
||||
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
|
||||
log_head_offset = 0;
|
||||
log_tail_offset = 0;
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
|
||||
// Write varied-size entries
|
||||
for (uint32_t i = 0; i < 100; i++) {
|
||||
uint8_t size = (i % 3 == 0) ? 16 : (i % 3 == 1) ? 48 : 32;
|
||||
uint8_t data[256];
|
||||
|
||||
// Fill with pattern
|
||||
for (uint8_t j = 0; j < size; j++) {
|
||||
data[j] = (i + j) & 0xFF;
|
||||
}
|
||||
|
||||
uint8_t type = LOG_TYPE_DATA + (i % 4); // Vary types
|
||||
write_log(type, data, size);
|
||||
|
||||
if (i % 10 == 0) {
|
||||
ESP_LOGI(TAG, "Wrote entry %lu (type=0x%02X, size=%d)",
|
||||
(unsigned long)i, type, size);
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t write_dummy_log_2(void) {
|
||||
ESP_LOGI(TAG, "Writing dummy variable-length log pattern 2");
|
||||
|
||||
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
|
||||
log_head_offset = 1000;
|
||||
log_tail_offset = 5000;
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
|
||||
for (uint32_t i = 0; i < 50; i++) {
|
||||
uint8_t size = 10 + (i * 3) % 200; // Varied sizes
|
||||
uint8_t data[256];
|
||||
memset(data, 0xAA + i, size);
|
||||
|
||||
write_log(LOG_TYPE_SENSOR, data, size);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t write_dummy_log_3(void) {
|
||||
ESP_LOGI(TAG, "Writing dummy variable-length log pattern 3");
|
||||
|
||||
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
|
||||
log_head_offset = 8000;
|
||||
log_tail_offset = 2000;
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
|
||||
// Write maximum-size entries
|
||||
for (uint32_t i = 0; i < 20; i++) {
|
||||
uint8_t data[LOG_MAX_PAYLOAD];
|
||||
for (uint16_t j = 0; j < LOG_MAX_PAYLOAD; j++) {
|
||||
data[j] = (i * 17 + j) & 0xFF;
|
||||
}
|
||||
|
||||
write_log(LOG_TYPE_DEBUG, data, LOG_MAX_PAYLOAD);
|
||||
|
||||
ESP_LOGI(TAG, "Wrote max-size entry %lu", (unsigned long)i);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Update storage_deinit to stop the task
|
||||
void storage_deinit(void) {
|
||||
// Stop the log writer task
|
||||
if (log_task_running) {
|
||||
log_task_running = false;
|
||||
|
||||
// Wait a bit for task to finish
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
if (log_task_handle != NULL) {
|
||||
esp_task_wdt_delete(log_task_handle);
|
||||
log_task_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the queue
|
||||
if (log_queue != NULL) {
|
||||
vQueueDelete(log_queue);
|
||||
log_queue = NULL;
|
||||
}
|
||||
|
||||
storage_partition = NULL;
|
||||
log_initialized = false;
|
||||
if (log_mutex) {
|
||||
vSemaphoreDelete(log_mutex);
|
||||
log_mutex = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
// ============================================================================
|
||||
#define FLASH_SECTOR_SIZE 4096
|
||||
#define PARAMS_OFFSET 0
|
||||
#define LOG_START_OFFSET 4096 // Start after first sector (parameters)
|
||||
#define PARAMETER_NUM_SECTORS 4
|
||||
#define LOG_START_OFFSET FLASH_SECTOR_SIZE*PARAMETER_NUM_SECTORS // Start after first sector (parameters)
|
||||
|
||||
// ============================================================================
|
||||
// LOG ENTRY TYPE DEFINITIONS (Magic values 0xC0-0xCF)
|
||||
@@ -40,7 +41,6 @@
|
||||
// [1]: Payload size (0-255 bytes)
|
||||
// [2-N]: Payload data
|
||||
typedef struct {
|
||||
uint8_t type; // Type/Magic byte (0xC0-0xCF range)
|
||||
uint8_t size; // Payload size in bytes (0-255)
|
||||
uint8_t data[]; // Flexible array member for payload
|
||||
} __attribute__((packed)) log_entry_header_t;
|
||||
@@ -51,7 +51,6 @@ typedef struct {
|
||||
// PARAMETER SYSTEM
|
||||
// ============================================================================
|
||||
|
||||
#define PARAMETER_NUM_SECTORS 4
|
||||
|
||||
#define PARAM_LIST \
|
||||
PARAM_DEF(BOOT_TIME, i32, 0, "us") \
|
||||
@@ -97,7 +96,8 @@ typedef struct {
|
||||
PARAM_DEF(JACK_I_DOWN, f32, 8.0, "A") \
|
||||
PARAM_DEF(V_SENS_K, f32, 0.00766666666, "V/mV") \
|
||||
PARAM_DEF(BUILD_VERSION, str, "undefined", "") \
|
||||
PARAM_DEF(SAFETY_BREAK_US, u32, 200000, "") \
|
||||
PARAM_DEF(SAFETY_BREAK_US, u32, 300000, "") \
|
||||
PARAM_DEF(SAFETY_MAKE_US, u32, 1000000, "") \
|
||||
|
||||
// Generate enum for parameter indices
|
||||
#define PARAM_DEF(name, type, default_val, unit) PARAM_##name,
|
||||
@@ -163,11 +163,11 @@ esp_err_t commit_params(void);
|
||||
|
||||
// Logging functions
|
||||
esp_err_t log_init(void);
|
||||
esp_err_t write_log(uint8_t type, const uint8_t* data, uint8_t size);
|
||||
uint32_t get_log_head(void);
|
||||
uint32_t get_log_tail(void);
|
||||
uint32_t get_log_offset(void);
|
||||
uint32_t get_log_size(void);
|
||||
esp_err_t log_write(uint8_t* buf, uint8_t len);
|
||||
uint32_t log_get_head(void);
|
||||
uint32_t log_get_tail(void);
|
||||
uint32_t log_get_offset(void);
|
||||
uint32_t log_get_size(void);
|
||||
|
||||
esp_err_t factory_reset();
|
||||
|
||||
|
||||
180
main/webserver.c
180
main/webserver.c
@@ -99,9 +99,9 @@ static esp_err_t root_get_handler(httpd_req_t *req) {
|
||||
// Cache the storage partition pointer to avoid repeated lookups
|
||||
static const esp_partition_t *cached_storage_partition = NULL;
|
||||
|
||||
// In webserver.c - Replace the log_handler function
|
||||
|
||||
static esp_err_t log_handler(httpd_req_t *req) {
|
||||
//ESP_LOGI(TAG, "log_handler");
|
||||
|
||||
if (req == NULL) {
|
||||
ESP_LOGE(TAG, "Null request pointer");
|
||||
return ESP_FAIL;
|
||||
@@ -112,7 +112,7 @@ static esp_err_t log_handler(httpd_req_t *req) {
|
||||
int32_t tail = -1;
|
||||
|
||||
if (req->method == HTTP_GET) {
|
||||
// give the whole log
|
||||
// GET without parameters - return JSON + full log
|
||||
}
|
||||
else if (req->method == HTTP_POST) {
|
||||
int ret = httpd_req_recv(req, httpBuffer, sizeof(httpBuffer));
|
||||
@@ -130,12 +130,9 @@ static esp_err_t log_handler(httpd_req_t *req) {
|
||||
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Request too large");
|
||||
}
|
||||
|
||||
httpBuffer[ret] = '\0'; // Null-terminate the string
|
||||
httpBuffer[ret] = '\0';
|
||||
|
||||
//ESP_LOGI(TAG, "LOG POST %.*s", ret, httpBuffer);
|
||||
|
||||
if(sscanf(httpBuffer, "%ld", (long*)&tail) != 1) {
|
||||
// if malformed, just send the whole log.
|
||||
ESP_LOGW(TAG, "Malformed tail parameter, using default");
|
||||
tail = -1;
|
||||
}
|
||||
@@ -145,10 +142,8 @@ static esp_err_t log_handler(httpd_req_t *req) {
|
||||
return httpd_resp_send_err(req, HTTPD_405_METHOD_NOT_ALLOWED, "Method not allowed");
|
||||
}
|
||||
|
||||
// Use cached partition pointer instead of looking it up each time
|
||||
const esp_partition_t *storage_partition = cached_storage_partition;
|
||||
if (storage_partition == NULL) {
|
||||
// Fall back to lookup if cache is empty (shouldn't happen in normal operation)
|
||||
storage_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_ANY,
|
||||
"storage");
|
||||
@@ -160,69 +155,102 @@ static esp_err_t log_handler(httpd_req_t *req) {
|
||||
cached_storage_partition = storage_partition;
|
||||
}
|
||||
|
||||
// Get head and tail atomically and store in local variables
|
||||
// This releases the mutex before we do any partition operations
|
||||
int32_t head = get_log_head();
|
||||
int32_t log_start = get_log_offset();
|
||||
int32_t head = log_get_head();
|
||||
int32_t log_start = log_get_offset();
|
||||
|
||||
if (tail < 0) {
|
||||
tail = get_log_tail();
|
||||
tail = log_get_tail();
|
||||
} else {
|
||||
// Validate tail is within log area bounds
|
||||
if (tail < log_start || tail >= (int32_t)storage_partition->size) {
|
||||
ESP_LOGW(TAG, "Invalid tail pointer %ld, using current tail", (long)tail);
|
||||
tail = get_log_tail();
|
||||
tail = log_get_tail();
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate total size to send
|
||||
int32_t total_size;
|
||||
// Calculate log data size
|
||||
int32_t log_data_size;
|
||||
if (tail == head) {
|
||||
// Empty log - just send pointers
|
||||
total_size = 8;
|
||||
log_data_size = 0;
|
||||
} else if (tail < head) {
|
||||
// Normal case: tail before head
|
||||
total_size = head - tail + 8; // +8 for head/tail pointers
|
||||
log_data_size = head - tail;
|
||||
} else {
|
||||
// Wrapped case: tail after head
|
||||
total_size = (storage_partition->size - tail) + (head - log_start) + 8;
|
||||
log_data_size = (storage_partition->size - tail) + (head - log_start);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Log request: tail=%ld, head=%ld, total_size=%ld",
|
||||
(long)tail, (long)head, (long)total_size);
|
||||
// Generate JSON header (same as /get endpoint)
|
||||
cJSON *json_response = comms_handle_get();
|
||||
if (json_response == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to generate JSON response");
|
||||
return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR,
|
||||
"Failed to generate response");
|
||||
}
|
||||
|
||||
char *json_str = cJSON_PrintUnformatted(json_response);
|
||||
cJSON_Delete(json_response);
|
||||
|
||||
if (json_str == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to serialize JSON");
|
||||
return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR,
|
||||
"Failed to serialize response");
|
||||
}
|
||||
|
||||
uint32_t json_len = strlen(json_str);
|
||||
|
||||
// Total size: 4 (json length) + json + 8 (head/tail) + log_data
|
||||
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",
|
||||
(long)tail, (long)head, (unsigned long)json_len, (long)log_data_size,
|
||||
(unsigned long)total_size);
|
||||
|
||||
// Send HTTP headers
|
||||
char len_str[16];
|
||||
int written = snprintf(len_str, sizeof(len_str), "%u", (unsigned)total_size);
|
||||
if (written < 0 || written >= (int)sizeof(len_str)) {
|
||||
ESP_LOGE(TAG, "Failed to format content length");
|
||||
return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR,
|
||||
"Internal error formatting response");
|
||||
}
|
||||
snprintf(len_str, sizeof(len_str), "%u", (unsigned)total_size);
|
||||
|
||||
esp_err_t err = httpd_resp_set_type(req, "application/octet-stream");
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to set response type: %s", esp_err_to_name(err));
|
||||
free(json_str);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = httpd_resp_set_hdr(req, "Content-Disposition", "attachment; filename=\"sc_storage.bin\"");
|
||||
err = httpd_resp_set_hdr(req, "Content-Disposition", "attachment; filename=\"sc_log.bin\"");
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to set content disposition: %s", esp_err_to_name(err));
|
||||
free(json_str);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = httpd_resp_set_hdr(req, "Content-Length", len_str);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to set content length: %s", esp_err_to_name(err));
|
||||
free(json_str);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Send head/tail pointers in big-endian format
|
||||
// Send JSON length (4 bytes, big-endian)
|
||||
uint32_t json_len_be = htobe32(json_len);
|
||||
memcpy(&httpBuffer[0], &json_len_be, 4);
|
||||
err = httpd_resp_send_chunk(req, (const char *)httpBuffer, 4);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to send JSON length: %s", esp_err_to_name(err));
|
||||
free(json_str);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Send JSON string
|
||||
err = httpd_resp_send_chunk(req, json_str, json_len);
|
||||
free(json_str);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to send JSON: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
// Send head/tail pointers (8 bytes, big-endian)
|
||||
int32_t htail = htobe32(tail);
|
||||
int32_t hhead = htobe32(head);
|
||||
memcpy(&httpBuffer[0], &(htail), 4);
|
||||
memcpy(&httpBuffer[4], &(hhead), 4);
|
||||
memcpy(&httpBuffer[0], &htail, 4);
|
||||
memcpy(&httpBuffer[4], &hhead, 4);
|
||||
|
||||
err = httpd_resp_send_chunk(req, (const char *)httpBuffer, 8);
|
||||
if (err != ESP_OK) {
|
||||
@@ -230,43 +258,58 @@ static esp_err_t log_handler(httpd_req_t *req) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Send log data (same as before)
|
||||
int32_t offset = tail;
|
||||
|
||||
// Only send data if there's something to send
|
||||
if (tail != head) {
|
||||
// Handle wrapped case: send from tail to end of partition first
|
||||
if (tail > head) {
|
||||
//ESP_LOGI(TAG, "Wrapped log: sending tail=%ld to partition_end=%lu",
|
||||
// (long)tail, (unsigned long)storage_partition->size);
|
||||
|
||||
while (offset < (int32_t)storage_partition->size) {
|
||||
size_t to_read = MIN(sizeof(httpBuffer), storage_partition->size - offset);
|
||||
err = esp_partition_read(storage_partition, offset, httpBuffer, to_read);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read partition at offset %ld: %s",
|
||||
(long)offset, esp_err_to_name(err));
|
||||
return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR,
|
||||
"Failed to read storage");
|
||||
}
|
||||
|
||||
err = httpd_resp_send_chunk(req, (const char *)httpBuffer, to_read);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to send chunk at offset %ld: %s",
|
||||
(long)offset, esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
offset += to_read;
|
||||
if (tail == head) {
|
||||
// Empty log, nothing more to send
|
||||
}
|
||||
else if (tail < head) {
|
||||
// Normal case: tail before head
|
||||
while (offset < head) {
|
||||
size_t to_read = MIN(sizeof(httpBuffer), head - offset);
|
||||
err = esp_partition_read(storage_partition, offset, httpBuffer, to_read);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read partition at offset %ld: %s",
|
||||
(long)offset, esp_err_to_name(err));
|
||||
return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR,
|
||||
"Failed to read storage");
|
||||
}
|
||||
|
||||
// Wrap to beginning of log area
|
||||
offset = log_start;
|
||||
//ESP_LOGI(TAG, "Wrapped to log start, offset=%ld", (long)offset);
|
||||
err = httpd_resp_send_chunk(req, (const char *)httpBuffer, to_read);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to send chunk at offset %ld: %s",
|
||||
(long)offset, esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
offset += to_read;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Wrapped case: tail after head, read from tail to end, then start to head
|
||||
while (offset < (int32_t)storage_partition->size) {
|
||||
size_t to_read = MIN(sizeof(httpBuffer), storage_partition->size - offset);
|
||||
err = esp_partition_read(storage_partition, offset, httpBuffer, to_read);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read partition at offset %ld: %s",
|
||||
(long)offset, esp_err_to_name(err));
|
||||
return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR,
|
||||
"Failed to read storage");
|
||||
}
|
||||
|
||||
err = httpd_resp_send_chunk(req, (const char *)httpBuffer, to_read);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to send chunk at offset %ld: %s",
|
||||
(long)offset, esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
offset += to_read;
|
||||
}
|
||||
|
||||
// Send from current offset to head
|
||||
//ESP_LOGI(TAG, "Sending final section: offset=%ld to head=%ld", (long)offset, (long)head);
|
||||
|
||||
// Now read from start to head
|
||||
offset = log_start;
|
||||
while (offset < head) {
|
||||
size_t to_read = MIN(sizeof(httpBuffer), head - offset);
|
||||
err = esp_partition_read(storage_partition, offset, httpBuffer, to_read);
|
||||
@@ -295,12 +338,9 @@ static esp_err_t log_handler(httpd_req_t *req) {
|
||||
return err;
|
||||
}
|
||||
|
||||
//ESP_LOGI(TAG, "Successfully sent log data");
|
||||
|
||||
err = httpd_resp_set_hdr(req, "Connection", "close");
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to set connection header: %s", esp_err_to_name(err));
|
||||
// Continue anyway
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
Reference in New Issue
Block a user