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