storage partition overhaul
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/version.cmake)
|
||||
|
||||
idf_component_register(
|
||||
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 bt_hid.c # list the source files of this component
|
||||
SRCS main.c log_test.c partition_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 bt_hid.c # list the source files of this component
|
||||
INCLUDE_DIRS "." "${CMAKE_BINARY_DIR}"
|
||||
PRIV_INCLUDE_DIRS # optional, add here private include directories
|
||||
|
||||
|
||||
12
main/i2c.c
12
main/i2c.c
@@ -52,6 +52,18 @@ esp_err_t i2c_init(void) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2c_post(void) {
|
||||
// Verify TCA9555 responds by reading input port 0
|
||||
uint16_t val = 0;
|
||||
esp_err_t err = tca_read_word(TCA_REG_INPUT0, &val);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE("I2C", "POST: TCA9555 read failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI("I2C", "POST: TCA9555 OK (port0=0x%04X)", val);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2c_set_relays(relay_port_t states) {
|
||||
return tca_write_word_8(TCA_REG_OUTPUT1, states.raw);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ typedef union {
|
||||
|
||||
// Public Functions
|
||||
esp_err_t i2c_init(void);
|
||||
esp_err_t i2c_post(void);
|
||||
esp_err_t i2c_stop(void);
|
||||
|
||||
esp_err_t i2c_set_relays(relay_port_t states);
|
||||
|
||||
86
main/main.c
86
main/main.c
@@ -2,6 +2,7 @@
|
||||
#include "esp_system.h"
|
||||
#include "i2c.h"
|
||||
#include "log_test.h"
|
||||
#include "partition_test.h"
|
||||
#include "storage.h"
|
||||
#include "uart_comms.h"
|
||||
#include "esp_err.h"
|
||||
@@ -20,6 +21,23 @@
|
||||
|
||||
#define TAG "MAIN"
|
||||
|
||||
#define POST_MAX_RETRIES 3
|
||||
|
||||
// Try an init function up to POST_MAX_RETRIES times. On final failure, reboot.
|
||||
// Critical inits (ADC, I2C, storage, FSM, sensors) use this — a permanent failure
|
||||
// feeds the OTA rollback reset counter via the panic→reboot path.
|
||||
static void init_critical(const char *name, esp_err_t (*fn)(void)) {
|
||||
for (int attempt = 1; attempt <= POST_MAX_RETRIES; attempt++) {
|
||||
esp_err_t err = fn();
|
||||
if (err == ESP_OK) return;
|
||||
ESP_LOGE(TAG, "%s FAILED (attempt %d/%d): %s", name, attempt, POST_MAX_RETRIES, esp_err_to_name(err));
|
||||
if (attempt < POST_MAX_RETRIES) vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
ESP_LOGE(TAG, "%s FAILED after %d attempts — rebooting", name, POST_MAX_RETRIES);
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
int64_t last_bat_log_time = 0;
|
||||
esp_err_t send_bat_log() {
|
||||
if(!rtc_is_set()) return ESP_OK;
|
||||
@@ -103,27 +121,21 @@ void drive_leds(led_state_t state) {
|
||||
|
||||
void app_main(void) {esp_task_wdt_add(NULL);
|
||||
|
||||
//run_all_log_tests();
|
||||
|
||||
ESP_LOGI(TAG, "Firmware: %s", FIRMWARE_STRING);
|
||||
ESP_LOGI(TAG, "Version: %s", FIRMWARE_VERSION);
|
||||
ESP_LOGI(TAG, "Branch: %s", FIRMWARE_BRANCH);
|
||||
ESP_LOGI(TAG, "Built: %s", BUILD_DATE);
|
||||
|
||||
// TODO: Check wdt stuff
|
||||
// TODO: Stack Overflow Detection
|
||||
// TODO: Remove XTAL crystal stuff
|
||||
// TODO: Confirm whether external RTC crystal can be dropped (see TODO.md #13)
|
||||
if (rtc_xtal_init() != ESP_OK) ESP_LOGE(TAG, "RTC FAILED");
|
||||
rtc_restore_time(); // Recover time from RTC domain if we crashed
|
||||
|
||||
// Say hello; turn on the lights
|
||||
rtc_wakeup_cause(); // log wakeup cause (informational only) // TODO: Shouldnt be needed anymore
|
||||
if (i2c_init() != ESP_OK) ESP_LOGE(TAG, "I2C FAILED");
|
||||
// Critical inits — retry up to 3 times, then reboot (feeds OTA rollback counter)
|
||||
init_critical("I2C", i2c_init);
|
||||
i2c_post(); // verify TCA9555 responds
|
||||
i2c_set_relays((relay_port_t){.raw=0});
|
||||
drive_leds(LED_STATE_BOOTING);
|
||||
|
||||
// TODO: How many tasks do we have?
|
||||
|
||||
|
||||
// Check for factory reset condition: Cold boot (power-on/ext-reset) + button held
|
||||
esp_reset_reason_t boot_reset_reason = esp_reset_reason();
|
||||
@@ -170,13 +182,17 @@ void app_main(void) {esp_task_wdt_add(NULL);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
// Every boot we load parameters and monitor solar, no matter what
|
||||
// TODO: Do things with errors (put in real log? then reset. "assert with LOGE"?)
|
||||
if (adc_init() != ESP_OK) ESP_LOGE(TAG, "ADC FAILED");
|
||||
if (storage_init() != ESP_OK) ESP_LOGE(TAG, "STORAGE FAILED");
|
||||
if (log_init() != ESP_OK) ESP_LOGE(TAG, "LOG FAILED");
|
||||
// TODO: figure out how long logging takes (for reference, and comp to wdt)
|
||||
|
||||
// Critical inits — retry up to 3 times, then reboot
|
||||
init_critical("ADC", adc_init);
|
||||
init_critical("STORAGE", storage_init);
|
||||
init_critical("LOG", log_init);
|
||||
|
||||
// POST checks — verify hardware is responding correctly
|
||||
adc_post(); // ADC channels readable and not frozen
|
||||
storage_post(); // flash write-read-verify on test sector
|
||||
|
||||
//run_all_log_tests();
|
||||
|
||||
esp_reset_reason_t reset_reason = esp_reset_reason();
|
||||
esp_sleep_wakeup_cause_t wake_cause = esp_sleep_get_wakeup_cause();
|
||||
|
||||
@@ -189,9 +205,7 @@ void app_main(void) {esp_task_wdt_add(NULL);
|
||||
log_write(boot_entry, sizeof(boot_entry), LOG_TYPE_BOOT);
|
||||
}
|
||||
|
||||
// TODO: make sure that this is "crash proof"
|
||||
// TODO: OTA rollback (triggered how? preferably with hardware... or if there are 5 resets in a row [check bootloader?]. also need way to nuke the storage partition or safe boot)
|
||||
// TODO: (maybe) recovery partition that allows uploading firmware
|
||||
// TODO: OTA rollback counter (see TODO.md #3)
|
||||
// Write a crash log entry if we rebooted unexpectedly
|
||||
if (reset_reason == ESP_RST_PANIC ||
|
||||
reset_reason == ESP_RST_INT_WDT ||
|
||||
@@ -205,31 +219,19 @@ void app_main(void) {esp_task_wdt_add(NULL);
|
||||
log_write(crash_entry, sizeof(crash_entry), LOG_TYPE_CRASH);
|
||||
}
|
||||
|
||||
// TODO: is this reasonable now that we eliminated deep sleep?
|
||||
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
|
||||
|
||||
send_bat_log();
|
||||
|
||||
// TODO: test strategy!!! (software verification, and unit bringup)
|
||||
// TODO: A->D bringup; sanity check (sum up all inputs, wait 5ms, sum again, make sure there is a change (not frozen))
|
||||
|
||||
// TODO: make sure sdkconfig is sane. Make notes, have claude figure this out properly
|
||||
// TODO: fix managed_components
|
||||
|
||||
//send_log();
|
||||
|
||||
//write_dummy_log_1();
|
||||
|
||||
/*** FULL BOOT — always, every boot ***/
|
||||
if (uart_init() != ESP_OK) ESP_LOGE(TAG, "UART FAILED");
|
||||
//if (power_init() != ESP_OK) ESP_LOGE(TAG, "POWER FAILED");
|
||||
|
||||
// TODO: Seriously, log all the errors on bluetooth
|
||||
if (rf_433_init() != ESP_OK) ESP_LOGE(TAG, "RF FAILED");
|
||||
if (bt_hid_init() != ESP_OK) ESP_LOGE(TAG, "BT HID FAILED");
|
||||
if (fsm_init() != ESP_OK) ESP_LOGE(TAG, "FSM FAILED");
|
||||
//if (sensors_init() != ESP_OK) ESP_LOGE(TAG, "SENSORS FAILED"); // TODO: Why is this off?
|
||||
|
||||
/*** FULL BOOT ***/
|
||||
// Critical — must succeed or reboot
|
||||
init_critical("UART", uart_init);
|
||||
init_critical("FSM", fsm_init);
|
||||
// sensors_init() is called inside control_task() — see control_fsm.c:185
|
||||
|
||||
// Non-critical — log error but continue booting
|
||||
if (rf_433_init() != ESP_OK) ESP_LOGE(TAG, "RF FAILED");
|
||||
if (bt_hid_init() != ESP_OK) ESP_LOGE(TAG, "BT HID FAILED");
|
||||
if (webserver_init() != ESP_OK) ESP_LOGE(TAG, "WEBSERVER FAILED");
|
||||
|
||||
/*** MAIN LOOP ***/
|
||||
|
||||
467
main/partition_test.c
Normal file
467
main/partition_test.c
Normal file
@@ -0,0 +1,467 @@
|
||||
#include "partition_test.h"
|
||||
#include "storage.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "PART_TEST"
|
||||
|
||||
// ============================================================================
|
||||
// Test 1: Params partition read/write
|
||||
// ============================================================================
|
||||
bool test_params_partition_rw(void) {
|
||||
ESP_LOGI(TAG, "=== Test: params partition read/write ===");
|
||||
|
||||
const esp_partition_t *part = esp_partition_find_first(
|
||||
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "params");
|
||||
if (part == NULL) {
|
||||
ESP_LOGE(TAG, "FAIL: params partition not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "params partition: offset=0x%lx size=%lu",
|
||||
(unsigned long)part->address, (unsigned long)part->size);
|
||||
|
||||
// Erase first sector
|
||||
esp_err_t err = esp_partition_erase_range(part, 0, FLASH_SECTOR_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: erase failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write test pattern
|
||||
uint8_t write_buf[32];
|
||||
for (int i = 0; i < 32; i++) write_buf[i] = (uint8_t)(0xAA ^ i);
|
||||
|
||||
err = esp_partition_write(part, 0, write_buf, sizeof(write_buf));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: write failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read back and verify
|
||||
uint8_t read_buf[32];
|
||||
err = esp_partition_read(part, 0, read_buf, sizeof(read_buf));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: read failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(write_buf, read_buf, sizeof(write_buf)) != 0) {
|
||||
ESP_LOGE(TAG, "FAIL: data mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify erased area reads 0xFF
|
||||
uint8_t erased_check;
|
||||
err = esp_partition_read(part, 64, &erased_check, 1);
|
||||
if (err != ESP_OK || erased_check != 0xFF) {
|
||||
ESP_LOGE(TAG, "FAIL: erased area not 0xFF (got 0x%02X)", erased_check);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clean up
|
||||
esp_partition_erase_range(part, 0, FLASH_SECTOR_SIZE);
|
||||
|
||||
ESP_LOGI(TAG, "PASS");
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test 2: Log partition read/write
|
||||
// ============================================================================
|
||||
bool test_log_partition_rw(void) {
|
||||
ESP_LOGI(TAG, "=== Test: log partition read/write ===");
|
||||
|
||||
const esp_partition_t *part = esp_partition_find_first(
|
||||
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "log");
|
||||
if (part == NULL) {
|
||||
ESP_LOGE(TAG, "FAIL: log partition not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "log partition: offset=0x%lx size=%lu",
|
||||
(unsigned long)part->address, (unsigned long)part->size);
|
||||
|
||||
// Verify size matches expectations (108K = 27 sectors)
|
||||
uint32_t expected_sectors = part->size / FLASH_SECTOR_SIZE;
|
||||
ESP_LOGI(TAG, "log partition has %lu sectors", (unsigned long)expected_sectors);
|
||||
|
||||
// Test write at start of partition
|
||||
esp_err_t err = esp_partition_erase_range(part, 0, FLASH_SECTOR_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: erase sector 0 failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t write_buf[16] = {0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04,
|
||||
0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C};
|
||||
err = esp_partition_write(part, 0, write_buf, sizeof(write_buf));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: write failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t read_buf[16];
|
||||
err = esp_partition_read(part, 0, read_buf, sizeof(read_buf));
|
||||
if (err != ESP_OK || memcmp(write_buf, read_buf, sizeof(write_buf)) != 0) {
|
||||
ESP_LOGE(TAG, "FAIL: read-back mismatch at offset 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test write at last sector
|
||||
uint32_t last_sector = part->size - FLASH_SECTOR_SIZE;
|
||||
err = esp_partition_erase_range(part, last_sector, FLASH_SECTOR_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: erase last sector failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = esp_partition_write(part, last_sector, write_buf, sizeof(write_buf));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: write to last sector failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = esp_partition_read(part, last_sector, read_buf, sizeof(read_buf));
|
||||
if (err != ESP_OK || memcmp(write_buf, read_buf, sizeof(write_buf)) != 0) {
|
||||
ESP_LOGE(TAG, "FAIL: read-back mismatch at last sector");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clean up
|
||||
esp_partition_erase_range(part, 0, FLASH_SECTOR_SIZE);
|
||||
esp_partition_erase_range(part, last_sector, FLASH_SECTOR_SIZE);
|
||||
|
||||
ESP_LOGI(TAG, "PASS");
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test 3: POST test partition read/write
|
||||
// ============================================================================
|
||||
bool test_post_partition_rw(void) {
|
||||
ESP_LOGI(TAG, "=== Test: post_test partition read/write ===");
|
||||
|
||||
const esp_partition_t *part = esp_partition_find_first(
|
||||
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "post_test");
|
||||
if (part == NULL) {
|
||||
ESP_LOGE(TAG, "FAIL: post_test partition not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "post_test partition: offset=0x%lx size=%lu",
|
||||
(unsigned long)part->address, (unsigned long)part->size);
|
||||
|
||||
// Verify it's exactly 4K (1 sector)
|
||||
if (part->size != FLASH_SECTOR_SIZE) {
|
||||
ESP_LOGE(TAG, "FAIL: expected 4096 bytes, got %lu", (unsigned long)part->size);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Run the actual storage_post() function
|
||||
esp_err_t err = storage_post();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: storage_post() returned %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the sector is clean after POST (it erases on completion)
|
||||
uint8_t check;
|
||||
err = esp_partition_read(part, 0, &check, 1);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: read after POST failed");
|
||||
return false;
|
||||
}
|
||||
if (check != 0xFF) {
|
||||
ESP_LOGE(TAG, "FAIL: POST didn't clean up (byte 0 = 0x%02X)", check);
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "PASS");
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test 4: Partitions are independent (writing to one doesn't corrupt another)
|
||||
// ============================================================================
|
||||
bool test_partitions_independent(void) {
|
||||
ESP_LOGI(TAG, "=== Test: partition independence ===");
|
||||
|
||||
const esp_partition_t *params = esp_partition_find_first(
|
||||
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "params");
|
||||
const esp_partition_t *log = esp_partition_find_first(
|
||||
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "log");
|
||||
const esp_partition_t *post = esp_partition_find_first(
|
||||
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "post_test");
|
||||
|
||||
if (!params || !log || !post) {
|
||||
ESP_LOGE(TAG, "FAIL: one or more partitions not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify no overlap
|
||||
uint32_t params_end = params->address + params->size;
|
||||
uint32_t log_end = log->address + log->size;
|
||||
uint32_t post_end = post->address + post->size;
|
||||
|
||||
ESP_LOGI(TAG, "params: 0x%lx - 0x%lx", (unsigned long)params->address, (unsigned long)params_end);
|
||||
ESP_LOGI(TAG, "log: 0x%lx - 0x%lx", (unsigned long)log->address, (unsigned long)log_end);
|
||||
ESP_LOGI(TAG, "post: 0x%lx - 0x%lx", (unsigned long)post->address, (unsigned long)post_end);
|
||||
|
||||
bool overlap = false;
|
||||
if (params->address < log_end && log->address < params_end) overlap = true;
|
||||
if (params->address < post_end && post->address < params_end) overlap = true;
|
||||
if (log->address < post_end && post->address < log_end) overlap = true;
|
||||
|
||||
if (overlap) {
|
||||
ESP_LOGE(TAG, "FAIL: partitions overlap!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write a sentinel to each partition, then verify none were corrupted
|
||||
esp_partition_erase_range(params, 0, FLASH_SECTOR_SIZE);
|
||||
esp_partition_erase_range(log, 0, FLASH_SECTOR_SIZE);
|
||||
esp_partition_erase_range(post, 0, FLASH_SECTOR_SIZE);
|
||||
|
||||
uint8_t pat_params[4] = {0x11, 0x22, 0x33, 0x44};
|
||||
uint8_t pat_log[4] = {0x55, 0x66, 0x77, 0x88};
|
||||
uint8_t pat_post[4] = {0x99, 0xAA, 0xBB, 0xCC};
|
||||
|
||||
esp_partition_write(params, 0, pat_params, 4);
|
||||
esp_partition_write(log, 0, pat_log, 4);
|
||||
esp_partition_write(post, 0, pat_post, 4);
|
||||
|
||||
// Read back all three and verify
|
||||
uint8_t rb[4];
|
||||
|
||||
esp_partition_read(params, 0, rb, 4);
|
||||
if (memcmp(rb, pat_params, 4) != 0) {
|
||||
ESP_LOGE(TAG, "FAIL: params sentinel corrupted");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_partition_read(log, 0, rb, 4);
|
||||
if (memcmp(rb, pat_log, 4) != 0) {
|
||||
ESP_LOGE(TAG, "FAIL: log sentinel corrupted");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_partition_read(post, 0, rb, 4);
|
||||
if (memcmp(rb, pat_post, 4) != 0) {
|
||||
ESP_LOGE(TAG, "FAIL: post sentinel corrupted");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clean up
|
||||
esp_partition_erase_range(params, 0, FLASH_SECTOR_SIZE);
|
||||
esp_partition_erase_range(log, 0, FLASH_SECTOR_SIZE);
|
||||
esp_partition_erase_range(post, 0, FLASH_SECTOR_SIZE);
|
||||
|
||||
ESP_LOGI(TAG, "PASS");
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test 5: Parameter commit and reload
|
||||
// ============================================================================
|
||||
bool test_params_persist_after_commit(void) {
|
||||
ESP_LOGI(TAG, "=== Test: params persist after commit ===");
|
||||
|
||||
// Save original value
|
||||
param_value_t original = get_param_value_t(PARAM_DRIVE_DIST);
|
||||
float orig_val = original.f32;
|
||||
|
||||
// Set a distinctive test value
|
||||
float test_val = 99.99f;
|
||||
param_value_t test = {.f32 = test_val};
|
||||
esp_err_t err = set_param_value_t(PARAM_DRIVE_DIST, test);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: set_param_value_t failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Commit to flash
|
||||
err = commit_params();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: commit_params failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the value stuck in RAM
|
||||
param_value_t readback = get_param_value_t(PARAM_DRIVE_DIST);
|
||||
if (readback.f32 != test_val) {
|
||||
ESP_LOGE(TAG, "FAIL: RAM value mismatch (got %.2f, expected %.2f)",
|
||||
readback.f32, test_val);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Re-init storage to force reload from flash
|
||||
err = storage_init();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: storage_init failed on reload: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
readback = get_param_value_t(PARAM_DRIVE_DIST);
|
||||
if (readback.f32 != test_val) {
|
||||
ESP_LOGE(TAG, "FAIL: flash value mismatch after reload (got %.2f, expected %.2f)",
|
||||
readback.f32, test_val);
|
||||
// Restore original before returning
|
||||
param_value_t orig = {.f32 = orig_val};
|
||||
set_param_value_t(PARAM_DRIVE_DIST, orig);
|
||||
commit_params();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Restore original value
|
||||
param_value_t orig = {.f32 = orig_val};
|
||||
set_param_value_t(PARAM_DRIVE_DIST, orig);
|
||||
err = commit_params();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "WARN: failed to restore original value");
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "PASS");
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test 6: Log write/read cycle through the log API
|
||||
// ============================================================================
|
||||
bool test_log_write_read_cycle(void) {
|
||||
ESP_LOGI(TAG, "=== Test: log write/read cycle ===");
|
||||
|
||||
// Erase and reinit log
|
||||
esp_err_t err = log_erase_all_sectors();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: log_erase_all_sectors failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_task_wdt_reset();
|
||||
|
||||
// Write 3 entries with different types
|
||||
uint8_t data1[] = {0x01, 0x02, 0x03, 0x04};
|
||||
uint8_t data2[] = {0xAA, 0xBB, 0xCC};
|
||||
uint8_t data3[] = {0xFF, 0xFE, 0xFD, 0xFC, 0xFB};
|
||||
|
||||
err = log_write_blocking_test(data1, sizeof(data1), LOG_TYPE_DATA);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: write 1 failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = log_write_blocking_test(data2, sizeof(data2), LOG_TYPE_EVENT);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: write 2 failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = log_write_blocking_test(data3, sizeof(data3), LOG_TYPE_SENSOR);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: write 3 failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for writes to flush through the queue
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
esp_task_wdt_reset();
|
||||
|
||||
// Read them back
|
||||
log_read_reset();
|
||||
uint8_t read_buf[LOG_MAX_PAYLOAD];
|
||||
uint8_t read_len, read_type;
|
||||
|
||||
// Entry 1
|
||||
err = log_read(&read_len, read_buf, &read_type);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: read 1 failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
if (read_len != sizeof(data1) || read_type != LOG_TYPE_DATA ||
|
||||
memcmp(read_buf, data1, sizeof(data1)) != 0) {
|
||||
ESP_LOGE(TAG, "FAIL: entry 1 mismatch (len=%d type=0x%02X)", read_len, read_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Entry 2
|
||||
err = log_read(&read_len, read_buf, &read_type);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: read 2 failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
if (read_len != sizeof(data2) || read_type != LOG_TYPE_EVENT ||
|
||||
memcmp(read_buf, data2, sizeof(data2)) != 0) {
|
||||
ESP_LOGE(TAG, "FAIL: entry 2 mismatch (len=%d type=0x%02X)", read_len, read_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Entry 3
|
||||
err = log_read(&read_len, read_buf, &read_type);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "FAIL: read 3 failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
if (read_len != sizeof(data3) || read_type != LOG_TYPE_SENSOR ||
|
||||
memcmp(read_buf, data3, sizeof(data3)) != 0) {
|
||||
ESP_LOGE(TAG, "FAIL: entry 3 mismatch (len=%d type=0x%02X)", read_len, read_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify no more entries
|
||||
err = log_read(&read_len, read_buf, &read_type);
|
||||
if (err != ESP_ERR_NOT_FOUND) {
|
||||
ESP_LOGE(TAG, "FAIL: expected no more entries, got err=%s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "PASS");
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test runner
|
||||
// ============================================================================
|
||||
esp_err_t run_partition_tests(void) {
|
||||
ESP_LOGI(TAG, "");
|
||||
ESP_LOGI(TAG, "=================================================");
|
||||
ESP_LOGI(TAG, " PARTITION VERIFICATION TEST SUITE");
|
||||
ESP_LOGI(TAG, "=================================================");
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
bool (*fn)(void);
|
||||
} test_entry_t;
|
||||
|
||||
test_entry_t tests[] = {
|
||||
{"params partition r/w", test_params_partition_rw},
|
||||
{"log partition r/w", test_log_partition_rw},
|
||||
{"post_test partition r/w", test_post_partition_rw},
|
||||
{"partition independence", test_partitions_independent},
|
||||
{"params persist after commit", test_params_persist_after_commit},
|
||||
{"log write/read cycle", test_log_write_read_cycle},
|
||||
};
|
||||
|
||||
int num_tests = sizeof(tests) / sizeof(tests[0]);
|
||||
int passed = 0;
|
||||
|
||||
for (int i = 0; i < num_tests; i++) {
|
||||
esp_task_wdt_reset();
|
||||
ESP_LOGI(TAG, "");
|
||||
bool result = tests[i].fn();
|
||||
if (result) passed++;
|
||||
else ESP_LOGE(TAG, "FAILED: %s", tests[i].name);
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "");
|
||||
ESP_LOGI(TAG, "=================================================");
|
||||
ESP_LOGI(TAG, " RESULTS: %d/%d passed", passed, num_tests);
|
||||
ESP_LOGI(TAG, "=================================================");
|
||||
|
||||
return (passed == num_tests) ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
19
main/partition_test.h
Normal file
19
main/partition_test.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef PARTITION_TEST_H
|
||||
#define PARTITION_TEST_H
|
||||
|
||||
#include "esp_err.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
// Run all partition verification tests
|
||||
// Call from app_main() after storage_init() and log_init()
|
||||
esp_err_t run_partition_tests(void);
|
||||
|
||||
// Individual tests
|
||||
bool test_params_partition_rw(void);
|
||||
bool test_log_partition_rw(void);
|
||||
bool test_post_partition_rw(void);
|
||||
bool test_partitions_independent(void);
|
||||
bool test_params_persist_after_commit(void);
|
||||
bool test_log_write_read_cycle(void);
|
||||
|
||||
#endif // PARTITION_TEST_H
|
||||
@@ -179,6 +179,41 @@ esp_err_t adc_init() {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t adc_post(void) {
|
||||
// Read all 4 channels twice with a short delay; flag if frozen or wildly out of range
|
||||
const adc_channel_t channels[] = { PIN_V_ISENS1, PIN_V_ISENS2, PIN_V_ISENS3, PIN_V_SENS_BAT };
|
||||
const char *names[] = { "ISENS1", "ISENS2", "ISENS3", "BATTERY" };
|
||||
int first[4], second[4];
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (adc_oneshot_read(adc1_handle, channels[i], &first[i]) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "POST: ADC read failed on %s", names[i]);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(5));
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (adc_oneshot_read(adc1_handle, channels[i], &second[i]) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "POST: ADC read failed on %s (2nd)", names[i]);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for frozen ADC (identical readings on noise-bearing current sense channels)
|
||||
for (int i = 0; i < 3; i++) { // only current sense, not battery (battery can be stable)
|
||||
if (first[i] == second[i] && first[i] != 0) {
|
||||
ESP_LOGW(TAG, "POST: ADC %s may be frozen (both reads = %d)", names[i], first[i]);
|
||||
// Warning only — a truly stuck ADC will trip efuse protections anyway
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "POST: ADC OK (BAT=%d/%d, I1=%d/%d, I2=%d/%d, I3=%d/%d)",
|
||||
first[3], second[3], first[0], second[0], first[1], second[1], first[2], second[2]);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
float get_raw_battery_voltage(void) {
|
||||
int adc_raw = 0;
|
||||
int voltage_mv = 0;
|
||||
|
||||
@@ -38,6 +38,7 @@ esp_err_t process_bridge_current(bridge_t bridge);
|
||||
esp_err_t process_battery_voltage();
|
||||
|
||||
esp_err_t adc_init();
|
||||
esp_err_t adc_post(void);
|
||||
esp_err_t power_init();
|
||||
esp_err_t power_stop();
|
||||
|
||||
|
||||
363
main/storage.c
363
main/storage.c
@@ -7,6 +7,7 @@
|
||||
#include "esp_crc.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "storage.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "nvs_flash.h"
|
||||
@@ -107,11 +108,13 @@ size_t param_type_size(param_type_e x) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Partition pointer
|
||||
static const esp_partition_t *storage_partition = NULL;
|
||||
// Partition pointers (separate partitions for params, log, and POST test)
|
||||
static const esp_partition_t *params_partition = NULL;
|
||||
static const esp_partition_t *log_partition = NULL;
|
||||
static const esp_partition_t *post_partition = NULL;
|
||||
|
||||
// Log head/tail tracking with mutex protection
|
||||
// These now track byte offsets within the log area, not entry indices
|
||||
// These track byte offsets within the log partition (0-based)
|
||||
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;
|
||||
@@ -134,8 +137,13 @@ uint32_t log_get_tail(void) {
|
||||
return tail;
|
||||
}
|
||||
|
||||
uint32_t log_get_offset(void) {
|
||||
return LOG_START_OFFSET;
|
||||
uint32_t log_get_offset(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t log_get_size(void) {
|
||||
if (log_partition == NULL) return 0;
|
||||
return log_partition->size;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -350,23 +358,22 @@ static void unpack_param(const uint8_t *src, param_idx_t id) {
|
||||
// COMMIT PARAMETERS TO FLASH
|
||||
// ============================================================================
|
||||
esp_err_t commit_params(void) {
|
||||
if (storage_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Storage partition not initialized");
|
||||
if (params_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Params partition not initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
|
||||
ESP_LOGI(TAG, "Committing %d parameters to flash...", NUM_PARAMS);
|
||||
|
||||
// Erase parameter sectors first
|
||||
esp_err_t err = esp_partition_erase_range(storage_partition, PARAMS_OFFSET,
|
||||
PARAMETER_NUM_SECTORS * FLASH_SECTOR_SIZE);
|
||||
|
||||
// Erase entire params partition
|
||||
esp_err_t err = esp_partition_erase_range(params_partition, 0, params_partition->size);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to erase parameter sectors: %s", esp_err_to_name(err));
|
||||
ESP_LOGE(TAG, "Failed to erase params partition: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
// Write each parameter with CRC
|
||||
uint32_t flash_offset = PARAMS_OFFSET;
|
||||
uint32_t flash_offset = 0;
|
||||
for (int i = 0; i < NUM_PARAMS; i++) {
|
||||
param_stored_t stored;
|
||||
memset(&stored, 0, sizeof(param_stored_t));
|
||||
@@ -380,7 +387,7 @@ esp_err_t commit_params(void) {
|
||||
stored.crc = esp_crc32_le(crc_input, stored.data, size);
|
||||
|
||||
// Write to flash
|
||||
err = esp_partition_write(storage_partition, flash_offset,
|
||||
err = esp_partition_write(params_partition, flash_offset,
|
||||
&stored, sizeof(param_stored_t));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to write parameter %d (%s): %s",
|
||||
@@ -419,6 +426,58 @@ esp_err_t factory_reset(void) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// FLASH POST (Power-On Self-Test)
|
||||
// ============================================================================
|
||||
esp_err_t storage_post(void) {
|
||||
if (post_partition == NULL) {
|
||||
// Find post_test partition if not already found
|
||||
post_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_ANY,
|
||||
"post_test");
|
||||
if (post_partition == NULL) {
|
||||
ESP_LOGE(TAG, "POST: post_test partition not found");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t write_buf[16];
|
||||
uint8_t read_buf[16];
|
||||
|
||||
// Fill with a pattern based on boot time so we don't pass on stale data
|
||||
uint32_t seed = (uint32_t)esp_timer_get_time();
|
||||
for (int i = 0; i < 16; i++) write_buf[i] = (uint8_t)(seed + i * 37);
|
||||
|
||||
esp_err_t err = esp_partition_erase_range(post_partition, 0, FLASH_SECTOR_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "POST: flash erase failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_partition_write(post_partition, 0, write_buf, sizeof(write_buf));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "POST: flash write failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_partition_read(post_partition, 0, read_buf, sizeof(read_buf));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "POST: flash read failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
if (memcmp(write_buf, read_buf, sizeof(write_buf)) != 0) {
|
||||
ESP_LOGE(TAG, "POST: flash verify MISMATCH");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Erase the test sector so it's clean for next boot
|
||||
esp_partition_erase_range(post_partition, 0, FLASH_SECTOR_SIZE);
|
||||
|
||||
ESP_LOGI(TAG, "POST: flash OK");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// STORAGE INITIALIZATION
|
||||
// ============================================================================
|
||||
@@ -443,27 +502,27 @@ esp_err_t storage_init(void) {
|
||||
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");
|
||||
|
||||
params_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_ANY,
|
||||
"params");
|
||||
if (params_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Params partition not found");
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Storage partition found: size=%lu bytes",
|
||||
(unsigned long)storage_partition->size);
|
||||
|
||||
|
||||
ESP_LOGI(TAG, "Params partition found: size=%lu bytes",
|
||||
(unsigned long)params_partition->size);
|
||||
|
||||
// Load parameters from flash
|
||||
uint32_t flash_offset = PARAMS_OFFSET;
|
||||
uint32_t flash_offset = 0;
|
||||
//bool all_valid = true;
|
||||
|
||||
for (int i = 0; i < NUM_PARAMS; i++) {
|
||||
param_stored_t stored;
|
||||
|
||||
esp_err_t err = esp_partition_read(storage_partition, flash_offset,
|
||||
esp_err_t err = esp_partition_read(params_partition, flash_offset,
|
||||
&stored, sizeof(param_stored_t));
|
||||
|
||||
if (err != ESP_OK) {
|
||||
@@ -509,63 +568,41 @@ static inline uint32_t log_sector_end(uint32_t x) {
|
||||
|
||||
// Helper function to check if a sector is erased (starts with 0xFF)
|
||||
static bool is_sector_erased(uint32_t x) {
|
||||
uint8_t buf; //[256];
|
||||
esp_err_t err = esp_partition_read(storage_partition, LOG_START_OFFSET + x * FLASH_SECTOR_SIZE, &buf, 1);
|
||||
uint8_t buf;
|
||||
esp_err_t err = esp_partition_read(log_partition, x * FLASH_SECTOR_SIZE, &buf, 1);
|
||||
if (err != ESP_OK) return false;
|
||||
if (buf == 0xFF) return true;
|
||||
|
||||
/*for (int i = 0; i < 256; i++) {
|
||||
if (buf[i] != 0xFF) return false;
|
||||
}*/
|
||||
return false;
|
||||
return (buf == 0xFF);
|
||||
}
|
||||
|
||||
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);
|
||||
uint8_t buf;
|
||||
esp_err_t err = esp_partition_read(log_partition, (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 (buf != 0xFF);
|
||||
}
|
||||
|
||||
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)
|
||||
/*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;
|
||||
if (buf )
|
||||
|
||||
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, uint8_t type) {
|
||||
if (!log_initialized || storage_partition == NULL) {
|
||||
if (!log_initialized || log_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_queue == NULL) {
|
||||
ESP_LOGE(TAG, "Log queue not initialized");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
if (type == 0xFF) {
|
||||
ESP_LOGE(TAG, "Attempt to log with type=0xFF; not allowed");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
@@ -588,67 +625,65 @@ esp_err_t log_write(uint8_t* buf, uint8_t len, uint8_t type) {
|
||||
|
||||
// The actual blocking write function (called by the task)
|
||||
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 || log_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);
|
||||
|
||||
|
||||
// check if we will overrun the sector
|
||||
if (log_head_offset + len+2 >= 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
|
||||
char zeros[256] = {0};
|
||||
esp_partition_write(storage_partition,
|
||||
esp_partition_write(log_partition,
|
||||
log_head_offset, &zeros,
|
||||
log_sector_end(log_head_offset)-log_head_offset);
|
||||
|
||||
|
||||
// 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;
|
||||
|
||||
if (log_head_offset >= log_partition->size)
|
||||
log_head_offset = 0;
|
||||
|
||||
// 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, log_head_offset,
|
||||
esp_err_t err = esp_partition_read(log_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)log_head_offset);
|
||||
err = esp_partition_erase_range(storage_partition,
|
||||
log_head_offset,
|
||||
err = esp_partition_erase_range(log_partition,
|
||||
log_head_offset,
|
||||
FLASH_SECTOR_SIZE);
|
||||
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to erase sector: %s", esp_err_to_name(err));
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 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, "Erased; Tail/Head are now %lu/%lu",
|
||||
if (log_tail_offset >= log_partition->size)
|
||||
log_tail_offset = 0;
|
||||
|
||||
ESP_LOGI(TAG, "Erased; Tail/Head are now %lu/%lu",
|
||||
(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+1, buf, len-1);
|
||||
esp_partition_write(storage_partition, log_head_offset+len, &type, 1);
|
||||
|
||||
|
||||
|
||||
esp_partition_write(log_partition, log_head_offset, &len, 1);
|
||||
esp_partition_write(log_partition, log_head_offset+1, buf, len-1);
|
||||
esp_partition_write(log_partition, log_head_offset+len, &type, 1);
|
||||
|
||||
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);
|
||||
@@ -686,21 +721,29 @@ static void log_writer_task(void *pvParameters) {
|
||||
|
||||
// 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;
|
||||
// Find the log partition
|
||||
log_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_ANY,
|
||||
"log");
|
||||
if (log_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Log partition not found");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
if (log_mutex == NULL) {
|
||||
log_mutex = xSemaphoreCreateMutex();
|
||||
if (log_mutex == NULL) return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
||||
uint32_t num_sectors = log_partition->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;
|
||||
log_head_offset = 0;
|
||||
log_tail_offset = 0;
|
||||
|
||||
// Binary search for the first non-full sector
|
||||
int32_t l = 0;
|
||||
@@ -750,10 +793,10 @@ esp_err_t log_init() {
|
||||
}
|
||||
|
||||
if (head_sector == -1) {
|
||||
if (is_sector_erased(LOG_START_OFFSET)) {
|
||||
if (is_sector_erased(0)) {
|
||||
ESP_LOGI(TAG, "Log is empty");
|
||||
log_head_offset = LOG_START_OFFSET;
|
||||
log_tail_offset = LOG_START_OFFSET;
|
||||
log_head_offset = 0;
|
||||
log_tail_offset = 0;
|
||||
} else {
|
||||
head_sector = 0;
|
||||
ESP_LOGW(TAG, "Log appears full, searching from start");
|
||||
@@ -763,17 +806,17 @@ esp_err_t log_init() {
|
||||
// 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;
|
||||
cursor = (head_sector - 1) * FLASH_SECTOR_SIZE;
|
||||
} else {
|
||||
cursor = LOG_START_OFFSET;
|
||||
cursor = 0;
|
||||
}
|
||||
|
||||
uint32_t head_sector_start = LOG_START_OFFSET + head_sector * FLASH_SECTOR_SIZE;
|
||||
|
||||
|
||||
uint32_t head_sector_start = head_sector * FLASH_SECTOR_SIZE;
|
||||
|
||||
bool found_head = false;
|
||||
while (cursor < head_sector_start + FLASH_SECTOR_SIZE && cursor < storage_partition->size) {
|
||||
while (cursor < head_sector_start + FLASH_SECTOR_SIZE && cursor < log_partition->size) {
|
||||
uint8_t buf;
|
||||
esp_err_t err = esp_partition_read(storage_partition, cursor, &buf, 1);
|
||||
esp_err_t err = esp_partition_read(log_partition, cursor, &buf, 1);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read during log init at offset %lu", (unsigned long)cursor);
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
@@ -798,9 +841,9 @@ esp_err_t log_init() {
|
||||
}
|
||||
|
||||
if (tail_sector >= 0) {
|
||||
log_tail_offset = LOG_START_OFFSET + tail_sector * FLASH_SECTOR_SIZE;
|
||||
log_tail_offset = tail_sector * FLASH_SECTOR_SIZE;
|
||||
} else {
|
||||
log_tail_offset = LOG_START_OFFSET;
|
||||
log_tail_offset = 0;
|
||||
}
|
||||
|
||||
log_initialized = true;
|
||||
@@ -866,7 +909,9 @@ void storage_deinit(void) {
|
||||
log_queue = NULL;
|
||||
}
|
||||
|
||||
storage_partition = NULL;
|
||||
params_partition = NULL;
|
||||
log_partition = NULL;
|
||||
post_partition = NULL;
|
||||
log_initialized = false;
|
||||
if (log_mutex) {
|
||||
vSemaphoreDelete(log_mutex);
|
||||
@@ -879,54 +924,12 @@ void storage_deinit(void) {
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* 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):
|
||||
// ============================================================================
|
||||
// LOG READ / TEST SUPPORT FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
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
|
||||
*
|
||||
@@ -952,18 +955,18 @@ esp_err_t log_read(uint8_t* len, uint8_t* buf, uint8_t* type) {
|
||||
// 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) {
|
||||
if (!log_initialized || log_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;
|
||||
@@ -977,7 +980,7 @@ esp_err_t log_read(uint8_t* len, uint8_t* buf, uint8_t* type) {
|
||||
|
||||
// Read the length byte
|
||||
uint8_t entry_len;
|
||||
esp_err_t err = esp_partition_read(storage_partition, log_read_cursor, &entry_len, 1);
|
||||
esp_err_t err = esp_partition_read(log_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);
|
||||
@@ -996,8 +999,8 @@ esp_err_t log_read(uint8_t* len, uint8_t* buf, uint8_t* type) {
|
||||
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;
|
||||
if (next_sector >= log_partition->size) {
|
||||
log_read_cursor = 0;
|
||||
} else {
|
||||
log_read_cursor = next_sector;
|
||||
}
|
||||
@@ -1015,7 +1018,7 @@ esp_err_t log_read(uint8_t* len, uint8_t* buf, uint8_t* type) {
|
||||
|
||||
// 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);
|
||||
err = esp_partition_read(log_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);
|
||||
@@ -1024,7 +1027,7 @@ esp_err_t log_read(uint8_t* len, uint8_t* buf, uint8_t* type) {
|
||||
|
||||
// Read the type byte
|
||||
uint8_t entry_type;
|
||||
err = esp_partition_read(storage_partition, log_read_cursor + entry_len, &entry_type, 1);
|
||||
err = esp_partition_read(log_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);
|
||||
@@ -1045,8 +1048,8 @@ esp_err_t log_read(uint8_t* len, uint8_t* buf, uint8_t* type) {
|
||||
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_read_cursor >= log_partition->size) {
|
||||
log_read_cursor = 0;
|
||||
}
|
||||
|
||||
if (log_mutex) xSemaphoreGive(log_mutex);
|
||||
@@ -1078,37 +1081,31 @@ void log_read_reset(void) {
|
||||
* @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");
|
||||
if (log_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Log 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);
|
||||
|
||||
ESP_LOGI(TAG, "Erasing all log sectors (%lu bytes)...", (unsigned long)log_partition->size);
|
||||
|
||||
esp_err_t err = esp_partition_erase_range(log_partition, 0, log_partition->size);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to erase log area: %s", esp_err_to_name(err));
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
// FLASH LAYOUT CONSTANTS
|
||||
// ============================================================================
|
||||
#define FLASH_SECTOR_SIZE 4096
|
||||
#define PARAMS_OFFSET 0
|
||||
#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)
|
||||
@@ -153,6 +150,7 @@ typedef struct {
|
||||
|
||||
// Initialization
|
||||
esp_err_t storage_init(void);
|
||||
esp_err_t storage_post(void);
|
||||
void storage_deinit(void);
|
||||
|
||||
// Parameter access
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
const unsigned char PROGMEM html_content_gz[] = {
|
||||
0x1f, 0x8b, 0x08, 0x00, 0xd7, 0xc3, 0xb2, 0x69, 0x02, 0xff, 0xed, 0x3d, 0xfb, 0x5b, 0xdb, 0x48,
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x18, 0x55, 0xb3, 0x69, 0x02, 0xff, 0xed, 0x3d, 0xfb, 0x5b, 0xdb, 0x48,
|
||||
0x92, 0x3f, 0xef, 0xfc, 0x15, 0x0d, 0x93, 0x21, 0x52, 0x10, 0xb2, 0x0d, 0x64, 0x66, 0xd6, 0x46,
|
||||
0x66, 0x09, 0x38, 0x3b, 0x4c, 0x12, 0xe0, 0xc3, 0x90, 0xcc, 0x1c, 0xc7, 0x87, 0x64, 0xab, 0x8d,
|
||||
0x35, 0xc8, 0x92, 0x57, 0x92, 0x21, 0x5e, 0xe3, 0xff, 0xfd, 0xaa, 0xfa, 0x21, 0xb5, 0x1e, 0x36,
|
||||
|
||||
@@ -99,7 +99,7 @@ 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;
|
||||
static const esp_partition_t *cached_log_partition = NULL;
|
||||
|
||||
// In webserver.c - Replace the log_handler function
|
||||
|
||||
@@ -144,17 +144,17 @@ static esp_err_t log_handler(httpd_req_t *req) {
|
||||
return httpd_resp_send_err(req, HTTPD_405_METHOD_NOT_ALLOWED, "Method not allowed");
|
||||
}
|
||||
|
||||
const esp_partition_t *storage_partition = cached_storage_partition;
|
||||
if (storage_partition == NULL) {
|
||||
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");
|
||||
return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR,
|
||||
"Storage partition not found");
|
||||
const esp_partition_t *log_part = cached_log_partition;
|
||||
if (log_part == NULL) {
|
||||
log_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_ANY,
|
||||
"log");
|
||||
if (log_part == NULL) {
|
||||
ESP_LOGE(TAG, "Log partition not found");
|
||||
return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR,
|
||||
"Log partition not found");
|
||||
}
|
||||
cached_storage_partition = storage_partition;
|
||||
cached_log_partition = log_part;
|
||||
}
|
||||
|
||||
int32_t head = log_get_head();
|
||||
@@ -163,7 +163,7 @@ static esp_err_t log_handler(httpd_req_t *req) {
|
||||
if (tail < 0) {
|
||||
tail = log_get_tail();
|
||||
} else {
|
||||
if (tail < log_start || tail >= (int32_t)storage_partition->size) {
|
||||
if (tail < log_start || tail >= (int32_t)log_part->size) {
|
||||
ESP_LOGW(TAG, "Invalid tail pointer %ld, using current tail", (long)tail);
|
||||
tail = log_get_tail();
|
||||
}
|
||||
@@ -176,7 +176,7 @@ static esp_err_t log_handler(httpd_req_t *req) {
|
||||
} else if (tail < head) {
|
||||
log_data_size = head - tail;
|
||||
} else {
|
||||
log_data_size = (storage_partition->size - tail) + (head - log_start);
|
||||
log_data_size = (log_part->size - tail) + (head - log_start);
|
||||
}
|
||||
|
||||
// Generate JSON header (same as /get endpoint)
|
||||
@@ -270,7 +270,7 @@ static esp_err_t log_handler(httpd_req_t *req) {
|
||||
// Normal case: tail before head
|
||||
while (offset < head) {
|
||||
size_t to_read = MIN(sizeof(http_buffer), head - offset);
|
||||
err = esp_partition_read(storage_partition, offset, http_buffer, to_read);
|
||||
err = esp_partition_read(log_part, offset, http_buffer, to_read);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read partition at offset %ld: %s",
|
||||
(long)offset, esp_err_to_name(err));
|
||||
@@ -290,9 +290,9 @@ static esp_err_t log_handler(httpd_req_t *req) {
|
||||
}
|
||||
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(http_buffer), storage_partition->size - offset);
|
||||
err = esp_partition_read(storage_partition, offset, http_buffer, to_read);
|
||||
while (offset < (int32_t)log_part->size) {
|
||||
size_t to_read = MIN(sizeof(http_buffer), log_part->size - offset);
|
||||
err = esp_partition_read(log_part, offset, http_buffer, to_read);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read partition at offset %ld: %s",
|
||||
(long)offset, esp_err_to_name(err));
|
||||
@@ -314,7 +314,7 @@ static esp_err_t log_handler(httpd_req_t *req) {
|
||||
offset = log_start;
|
||||
while (offset < head) {
|
||||
size_t to_read = MIN(sizeof(http_buffer), head - offset);
|
||||
err = esp_partition_read(storage_partition, offset, http_buffer, to_read);
|
||||
err = esp_partition_read(log_part, offset, http_buffer, to_read);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read partition at offset %ld: %s",
|
||||
(long)offset, esp_err_to_name(err));
|
||||
|
||||
Reference in New Issue
Block a user