Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 61 additions & 2 deletions firmware/esp32-csi-node/main/c6_sync_espnow.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,70 @@
#include "esp_idf_version.h"
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
#include "nvs_config.h" /* g_nvs_config: fleet size and beacon override */
#include <string.h>

static const char *TAG = "c6_espnow";

#define BEACON_MAGIC 0x53454E50u /* 'SENP' little-endian */
#define BEACON_PROTO_VER 0x01
#define BEACON_PERIOD_MS 100

/* Sizing constants for the derivation below.
*
* BEACON_AP_BUDGET_FPS is what the associated AP contributes to the same
* 50 Hz receive gate — measured 10-15 fps on this fleet, taken at the
* pessimistic end so the derived period errs slow.
*
* BEACON_MARGIN_NUM/DEN is the headroom over the bare inequality. At three
* nodes the inequality demands >= 57 ms and the value proven on hardware is
* 80 ms, which is where 7/5 comes from — the derivation returns 81 ms at N=3,
* so it reproduces the measured setting rather than being fitted after it.
*
* Across fleet sizes this holds the offered load at ~40 fps against the 50 fps
* gate — 3 nodes 81 ms, 6 nodes 200 ms, 9 nodes 320 ms, 13 nodes 480 ms — so
* every size keeps the same 10 fps of headroom rather than one being tuned
* and the rest inheriting whatever falls out. */
#define BEACON_AP_BUDGET_FPS 15
#define BEACON_GATE_FPS 50
#define BEACON_MARGIN_NUM 7
#define BEACON_MARGIN_DEN 5
#define BEACON_PERIOD_MIN_MS 20
#define BEACON_PERIOD_MAX_MS 2000

static uint32_t s_beacon_period_ms = BEACON_PERIOD_MS;

/* Effective beacon period for this node.
*
* An explicit NVS `beacon_ms` wins, for benching. Otherwise derive from the
* provisioned fleet size, because that is the quantity the period actually
* depends on and it is already provisioned for TDM:
*
* period >= (N - 1) * 1000 / (GATE - AP_BUDGET) then * margin
*
* N=3 -> 80 ms (the measured value), N=9 -> 320 ms, N=13 -> 480 ms.
*
* A fleet-size change is therefore a re-provision, never a reflash — which
* is the whole point, since getting this wrong does not degrade gracefully:
* an over-fast beacon overruns the receive gate, 60-90% of peer frames are
* discarded at random, and the transmit queue can wedge outright. */
uint32_t c6_sync_espnow_period_ms(void)
{
if (g_nvs_config.beacon_period_ms != 0) {
return g_nvs_config.beacon_period_ms;
}
uint32_t n = g_nvs_config.tdm_node_count;
if (n < 2) {
/* Unprovisioned or a single node: nobody to collide with. */
return BEACON_PERIOD_MS;
}
uint32_t budget = BEACON_GATE_FPS - BEACON_AP_BUDGET_FPS; /* 35 fps */
uint32_t base = ((n - 1) * 1000U + budget - 1) / budget; /* ceil */
uint32_t period = (base * BEACON_MARGIN_NUM) / BEACON_MARGIN_DEN;
if (period < BEACON_PERIOD_MIN_MS) period = BEACON_PERIOD_MIN_MS;
if (period > BEACON_PERIOD_MAX_MS) period = BEACON_PERIOD_MAX_MS;
return period;
}
#define VALID_WINDOW_MS 3000

typedef struct __attribute__((packed)) {
Expand Down Expand Up @@ -218,8 +275,10 @@ esp_err_t c6_sync_espnow_init(void)
s_leader_id = s_local_id;
s_last_seen_us = (uint64_t)esp_timer_get_time();

s_beacon_period_ms = c6_sync_espnow_period_ms();

s_beacon_timer = xTimerCreate("c6_espnow_beacon",
pdMS_TO_TICKS(BEACON_PERIOD_MS),
pdMS_TO_TICKS(s_beacon_period_ms),
pdTRUE, NULL, beacon_timer_cb);
if (s_beacon_timer == NULL) {
ESP_LOGE(TAG, "xTimerCreate failed");
Expand All @@ -228,7 +287,7 @@ esp_err_t c6_sync_espnow_init(void)
xTimerStart(s_beacon_timer, 0);

ESP_LOGI(TAG, "init done: local_id=%012llx leader=yes(candidate) period=%ums",
(unsigned long long)s_local_id, (unsigned)BEACON_PERIOD_MS);
(unsigned long long)s_local_id, (unsigned)s_beacon_period_ms);
return ESP_OK;
}

Expand Down
2 changes: 2 additions & 0 deletions firmware/esp32-csi-node/main/c6_sync_espnow.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,6 @@ uint32_t c6_sync_espnow_rx_magic_match(void);

#ifdef __cplusplus
}
uint32_t c6_sync_espnow_period_ms(void);

#endif
13 changes: 13 additions & 0 deletions firmware/esp32-csi-node/main/nvs_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,19 @@ void nvs_config_load(nvs_config_t *cfg)
}
}

uint16_t beacon_ms_val;
if (nvs_get_u16(handle, "beacon_ms", &beacon_ms_val) == ESP_OK) {
if (beacon_ms_val == 0 || (beacon_ms_val >= 20 && beacon_ms_val <= 2000)) {
cfg->beacon_period_ms = beacon_ms_val;
ESP_LOGI(TAG, "NVS override: beacon_period_ms=%u%s",
(unsigned)cfg->beacon_period_ms,
cfg->beacon_period_ms == 0 ? " (derive from fleet size)" : "");
} else {
ESP_LOGW(TAG, "NVS beacon_ms=%u out of range [20,2000], ignored",
(unsigned)beacon_ms_val);
}
}

/* ADR-039: Edge intelligence overrides. */
uint8_t edge_tier_val;
if (nvs_get_u8(handle, "edge_tier", &edge_tier_val) == ESP_OK) {
Expand Down
9 changes: 9 additions & 0 deletions firmware/esp32-csi-node/main/nvs_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ typedef struct {
uint8_t tdm_slot_index; /**< This node's TDM slot index (0-based). */
uint8_t tdm_node_count; /**< Total nodes in the TDM schedule. */

/* ADR-345: ESP-NOW sensing-beacon cadence.
*
* 0 = derive from tdm_node_count (the normal case). A non-zero value is an
* explicit override for benching; see c6_sync_espnow_period_ms(). */
uint16_t beacon_period_ms;

/* ADR-039: Edge intelligence configuration */
uint8_t edge_tier; /**< Processing tier (0=raw, 1=basic, 2=full). */
float presence_thresh; /**< Presence threshold (0 = auto-calibrate). */
Expand Down Expand Up @@ -73,4 +79,7 @@ typedef struct {
*/
void nvs_config_load(nvs_config_t *cfg);

/** Live configuration, loaded once at boot by app_main(). */
extern nvs_config_t g_nvs_config;

#endif /* NVS_CONFIG_H */
Loading