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:
@@ -29,6 +29,7 @@ idf_component_register(
|
||||
"cmd/cmd_espnow_echo_ping.c"
|
||||
"cmd/cmd_espnow_find_me.c"
|
||||
"cmd/cmd_restart.c"
|
||||
"cmd/cmd_client_id_map_reset.c"
|
||||
"pod_reboot.c"
|
||||
"cmd/cmd_led_ring.c"
|
||||
"cmd/cmd_battery.c"
|
||||
|
||||
+144
-18
@@ -1,4 +1,5 @@
|
||||
#include "client_registry.h"
|
||||
#include "pod_settings.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/idf_additions.h"
|
||||
@@ -13,12 +14,68 @@ typedef struct {
|
||||
} client_slot_t;
|
||||
|
||||
static client_slot_t s_clients[CLIENT_REGISTRY_MAX];
|
||||
static pod_id_map_t s_id_map;
|
||||
|
||||
static struct {
|
||||
board_lipo_reading_t reading;
|
||||
uint32_t updated_at;
|
||||
} s_master_battery;
|
||||
|
||||
static bool id_in_use(uint32_t id, const uint8_t *except_mac) {
|
||||
for (uint8_t i = 0; i < s_id_map.count; i++) {
|
||||
if (s_id_map.entries[i].id != id) {
|
||||
continue;
|
||||
}
|
||||
if (except_mac != NULL &&
|
||||
memcmp(s_id_map.entries[i].mac, except_mac, CLIENT_MAC_LEN) == 0) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int find_map_index(const uint8_t mac[CLIENT_MAC_LEN]) {
|
||||
for (uint8_t i = 0; i < s_id_map.count; i++) {
|
||||
if (memcmp(s_id_map.entries[i].mac, mac, CLIENT_MAC_LEN) == 0) {
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static esp_err_t persist_id_map(void) {
|
||||
return pod_settings_save_id_map(&s_id_map);
|
||||
}
|
||||
|
||||
static uint32_t alloc_next_free_id(void) {
|
||||
if (s_id_map.count >= POD_ID_MAP_MAX) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t start = s_id_map.next_id;
|
||||
if (start < POD_CLIENT_ID_MIN) {
|
||||
start = (uint8_t)POD_CLIENT_ID_MIN;
|
||||
}
|
||||
for (uint32_t n = 0; n < POD_CLIENT_ID_MAX; n++) {
|
||||
uint32_t id = (uint32_t)start + n;
|
||||
if (id > POD_CLIENT_ID_MAX) {
|
||||
id = id - POD_CLIENT_ID_MAX;
|
||||
}
|
||||
if (id < POD_CLIENT_ID_MIN) {
|
||||
continue;
|
||||
}
|
||||
if (!id_in_use(id, NULL)) {
|
||||
uint32_t next = id + 1;
|
||||
if (next > POD_CLIENT_ID_MAX) {
|
||||
next = POD_CLIENT_ID_MIN;
|
||||
}
|
||||
s_id_map.next_id = (uint8_t)next;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t client_registry_now_ms(void) {
|
||||
return (uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS);
|
||||
}
|
||||
@@ -95,7 +152,84 @@ static client_slot_t *alloc_slot(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void client_registry_init(void) { memset(s_clients, 0, sizeof(s_clients)); }
|
||||
void client_registry_init(void) {
|
||||
memset(s_clients, 0, sizeof(s_clients));
|
||||
memset(&s_id_map, 0, sizeof(s_id_map));
|
||||
s_id_map.next_id = (uint8_t)POD_CLIENT_ID_MIN;
|
||||
(void)pod_settings_load_id_map(&s_id_map);
|
||||
if (s_id_map.next_id < POD_CLIENT_ID_MIN) {
|
||||
s_id_map.next_id = (uint8_t)POD_CLIENT_ID_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t client_registry_assign_id(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t preferred_id, uint32_t *out_id) {
|
||||
if (mac == NULL || out_id == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
int idx = find_map_index(mac);
|
||||
if (idx >= 0) {
|
||||
*out_id = s_id_map.entries[idx].id;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint32_t id = 0;
|
||||
if (preferred_id >= POD_CLIENT_ID_MIN && preferred_id <= POD_CLIENT_ID_MAX &&
|
||||
!id_in_use(preferred_id, mac)) {
|
||||
id = preferred_id;
|
||||
} else {
|
||||
id = alloc_next_free_id();
|
||||
}
|
||||
if (id == 0) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if (s_id_map.count >= POD_ID_MAP_MAX) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
memcpy(s_id_map.entries[s_id_map.count].mac, mac, CLIENT_MAC_LEN);
|
||||
s_id_map.entries[s_id_map.count].id = (uint8_t)id;
|
||||
s_id_map.count++;
|
||||
if (id >= s_id_map.next_id) {
|
||||
uint32_t next = id + 1;
|
||||
if (next > POD_CLIENT_ID_MAX) {
|
||||
next = POD_CLIENT_ID_MIN;
|
||||
}
|
||||
s_id_map.next_id = (uint8_t)next;
|
||||
}
|
||||
|
||||
esp_err_t err = persist_id_map();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "persist id_map failed after assign id=%lu: %s",
|
||||
(unsigned long)id, esp_err_to_name(err));
|
||||
}
|
||||
*out_id = id;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void client_registry_clear_id_map(void) {
|
||||
memset(s_clients, 0, sizeof(s_clients));
|
||||
memset(&s_id_map, 0, sizeof(s_id_map));
|
||||
s_id_map.next_id = (uint8_t)POD_CLIENT_ID_MIN;
|
||||
esp_err_t err = pod_settings_clear_id_map();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "clear id_map NVS failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
ESP_LOGI(TAG, "cleared id map and live registry");
|
||||
}
|
||||
|
||||
void client_registry_mark_assign_sent(const uint8_t mac[CLIENT_MAC_LEN]) {
|
||||
if (mac == NULL) {
|
||||
return;
|
||||
}
|
||||
client_slot_t *slot = find_slot(mac);
|
||||
if (slot == NULL) {
|
||||
return;
|
||||
}
|
||||
slot->info.last_assign_sent_at = now_ms();
|
||||
}
|
||||
|
||||
const client_info_t *
|
||||
client_registry_find_by_mac(const uint8_t mac[CLIENT_MAC_LEN]) {
|
||||
@@ -171,18 +305,19 @@ void client_registry_check_timeouts(uint32_t timeout_ms) {
|
||||
uint32_t now = now_ms();
|
||||
|
||||
for (size_t i = 0; i < CLIENT_REGISTRY_MAX; i++) {
|
||||
if (!s_clients[i].active || !s_clients[i].info.available) {
|
||||
if (!s_clients[i].active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t elapsed = now - s_clients[i].info.last_success_ping_at;
|
||||
if (elapsed > timeout_ms) {
|
||||
s_clients[i].info.available = false;
|
||||
char mac_str[18];
|
||||
mac_to_str(s_clients[i].info.mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGW(TAG, "client inactive id=%lu mac=%s (no heartbeat for %lu ms)",
|
||||
ESP_LOGW(TAG, "client dropped id=%lu mac=%s (no heartbeat for %lu ms)",
|
||||
(unsigned long)s_clients[i].info.id, mac_str,
|
||||
(unsigned long)elapsed);
|
||||
s_clients[i].active = false;
|
||||
memset(&s_clients[i].info, 0, sizeof(s_clients[i].info));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -552,24 +687,15 @@ esp_err_t client_registry_update_battery(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
|
||||
client_slot_t *slot = find_slot(mac);
|
||||
if (slot == NULL) {
|
||||
bool is_new = false;
|
||||
esp_err_t err = client_registry_upsert(mac, slave_id, 0, true, false, &is_new);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
slot = find_slot(mac);
|
||||
if (slot == NULL) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
ESP_LOGI(TAG, "battery auto-registered id=%lu (report before heartbeat)",
|
||||
(unsigned long)slave_id);
|
||||
/* Ids are assigned on presence only; ignore early battery reports. */
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (slot->info.id != slave_id) {
|
||||
ESP_LOGW(TAG, "battery id %lu → %lu for mac %02x:…:%02x",
|
||||
if (slave_id != 0 && slot->info.id != slave_id) {
|
||||
ESP_LOGW(TAG, "battery id mismatch slot=%lu report=%lu mac %02x:…:%02x",
|
||||
(unsigned long)slot->info.id, (unsigned long)slave_id, mac[0],
|
||||
mac[5]);
|
||||
slot->info.id = slave_id;
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
slot->info.lipo1_valid = lipo1_valid;
|
||||
|
||||
+23
-1
@@ -47,6 +47,8 @@ typedef struct {
|
||||
uint32_t lipo1_mv;
|
||||
uint32_t lipo2_mv;
|
||||
uint32_t battery_updated_at;
|
||||
/** Master: last ESPNOW_ASSIGN_ID send time (rate-limit resends). */
|
||||
uint32_t last_assign_sent_at;
|
||||
} client_info_t;
|
||||
|
||||
#define CLIENT_REGISTRY_DEFAULT_ACCEL_DEADZONE 100u
|
||||
@@ -55,6 +57,23 @@ typedef struct {
|
||||
|
||||
void client_registry_init(void);
|
||||
|
||||
/**
|
||||
* Assign a stable logical id for mac (1…255). Honors preferred if free or
|
||||
* already owned by mac; otherwise allocates the next free id. Persists the
|
||||
* master id map to NVS.
|
||||
*/
|
||||
esp_err_t client_registry_assign_id(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t preferred_id, uint32_t *out_id);
|
||||
|
||||
/**
|
||||
* Clear NVS id map, RAM map, and all live registry slots. Does not notify
|
||||
* slaves (caller sends CLEAR_ID_BINDING).
|
||||
*/
|
||||
void client_registry_clear_id_map(void);
|
||||
|
||||
/** Record that ASSIGN_ID was sent to this MAC (for resend rate limiting). */
|
||||
void client_registry_mark_assign_sent(const uint8_t mac[CLIENT_MAC_LEN]);
|
||||
|
||||
/** Milliseconds since boot (same clock as stored ping timestamps). */
|
||||
uint32_t client_registry_now_ms(void);
|
||||
|
||||
@@ -75,7 +94,10 @@ esp_err_t client_registry_heartbeat(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t id, uint32_t version, bool used,
|
||||
bool *out_is_new, bool *out_reactivated);
|
||||
|
||||
/** Mark clients inactive when last_success_ping_at is older than timeout_ms. */
|
||||
/**
|
||||
* Drop live registry slots whose last_success_ping_at is older than
|
||||
* timeout_ms (NVS id map unchanged). Offline clients disappear from CLIENT_INFO.
|
||||
*/
|
||||
void client_registry_check_timeouts(uint32_t timeout_ms);
|
||||
|
||||
size_t client_registry_count(void);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#include "cmd_client_id_map_reset.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_now_comm.h"
|
||||
#include "esp_now_core.h"
|
||||
#include "uart_cmd.h"
|
||||
|
||||
static const char *TAG = "[ID_MAP_RESET]";
|
||||
|
||||
static void reply(bool success) {
|
||||
alox_UartMessage response;
|
||||
uart_cmd_init_response(&response, alox_MessageType_CLIENT_ID_MAP_RESET,
|
||||
alox_UartMessage_client_id_map_reset_response_tag);
|
||||
response.payload.client_id_map_reset_response.success = success;
|
||||
uart_cmd_send(&response, TAG);
|
||||
}
|
||||
|
||||
static void handle_client_id_map_reset(const uint8_t *data, size_t len) {
|
||||
(void)data;
|
||||
(void)len;
|
||||
|
||||
esp_err_t err;
|
||||
if (esp_now_core_is_master()) {
|
||||
ESP_LOGI(TAG, "reset master id map + notify online slaves");
|
||||
err = esp_now_comm_reset_client_id_map();
|
||||
} else {
|
||||
ESP_LOGI(TAG, "clear local slave id binding");
|
||||
err = esp_now_comm_clear_local_id_binding();
|
||||
}
|
||||
reply(err == ESP_OK);
|
||||
}
|
||||
|
||||
void cmd_client_id_map_reset_register(void) {
|
||||
uart_cmd_register(alox_MessageType_CLIENT_ID_MAP_RESET,
|
||||
handle_client_id_map_reset);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void cmd_client_id_map_reset_register(void);
|
||||
@@ -55,6 +55,22 @@ esp_err_t esp_now_comm_send_led_ring(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
esp_err_t esp_now_comm_send_restart(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t client_id);
|
||||
|
||||
/** Master: tell slave its assigned logical client id. */
|
||||
esp_err_t esp_now_comm_send_assign_id(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t assigned_id);
|
||||
|
||||
/** Master: tell slave to forget persisted master/id binding. */
|
||||
esp_err_t esp_now_comm_send_clear_id_binding(const uint8_t mac[CLIENT_MAC_LEN]);
|
||||
|
||||
/**
|
||||
* Master: clear NVS id map + live registry and unicast CLEAR_ID_BINDING to
|
||||
* every currently registered live slave (snapshot before clear).
|
||||
*/
|
||||
esp_err_t esp_now_comm_reset_client_id_map(void);
|
||||
|
||||
/** Slave: clear local NVS master/id binding (UART reset on slave). */
|
||||
esp_err_t esp_now_comm_clear_local_id_binding(void);
|
||||
|
||||
/** Master → slave OTA (unicast). */
|
||||
esp_err_t esp_now_comm_send_ota_start(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t total_size);
|
||||
|
||||
+117
-7
@@ -22,6 +22,8 @@ static const uint8_t ESPNOW_BCAST[ESP_NOW_ETH_ALEN] = {0xff, 0xff, 0xff,
|
||||
#define ESPNOW_CLIENT_TIMEOUT_MS \
|
||||
(ESPNOW_HEARTBEAT_INTERVAL_MS * ESPNOW_HEARTBEAT_MISS_COUNT)
|
||||
#define ESPNOW_BATTERY_INTERVAL_MS 30000
|
||||
/** Min gap between ASSIGN_ID resends when slave has not yet echoed the id. */
|
||||
#define ESPNOW_ASSIGN_RESEND_MS 2000u
|
||||
|
||||
static const char *TAG = "[ESPNOW_M]";
|
||||
|
||||
@@ -168,6 +170,22 @@ static esp_err_t send_restart(const uint8_t *dest_mac, uint32_t client_id) {
|
||||
return esp_now_core_send(dest_mac, &msg);
|
||||
}
|
||||
|
||||
static esp_err_t send_assign_id(const uint8_t *dest_mac, uint32_t assigned_id) {
|
||||
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
||||
msg.type = alox_EspNowMessageType_ESPNOW_ASSIGN_ID;
|
||||
msg.which_payload = alox_EspNowMessage_assign_id_tag;
|
||||
msg.payload.assign_id.assigned_id = assigned_id;
|
||||
/* Fast path: avoid 8× NO_MEM retries blocking the shared send lock. */
|
||||
return esp_now_core_send_fast(dest_mac, &msg);
|
||||
}
|
||||
|
||||
static esp_err_t send_clear_id_binding(const uint8_t *dest_mac) {
|
||||
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
||||
msg.type = alox_EspNowMessageType_ESPNOW_CLEAR_ID_BINDING;
|
||||
msg.which_payload = alox_EspNowMessage_clear_id_binding_tag;
|
||||
return esp_now_core_send_fast(dest_mac, &msg);
|
||||
}
|
||||
|
||||
static esp_err_t send_tap_notify(const uint8_t *dest_mac, uint32_t client_id,
|
||||
bool single, bool double_tap, bool triple) {
|
||||
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
||||
@@ -251,6 +269,70 @@ esp_err_t esp_now_comm_send_restart(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_now_comm_send_assign_id(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t assigned_id) {
|
||||
if (mac == NULL || !esp_now_core_is_master()) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(mac, mac_str, sizeof(mac_str));
|
||||
esp_err_t err = send_assign_id(mac, assigned_id);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "unicast ASSIGN_ID %lu to %s", (unsigned long)assigned_id,
|
||||
mac_str);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "unicast ASSIGN_ID to %s failed: %s", mac_str,
|
||||
esp_err_to_name(err));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_now_comm_send_clear_id_binding(const uint8_t mac[CLIENT_MAC_LEN]) {
|
||||
if (mac == NULL || !esp_now_core_is_master()) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(mac, mac_str, sizeof(mac_str));
|
||||
esp_err_t err = send_clear_id_binding(mac);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "unicast CLEAR_ID_BINDING to %s", mac_str);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "unicast CLEAR_ID_BINDING to %s failed: %s", mac_str,
|
||||
esp_err_to_name(err));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_now_comm_reset_client_id_map(void) {
|
||||
if (!esp_now_core_is_master()) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
uint8_t macs[CLIENT_REGISTRY_MAX][CLIENT_MAC_LEN];
|
||||
size_t n = client_registry_count();
|
||||
if (n > CLIENT_REGISTRY_MAX) {
|
||||
n = CLIENT_REGISTRY_MAX;
|
||||
}
|
||||
size_t copied = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
const client_info_t *c = client_registry_at(i);
|
||||
if (c == NULL) {
|
||||
continue;
|
||||
}
|
||||
memcpy(macs[copied], c->mac, CLIENT_MAC_LEN);
|
||||
copied++;
|
||||
}
|
||||
|
||||
client_registry_clear_id_map();
|
||||
|
||||
for (size_t i = 0; i < copied; i++) {
|
||||
(void)esp_now_comm_send_clear_id_binding(macs[i]);
|
||||
}
|
||||
ESP_LOGI(TAG, "client id map reset; notified %u slave(s)", (unsigned)copied);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_now_comm_send_find_me(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t client_id) {
|
||||
if (mac == NULL || !esp_now_core_is_master()) {
|
||||
@@ -495,25 +577,53 @@ static void handle_client_presence(const alox_EspNowSlavePresence *presence,
|
||||
|
||||
esp_now_core_ensure_peer(mac);
|
||||
|
||||
uint32_t assigned_id = 0;
|
||||
esp_err_t err =
|
||||
client_registry_assign_id(mac, presence->slave_id, &assigned_id);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "id assign failed: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_new = false;
|
||||
bool reactivated = false;
|
||||
esp_err_t err = client_registry_heartbeat(
|
||||
mac, presence->slave_id, presence->version, presence->used, &is_new,
|
||||
&reactivated);
|
||||
err = client_registry_heartbeat(mac, assigned_id, presence->version,
|
||||
presence->used, &is_new, &reactivated);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "client registry full");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send ASSIGN when new/rejoined, or when slave has not echoed the id yet.
|
||||
* Rate-limit mismatch resends so failed assigns do not flood ESP-NOW buffers
|
||||
* (ESP_ERR_ESPNOW_NO_MEM) and block other unicast traffic.
|
||||
*/
|
||||
const bool needs_assign =
|
||||
is_new || reactivated || presence->slave_id != assigned_id;
|
||||
if (needs_assign) {
|
||||
const client_info_t *client = client_registry_find_by_mac(mac);
|
||||
const bool due =
|
||||
is_new || reactivated || client == NULL ||
|
||||
client->last_assign_sent_at == 0 ||
|
||||
client_registry_ms_since(client->last_assign_sent_at) >=
|
||||
ESPNOW_ASSIGN_RESEND_MS;
|
||||
if (due) {
|
||||
if (esp_now_comm_send_assign_id(mac, assigned_id) == ESP_OK) {
|
||||
client_registry_mark_assign_sent(mac);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(mac, mac_str, sizeof(mac_str));
|
||||
if (is_new) {
|
||||
ESP_LOGI(TAG, "client registered id=%lu mac=%s ver=%lu",
|
||||
(unsigned long)presence->slave_id, mac_str,
|
||||
(unsigned long)presence->version);
|
||||
ESP_LOGI(TAG, "client registered id=%lu (preferred=%lu) mac=%s ver=%lu",
|
||||
(unsigned long)assigned_id, (unsigned long)presence->slave_id,
|
||||
mac_str, (unsigned long)presence->version);
|
||||
} else if (reactivated) {
|
||||
ESP_LOGI(TAG, "client reconnected id=%lu mac=%s",
|
||||
(unsigned long)presence->slave_id, mac_str);
|
||||
(unsigned long)assigned_id, mac_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+132
-36
@@ -37,6 +37,11 @@ static bool s_tap_notify_double;
|
||||
static bool s_tap_notify_triple;
|
||||
static uint8_t s_master_mac[ESP_NOW_ETH_ALEN];
|
||||
static uint32_t s_last_discover_ms;
|
||||
/** Logical id assigned by current/last master (0 = unbound). */
|
||||
static uint32_t s_assigned_id;
|
||||
/** Master MAC from NVS binding (may differ from current s_master_mac). */
|
||||
static uint8_t s_bound_master_mac[ESP_NOW_ETH_ALEN];
|
||||
static bool s_has_binding;
|
||||
|
||||
typedef enum {
|
||||
SLAVE_TX_SLAVE_INFO = 1,
|
||||
@@ -45,15 +50,91 @@ typedef enum {
|
||||
|
||||
static QueueHandle_t s_tx_queue;
|
||||
|
||||
static void queue_tx(slave_tx_op_t op);
|
||||
|
||||
static bool from_joined_master(const uint8_t *master_mac) {
|
||||
return s_joined && esp_now_core_mac_equal(master_mac, s_master_mac);
|
||||
}
|
||||
|
||||
static uint32_t my_client_id(void) { return s_assigned_id; }
|
||||
|
||||
/** True when command targets this slave (0 = broadcast / any). */
|
||||
static bool client_id_matches(uint32_t client_id) {
|
||||
if (client_id == 0) {
|
||||
return true;
|
||||
}
|
||||
return s_assigned_id != 0 && client_id == s_assigned_id;
|
||||
}
|
||||
|
||||
static uint32_t preferred_id_for_master(void) {
|
||||
if (!s_has_binding || s_assigned_id == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (!s_joined || !esp_now_core_mac_equal(s_master_mac, s_bound_master_mac)) {
|
||||
return 0;
|
||||
}
|
||||
return s_assigned_id;
|
||||
}
|
||||
|
||||
static void load_id_binding(void) {
|
||||
uint8_t mac[ESP_NOW_ETH_ALEN];
|
||||
uint32_t id = 0;
|
||||
s_has_binding = pod_settings_load_id_binding(mac, &id);
|
||||
if (s_has_binding) {
|
||||
memcpy(s_bound_master_mac, mac, ESP_NOW_ETH_ALEN);
|
||||
s_assigned_id = id;
|
||||
ESP_LOGI(TAG, "loaded id binding id=%lu master=%02x:…:%02x",
|
||||
(unsigned long)id, mac[0], mac[5]);
|
||||
} else {
|
||||
memset(s_bound_master_mac, 0, sizeof(s_bound_master_mac));
|
||||
s_assigned_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_assigned_id(uint32_t assigned_id) {
|
||||
if (assigned_id < POD_CLIENT_ID_MIN || assigned_id > POD_CLIENT_ID_MAX) {
|
||||
ESP_LOGW(TAG, "ignore invalid assigned_id=%lu", (unsigned long)assigned_id);
|
||||
return;
|
||||
}
|
||||
const bool same =
|
||||
s_has_binding && s_assigned_id == assigned_id &&
|
||||
esp_now_core_mac_equal(s_bound_master_mac, s_master_mac);
|
||||
s_assigned_id = assigned_id;
|
||||
memcpy(s_bound_master_mac, s_master_mac, ESP_NOW_ETH_ALEN);
|
||||
s_has_binding = true;
|
||||
if (same) {
|
||||
return;
|
||||
}
|
||||
if (pod_settings_save_id_binding(s_master_mac, assigned_id) != ESP_OK) {
|
||||
ESP_LOGW(TAG, "assigned id=%lu but NVS save failed",
|
||||
(unsigned long)assigned_id);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "assigned id=%lu (persisted)", (unsigned long)assigned_id);
|
||||
}
|
||||
queue_tx(SLAVE_TX_BATTERY);
|
||||
}
|
||||
|
||||
static void clear_id_binding_local(void) {
|
||||
s_assigned_id = 0;
|
||||
s_has_binding = false;
|
||||
memset(s_bound_master_mac, 0, sizeof(s_bound_master_mac));
|
||||
(void)pod_settings_clear_id_binding();
|
||||
ESP_LOGI(TAG, "cleared local id binding");
|
||||
}
|
||||
|
||||
esp_err_t esp_now_comm_clear_local_id_binding(void) {
|
||||
if (esp_now_core_is_master()) {
|
||||
return ESP_OK;
|
||||
}
|
||||
clear_id_binding_local();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void fill_presence(alox_EspNowSlavePresence *presence) {
|
||||
const uint8_t *own = esp_now_core_own_mac();
|
||||
presence->network = esp_now_core_network();
|
||||
presence->version = POWERPOD_FW_VERSION;
|
||||
presence->slave_id = own[5];
|
||||
presence->slave_id = preferred_id_for_master();
|
||||
presence->available = true;
|
||||
presence->used = false;
|
||||
esp_now_proto_setup_presence_encode(presence, own);
|
||||
@@ -139,7 +220,7 @@ static void queue_tx(slave_tx_op_t op) {
|
||||
}
|
||||
|
||||
static void send_battery_to_master(void) {
|
||||
if (!s_joined) {
|
||||
if (!s_joined || s_assigned_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -147,7 +228,7 @@ static void send_battery_to_master(void) {
|
||||
board_input_read_lipo(&reading);
|
||||
|
||||
alox_EspNowBatteryReport report = alox_EspNowBatteryReport_init_zero;
|
||||
report.client_id = esp_now_core_own_mac()[5];
|
||||
report.client_id = s_assigned_id;
|
||||
report.lipo1_valid = reading.lipo1_valid;
|
||||
report.lipo2_valid = reading.lipo2_valid;
|
||||
report.lipo1_mv = reading.lipo1_mv;
|
||||
@@ -261,10 +342,7 @@ static void handle_echo_ping(const uint8_t *master_mac,
|
||||
|
||||
static void handle_restart(const uint8_t *master_mac,
|
||||
const alox_EspNowRestart *req) {
|
||||
const uint8_t *own = esp_now_core_own_mac();
|
||||
uint32_t my_id = own[5];
|
||||
|
||||
if (req->client_id != 0 && req->client_id != my_id) {
|
||||
if (!client_id_matches(req->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -273,14 +351,14 @@ static void handle_restart(const uint8_t *master_mac,
|
||||
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(master_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG, "RESTART from master %s (id=%lu)", mac_str, (unsigned long)my_id);
|
||||
ESP_LOGI(TAG, "RESTART from master %s (id=%lu)", mac_str,
|
||||
(unsigned long)my_client_id());
|
||||
pod_schedule_restart();
|
||||
}
|
||||
|
||||
static void handle_battery_query(const uint8_t *master_mac,
|
||||
const alox_EspNowBatteryQuery *query) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (query->client_id != 0 && query->client_id != my_id) {
|
||||
if (!client_id_matches(query->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -291,8 +369,7 @@ static void handle_battery_query(const uint8_t *master_mac,
|
||||
|
||||
static void handle_led_ring(const uint8_t *master_mac,
|
||||
const alox_EspNowLedRing *msg) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (msg->client_id != 0 && msg->client_id != my_id) {
|
||||
if (!client_id_matches(msg->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -313,13 +390,12 @@ static void handle_led_ring(const uint8_t *master_mac,
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(master_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG, "LED_RING mode %lu from master %s (id=%lu)",
|
||||
(unsigned long)req.mode, mac_str, (unsigned long)my_id);
|
||||
(unsigned long)req.mode, mac_str, (unsigned long)my_client_id());
|
||||
cmd_led_ring_apply(&req);
|
||||
}
|
||||
|
||||
static void handle_find_me(const uint8_t *master_mac, const alox_EspNowFindMe *req) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (req->client_id != 0 && req->client_id != my_id) {
|
||||
if (!client_id_matches(req->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -327,14 +403,14 @@ static void handle_find_me(const uint8_t *master_mac, const alox_EspNowFindMe *r
|
||||
}
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(master_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG, "FIND_ME from master %s (id=%lu)", mac_str, (unsigned long)my_id);
|
||||
ESP_LOGI(TAG, "FIND_ME from master %s (id=%lu)", mac_str,
|
||||
(unsigned long)my_client_id());
|
||||
led_ring_find_me();
|
||||
}
|
||||
|
||||
static void handle_accel_stream(const uint8_t *master_mac,
|
||||
const alox_EspNowAccelStream *cfg) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (cfg->client_id != 0 && cfg->client_id != my_id) {
|
||||
if (!client_id_matches(cfg->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -344,13 +420,12 @@ static void handle_accel_stream(const uint8_t *master_mac,
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(master_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG, "accel stream %s from master %s (id=%lu)",
|
||||
cfg->enable ? "on" : "off", mac_str, (unsigned long)my_id);
|
||||
cfg->enable ? "on" : "off", mac_str, (unsigned long)my_client_id());
|
||||
}
|
||||
|
||||
static void handle_tap_notify(const uint8_t *master_mac,
|
||||
const alox_EspNowTapNotify *cfg) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (cfg->client_id != 0 && cfg->client_id != my_id) {
|
||||
if (!client_id_matches(cfg->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -364,13 +439,12 @@ static void handle_tap_notify(const uint8_t *master_mac,
|
||||
ESP_LOGI(TAG,
|
||||
"tap notify single=%d double=%d triple=%d from master %s (id=%lu)",
|
||||
cfg->single, cfg->double_tap, cfg->triple, mac_str,
|
||||
(unsigned long)my_id);
|
||||
(unsigned long)my_client_id());
|
||||
}
|
||||
|
||||
static void handle_accel_deadzone(const uint8_t *master_mac,
|
||||
const alox_EspNowAccelDeadzone *cfg) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (cfg->client_id != 0 && cfg->client_id != my_id) {
|
||||
if (!client_id_matches(cfg->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -380,7 +454,7 @@ static void handle_accel_deadzone(const uint8_t *master_mac,
|
||||
esp_now_core_mac_to_str(master_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG,
|
||||
"accel deadzone from master %s: %lu LSB id=%lu (sensor %s)", mac_str,
|
||||
(unsigned long)cfg->deadzone, (unsigned long)my_id,
|
||||
(unsigned long)cfg->deadzone, (unsigned long)my_client_id(),
|
||||
bma456_is_ready() ? "ok" : "not installed");
|
||||
bma456_set_accel_deadzone(cfg->deadzone);
|
||||
if (pod_settings_save_accel_deadzone(cfg->deadzone) != ESP_OK) {
|
||||
@@ -391,8 +465,7 @@ static void handle_accel_deadzone(const uint8_t *master_mac,
|
||||
|
||||
static void handle_tap_config(const uint8_t *master_mac,
|
||||
const alox_EspNowTapConfig *msg) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (msg->client_id != 0 && msg->client_id != my_id) {
|
||||
if (!client_id_matches(msg->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -415,7 +488,7 @@ static void handle_tap_config(const uint8_t *master_mac,
|
||||
esp_now_core_mac_to_str(master_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG,
|
||||
"tap config from master %s: sens=%u id=%lu (sensor %s)", mac_str,
|
||||
(unsigned)cfg.tap_sens_thres, (unsigned long)my_id,
|
||||
(unsigned)cfg.tap_sens_thres, (unsigned long)my_client_id(),
|
||||
bma456_is_ready() ? "ok" : "not installed");
|
||||
|
||||
if (!bma456_tap_config_valid(&cfg)) {
|
||||
@@ -431,6 +504,21 @@ static void handle_tap_config(const uint8_t *master_mac,
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_assign_id(const uint8_t *master_mac,
|
||||
const alox_EspNowAssignId *msg) {
|
||||
if (!from_joined_master(master_mac)) {
|
||||
return;
|
||||
}
|
||||
apply_assigned_id(msg->assigned_id);
|
||||
}
|
||||
|
||||
static void handle_clear_id_binding(const uint8_t *master_mac) {
|
||||
if (!from_joined_master(master_mac)) {
|
||||
return;
|
||||
}
|
||||
clear_id_binding_local();
|
||||
}
|
||||
|
||||
static void handle_discover(const uint8_t *sender_mac,
|
||||
const alox_EspNowDiscover *discover) {
|
||||
if (discover->network != esp_now_core_network()) {
|
||||
@@ -464,11 +552,12 @@ static void handle_discover(const uint8_t *sender_mac,
|
||||
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(sender_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG, "joined network %u, master %s", (unsigned)discover->network,
|
||||
mac_str);
|
||||
ESP_LOGI(TAG, "joined network %u, master %s (preferred_id=%lu)",
|
||||
(unsigned)discover->network, mac_str,
|
||||
(unsigned long)preferred_id_for_master());
|
||||
|
||||
queue_tx(SLAVE_TX_SLAVE_INFO);
|
||||
queue_tx(SLAVE_TX_BATTERY);
|
||||
/* Battery after ASSIGN_ID so reports carry the definitive client_id. */
|
||||
}
|
||||
|
||||
static void check_master_timeout(void) {
|
||||
@@ -489,14 +578,14 @@ static void check_master_timeout(void) {
|
||||
|
||||
static void accel_stream_task(void *param) {
|
||||
(void)param;
|
||||
const uint8_t *own = esp_now_core_own_mac();
|
||||
|
||||
ESP_LOGI(TAG, "accel stream task (interval %u ms)",
|
||||
(unsigned)ESPNOW_ACCEL_INTERVAL_MS);
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(ESPNOW_ACCEL_INTERVAL_MS));
|
||||
if (!s_joined || !s_accel_stream_enabled || !bma456_is_ready()) {
|
||||
if (!s_joined || s_assigned_id == 0 || !s_accel_stream_enabled ||
|
||||
!bma456_is_ready()) {
|
||||
continue;
|
||||
}
|
||||
int16_t x = 0;
|
||||
@@ -505,13 +594,13 @@ static void accel_stream_task(void *param) {
|
||||
if (bma456_read_accel(&x, &y, &z) != ESP_OK) {
|
||||
continue;
|
||||
}
|
||||
(void)send_accel_sample(s_master_mac, own[5], x, y, z);
|
||||
(void)send_accel_sample(s_master_mac, s_assigned_id, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_bma456_tap(bma456_tap_kind_t kind, void *ctx) {
|
||||
(void)ctx;
|
||||
if (!s_joined) {
|
||||
if (!s_joined || s_assigned_id == 0) {
|
||||
return;
|
||||
}
|
||||
bool enabled = false;
|
||||
@@ -531,7 +620,7 @@ static void on_bma456_tap(bma456_tap_kind_t kind, void *ctx) {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
(void)send_tap_event(s_master_mac, esp_now_core_own_mac()[5], (uint32_t)kind);
|
||||
(void)send_tap_event(s_master_mac, s_assigned_id, (uint32_t)kind);
|
||||
}
|
||||
|
||||
static void heartbeat_task(void *param) {
|
||||
@@ -654,6 +743,12 @@ void esp_now_slave_on_recv(const esp_now_recv_info_t *info, const uint8_t *data,
|
||||
handle_restart(info->src_addr, &msg.payload.restart);
|
||||
}
|
||||
break;
|
||||
case alox_EspNowMessage_assign_id_tag:
|
||||
handle_assign_id(info->src_addr, &msg.payload.assign_id);
|
||||
break;
|
||||
case alox_EspNowMessage_clear_id_binding_tag:
|
||||
handle_clear_id_binding(info->src_addr);
|
||||
break;
|
||||
case alox_EspNowMessage_ota_start_tag:
|
||||
if (from_joined_master(info->src_addr)) {
|
||||
ota_espnow_slave_on_start(info->src_addr, &msg.payload.ota_start);
|
||||
@@ -668,6 +763,7 @@ void esp_now_slave_on_recv(const esp_now_recv_info_t *info, const uint8_t *data,
|
||||
|
||||
esp_err_t esp_now_slave_start(void) {
|
||||
reset_join();
|
||||
load_id_binding();
|
||||
|
||||
s_tx_queue = xQueueCreate(4, sizeof(slave_tx_op_t));
|
||||
if (s_tx_queue == NULL) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -35,4 +35,36 @@ bool pod_settings_is_uv_latched(void);
|
||||
esp_err_t pod_settings_set_uv_latched(bool latched);
|
||||
esp_err_t pod_settings_clear_uv_latched(void);
|
||||
|
||||
/** Logical slave ids: 1…POD_CLIENT_ID_MAX (0 reserved for local master). */
|
||||
#define POD_CLIENT_ID_MIN 1u
|
||||
#define POD_CLIENT_ID_MAX 255u
|
||||
#define POD_ID_MAP_MAX 255u
|
||||
#define POD_ID_MAC_LEN 6u
|
||||
|
||||
typedef struct {
|
||||
uint8_t mac[POD_ID_MAC_LEN];
|
||||
uint8_t id;
|
||||
} pod_id_map_entry_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t next_id; /**< Next candidate when allocating (1…255). */
|
||||
uint8_t count;
|
||||
pod_id_map_entry_t entries[POD_ID_MAP_MAX];
|
||||
} pod_id_map_t;
|
||||
|
||||
/** Master: load MAC→id map from NVS (zeros *out if unset). */
|
||||
esp_err_t pod_settings_load_id_map(pod_id_map_t *out);
|
||||
esp_err_t pod_settings_save_id_map(const pod_id_map_t *map);
|
||||
esp_err_t pod_settings_clear_id_map(void);
|
||||
|
||||
/**
|
||||
* Slave: load persisted {master_mac, assigned_id}.
|
||||
* Returns true when a valid binding was loaded.
|
||||
*/
|
||||
bool pod_settings_load_id_binding(uint8_t master_mac[POD_ID_MAC_LEN],
|
||||
uint32_t *assigned_id_out);
|
||||
esp_err_t pod_settings_save_id_binding(const uint8_t master_mac[POD_ID_MAC_LEN],
|
||||
uint32_t assigned_id);
|
||||
esp_err_t pod_settings_clear_id_binding(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "cmd_espnow_echo_ping.h"
|
||||
#include "cmd_espnow_find_me.h"
|
||||
#include "cmd_restart.h"
|
||||
#include "cmd_client_id_map_reset.h"
|
||||
#include "cmd_client_info.h"
|
||||
#include "cmd_version.h"
|
||||
#include "cmd_ota.h"
|
||||
@@ -226,6 +227,7 @@ void app_main(void) {
|
||||
cmd_espnow_echo_ping_register();
|
||||
cmd_espnow_find_me_register();
|
||||
cmd_restart_register();
|
||||
cmd_client_id_map_reset_register();
|
||||
cmd_led_ring_register();
|
||||
cmd_battery_register();
|
||||
cmd_ota_register();
|
||||
|
||||
@@ -27,6 +27,12 @@ PB_BIND(alox_EspNowDiscover, alox_EspNowDiscover, AUTO)
|
||||
PB_BIND(alox_EspNowSlavePresence, alox_EspNowSlavePresence, AUTO)
|
||||
|
||||
|
||||
PB_BIND(alox_EspNowAssignId, alox_EspNowAssignId, AUTO)
|
||||
|
||||
|
||||
PB_BIND(alox_EspNowClearIdBinding, alox_EspNowClearIdBinding, AUTO)
|
||||
|
||||
|
||||
PB_BIND(alox_EspNowAccelDeadzone, alox_EspNowAccelDeadzone, AUTO)
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,11 @@ typedef enum _alox_EspNowMessageType {
|
||||
alox_EspNowMessageType_ESPNOW_TAP_EVENT = 18,
|
||||
alox_EspNowMessageType_ESPNOW_ECHO_PING = 19,
|
||||
alox_EspNowMessageType_ESPNOW_ECHO_PONG = 20,
|
||||
alox_EspNowMessageType_ESPNOW_SET_TAP_CONFIG = 21
|
||||
alox_EspNowMessageType_ESPNOW_SET_TAP_CONFIG = 21,
|
||||
/* * Master → slave: assigned logical client id after join/presence. */
|
||||
alox_EspNowMessageType_ESPNOW_ASSIGN_ID = 22,
|
||||
/* * Master → slave: forget persisted master/id binding (debug reset). */
|
||||
alox_EspNowMessageType_ESPNOW_CLEAR_ID_BINDING = 23
|
||||
} alox_EspNowMessageType;
|
||||
|
||||
/* Struct definitions */
|
||||
@@ -74,11 +78,22 @@ typedef struct _alox_EspNowSlavePresence {
|
||||
uint32_t network;
|
||||
pb_callback_t mac;
|
||||
uint32_t version;
|
||||
/* * Preferred id from NVS for this master (0 = none / unbound). */
|
||||
uint32_t slave_id;
|
||||
bool available;
|
||||
bool used;
|
||||
} alox_EspNowSlavePresence;
|
||||
|
||||
/* * Master → slave: definitive logical client id for this session/master. */
|
||||
typedef struct _alox_EspNowAssignId {
|
||||
uint32_t assigned_id;
|
||||
} alox_EspNowAssignId;
|
||||
|
||||
/* * Master → slave: clear persisted {master_mac, assigned_id} binding. */
|
||||
typedef struct _alox_EspNowClearIdBinding {
|
||||
char dummy_field;
|
||||
} alox_EspNowClearIdBinding;
|
||||
|
||||
typedef struct _alox_EspNowAccelDeadzone {
|
||||
uint32_t deadzone;
|
||||
uint32_t client_id; /* 0 = all slaves; otherwise only matching slave_id applies */
|
||||
@@ -204,6 +219,8 @@ typedef struct _alox_EspNowMessage {
|
||||
alox_EspNowEchoPing echo_ping;
|
||||
alox_EspNowEchoPong echo_pong;
|
||||
alox_EspNowTapConfig tap_config;
|
||||
alox_EspNowAssignId assign_id;
|
||||
alox_EspNowClearIdBinding clear_id_binding;
|
||||
} payload;
|
||||
} alox_EspNowMessage;
|
||||
|
||||
@@ -214,8 +231,10 @@ extern "C" {
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _alox_EspNowMessageType_MIN alox_EspNowMessageType_ESPNOW_UNKNOWN
|
||||
#define _alox_EspNowMessageType_MAX alox_EspNowMessageType_ESPNOW_SET_TAP_CONFIG
|
||||
#define _alox_EspNowMessageType_ARRAYSIZE ((alox_EspNowMessageType)(alox_EspNowMessageType_ESPNOW_SET_TAP_CONFIG+1))
|
||||
#define _alox_EspNowMessageType_MAX alox_EspNowMessageType_ESPNOW_CLEAR_ID_BINDING
|
||||
#define _alox_EspNowMessageType_ARRAYSIZE ((alox_EspNowMessageType)(alox_EspNowMessageType_ESPNOW_CLEAR_ID_BINDING+1))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -248,6 +267,8 @@ extern "C" {
|
||||
#define alox_EspNowRestart_init_default {0}
|
||||
#define alox_EspNowDiscover_init_default {0, 0}
|
||||
#define alox_EspNowSlavePresence_init_default {0, {{NULL}, NULL}, 0, 0, 0, 0}
|
||||
#define alox_EspNowAssignId_init_default {0}
|
||||
#define alox_EspNowClearIdBinding_init_default {0}
|
||||
#define alox_EspNowAccelDeadzone_init_default {0, 0}
|
||||
#define alox_EspNowAccelStream_init_default {0, 0}
|
||||
#define alox_EspNowAccelSample_init_default {0, 0, 0, 0}
|
||||
@@ -269,6 +290,8 @@ extern "C" {
|
||||
#define alox_EspNowRestart_init_zero {0}
|
||||
#define alox_EspNowDiscover_init_zero {0, 0}
|
||||
#define alox_EspNowSlavePresence_init_zero {0, {{NULL}, NULL}, 0, 0, 0, 0}
|
||||
#define alox_EspNowAssignId_init_zero {0}
|
||||
#define alox_EspNowClearIdBinding_init_zero {0}
|
||||
#define alox_EspNowAccelDeadzone_init_zero {0, 0}
|
||||
#define alox_EspNowAccelStream_init_zero {0, 0}
|
||||
#define alox_EspNowAccelSample_init_zero {0, 0, 0, 0}
|
||||
@@ -300,6 +323,7 @@ extern "C" {
|
||||
#define alox_EspNowSlavePresence_slave_id_tag 4
|
||||
#define alox_EspNowSlavePresence_available_tag 5
|
||||
#define alox_EspNowSlavePresence_used_tag 6
|
||||
#define alox_EspNowAssignId_assigned_id_tag 1
|
||||
#define alox_EspNowAccelDeadzone_deadzone_tag 1
|
||||
#define alox_EspNowAccelDeadzone_client_id_tag 2
|
||||
#define alox_EspNowAccelStream_enable_tag 1
|
||||
@@ -368,6 +392,8 @@ extern "C" {
|
||||
#define alox_EspNowMessage_echo_ping_tag 20
|
||||
#define alox_EspNowMessage_echo_pong_tag 21
|
||||
#define alox_EspNowMessage_tap_config_tag 22
|
||||
#define alox_EspNowMessage_assign_id_tag 23
|
||||
#define alox_EspNowMessage_clear_id_binding_tag 24
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define alox_EspNowUnicastTest_FIELDLIST(X, a) \
|
||||
@@ -413,6 +439,16 @@ X(a, STATIC, SINGULAR, BOOL, used, 6)
|
||||
#define alox_EspNowSlavePresence_CALLBACK pb_default_field_callback
|
||||
#define alox_EspNowSlavePresence_DEFAULT NULL
|
||||
|
||||
#define alox_EspNowAssignId_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, assigned_id, 1)
|
||||
#define alox_EspNowAssignId_CALLBACK NULL
|
||||
#define alox_EspNowAssignId_DEFAULT NULL
|
||||
|
||||
#define alox_EspNowClearIdBinding_FIELDLIST(X, a) \
|
||||
|
||||
#define alox_EspNowClearIdBinding_CALLBACK NULL
|
||||
#define alox_EspNowClearIdBinding_DEFAULT NULL
|
||||
|
||||
#define alox_EspNowAccelDeadzone_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, deadzone, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, client_id, 2)
|
||||
@@ -534,7 +570,9 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,tap_notify,payload.tap_notify), 18)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,tap_event,payload.tap_event), 19) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,echo_ping,payload.echo_ping), 20) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,echo_pong,payload.echo_pong), 21) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,tap_config,payload.tap_config), 22)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,tap_config,payload.tap_config), 22) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,assign_id,payload.assign_id), 23) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,clear_id_binding,payload.clear_id_binding), 24)
|
||||
#define alox_EspNowMessage_CALLBACK NULL
|
||||
#define alox_EspNowMessage_DEFAULT NULL
|
||||
#define alox_EspNowMessage_payload_discover_MSGTYPE alox_EspNowDiscover
|
||||
@@ -558,6 +596,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,tap_config,payload.tap_config), 22)
|
||||
#define alox_EspNowMessage_payload_echo_ping_MSGTYPE alox_EspNowEchoPing
|
||||
#define alox_EspNowMessage_payload_echo_pong_MSGTYPE alox_EspNowEchoPong
|
||||
#define alox_EspNowMessage_payload_tap_config_MSGTYPE alox_EspNowTapConfig
|
||||
#define alox_EspNowMessage_payload_assign_id_MSGTYPE alox_EspNowAssignId
|
||||
#define alox_EspNowMessage_payload_clear_id_binding_MSGTYPE alox_EspNowClearIdBinding
|
||||
|
||||
extern const pb_msgdesc_t alox_EspNowUnicastTest_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowEchoPing_msg;
|
||||
@@ -566,6 +606,8 @@ extern const pb_msgdesc_t alox_EspNowFindMe_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowRestart_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowDiscover_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowSlavePresence_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowAssignId_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowClearIdBinding_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowAccelDeadzone_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowAccelStream_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowAccelSample_msg;
|
||||
@@ -589,6 +631,8 @@ extern const pb_msgdesc_t alox_EspNowMessage_msg;
|
||||
#define alox_EspNowRestart_fields &alox_EspNowRestart_msg
|
||||
#define alox_EspNowDiscover_fields &alox_EspNowDiscover_msg
|
||||
#define alox_EspNowSlavePresence_fields &alox_EspNowSlavePresence_msg
|
||||
#define alox_EspNowAssignId_fields &alox_EspNowAssignId_msg
|
||||
#define alox_EspNowClearIdBinding_fields &alox_EspNowClearIdBinding_msg
|
||||
#define alox_EspNowAccelDeadzone_fields &alox_EspNowAccelDeadzone_msg
|
||||
#define alox_EspNowAccelStream_fields &alox_EspNowAccelStream_msg
|
||||
#define alox_EspNowAccelSample_fields &alox_EspNowAccelSample_msg
|
||||
@@ -611,8 +655,10 @@ extern const pb_msgdesc_t alox_EspNowMessage_msg;
|
||||
#define alox_EspNowAccelDeadzone_size 12
|
||||
#define alox_EspNowAccelSample_size 24
|
||||
#define alox_EspNowAccelStream_size 8
|
||||
#define alox_EspNowAssignId_size 6
|
||||
#define alox_EspNowBatteryQuery_size 6
|
||||
#define alox_EspNowBatteryReport_size 22
|
||||
#define alox_EspNowClearIdBinding_size 0
|
||||
#define alox_EspNowDiscover_size 8
|
||||
#define alox_EspNowEchoPing_size 22
|
||||
#define alox_EspNowEchoPong_size 22
|
||||
|
||||
@@ -27,6 +27,10 @@ enum EspNowMessageType {
|
||||
ESPNOW_ECHO_PING = 19;
|
||||
ESPNOW_ECHO_PONG = 20;
|
||||
ESPNOW_SET_TAP_CONFIG = 21;
|
||||
/** Master → slave: assigned logical client id after join/presence. */
|
||||
ESPNOW_ASSIGN_ID = 22;
|
||||
/** Master → slave: forget persisted master/id binding (debug reset). */
|
||||
ESPNOW_CLEAR_ID_BINDING = 23;
|
||||
}
|
||||
|
||||
message EspNowUnicastTest {
|
||||
@@ -67,11 +71,20 @@ message EspNowSlavePresence {
|
||||
uint32 network = 1;
|
||||
bytes mac = 2;
|
||||
uint32 version = 3;
|
||||
/** Preferred id from NVS for this master (0 = none / unbound). */
|
||||
uint32 slave_id = 4;
|
||||
bool available = 5;
|
||||
bool used = 6;
|
||||
}
|
||||
|
||||
/** Master → slave: definitive logical client id for this session/master. */
|
||||
message EspNowAssignId {
|
||||
uint32 assigned_id = 1;
|
||||
}
|
||||
|
||||
/** Master → slave: clear persisted {master_mac, assigned_id} binding. */
|
||||
message EspNowClearIdBinding {}
|
||||
|
||||
message EspNowAccelDeadzone {
|
||||
uint32 deadzone = 1;
|
||||
uint32 client_id = 2; // 0 = all slaves; otherwise only matching slave_id applies
|
||||
@@ -193,5 +206,7 @@ message EspNowMessage {
|
||||
EspNowEchoPing echo_ping = 20;
|
||||
EspNowEchoPong echo_pong = 21;
|
||||
EspNowTapConfig tap_config = 22;
|
||||
EspNowAssignId assign_id = 23;
|
||||
EspNowClearIdBinding clear_id_binding = 24;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,12 @@ PB_BIND(alox_RestartRequest, alox_RestartRequest, AUTO)
|
||||
PB_BIND(alox_RestartResponse, alox_RestartResponse, AUTO)
|
||||
|
||||
|
||||
PB_BIND(alox_ClientIdMapResetRequest, alox_ClientIdMapResetRequest, AUTO)
|
||||
|
||||
|
||||
PB_BIND(alox_ClientIdMapResetResponse, alox_ClientIdMapResetResponse, AUTO)
|
||||
|
||||
|
||||
PB_BIND(alox_SetLogLevelRequest, alox_SetLogLevelRequest, AUTO)
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,11 @@ typedef enum _alox_MessageType {
|
||||
/* * Host → master: get/set BMA456H multitap detection parameters. */
|
||||
alox_MessageType_TAP_CONFIG = 32,
|
||||
/* * Master → host (BLE only): periodic pushed cache snapshot with ESP timestamp. */
|
||||
alox_MessageType_BLE_STREAM_TICK = 33
|
||||
alox_MessageType_BLE_STREAM_TICK = 33,
|
||||
/* *
|
||||
Host → device: clear master client-id NVS map + live registry (and notify
|
||||
online slaves), or on a slave clear local master/id binding only. */
|
||||
alox_MessageType_CLIENT_ID_MAP_RESET = 34
|
||||
} alox_MessageType;
|
||||
|
||||
typedef enum _alox_TapKind {
|
||||
@@ -323,6 +327,15 @@ typedef struct _alox_RestartResponse {
|
||||
uint32_t client_id;
|
||||
} alox_RestartResponse;
|
||||
|
||||
/* * Host → master or slave: clear persisted client-id assignment state. */
|
||||
typedef struct _alox_ClientIdMapResetRequest {
|
||||
char dummy_field;
|
||||
} alox_ClientIdMapResetRequest;
|
||||
|
||||
typedef struct _alox_ClientIdMapResetResponse {
|
||||
bool success;
|
||||
} alox_ClientIdMapResetResponse;
|
||||
|
||||
/* * Host → master: read/write global log level (esp_log_level_set("*", …)). */
|
||||
typedef struct _alox_SetLogLevelRequest {
|
||||
bool write;
|
||||
@@ -456,6 +469,8 @@ typedef struct _alox_UartMessage {
|
||||
alox_TapConfigRequest tap_config_request;
|
||||
alox_TapConfigResponse tap_config_response;
|
||||
alox_BleStreamTick ble_stream_tick;
|
||||
alox_ClientIdMapResetRequest client_id_map_reset_request;
|
||||
alox_ClientIdMapResetResponse client_id_map_reset_response;
|
||||
} payload;
|
||||
} alox_UartMessage;
|
||||
|
||||
@@ -466,8 +481,8 @@ extern "C" {
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _alox_MessageType_MIN alox_MessageType_UNKNOWN
|
||||
#define _alox_MessageType_MAX alox_MessageType_BLE_STREAM_TICK
|
||||
#define _alox_MessageType_ARRAYSIZE ((alox_MessageType)(alox_MessageType_BLE_STREAM_TICK+1))
|
||||
#define _alox_MessageType_MAX alox_MessageType_CLIENT_ID_MAP_RESET
|
||||
#define _alox_MessageType_ARRAYSIZE ((alox_MessageType)(alox_MessageType_CLIENT_ID_MAP_RESET+1))
|
||||
|
||||
#define _alox_TapKind_MIN alox_TapKind_TAP_NONE
|
||||
#define _alox_TapKind_MAX alox_TapKind_TAP_TRIPLE
|
||||
@@ -521,6 +536,8 @@ extern "C" {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -561,6 +578,8 @@ extern "C" {
|
||||
#define alox_EspNowFindMeResponse_init_default {0, 0}
|
||||
#define alox_RestartRequest_init_default {0}
|
||||
#define alox_RestartResponse_init_default {0, 0}
|
||||
#define alox_ClientIdMapResetRequest_init_default {0}
|
||||
#define alox_ClientIdMapResetResponse_init_default {0}
|
||||
#define alox_SetLogLevelRequest_init_default {0, 0}
|
||||
#define alox_SetLogLevelResponse_init_default {0, 0}
|
||||
#define alox_TapConfigRequest_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
@@ -608,6 +627,8 @@ extern "C" {
|
||||
#define alox_EspNowFindMeResponse_init_zero {0, 0}
|
||||
#define alox_RestartRequest_init_zero {0}
|
||||
#define alox_RestartResponse_init_zero {0, 0}
|
||||
#define alox_ClientIdMapResetRequest_init_zero {0}
|
||||
#define alox_ClientIdMapResetResponse_init_zero {0}
|
||||
#define alox_SetLogLevelRequest_init_zero {0, 0}
|
||||
#define alox_SetLogLevelResponse_init_zero {0, 0}
|
||||
#define alox_TapConfigRequest_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
@@ -739,6 +760,7 @@ extern "C" {
|
||||
#define alox_RestartRequest_client_id_tag 1
|
||||
#define alox_RestartResponse_success_tag 1
|
||||
#define alox_RestartResponse_client_id_tag 2
|
||||
#define alox_ClientIdMapResetResponse_success_tag 1
|
||||
#define alox_SetLogLevelRequest_write_tag 1
|
||||
#define alox_SetLogLevelRequest_level_tag 2
|
||||
#define alox_SetLogLevelResponse_success_tag 1
|
||||
@@ -822,6 +844,8 @@ extern "C" {
|
||||
#define alox_UartMessage_tap_config_request_tag 39
|
||||
#define alox_UartMessage_tap_config_response_tag 40
|
||||
#define alox_UartMessage_ble_stream_tick_tag 41
|
||||
#define alox_UartMessage_client_id_map_reset_request_tag 42
|
||||
#define alox_UartMessage_client_id_map_reset_response_tag 43
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define alox_UartMessage_FIELDLIST(X, a) \
|
||||
@@ -861,7 +885,9 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,set_log_level_request,payload.set_lo
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,set_log_level_response,payload.set_log_level_response), 38) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,tap_config_request,payload.tap_config_request), 39) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,tap_config_response,payload.tap_config_response), 40) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,ble_stream_tick,payload.ble_stream_tick), 41)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,ble_stream_tick,payload.ble_stream_tick), 41) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,client_id_map_reset_request,payload.client_id_map_reset_request), 42) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,client_id_map_reset_response,payload.client_id_map_reset_response), 43)
|
||||
#define alox_UartMessage_CALLBACK NULL
|
||||
#define alox_UartMessage_DEFAULT NULL
|
||||
#define alox_UartMessage_payload_ack_payload_MSGTYPE alox_Ack
|
||||
@@ -900,6 +926,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,ble_stream_tick,payload.ble_stream_t
|
||||
#define alox_UartMessage_payload_tap_config_request_MSGTYPE alox_TapConfigRequest
|
||||
#define alox_UartMessage_payload_tap_config_response_MSGTYPE alox_TapConfigResponse
|
||||
#define alox_UartMessage_payload_ble_stream_tick_MSGTYPE alox_BleStreamTick
|
||||
#define alox_UartMessage_payload_client_id_map_reset_request_MSGTYPE alox_ClientIdMapResetRequest
|
||||
#define alox_UartMessage_payload_client_id_map_reset_response_MSGTYPE alox_ClientIdMapResetResponse
|
||||
|
||||
#define alox_Ack_FIELDLIST(X, a) \
|
||||
|
||||
@@ -1170,6 +1198,16 @@ X(a, STATIC, SINGULAR, UINT32, client_id, 2)
|
||||
#define alox_RestartResponse_CALLBACK NULL
|
||||
#define alox_RestartResponse_DEFAULT NULL
|
||||
|
||||
#define alox_ClientIdMapResetRequest_FIELDLIST(X, a) \
|
||||
|
||||
#define alox_ClientIdMapResetRequest_CALLBACK NULL
|
||||
#define alox_ClientIdMapResetRequest_DEFAULT NULL
|
||||
|
||||
#define alox_ClientIdMapResetResponse_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, BOOL, success, 1)
|
||||
#define alox_ClientIdMapResetResponse_CALLBACK NULL
|
||||
#define alox_ClientIdMapResetResponse_DEFAULT NULL
|
||||
|
||||
#define alox_SetLogLevelRequest_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, BOOL, write, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, level, 2)
|
||||
@@ -1298,6 +1336,8 @@ extern const pb_msgdesc_t alox_EspNowFindMeRequest_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowFindMeResponse_msg;
|
||||
extern const pb_msgdesc_t alox_RestartRequest_msg;
|
||||
extern const pb_msgdesc_t alox_RestartResponse_msg;
|
||||
extern const pb_msgdesc_t alox_ClientIdMapResetRequest_msg;
|
||||
extern const pb_msgdesc_t alox_ClientIdMapResetResponse_msg;
|
||||
extern const pb_msgdesc_t alox_SetLogLevelRequest_msg;
|
||||
extern const pb_msgdesc_t alox_SetLogLevelResponse_msg;
|
||||
extern const pb_msgdesc_t alox_TapConfigRequest_msg;
|
||||
@@ -1347,6 +1387,8 @@ extern const pb_msgdesc_t alox_OtaSlaveProgressResponse_msg;
|
||||
#define alox_EspNowFindMeResponse_fields &alox_EspNowFindMeResponse_msg
|
||||
#define alox_RestartRequest_fields &alox_RestartRequest_msg
|
||||
#define alox_RestartResponse_fields &alox_RestartResponse_msg
|
||||
#define alox_ClientIdMapResetRequest_fields &alox_ClientIdMapResetRequest_msg
|
||||
#define alox_ClientIdMapResetResponse_fields &alox_ClientIdMapResetResponse_msg
|
||||
#define alox_SetLogLevelRequest_fields &alox_SetLogLevelRequest_msg
|
||||
#define alox_SetLogLevelResponse_fields &alox_SetLogLevelResponse_msg
|
||||
#define alox_TapConfigRequest_fields &alox_TapConfigRequest_msg
|
||||
@@ -1382,6 +1424,8 @@ extern const pb_msgdesc_t alox_OtaSlaveProgressResponse_msg;
|
||||
#define alox_CacheClientTap_size 8
|
||||
#define alox_CacheStatusRequest_size 0
|
||||
#define alox_CacheStatusResponse_size 736
|
||||
#define alox_ClientIdMapResetRequest_size 0
|
||||
#define alox_ClientIdMapResetResponse_size 2
|
||||
#define alox_ClientInput_size 22
|
||||
#define alox_EspNowEchoPingRequest_size 17
|
||||
#define alox_EspNowEchoPingResponse_size 25
|
||||
|
||||
@@ -37,6 +37,11 @@ enum MessageType {
|
||||
TAP_CONFIG = 32;
|
||||
/** Master → host (BLE only): periodic pushed cache snapshot with ESP timestamp. */
|
||||
BLE_STREAM_TICK = 33;
|
||||
/**
|
||||
* Host → device: clear master client-id NVS map + live registry (and notify
|
||||
* online slaves), or on a slave clear local master/id binding only.
|
||||
*/
|
||||
CLIENT_ID_MAP_RESET = 34;
|
||||
}
|
||||
|
||||
message UartMessage {
|
||||
@@ -78,6 +83,8 @@ message UartMessage {
|
||||
TapConfigRequest tap_config_request = 39;
|
||||
TapConfigResponse tap_config_response = 40;
|
||||
BleStreamTick ble_stream_tick = 41;
|
||||
ClientIdMapResetRequest client_id_map_reset_request = 42;
|
||||
ClientIdMapResetResponse client_id_map_reset_response = 43;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,6 +356,13 @@ message RestartResponse {
|
||||
uint32 client_id = 2;
|
||||
}
|
||||
|
||||
/** Host → master or slave: clear persisted client-id assignment state. */
|
||||
message ClientIdMapResetRequest {}
|
||||
|
||||
message ClientIdMapResetResponse {
|
||||
bool success = 1;
|
||||
}
|
||||
|
||||
/** Host → master: read/write global log level (esp_log_level_set("*", …)). */
|
||||
message SetLogLevelRequest {
|
||||
bool write = 1;
|
||||
|
||||
Reference in New Issue
Block a user