Fix ESP-NOW unicast by using sender MAC in client registry.

Register slaves from recv src_addr instead of protobuf mac bytes, add
ESPNOW_UNICAST_TEST for path verification, restore unicast deadzone, and
expose unicast-test in goTool.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-18 23:15:03 +02:00
co-authored by Cursor
parent ee38ce551a
commit 241e82b35b
20 changed files with 585 additions and 102 deletions
+62 -14
View File
@@ -110,7 +110,8 @@ static esp_err_t send_message(const uint8_t *dest_mac,
return err;
}
static esp_err_t send_accel_deadzone(uint32_t client_id, uint32_t deadzone) {
static esp_err_t send_accel_deadzone(const uint8_t *dest_mac, uint32_t client_id,
uint32_t deadzone) {
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
msg.type = alox_EspNowMessageType_ESPNOW_SET_ACCEL_DEADZONE;
@@ -118,22 +119,50 @@ static esp_err_t send_accel_deadzone(uint32_t client_id, uint32_t deadzone) {
msg.payload.accel_deadzone.deadzone = deadzone;
msg.payload.accel_deadzone.client_id = client_id;
return send_message(ESPNOW_BCAST, &msg);
return send_message(dest_mac, &msg);
}
esp_err_t esp_now_comm_send_accel_deadzone(uint32_t client_id, uint32_t deadzone) {
if (!s_config.master) {
static esp_err_t send_unicast_test(const uint8_t *dest_mac, uint32_t seq) {
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
msg.type = alox_EspNowMessageType_ESPNOW_UNICAST_TEST;
msg.which_payload = alox_EspNowMessage_unicast_test_tag;
msg.payload.unicast_test.seq = seq;
return send_message(dest_mac, &msg);
}
esp_err_t esp_now_comm_send_unicast_test(const uint8_t mac[CLIENT_MAC_LEN],
uint32_t seq) {
if (mac == NULL || !s_config.master) {
return ESP_ERR_INVALID_STATE;
}
esp_err_t err = send_accel_deadzone(client_id, deadzone);
char mac_str[18];
mac_to_str(mac, mac_str, sizeof(mac_str));
esp_err_t err = send_unicast_test(mac, seq);
if (err == ESP_OK) {
ESP_LOGI(TAG,
"broadcast SET_ACCEL_DEADZONE deadzone=%lu client_id=%lu%s",
(unsigned long)deadzone, (unsigned long)client_id,
client_id == 0 ? " (all slaves)" : "");
ESP_LOGI(TAG, "unicast TEST to %s seq=%lu", mac_str, (unsigned long)seq);
} else {
ESP_LOGW(TAG, "broadcast SET_ACCEL_DEADZONE failed: %s",
ESP_LOGW(TAG, "unicast TEST to %s failed: %s", mac_str, esp_err_to_name(err));
}
return err;
}
esp_err_t esp_now_comm_send_accel_deadzone(const uint8_t mac[CLIENT_MAC_LEN],
uint32_t client_id, uint32_t deadzone) {
if (mac == NULL || !s_config.master) {
return ESP_ERR_INVALID_STATE;
}
char mac_str[18];
mac_to_str(mac, mac_str, sizeof(mac_str));
esp_err_t err = send_accel_deadzone(mac, client_id, deadzone);
if (err == ESP_OK) {
ESP_LOGI(TAG, "unicast SET_ACCEL_DEADZONE to %s: deadzone=%lu client_id=%lu",
mac_str, (unsigned long)deadzone, (unsigned long)client_id);
} else {
ESP_LOGW(TAG, "unicast SET_ACCEL_DEADZONE to %s failed: %s", mac_str,
esp_err_to_name(err));
}
return err;
@@ -163,6 +192,15 @@ static void slave_reset_join(void) {
s_last_discover_ms = 0;
}
static void handle_slave_unicast_test(const uint8_t *master_mac,
const alox_EspNowUnicastTest *test) {
char mac_str[18];
mac_to_str(master_mac, mac_str, sizeof(mac_str));
ESP_LOGI(TAG, "UNICAST TEST OK from master %s seq=%lu (joined=%d)",
mac_str, (unsigned long)test->seq, (int)s_slave_joined);
}
static void handle_slave_accel_deadzone(const uint8_t *master_mac,
const alox_EspNowAccelDeadzone *cfg) {
uint32_t my_id = s_own_mac[5];
@@ -192,6 +230,8 @@ static void handle_client_presence(const alox_EspNowSlavePresence *presence,
return;
}
ensure_peer(mac);
bool is_new = false;
bool reactivated = false;
esp_err_t err = client_registry_heartbeat(
@@ -309,10 +349,17 @@ static void espnow_recv_cb(const esp_now_recv_info_t *info, const uint8_t *data,
return;
}
if (s_slave_joined && mac_equal(info->src_addr, s_master_mac)) {
ensure_peer(info->src_addr);
}
switch (msg.which_payload) {
case alox_EspNowMessage_discover_tag:
handle_discover(info->src_addr, &msg.payload.discover);
break;
case alox_EspNowMessage_unicast_test_tag:
handle_slave_unicast_test(info->src_addr, &msg.payload.unicast_test);
break;
case alox_EspNowMessage_accel_deadzone_tag:
handle_slave_accel_deadzone(info->src_addr, &msg.payload.accel_deadzone);
break;
@@ -325,17 +372,17 @@ static void espnow_recv_cb(const esp_now_recv_info_t *info, const uint8_t *data,
}
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
uint8_t peer_mac[CLIENT_MAC_LEN];
if (esp_now_proto_decode_with_mac(data, (size_t)len, &msg, peer_mac) !=
ESP_OK) {
if (esp_now_proto_decode(data, (size_t)len, &msg) != ESP_OK) {
ESP_LOGW(TAG, "master: ESP-NOW decode failed (%d bytes)", len);
return;
}
const alox_EspNowSlavePresence *presence = esp_now_proto_get_presence(&msg);
if (presence != NULL) {
handle_client_presence(presence, peer_mac);
/* Registry key is the ESP-NOW sender MAC, not the optional protobuf mac field. */
ensure_peer(info->src_addr);
handle_client_presence(presence, info->src_addr);
}
}
@@ -382,6 +429,7 @@ static esp_err_t init_wifi_stack(uint8_t channel) {
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
ESP_ERROR_CHECK(esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE));
return ESP_OK;