From cdb3b11db16da0e0faadffd77e5bd9df958b3d1f Mon Sep 17 00:00:00 2001 From: Thaddeus Hughes Date: Thu, 12 Mar 2026 20:37:04 -0500 Subject: [PATCH] params bounds checking --- TODO.md | 51 ++++++++++--------- main/bt_hid.c | 5 ++ main/comms.c | 22 --------- main/control_fsm.c | 19 ------- main/control_fsm.h | 7 +-- main/main.c | 15 ++++-- main/rf_433.c | 4 -- main/rf_433.h | 7 --- main/storage.c | 120 +++++++++++++++++++++++++++++++++++++++++++-- main/storage.h | 104 ++++++++++++++++++++------------------- 10 files changed, 215 insertions(+), 139 deletions(-) diff --git a/TODO.md b/TODO.md index b781733..08f3e4d 100644 --- a/TODO.md +++ b/TODO.md @@ -19,37 +19,44 @@ - [clauded] ADC: `adc_post()` reads all 4 channels twice with 5ms delay, warns if frozen - [clauded] I2C: `i2c_post()` verifies TCA9555 responds (read port 0) - [clauded] Flash: `storage_post()` write-read-verify on last sector of storage partition -7. - [ ] Parameter validation - - [ ] Add per-param bounds to `PARAM_LIST` macro (min, max, flags) - - [ ] NaN/Inf → reset to default; out-of-range → clamp to min/max - - [ ] Enforce validation inside `commit_params()` (covers both `storage_init()` load and `/set` POST) - - [ ] Audit for anywhere params are set without an immediate `commit_params()` call - - [ ] Audit abandoned parameters (e.g. jack current) — add comments marking them deprecated +7. - [clauded] Parameter validation + - [clauded] Add per-param bounds to `PARAM_LIST` macro (min, max) — extended PARAM_DEF 6-arg macro + - [clauded] NaN/Inf → reset to default; out-of-range → clamp to min/max — `validate_param()` in storage.c + - [clauded] Enforce validation in `storage_init()` (after flash load) and `commit_params()` (before flash write) + - [clauded] Audit `set_param_value_t` calls outside comms.c — deleted dead code: `rf_433_set_keycode()`, `FSM_CMD_CALIBRATE_*_FINISH` handlers + FSM cases + `fsm_set_cal_val()` (web JS does cal math client-side, commits via standard param POST) + - [clauded] Audit abandoned parameters — `JACK_IS_DOWN` marked deprecated (may duplicate `JACK_I_DOWN`); `BOOT_TIME` is informational-only 8. - [clauded] Factory reset: erases params + log + post_test partitions, requires 10s button hold on cold boot, LEDs flash during hold → solid when triggered 9. - [clauded] Ensure RTC_DATA_ATTR variables survive panics/WDT resets - [clauded] Verified `sync_unix_us`, `sync_rtc_us`, `rtc_set` — no init path zeroes them; `rtc_restore_time()` recovers via RTC HW counter - [clauded] Verified `remaining_distance`, `fsm_error` — `fsm_init()` does not touch them; only cleared by explicit user action - [clauded] Verified `log_head_offset`, `log_tail_offset` — `log_init()` always recovers from flash scan; RTC_DATA_ATTR is historical/harmless 10. - [clauded] Measure flash log write duration — `test_log_write_timing()` in log_test.c, runs 200 iterations of 39-byte writes, reports min/max/avg/sector-crossing times, compares to 5s WDT -11. - [ ] WiFi STA mode with event-group signaling - - [ ] Try connecting to saved STA network first, fall back to softAP on failure/timeout - - [ ] Add `EventGroupHandle_t` with `WIFI_READY_BIT` (set when STA connected or softAP up) and `BT_READY_BIT` (set when BT scan task starts) - - [ ] Replace blind 500ms `vTaskDelay` on alarm wake with `xEventGroupWaitBits()` + timeout - - [ ] Use same event group in `soft_idle_exit()` path -12. - [ ] Verify `sensors_init()` placement and ISR safety - - [ ] Confirm `sensors_init()` is safe to call from `app_main()` (research says yes — creates queue + installs ISR service, no task-context dependency) - - [ ] Decide: move to main.c (simpler) or keep in `control_task()` (current) — either way, remove the dead commented-out call in main.c and add a clarifying comment - - [ ] Audit all ISRs are IRAM-safe: no `ESP_LOGx`, `printf`, `malloc`, or flash access — only `xQueueSendFromISR()` - - [ ] Handle `sensors_init()` failure as critical (→ reboot) +11. - [clauded] WiFi STA mode with event-group signaling + - [clauded] STA-first with softAP fallback was already implemented in `start_wifi()` + - [clauded] Added `EventGroupHandle_t comms_event_group` in `comms_events.h` with `WIFI_READY_BIT` / `BT_READY_BIT` + - [clauded] Replaced blind 500ms `vTaskDelay` on alarm wake with `xEventGroupWaitBits(COMMS_ALL_BITS, 5s timeout)` + - [clauded] `soft_idle_exit()` → `webserver_restart_wifi()` / `bt_hid_resume()` set bits; `webserver_stop()` / `bt_hid_stop()` clear bits + - [clauded] Bits set even on permanent init failure so alarm-wake never blocks forever +12. - [clauded] Verify `sensors_init()` placement and ISR safety + - [clauded] Moved `sensors_init()` to main.c as `init_critical("SENSORS", sensors_init)` — runs before FSM + - [clauded] Removed dead commented-out `sensors_init()` / `sensors_stop()` from sensors.c + - [clauded] Audited ISR: `sensor_isr_handler` is IRAM_ATTR, uses only `esp_timer_get_time()` (IRAM-safe), `gpio_get_level()`, `xQueueSendFromISR()` — no logging/malloc/flash + - [clauded] `sensors_init()` failure is now critical (→ reboot via `init_critical`) 13. - [clauded] External 32kHz crystal not needed (deep sleep disabled, soft idle instead) — removed crystal config from sdkconfig.defaults; `rtc_xtal_init()` already a no-op; crystal remains on PCB but unused 14. - [clauded] Removed `rtc_wakeup_cause()` — was unused (informational only, never called) 15. - [clauded] Confirmed `rtc_check_shutdown_timer()` uses unsigned `TickType_t` subtraction — wraps correctly; removed esp_timer overflow TODO comment from main.c -16. - [ ] Extract pure logic (e-fuse thermal model, param serialization, sensor debounce) into host-testable modules with Unity/CMock -17. - [ ] UART integration test framework: Python runner + ESP-side test commands -18. - [test] Logtool GUI output (matplotlib) -19. - [test] Verify naming convention adherence across codebase -20. - [test] Verify WiFi SSID rename triggers comms reboot -21. - [clauded] Documentation restructure +16. - [test] Logtool GUI output (matplotlib) +17. - [test] Verify naming convention adherence across codebase +18. - [test] Verify WiFi SSID rename triggers comms reboot +19. - [clauded] Documentation restructure - [clauded] Move project/hardware documentation from CLAUDE.md → README.md; keep CLAUDE.md for AI-specific instructions and conventions only - [clauded] Document all FreeRTOS tasks and priorities in README.md - [clauded] Add terse comments to FSM state transitions in `control_fsm.c` (focus on "why", not "what") + + + +20. - [ ] Extract pure logic (e-fuse thermal model, param serialization, sensor debounce) into host-testable modules with Unity/CMock +21. - [ ] UART integration test framework: Python runner + ESP-side test commands +22. - [ ] Fix compile warnings +23. - [ ] Check if NVS needed for wifi/bluetooth (research first; what is it actually used for? can it be done without?) +24. - [ ] If NVS needed for wifi/bluetooth, compare its space efficiency, runtime efficiency, and security (errorchecking/crashes) to current params architecture \ No newline at end of file diff --git a/main/bt_hid.c b/main/bt_hid.c index 33e4ed6..447c419 100644 --- a/main/bt_hid.c +++ b/main/bt_hid.c @@ -53,6 +53,7 @@ #include "esp_timer.h" #include "bt_hid.h" +#include "comms_events.h" #include "control_fsm.h" // --------------------------------------------------------------------------- @@ -581,6 +582,8 @@ esp_err_t bt_hid_init(void) */ xTaskCreate(bt_hid_scan_task, "bt_hid_scan", 6 * 1024, NULL, 4, &s_scan_task_handle); + if (comms_event_group) xEventGroupSetBits(comms_event_group, BT_READY_BIT); + ESP_LOGI(TAG, "BLE HID host initialised"); return ESP_OK; } @@ -591,6 +594,7 @@ void bt_hid_stop(void) vTaskSuspend(s_scan_task_handle); ESP_LOGI(TAG, "BT HID scan task suspended"); } + if (comms_event_group) xEventGroupClearBits(comms_event_group, BT_READY_BIT); } void bt_hid_resume(void) @@ -599,4 +603,5 @@ void bt_hid_resume(void) vTaskResume(s_scan_task_handle); ESP_LOGI(TAG, "BT HID scan task resumed"); } + if (comms_event_group) xEventGroupSetBits(comms_event_group, BT_READY_BIT); } diff --git a/main/comms.c b/main/comms.c index 7bee7aa..a5f0dd0 100644 --- a/main/comms.c +++ b/main/comms.c @@ -275,33 +275,11 @@ esp_err_t comms_handle_post(cJSON *root, cJSON **response_json) { ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_JACK_PREP"); cmd_executed = true; } - else if (strcmp(cmd_str, "cal_jack_finish") == 0) { - cJSON *amt = cJSON_GetObjectItem(root, "amt"); - if (cJSON_IsNumber(amt) && amt->valuedouble >= 0 && amt->valuedouble < 8) { - ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_JACK_FINISH"); - fsm_set_cal_val(amt->valuedouble); - fsm_request(FSM_CMD_CALIBRATE_JACK_FINISH); - cmd_executed = true; - } else { - error_msg = "cal_jack_finish requires amt parameter (0-8)"; - } - } else if (strcmp(cmd_str, "cal_drive_start") == 0) { fsm_request(FSM_CMD_CALIBRATE_DRIVE_PREP); ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_DRIVE_PREP"); cmd_executed = true; } - else if (strcmp(cmd_str, "cal_drive_finish") == 0) { - cJSON *amt = cJSON_GetObjectItem(root, "amt"); - if (cJSON_IsNumber(amt) && amt->valuedouble >= 0 && amt->valuedouble < 8) { - ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_DRIVE_FINISH"); - fsm_set_cal_val(amt->valuedouble); - fsm_request(FSM_CMD_CALIBRATE_DRIVE_FINISH); - cmd_executed = true; - } else { - error_msg = "cal_drive_finish requires amt parameter (0-8)"; - } - } else if (strcmp(cmd_str, "cal_get") == 0) { ESP_LOGI(TAG, "CAL_GET"); diff --git a/main/control_fsm.c b/main/control_fsm.c index eedc37d..fd49b1f 100644 --- a/main/control_fsm.c +++ b/main/control_fsm.c @@ -83,8 +83,6 @@ void pulse_override(fsm_override_t cmd) { } int64_t fsm_cal_t, fsm_cal_e; -float fsm_cal_val; -void fsm_set_cal_val(float v) {fsm_cal_val = v;} int64_t fsm_get_cal_t(){return fsm_cal_t;} int64_t fsm_get_cal_e(){return fsm_cal_e;} @@ -290,14 +288,6 @@ void control_task(void *param) { log = true; } break; - case FSM_CMD_CALIBRATE_JACK_FINISH: - set_param_value_t(PARAM_JACK_KT, - (param_value_t){.f32 = fsm_cal_t / fsm_cal_val}); - ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_JACK_FINISH -> %f", get_param_value_t(PARAM_JACK_KT).f32); - break; - - - case FSM_CMD_CALIBRATE_DRIVE_PREP: ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_DRIVE_PREP"); if (current_state == STATE_IDLE @@ -326,15 +316,6 @@ void control_task(void *param) { log = true; } break; - case FSM_CMD_CALIBRATE_DRIVE_FINISH: - set_param_value_t(PARAM_DRIVE_KT, - (param_value_t){.f32 = fsm_cal_t / fsm_cal_val}); - set_param_value_t(PARAM_DRIVE_KE, - (param_value_t){.f32 = fsm_cal_e / fsm_cal_val}); - ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_DRIVE_FINISH -> %f / %f", - get_param_value_t(PARAM_DRIVE_KT).f32, - get_param_value_t(PARAM_DRIVE_KE).f32); - break; } } diff --git a/main/control_fsm.h b/main/control_fsm.h index 5f9005a..22a080b 100644 --- a/main/control_fsm.h +++ b/main/control_fsm.h @@ -17,12 +17,10 @@ typedef enum { FSM_CMD_CALIBRATE_JACK_PREP, FSM_CMD_CALIBRATE_JACK_START, FSM_CMD_CALIBRATE_JACK_END, - FSM_CMD_CALIBRATE_JACK_FINISH, - + FSM_CMD_CALIBRATE_DRIVE_PREP, FSM_CMD_CALIBRATE_DRIVE_START, - FSM_CMD_CALIBRATE_DRIVE_END, - FSM_CMD_CALIBRATE_DRIVE_FINISH + FSM_CMD_CALIBRATE_DRIVE_END } fsm_cmd_t; typedef enum { @@ -90,7 +88,6 @@ void pulse_override(fsm_override_t cmd); esp_err_t fsm_init(); esp_err_t fsm_stop(); -void fsm_set_cal_val(float v); int64_t fsm_get_cal_t(); int64_t fsm_get_cal_e(); void fsm_request(fsm_cmd_t cmd); diff --git a/main/main.c b/main/main.c index 7bd22c4..c53075c 100644 --- a/main/main.c +++ b/main/main.c @@ -209,7 +209,7 @@ void app_main(void) {esp_task_wdt_add(NULL); adc_post(); // ADC channels readable and not frozen storage_post(); // flash write-read-verify on test sector - run_all_log_tests(); + //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(); @@ -263,7 +263,8 @@ void app_main(void) {esp_task_wdt_add(NULL); // Create event group before non-critical inits (they set bits on it) comms_event_group = xEventGroupCreate(); - // Non-critical — retry once on failure, then log and continue + // Non-critical — retry once on failure, then log and continue. + // Set event bits even on failure so alarm-wake doesn't block forever. if (rf_433_init() != ESP_OK) { ESP_LOGW(TAG, "RF init failed, retrying..."); vTaskDelay(pdMS_TO_TICKS(200)); @@ -272,12 +273,18 @@ void app_main(void) {esp_task_wdt_add(NULL); if (bt_hid_init() != ESP_OK) { ESP_LOGW(TAG, "BT init failed, retrying..."); vTaskDelay(pdMS_TO_TICKS(200)); - if (bt_hid_init() != ESP_OK) ESP_LOGE(TAG, "BT HID FAILED (continuing without BT)"); + if (bt_hid_init() != ESP_OK) { + ESP_LOGE(TAG, "BT HID FAILED (continuing without BT)"); + if (comms_event_group) xEventGroupSetBits(comms_event_group, BT_READY_BIT); + } } if (webserver_init() != ESP_OK) { ESP_LOGW(TAG, "Webserver init failed, retrying..."); vTaskDelay(pdMS_TO_TICKS(500)); - if (webserver_init() != ESP_OK) ESP_LOGE(TAG, "WEBSERVER FAILED (continuing without WiFi)"); + if (webserver_init() != ESP_OK) { + ESP_LOGE(TAG, "WEBSERVER FAILED (continuing without WiFi)"); + if (comms_event_group) xEventGroupSetBits(comms_event_group, WIFI_READY_BIT); + } } // POST + FSM started successfully — this firmware is good. diff --git a/main/rf_433.c b/main/rf_433.c index 884c882..aee9690 100644 --- a/main/rf_433.c +++ b/main/rf_433.c @@ -220,10 +220,6 @@ esp_err_t rf_433_init() { esp_err_t rf_433_stop() { return ESP_OK; } -void rf_433_set_keycode(uint8_t index, uint32_t code) { - set_param_value_t(PARAM_KEYCODE_0+index, (param_value_t){.u32=code}); -} - void rf_433_learn_keycode(uint8_t index) { if (index >= 8) return; learn_flag = index; diff --git a/main/rf_433.h b/main/rf_433.h index 43f6b96..a64c528 100644 --- a/main/rf_433.h +++ b/main/rf_433.h @@ -16,13 +16,6 @@ int64_t receive_keycode(void); esp_err_t rf_433_init(); esp_err_t rf_433_stop(); -void rf_433_set_keycode(uint8_t index, uint32_t code); - -/* -int8_t rf_433_get_keycode(); -int64_t rf_433_get_raw_keycode(); -*/ - void rf_433_learn_keycode(uint8_t index); void rf_433_cancel_learn_keycode(); diff --git a/main/storage.c b/main/storage.c index 570a69c..b3007e0 100644 --- a/main/storage.c +++ b/main/storage.c @@ -61,40 +61,54 @@ typedef struct { #define PARAM_NAME_STR(name) #name // Generate parameter table with live values (initialized to defaults) -#define PARAM_DEF(name, type, default_val, unit) PARAM_VALUE_INIT(type, default_val), +#define PARAM_DEF(name, type, default_val, unit, min, max) PARAM_VALUE_INIT(type, default_val), param_value_t parameter_table[NUM_PARAMS] = { PARAM_LIST }; #undef PARAM_DEF // Generate default values array -#define PARAM_DEF(name, type, default_val, unit) PARAM_VALUE_INIT(type, default_val), +#define PARAM_DEF(name, type, default_val, unit, min, max) PARAM_VALUE_INIT(type, default_val), const param_value_t parameter_defaults[NUM_PARAMS] = { PARAM_LIST }; #undef PARAM_DEF // Generate parameter types array -#define PARAM_DEF(name, type, default_val, unit) PARAM_TYPE_ENUM(type), +#define PARAM_DEF(name, type, default_val, unit, min, max) PARAM_TYPE_ENUM(type), const param_type_e parameter_types[NUM_PARAMS] = { PARAM_LIST }; #undef PARAM_DEF // Generate parameter names array -#define PARAM_DEF(name, type, default_val, unit) PARAM_NAME_STR(name), +#define PARAM_DEF(name, type, default_val, unit, min, max) PARAM_NAME_STR(name), const char* parameter_names[NUM_PARAMS] = { PARAM_LIST }; #undef PARAM_DEF // Generate parameter units array (8 chars max per unit) -#define PARAM_DEF(name, type, default_val, unit) unit, +#define PARAM_DEF(name, type, default_val, unit, min, max) unit, const char parameter_units[NUM_PARAMS][8] = { PARAM_LIST }; #undef PARAM_DEF +// Generate parameter min bounds array +#define PARAM_DEF(name, type, default_val, unit, min, max) PARAM_VALUE_INIT(type, min), +static const param_value_t parameter_mins[NUM_PARAMS] = { + PARAM_LIST +}; +#undef PARAM_DEF + +// Generate parameter max bounds array +#define PARAM_DEF(name, type, default_val, unit, min, max) PARAM_VALUE_INIT(type, max), +static const param_value_t parameter_maxs[NUM_PARAMS] = { + PARAM_LIST +}; +#undef PARAM_DEF + size_t param_type_size(param_type_e x) { switch(x) { case PARAM_TYPE_u16: return 2; @@ -108,6 +122,96 @@ size_t param_type_size(param_type_e x) { return -1; } +// ============================================================================ +// PARAMETER VALIDATION +// ============================================================================ +// Returns true if the value was modified (clamped or reset to default). +// - Strings: always skipped +// - Float NaN/Inf: reset to default +// - min == max (same raw bytes): skip bounds check (sentinel for "no bounds") +// - Otherwise: clamp to [min, max] +static bool validate_param(param_idx_t id) { + param_type_e type = parameter_types[id]; + + // String params: no numeric validation + if (type == PARAM_TYPE_str) return false; + + // Float types: NaN/Inf → reset to default + if (type == PARAM_TYPE_f32) { + if (isnanf(parameter_table[id].f32) || isinff(parameter_table[id].f32)) { + ESP_LOGW(TAG, "Param %s: NaN/Inf, reset to default", parameter_names[id]); + parameter_table[id] = parameter_defaults[id]; + return true; + } + } + if (type == PARAM_TYPE_f64) { + if (isnan(parameter_table[id].f64) || isinf(parameter_table[id].f64)) { + ESP_LOGW(TAG, "Param %s: NaN/Inf, reset to default", parameter_names[id]); + parameter_table[id] = parameter_defaults[id]; + return true; + } + } + + // Skip bounds check if min == max (sentinel) + size_t sz = param_type_size(type); + if (memcmp(¶meter_mins[id], ¶meter_maxs[id], sz) == 0) + return false; + + // Clamp to [min, max] per type + bool clamped = false; + switch (type) { + case PARAM_TYPE_u16: + if (parameter_table[id].u16 < parameter_mins[id].u16) { + parameter_table[id].u16 = parameter_mins[id].u16; clamped = true; + } else if (parameter_table[id].u16 > parameter_maxs[id].u16) { + parameter_table[id].u16 = parameter_maxs[id].u16; clamped = true; + } + break; + case PARAM_TYPE_i16: + if (parameter_table[id].i16 < parameter_mins[id].i16) { + parameter_table[id].i16 = parameter_mins[id].i16; clamped = true; + } else if (parameter_table[id].i16 > parameter_maxs[id].i16) { + parameter_table[id].i16 = parameter_maxs[id].i16; clamped = true; + } + break; + case PARAM_TYPE_u32: + if (parameter_table[id].u32 < parameter_mins[id].u32) { + parameter_table[id].u32 = parameter_mins[id].u32; clamped = true; + } else if (parameter_table[id].u32 > parameter_maxs[id].u32) { + parameter_table[id].u32 = parameter_maxs[id].u32; clamped = true; + } + break; + case PARAM_TYPE_i32: + if (parameter_table[id].i32 < parameter_mins[id].i32) { + parameter_table[id].i32 = parameter_mins[id].i32; clamped = true; + } else if (parameter_table[id].i32 > parameter_maxs[id].i32) { + parameter_table[id].i32 = parameter_maxs[id].i32; clamped = true; + } + break; + case PARAM_TYPE_f32: + if (parameter_table[id].f32 < parameter_mins[id].f32) { + parameter_table[id].f32 = parameter_mins[id].f32; clamped = true; + } else if (parameter_table[id].f32 > parameter_maxs[id].f32) { + parameter_table[id].f32 = parameter_maxs[id].f32; clamped = true; + } + break; + case PARAM_TYPE_f64: + if (parameter_table[id].f64 < parameter_mins[id].f64) { + parameter_table[id].f64 = parameter_mins[id].f64; clamped = true; + } else if (parameter_table[id].f64 > parameter_maxs[id].f64) { + parameter_table[id].f64 = parameter_maxs[id].f64; clamped = true; + } + break; + default: + break; + } + + if (clamped) { + ESP_LOGW(TAG, "Param %s: out of range, clamped", parameter_names[id]); + } + return clamped; +} + // 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; @@ -380,6 +484,9 @@ esp_err_t commit_params(void) { param_stored_t stored; memset(&stored, 0, sizeof(param_stored_t)); + // Validate before writing — clamp out-of-range, reset NaN/Inf + validate_param(i); + // Pack parameter data pack_param(stored.data, i); @@ -568,6 +675,9 @@ esp_err_t storage_init(void) { if (calculated_crc == stored.crc) { unpack_param(stored.data, i); + if (validate_param(i)) { + ESP_LOGW(TAG, "Param %d (%s) out of range after load, clamped", i, parameter_names[i]); + } } else { ESP_LOGW(TAG, "Parameter %d (%s) failed CRC check, using default", i, parameter_names[i]); diff --git a/main/storage.h b/main/storage.h index 9ddd870..5267cb2 100644 --- a/main/storage.h +++ b/main/storage.h @@ -51,61 +51,63 @@ typedef struct { // ============================================================================ -// TODO: Bounds checking / constraints (especially no division by zero, no NaNs, no infs) -// TODO: abandoned parameters (esp. jack current) +// PARAM_DEF(name, type, default, unit, min, max) +// min == max → skip bounds validation (used for keycodes, strings, informational params) +// Division-critical params have min > 0 to prevent div-by-zero +// NaN/Inf floats are always reset to default regardless of bounds #define PARAM_LIST \ - PARAM_DEF(BOOT_TIME, i32, 0, "us") \ - PARAM_DEF(NUM_MOVES, u32, 0, "") \ - PARAM_DEF(MOVE_START, u32, 0, "s") \ - PARAM_DEF(MOVE_END, u32, 0, "s") \ - PARAM_DEF(DRIVE_DIST, f32, 10, "ft") \ - PARAM_DEF(JACK_DIST, f32, 5, "in") \ - PARAM_DEF(DRIVE_KE, f32, 29.2, "n/ft") \ - PARAM_DEF(DRIVE_KT, f32, 2880000, "us/ft") \ - PARAM_DEF(JACK_KT, f32, 1428571, "ms/in") \ - PARAM_DEF(KEYCODE_0, u32, 0, "") \ - PARAM_DEF(KEYCODE_1, u32, 0, "") \ - PARAM_DEF(KEYCODE_2, u32, 0, "") \ - PARAM_DEF(KEYCODE_3, u32, 0, "") \ - PARAM_DEF(KEYCODE_4, u32, 0, "") \ - PARAM_DEF(KEYCODE_5, u32, 0, "") \ - PARAM_DEF(KEYCODE_6, u32, 0, "") \ - PARAM_DEF(KEYCODE_7, u32, 0, "") \ - PARAM_DEF(ADC_ALPHA_BATTERY, f32, 0.5, "-") \ - PARAM_DEF(ADC_ALPHA_ISENS, f32, 0.6, "-") \ - PARAM_DEF(ADC_ALPHA_IAZ, f32, 0.005, "-") \ - PARAM_DEF(ADC_DB_IAZ, f32, 5.0, "A") \ - PARAM_DEF(EFUSE_INOM_1, f32, 40.0, "A") \ - PARAM_DEF(EFUSE_INOM_2, f32, 14.0, "A") \ - PARAM_DEF(EFUSE_INOM_3, f32, 4.0, "A") \ - PARAM_DEF(EFUSE_HEAT_THRESH, f32, 60.0, "i/i^2-s") \ - PARAM_DEF(EFUSE_KINST, f32, 2.0, "i/i") \ - PARAM_DEF(EFUSE_TAUCOOL, f32, 0.2, "i") \ - PARAM_DEF(EFUSE_TCOOL, u32, 5000000, "us") \ - PARAM_DEF(LOW_PROTECTION_V, f32, 10.0, "V") \ - PARAM_DEF(LOW_PROTECTION_S, u32, 10, "s") \ - PARAM_DEF(CHG_LOW_V, f32, 5.0, "V") \ - PARAM_DEF(CHG_LOW_S, u32, 5, "s") \ - PARAM_DEF(CHG_BULK_S, u32, 20, "s") \ - PARAM_DEF(RF_PULSE_LENGTH, u32, 350000, "us") \ - PARAM_DEF(V_SENS_OFFSET, f32, 0.4, "V") \ - PARAM_DEF(NET_SSID, str, "", "") \ - PARAM_DEF(NET_PASS, str, "", "") \ - PARAM_DEF(WIFI_CHANNEL, u16, 6, "") \ - PARAM_DEF(WIFI_SSID, str, "sc.local", "") \ - PARAM_DEF(WIFI_PASS, str, "password", "") \ - PARAM_DEF(EFUSE_INRUSH_US, u32, 250000, "us") \ - PARAM_DEF(JACK_I_UP, f32, 8.0, "A") \ - PARAM_DEF(JACK_I_DOWN, f32, 15.0, "A") \ - PARAM_DEF(V_SENS_K, f32, 0.00766666666, "V/mV") \ - PARAM_DEF(BUILD_VERSION, str, "undefined", "") \ - PARAM_DEF(SAFETY_BREAK_US, u32, 300000, "") \ - PARAM_DEF(SAFETY_MAKE_US, u32, 1000000, "") \ - PARAM_DEF(JACK_IS_DOWN, f32, 8.0, "A") \ + PARAM_DEF(BOOT_TIME, i32, 0, "us", 0, 0) /* informational, skip */ \ + PARAM_DEF(NUM_MOVES, u32, 0, "", 0, 1000) \ + PARAM_DEF(MOVE_START, u32, 0, "s", 0, 86400) \ + PARAM_DEF(MOVE_END, u32, 0, "s", 0, 86400) \ + PARAM_DEF(DRIVE_DIST, f32, 10, "ft", 0.0, 100.0) \ + PARAM_DEF(JACK_DIST, f32, 5, "in", 0.0, 10.0) \ + PARAM_DEF(DRIVE_KE, f32, 29.2, "n/ft", 1.0, 1e9) \ + PARAM_DEF(DRIVE_KT, f32, 2880000, "us/ft", 1.0, 1e9) /* div-critical */ \ + PARAM_DEF(JACK_KT, f32, 1428571, "ms/in", 1.0, 1e9) /* div-critical */ \ + PARAM_DEF(KEYCODE_0, u32, 0, "", 0, 0) /* skip */ \ + PARAM_DEF(KEYCODE_1, u32, 0, "", 0, 0) \ + PARAM_DEF(KEYCODE_2, u32, 0, "", 0, 0) \ + PARAM_DEF(KEYCODE_3, u32, 0, "", 0, 0) \ + PARAM_DEF(KEYCODE_4, u32, 0, "", 0, 0) \ + PARAM_DEF(KEYCODE_5, u32, 0, "", 0, 0) \ + PARAM_DEF(KEYCODE_6, u32, 0, "", 0, 0) \ + PARAM_DEF(KEYCODE_7, u32, 0, "", 0, 0) \ + PARAM_DEF(ADC_ALPHA_BATTERY, f32, 0.5, "-", 0.0, 1.0) \ + PARAM_DEF(ADC_ALPHA_ISENS, f32, 0.6, "-", 0.0, 1.0) \ + PARAM_DEF(ADC_ALPHA_IAZ, f32, 0.005, "-", 0.0, 1.0) \ + PARAM_DEF(ADC_DB_IAZ, f32, 5.0, "A", 0.0, 200.0) \ + PARAM_DEF(EFUSE_INOM_1, f32, 40.0, "A", 0.0, 200.0) \ + PARAM_DEF(EFUSE_INOM_2, f32, 14.0, "A", 0.0, 200.0) \ + PARAM_DEF(EFUSE_INOM_3, f32, 4.0, "A", 0.0, 200.0) \ + PARAM_DEF(EFUSE_HEAT_THRESH, f32, 60.0, "i/i^2-s", 0.0, 1e9) \ + PARAM_DEF(EFUSE_KINST, f32, 2.0, "i/i", 0.01, 100.0) /* div-critical */ \ + PARAM_DEF(EFUSE_TAUCOOL, f32, 0.2, "i", 0.0, 100.0) \ + PARAM_DEF(EFUSE_TCOOL, u32, 5000000, "us", 0, 60000000) \ + PARAM_DEF(LOW_PROTECTION_V, f32, 10.0, "V", 0.0, 100.0) \ + PARAM_DEF(LOW_PROTECTION_S, u32, 10, "s", 0, 3600) \ + PARAM_DEF(CHG_LOW_V, f32, 5.0, "V", 0.0, 100.0) \ + PARAM_DEF(CHG_LOW_S, u32, 5, "s", 0, 3600) \ + PARAM_DEF(CHG_BULK_S, u32, 20, "s", 0, 3600) \ + PARAM_DEF(RF_PULSE_LENGTH, u32, 350000, "us", 0, 10000000) \ + PARAM_DEF(V_SENS_OFFSET, f32, 0.4, "V", -10.0, 10.0) \ + PARAM_DEF(NET_SSID, str, "", "", "", "") \ + PARAM_DEF(NET_PASS, str, "", "", "", "") \ + PARAM_DEF(WIFI_CHANNEL, u16, 6, "", 1, 14) \ + PARAM_DEF(WIFI_SSID, str, "sc.local", "", "", "") \ + PARAM_DEF(WIFI_PASS, str, "password", "", "", "") \ + PARAM_DEF(EFUSE_INRUSH_US, u32, 250000, "us", 0, 10000000) \ + PARAM_DEF(JACK_I_UP, f32, 8.0, "A", 0.0, 200.0) \ + PARAM_DEF(JACK_I_DOWN, f32, 15.0, "A", 0.0, 200.0) \ + PARAM_DEF(V_SENS_K, f32, 0.00766666666, "V/mV", 0.0, 1.0) \ + PARAM_DEF(BUILD_VERSION, str, "undefined", "", "", "") \ + PARAM_DEF(SAFETY_BREAK_US, u32, 300000, "", 0, 10000000) \ + PARAM_DEF(SAFETY_MAKE_US, u32, 1000000, "", 0, 10000000) \ + PARAM_DEF(JACK_IS_DOWN, f32, 8.0, "A", 0.0, 200.0) /* deprecated: may duplicate JACK_I_DOWN */ // Generate enum for parameter indices -#define PARAM_DEF(name, type, default_val, unit) PARAM_##name, +#define PARAM_DEF(name, type, default_val, unit, min, max) PARAM_##name, typedef enum { PARAM_LIST NUM_PARAMS