Assign sequential pod IDs from the master with NVS-stable bindings.
Replaces MAC-byte self-IDs with master allocation (1–255), slave preferred-ID reconnect, debug reset via UART/UI, and rate-limited ASSIGN to avoid ESP-NOW NO_MEM storms. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -10,9 +10,31 @@ static const char *NS = "powerpod";
|
||||
static const char *KEY_ACCEL_DZ = "accel_dz";
|
||||
static const char *KEY_UV_LATCH = "uv_latch";
|
||||
static const char *KEY_TAP_CFG = "tap_cfg";
|
||||
static const char *KEY_ID_MAP = "id_map";
|
||||
static const char *KEY_ID_BIND = "id_bind";
|
||||
|
||||
#define ACCEL_DEADZONE_MAX 4095u
|
||||
#define TAP_CFG_NVS_VERSION 1u
|
||||
#define ID_MAP_NVS_VERSION 1u
|
||||
#define ID_BIND_NVS_VERSION 1u
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t mac[POD_ID_MAC_LEN];
|
||||
uint8_t id;
|
||||
} id_map_entry_nvs_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t version;
|
||||
uint8_t next_id;
|
||||
uint8_t count;
|
||||
id_map_entry_nvs_t entries[POD_ID_MAP_MAX];
|
||||
} id_map_nvs_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t version;
|
||||
uint8_t master_mac[POD_ID_MAC_LEN];
|
||||
uint8_t assigned_id;
|
||||
} id_bind_nvs_t;
|
||||
|
||||
/** Packed layout for NVS blob (stable across rebuilds). */
|
||||
typedef struct __attribute__((packed)) {
|
||||
@@ -302,3 +324,232 @@ esp_err_t pod_settings_set_uv_latched(bool latched) {
|
||||
esp_err_t pod_settings_clear_uv_latched(void) {
|
||||
return pod_settings_set_uv_latched(false);
|
||||
}
|
||||
|
||||
esp_err_t pod_settings_load_id_map(pod_id_map_t *out) {
|
||||
if (out == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->next_id = (uint8_t)POD_CLIENT_ID_MIN;
|
||||
if (!s_nvs_ready) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open(NS, NVS_READONLY, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
id_map_nvs_t blob;
|
||||
memset(&blob, 0, sizeof(blob));
|
||||
size_t len = sizeof(blob);
|
||||
err = nvs_get_blob(handle, KEY_ID_MAP, &blob, &len);
|
||||
nvs_close(handle);
|
||||
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
return ESP_OK;
|
||||
}
|
||||
if (err != ESP_OK || len < 3 || blob.version != ID_MAP_NVS_VERSION) {
|
||||
ESP_LOGW(TAG, "read id_map failed: %s (len=%u)", esp_err_to_name(err),
|
||||
(unsigned)len);
|
||||
return ESP_OK;
|
||||
}
|
||||
if (blob.count > POD_ID_MAP_MAX) {
|
||||
ESP_LOGW(TAG, "id_map count %u invalid", (unsigned)blob.count);
|
||||
return ESP_OK;
|
||||
}
|
||||
if (blob.next_id < POD_CLIENT_ID_MIN) {
|
||||
blob.next_id = (uint8_t)POD_CLIENT_ID_MIN;
|
||||
}
|
||||
|
||||
out->next_id = blob.next_id;
|
||||
out->count = blob.count;
|
||||
for (uint8_t i = 0; i < blob.count; i++) {
|
||||
memcpy(out->entries[i].mac, blob.entries[i].mac, POD_ID_MAC_LEN);
|
||||
out->entries[i].id = blob.entries[i].id;
|
||||
}
|
||||
ESP_LOGI(TAG, "loaded id_map count=%u next_id=%u", (unsigned)out->count,
|
||||
(unsigned)out->next_id);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t pod_settings_save_id_map(const pod_id_map_t *map) {
|
||||
if (map == NULL || map->count > POD_ID_MAP_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (map->next_id < POD_CLIENT_ID_MIN) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t err = ensure_nvs();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
id_map_nvs_t blob;
|
||||
memset(&blob, 0, sizeof(blob));
|
||||
blob.version = ID_MAP_NVS_VERSION;
|
||||
blob.next_id = map->next_id;
|
||||
blob.count = map->count;
|
||||
for (uint8_t i = 0; i < map->count; i++) {
|
||||
memcpy(blob.entries[i].mac, map->entries[i].mac, POD_ID_MAC_LEN);
|
||||
blob.entries[i].id = map->entries[i].id;
|
||||
}
|
||||
|
||||
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_blob(handle, KEY_ID_MAP, &blob, sizeof(blob));
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "save id_map failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG, "saved id_map count=%u next_id=%u", (unsigned)map->count,
|
||||
(unsigned)map->next_id);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t pod_settings_clear_id_map(void) {
|
||||
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_erase_key(handle, KEY_ID_MAP);
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
err = ESP_OK;
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "clear id_map failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG, "cleared id_map");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool pod_settings_load_id_binding(uint8_t master_mac[POD_ID_MAC_LEN],
|
||||
uint32_t *assigned_id_out) {
|
||||
if (master_mac == NULL || assigned_id_out == NULL) {
|
||||
return false;
|
||||
}
|
||||
memset(master_mac, 0, POD_ID_MAC_LEN);
|
||||
*assigned_id_out = 0;
|
||||
if (!s_nvs_ready) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open(NS, NVS_READONLY, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
id_bind_nvs_t blob = {0};
|
||||
size_t len = sizeof(blob);
|
||||
err = nvs_get_blob(handle, KEY_ID_BIND, &blob, &len);
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK || len != sizeof(blob) ||
|
||||
blob.version != ID_BIND_NVS_VERSION) {
|
||||
return false;
|
||||
}
|
||||
if (blob.assigned_id < POD_CLIENT_ID_MIN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(master_mac, blob.master_mac, POD_ID_MAC_LEN);
|
||||
*assigned_id_out = blob.assigned_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t pod_settings_save_id_binding(const uint8_t master_mac[POD_ID_MAC_LEN],
|
||||
uint32_t assigned_id) {
|
||||
if (master_mac == NULL || assigned_id < POD_CLIENT_ID_MIN ||
|
||||
assigned_id > POD_CLIENT_ID_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t err = ensure_nvs();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
id_bind_nvs_t blob = {
|
||||
.version = ID_BIND_NVS_VERSION,
|
||||
.assigned_id = (uint8_t)assigned_id,
|
||||
};
|
||||
memcpy(blob.master_mac, master_mac, POD_ID_MAC_LEN);
|
||||
|
||||
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_blob(handle, KEY_ID_BIND, &blob, sizeof(blob));
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "save id_bind failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG, "saved id_bind id=%lu master=%02x:…:%02x",
|
||||
(unsigned long)assigned_id, master_mac[0], master_mac[5]);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t pod_settings_clear_id_binding(void) {
|
||||
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_erase_key(handle, KEY_ID_BIND);
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
err = ESP_OK;
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "clear id_bind failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG, "cleared id_bind");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user