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:
+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) {
|
||||
|
||||
Reference in New Issue
Block a user