diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 0950436..d5acdae 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -30,6 +30,7 @@ idf_component_register( "esp_now_proto.c" "bosch456.c" "board_input.c" + "pod_settings.c" "proto/uart_messages.pb.c" "proto/esp_now_messages.pb.c" "proto/pb_encode.c" diff --git a/main/README.md b/main/README.md index 1bfb8e6..5df4311 100644 --- a/main/README.md +++ b/main/README.md @@ -82,12 +82,14 @@ Powerpod uses the Bosch **BMA456H** (hearable) variant, not the generic `bma456w **Accel logging:** Samples are printed only when any axis changes by more than the **deadzone** (raw LSB) since the last logged sample (default **100**). This is a **software** filter on top of the sensor; it does not change BMA456 hardware thresholds. +**Persistence:** The local deadzone is stored in the **`nvs`** partition (namespace `powerpod`, key `accel_dz`) via `pod_settings.c`. Each node (master or slave) keeps its own value across reboot. Loaded at boot after `init_bma456()`; saved when set locally (UART `client_id = 0`, `all_clients` on master, or ESP-NOW deadzone on a slave). + **Configuration paths:** | Path | Effect | |------|--------| -| UART `ACCEL_DEADZONE` with `client_id = 0` | `bma456_set_accel_deadzone()` on the local node | -| ESP-NOW `SET_ACCEL_DEADZONE` | Same on a slave (no-op log path if sensor not installed) | +| UART `ACCEL_DEADZONE` with `client_id = 0` | Set + save local deadzone | +| ESP-NOW `SET_ACCEL_DEADZONE` | Set + save on the receiving slave | | `make gotool-deadzone-set DEADZONE=… CLIENT=0` | Host shortcut for local deadzone | **Logs:** `[BMA456] ACC X=… Y=… Z=…` when deadzone exceeded; `[BMA456] tap: single|double|triple` on interrupt. @@ -440,6 +442,7 @@ Target: ESP32-S3. Close serial monitor on the UART adapter port before running ` | `client_registry.c/h` | Registered slave table | | `bosch456.c/h` | BMA456H I2C driver, accel poll, tap INT, deadzone filter | | `board_input.c/h` | Taster GPIO12, LiPo ADC on GPIO1 / GPIO12 | +| `pod_settings.c/h` | NVS persistence (accel deadzone, …) | | `led_ring.c/h` | LED ring (digit display, progress bar) | | `cmd_led_ring.c` | UART `LED_RING` progress command | | `proto/uart_messages.proto` | UART protocol schema | diff --git a/main/cmd_accel_deadzone.c b/main/cmd_accel_deadzone.c index 9609b09..0471797 100644 --- a/main/cmd_accel_deadzone.c +++ b/main/cmd_accel_deadzone.c @@ -3,6 +3,7 @@ #include "cmd_accel_deadzone.h" #include "esp_log.h" #include "esp_now_comm.h" +#include "pod_settings.h" #include "uart_cmd.h" static const char *TAG = "[ACCEL_DZ]"; @@ -19,6 +20,14 @@ static void reply(uint32_t deadzone, uint32_t client_id, bool success, uart_cmd_send(&response, TAG); } +static void apply_local_deadzone(uint32_t deadzone) { + bma456_set_accel_deadzone(deadzone); + if (pod_settings_save_accel_deadzone(deadzone) != ESP_OK) { + ESP_LOGW(TAG, "deadzone %lu applied but not saved to NVS", + (unsigned long)deadzone); + } +} + static esp_err_t push_deadzone_to_slave(const client_info_t *client, uint32_t deadzone) { if (client == NULL) { @@ -67,7 +76,7 @@ static void handle_accel_deadzone(const uint8_t *data, size_t len) { } if (bma456_is_ready()) { - bma456_set_accel_deadzone(req.deadzone); + apply_local_deadzone(req.deadzone); } ESP_LOGI(TAG, "set deadzone %lu via unicast to %u/%u slaves", @@ -77,7 +86,7 @@ static void handle_accel_deadzone(const uint8_t *data, size_t len) { } if (req.client_id == 0) { - bma456_set_accel_deadzone(req.deadzone); + apply_local_deadzone(req.deadzone); ESP_LOGI(TAG, "set local deadzone %lu (no ESP-NOW; use -client or -all " "for slaves)", (unsigned long)req.deadzone); diff --git a/main/esp_now_comm.c b/main/esp_now_comm.c index 15d7076..2f0b4bd 100644 --- a/main/esp_now_comm.c +++ b/main/esp_now_comm.c @@ -3,6 +3,7 @@ #include "esp_now_comm.h" #include "led_ring.h" #include "ota_espnow.h" +#include "pod_settings.h" #include "esp_now_proto.h" #include "esp_err.h" #include "esp_event.h" @@ -13,7 +14,6 @@ #include "esp_wifi.h" #include "freertos/FreeRTOS.h" #include "freertos/idf_additions.h" -#include "nvs_flash.h" #include "ota_uart.h" #include #include @@ -399,6 +399,10 @@ static void handle_slave_accel_deadzone(const uint8_t *master_mac, bma456_is_ready() ? "ok" : "not installed"); bma456_set_accel_deadzone(cfg->deadzone); + if (pod_settings_save_accel_deadzone(cfg->deadzone) != ESP_OK) { + ESP_LOGW(TAG, "slave deadzone %lu applied but not saved to NVS", + (unsigned long)cfg->deadzone); + } } static void handle_client_presence(const alox_EspNowSlavePresence *presence, @@ -607,17 +611,6 @@ static void master_discover_task(void *param) { } static esp_err_t init_wifi_stack(uint8_t channel) { - esp_err_t err; - - err = nvs_flash_init(); - if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_ERROR_CHECK(nvs_flash_erase()); - err = nvs_flash_init(); - } - if (err != ESP_OK) { - return err; - } - ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); diff --git a/main/pod_settings.c b/main/pod_settings.c new file mode 100644 index 0000000..dc68f19 --- /dev/null +++ b/main/pod_settings.c @@ -0,0 +1,110 @@ +#include "pod_settings.h" +#include "bosch456.h" +#include "esp_log.h" +#include "nvs.h" +#include "nvs_flash.h" + +static const char *TAG = "[SETTINGS]"; +static const char *NS = "powerpod"; +static const char *KEY_ACCEL_DZ = "accel_dz"; + +#define ACCEL_DEADZONE_MAX 4095u + +static bool s_nvs_ready; + +static esp_err_t ensure_nvs(void) { + if (s_nvs_ready) { + return ESP_OK; + } + return pod_settings_init(); +} + +esp_err_t pod_settings_init(void) { + if (s_nvs_ready) { + return ESP_OK; + } + + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || + err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_LOGW(TAG, "NVS erase and re-init"); + ESP_ERROR_CHECK(nvs_flash_erase()); + err = nvs_flash_init(); + } + if (err != ESP_OK) { + ESP_LOGE(TAG, "nvs_flash_init failed: %s", esp_err_to_name(err)); + return err; + } + + s_nvs_ready = true; + ESP_LOGI(TAG, "NVS ready, accel deadzone %lu LSB (stored)", + (unsigned long)pod_settings_load_accel_deadzone()); + return ESP_OK; +} + +uint32_t pod_settings_load_accel_deadzone(void) { + if (!s_nvs_ready) { + return BMA456_DEFAULT_ACCEL_DEADZONE; + } + + nvs_handle_t handle; + esp_err_t err = nvs_open(NS, NVS_READONLY, &handle); + if (err != ESP_OK) { + return BMA456_DEFAULT_ACCEL_DEADZONE; + } + + uint32_t value = BMA456_DEFAULT_ACCEL_DEADZONE; + err = nvs_get_u32(handle, KEY_ACCEL_DZ, &value); + nvs_close(handle); + + if (err == ESP_ERR_NVS_NOT_FOUND) { + return BMA456_DEFAULT_ACCEL_DEADZONE; + } + if (err != ESP_OK) { + ESP_LOGW(TAG, "read accel_dz failed: %s", esp_err_to_name(err)); + return BMA456_DEFAULT_ACCEL_DEADZONE; + } + if (value > ACCEL_DEADZONE_MAX) { + return BMA456_DEFAULT_ACCEL_DEADZONE; + } + return value; +} + +esp_err_t pod_settings_save_accel_deadzone(uint32_t deadzone_lsb) { + if (deadzone_lsb > ACCEL_DEADZONE_MAX) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = ensure_nvs(); + if (err != ESP_OK) { + return err; + } + + nvs_handle_t handle; + err = nvs_open(NS, NVS_READWRITE, &handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "nvs_open failed: %s", esp_err_to_name(err)); + return err; + } + + err = nvs_set_u32(handle, KEY_ACCEL_DZ, deadzone_lsb); + if (err == ESP_OK) { + err = nvs_commit(handle); + } + nvs_close(handle); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "save accel_dz failed: %s", esp_err_to_name(err)); + return err; + } + + ESP_LOGI(TAG, "saved accel deadzone %lu LSB", (unsigned long)deadzone_lsb); + return ESP_OK; +} + +void pod_settings_apply_accel_deadzone(void) { + uint32_t deadzone = pod_settings_load_accel_deadzone(); + if (bma456_is_ready()) { + bma456_set_accel_deadzone(deadzone); + } +} diff --git a/main/pod_settings.h b/main/pod_settings.h new file mode 100644 index 0000000..5032a7b --- /dev/null +++ b/main/pod_settings.h @@ -0,0 +1,19 @@ +#ifndef POD_SETTINGS_H +#define POD_SETTINGS_H + +#include "esp_err.h" +#include + +/** Initialize NVS (idempotent) and log stored settings. Call once early in app_main. */ +esp_err_t pod_settings_init(void); + +/** Persist local accelerometer deadzone (LSB per axis). */ +esp_err_t pod_settings_save_accel_deadzone(uint32_t deadzone_lsb); + +/** Load deadzone from NVS, or BMA456_DEFAULT_ACCEL_DEADZONE if unset. */ +uint32_t pod_settings_load_accel_deadzone(void); + +/** Apply NVS deadzone to BMA456 when the sensor is present. */ +void pod_settings_apply_accel_deadzone(void); + +#endif diff --git a/main/powerpod.c b/main/powerpod.c index 8c33868..2663a54 100644 --- a/main/powerpod.c +++ b/main/powerpod.c @@ -22,6 +22,7 @@ #include "board_input.h" #include "bosch456.h" #include "led_ring.h" +#include "pod_settings.h" #include "uart.h" #include @@ -66,6 +67,10 @@ uint8_t reverse_high_nibble_lut(uint8_t n) { } void app_main(void) { + if (pod_settings_init() != ESP_OK) { + ESP_LOGW(TAG, "settings NVS init failed; using defaults"); + } + // Get Master Mode Pin gpio_reset_pin(DIP_MASTER); gpio_set_direction(DIP_MASTER, GPIO_MODE_INPUT); @@ -125,6 +130,8 @@ void app_main(void) { esp_err_t bma_err = init_bma456(bus_handle); if (bma_err != ESP_OK) { ESP_LOGI(TAG, "BMA456 init skipped: %s", esp_err_to_name(bma_err)); + } else { + pod_settings_apply_accel_deadzone(); } }