Refactor ESP-NOW air protocol to nanopb protobuf.
Add esp_now_messages.proto with EspNowMessage types, encode/decode helpers, and Makefile targets to regenerate firmware and UART schemas together. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+83
-76
@@ -1,5 +1,6 @@
|
||||
#include "client_registry.h"
|
||||
#include "esp_now_comm.h"
|
||||
#include "esp_now_proto.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
@@ -17,10 +18,6 @@
|
||||
#define POWERPOD_FW_VERSION 1u
|
||||
#endif
|
||||
|
||||
#define ESPNOW_MAGIC 0xA1
|
||||
#define ESPNOW_MSG_DISCOVER 1
|
||||
#define ESPNOW_MSG_SLAVE_INFO 2
|
||||
#define ESPNOW_MSG_HEARTBEAT 3
|
||||
#define ESPNOW_DISCOVER_INTERVAL_MS 500
|
||||
#define ESPNOW_HEARTBEAT_INTERVAL_MS 1000
|
||||
#define ESPNOW_HEARTBEAT_MISS_COUNT 3
|
||||
@@ -33,24 +30,6 @@ static const uint8_t ESPNOW_BCAST[ESP_NOW_ETH_ALEN] = {0xff, 0xff, 0xff,
|
||||
|
||||
static const char *TAG = "[ESPNOW]";
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t magic;
|
||||
uint8_t type;
|
||||
uint8_t network;
|
||||
uint8_t reserved;
|
||||
} espnow_discover_packet_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t magic;
|
||||
uint8_t type;
|
||||
uint8_t network;
|
||||
uint8_t mac[ESP_NOW_ETH_ALEN];
|
||||
uint32_t version;
|
||||
uint32_t slave_id;
|
||||
uint8_t available;
|
||||
uint8_t used;
|
||||
} espnow_slave_packet_t;
|
||||
|
||||
static app_config_t s_config;
|
||||
static uint8_t s_wifi_channel;
|
||||
static uint8_t s_own_mac[ESP_NOW_ETH_ALEN];
|
||||
@@ -98,30 +77,54 @@ static esp_err_t ensure_peer(const uint8_t *mac) {
|
||||
|
||||
static esp_err_t ensure_broadcast_peer(void) { return ensure_peer(ESPNOW_BCAST); }
|
||||
|
||||
static void build_slave_packet(espnow_slave_packet_t *pkt, uint8_t type) {
|
||||
pkt->magic = ESPNOW_MAGIC;
|
||||
pkt->type = type;
|
||||
pkt->network = s_config.network;
|
||||
memcpy(pkt->mac, s_own_mac, ESP_NOW_ETH_ALEN);
|
||||
pkt->version = POWERPOD_FW_VERSION;
|
||||
pkt->slave_id = s_own_mac[5];
|
||||
pkt->available = 1;
|
||||
pkt->used = 0;
|
||||
static void fill_presence(alox_EspNowSlavePresence *presence) {
|
||||
presence->network = s_config.network;
|
||||
presence->version = POWERPOD_FW_VERSION;
|
||||
presence->slave_id = s_own_mac[5];
|
||||
presence->available = true;
|
||||
presence->used = false;
|
||||
esp_now_proto_setup_presence_encode(presence, s_own_mac);
|
||||
}
|
||||
|
||||
static void send_slave_packet(const uint8_t *dest_mac, uint8_t type) {
|
||||
espnow_slave_packet_t pkt;
|
||||
build_slave_packet(&pkt, type);
|
||||
static esp_err_t send_message(const uint8_t *dest_mac,
|
||||
const alox_EspNowMessage *msg) {
|
||||
uint8_t buf[ESPNOW_PB_MAX_SIZE];
|
||||
size_t len = 0;
|
||||
|
||||
esp_err_t err = esp_now_proto_encode(msg, buf, sizeof(buf), &len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "encode failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (ensure_peer(dest_mac) != ESP_OK) {
|
||||
return;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_now_send(dest_mac, (const uint8_t *)&pkt, sizeof(pkt));
|
||||
err = esp_now_send(dest_mac, buf, len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "send type=%u failed: %s", (unsigned)type,
|
||||
ESP_LOGW(TAG, "send type=%u failed: %s", (unsigned)msg->type,
|
||||
esp_err_to_name(err));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void send_presence(const uint8_t *dest_mac,
|
||||
alox_EspNowMessageType type) {
|
||||
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
||||
alox_EspNowSlavePresence *presence = NULL;
|
||||
|
||||
msg.type = type;
|
||||
if (type == alox_EspNowMessageType_ESPNOW_SLAVE_INFO) {
|
||||
msg.which_payload = alox_EspNowMessage_slave_info_tag;
|
||||
presence = &msg.payload.slave_info;
|
||||
} else {
|
||||
msg.which_payload = alox_EspNowMessage_heartbeat_tag;
|
||||
presence = &msg.payload.heartbeat;
|
||||
}
|
||||
|
||||
fill_presence(presence);
|
||||
send_message(dest_mac, &msg);
|
||||
}
|
||||
|
||||
static void slave_reset_join(void) {
|
||||
@@ -130,15 +133,16 @@ static void slave_reset_join(void) {
|
||||
s_last_discover_ms = 0;
|
||||
}
|
||||
|
||||
static void handle_client_packet(const espnow_slave_packet_t *pkt) {
|
||||
if (pkt->network != s_config.network) {
|
||||
static void handle_client_presence(const alox_EspNowSlavePresence *presence,
|
||||
const uint8_t mac[CLIENT_MAC_LEN]) {
|
||||
if (presence->network != s_config.network) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_new = false;
|
||||
bool reactivated = false;
|
||||
esp_err_t err = client_registry_heartbeat(
|
||||
pkt->mac, pkt->slave_id, pkt->version, pkt->used != 0, &is_new,
|
||||
mac, presence->slave_id, presence->version, presence->used, &is_new,
|
||||
&reactivated);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "client registry full");
|
||||
@@ -146,20 +150,20 @@ static void handle_client_packet(const espnow_slave_packet_t *pkt) {
|
||||
}
|
||||
|
||||
char mac_str[18];
|
||||
mac_to_str(pkt->mac, mac_str, sizeof(mac_str));
|
||||
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)pkt->slave_id, mac_str,
|
||||
(unsigned long)pkt->version);
|
||||
(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)pkt->slave_id, mac_str);
|
||||
(unsigned long)presence->slave_id, mac_str);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_discover(const uint8_t *sender_mac,
|
||||
const espnow_discover_packet_t *pkt) {
|
||||
if (pkt->network != s_config.network) {
|
||||
const alox_EspNowDiscover *discover) {
|
||||
if (discover->network != s_config.network) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -183,9 +187,10 @@ static void handle_discover(const uint8_t *sender_mac,
|
||||
|
||||
char mac_str[18];
|
||||
mac_to_str(sender_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG, "joined network %u, master %s", (unsigned)pkt->network, mac_str);
|
||||
ESP_LOGI(TAG, "joined network %u, master %s", (unsigned)discover->network,
|
||||
mac_str);
|
||||
|
||||
send_slave_packet(sender_mac, ESPNOW_MSG_SLAVE_INFO);
|
||||
send_presence(sender_mac, alox_EspNowMessageType_ESPNOW_SLAVE_INFO);
|
||||
}
|
||||
|
||||
static void slave_check_master_timeout(void) {
|
||||
@@ -220,7 +225,7 @@ static void slave_heartbeat_task(void *param) {
|
||||
continue;
|
||||
}
|
||||
|
||||
send_slave_packet(s_master_mac, ESPNOW_MSG_HEARTBEAT);
|
||||
send_presence(s_master_mac, alox_EspNowMessageType_ESPNOW_HEARTBEAT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,51 +243,53 @@ static void master_monitor_task(void *param) {
|
||||
|
||||
static void espnow_recv_cb(const esp_now_recv_info_t *info, const uint8_t *data,
|
||||
int len) {
|
||||
if (info == NULL || data == NULL || len < 3) {
|
||||
if (info == NULL || data == NULL || len <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[0] != ESPNOW_MAGIC) {
|
||||
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
||||
uint8_t peer_mac[CLIENT_MAC_LEN];
|
||||
|
||||
esp_now_proto_setup_message_decode(&msg, peer_mac);
|
||||
if (esp_now_proto_decode(data, (size_t)len, &msg) != ESP_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data[1]) {
|
||||
case ESPNOW_MSG_DISCOVER:
|
||||
if (!s_config.master && len >= (int)sizeof(espnow_discover_packet_t)) {
|
||||
handle_discover(info->src_addr,
|
||||
(const espnow_discover_packet_t *)data);
|
||||
if (!s_config.master) {
|
||||
if (msg.type != alox_EspNowMessageType_ESPNOW_DISCOVER ||
|
||||
msg.which_payload != alox_EspNowMessage_discover_tag) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case ESPNOW_MSG_SLAVE_INFO:
|
||||
case ESPNOW_MSG_HEARTBEAT:
|
||||
if (s_config.master && len >= (int)sizeof(espnow_slave_packet_t)) {
|
||||
handle_client_packet((const espnow_slave_packet_t *)data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
handle_discover(info->src_addr, &msg.payload.discover);
|
||||
return;
|
||||
}
|
||||
|
||||
const alox_EspNowSlavePresence *presence = esp_now_proto_get_presence(&msg);
|
||||
if (presence == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.type != alox_EspNowMessageType_ESPNOW_SLAVE_INFO &&
|
||||
msg.type != alox_EspNowMessageType_ESPNOW_HEARTBEAT) {
|
||||
return;
|
||||
}
|
||||
|
||||
handle_client_presence(presence, peer_mac);
|
||||
}
|
||||
|
||||
static void master_discover_task(void *param) {
|
||||
(void)param;
|
||||
|
||||
espnow_discover_packet_t pkt = {
|
||||
.magic = ESPNOW_MAGIC,
|
||||
.type = ESPNOW_MSG_DISCOVER,
|
||||
.network = s_config.network,
|
||||
.reserved = 0,
|
||||
};
|
||||
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
||||
msg.type = alox_EspNowMessageType_ESPNOW_DISCOVER;
|
||||
msg.which_payload = alox_EspNowMessage_discover_tag;
|
||||
msg.payload.discover.network = s_config.network;
|
||||
|
||||
ESP_LOGI(TAG, "master discover task on network %u ch %u",
|
||||
(unsigned)s_config.network, (unsigned)s_wifi_channel);
|
||||
|
||||
while (1) {
|
||||
esp_err_t err =
|
||||
esp_now_send(ESPNOW_BCAST, (const uint8_t *)&pkt, sizeof(pkt));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "discover broadcast failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
send_message(ESPNOW_BCAST, &msg);
|
||||
vTaskDelay(pdMS_TO_TICKS(ESPNOW_DISCOVER_INTERVAL_MS));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user