Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 915943c

Browse files
authored
feat: ESP32 CSI MAC address filtering with NVS/Kconfig support (ruvnet#101)
* feat: add MAC address filter for ESP32 CSI collection In multi-AP environments, CSI frames from different access points get mixed together, corrupting the sensing signal. Add transmitter MAC filtering so only frames from a specified AP are processed. Implementation: - csi_collector: filter in wifi_csi_callback by comparing info->mac against configured MAC; log transmitter MAC in periodic debug output - csi_collector_set_filter_mac(): runtime API to enable/disable filter - Kconfig: CSI_FILTER_MAC option (format "AA:BB:CC:DD:EE:FF") - NVS: "filter_mac" 6-byte blob overrides Kconfig at runtime - nvs_config: parse Kconfig MAC string at boot, load NVS override - main: apply filter from config after csi_collector_init() When no filter is configured (default), behavior is unchanged — all transmitter MACs are accepted for backward compatibility. Fixes ruvnet#98 Co-Authored-By: claude-flow <[email protected]> * chore: add CLAUDE.local.md to .gitignore Local machine configuration (ESP-IDF paths, COM port, build instructions) should not be committed to the repository. Co-Authored-By: claude-flow <[email protected]>
1 parent 66392cb commit 915943c

7 files changed

Lines changed: 134 additions & 2 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Local machine configuration (not shared)
2+
CLAUDE.local.md
3+
14
# ESP32 firmware build artifacts and local config (contains WiFi credentials)
25
firmware/esp32-csi-node/build/
36
firmware/esp32-csi-node/sdkconfig

firmware/esp32-csi-node/main/Kconfig.projbuild

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,18 @@ menu "CSI Node Configuration"
3939
help
4040
WiFi channel to listen on for CSI data.
4141

42+
config CSI_FILTER_MAC
43+
string "CSI source MAC filter (AA:BB:CC:DD:EE:FF or empty)"
44+
default ""
45+
help
46+
When set to a valid MAC address (e.g. "AA:BB:CC:DD:EE:FF"),
47+
only CSI frames from that transmitter are processed. All
48+
other frames are silently dropped. This prevents signal
49+
mixing in multi-AP environments.
50+
51+
Leave empty to accept CSI from all transmitters.
52+
53+
Can be overridden at runtime via NVS key "filter_mac"
54+
(6-byte blob) without reflashing.
55+
4256
endmenu

firmware/esp32-csi-node/main/csi_collector.c

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ static uint32_t s_sequence = 0;
2626
static uint32_t s_cb_count = 0;
2727
static uint32_t s_send_ok = 0;
2828
static uint32_t s_send_fail = 0;
29+
static uint32_t s_filtered = 0;
30+
31+
/* ---- MAC address filter (Issue #98) ---- */
32+
33+
/** When non-zero, only CSI from s_filter_mac is accepted. */
34+
static uint8_t s_filter_enabled = 0;
35+
36+
/** The accepted transmitter MAC address (6 bytes). */
37+
static uint8_t s_filter_mac[6] = {0};
2938

3039
/* ---- ADR-029: Channel-hop state ---- */
3140

@@ -124,18 +133,52 @@ size_t csi_serialize_frame(const wifi_csi_info_t *info, uint8_t *buf, size_t buf
124133
return frame_size;
125134
}
126135

136+
void csi_collector_set_filter_mac(const uint8_t *mac)
137+
{
138+
if (mac == NULL) {
139+
s_filter_enabled = 0;
140+
memset(s_filter_mac, 0, 6);
141+
ESP_LOGI(TAG, "MAC filter disabled — accepting CSI from all transmitters");
142+
} else {
143+
memcpy(s_filter_mac, mac, 6);
144+
s_filter_enabled = 1;
145+
ESP_LOGI(TAG, "MAC filter enabled: only accepting %02X:%02X:%02X:%02X:%02X:%02X",
146+
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
147+
}
148+
s_filtered = 0;
149+
}
150+
127151
/**
128152
* WiFi CSI callback — invoked by ESP-IDF when CSI data is available.
153+
*
154+
* When a MAC filter is active, frames from non-matching transmitters are
155+
* silently dropped to prevent signal mixing in multi-AP environments.
129156
*/
130157
static void wifi_csi_callback(void *ctx, wifi_csi_info_t *info)
131158
{
132159
(void)ctx;
133160
s_cb_count++;
134161

162+
/* ---- MAC address filter (Issue #98) ---- */
163+
if (s_filter_enabled) {
164+
if (memcmp(info->mac, s_filter_mac, 6) != 0) {
165+
s_filtered++;
166+
if (s_filtered <= 3 || (s_filtered % 500) == 0) {
167+
ESP_LOGD(TAG, "Filtered CSI from %02X:%02X:%02X:%02X:%02X:%02X (dropped %lu)",
168+
info->mac[0], info->mac[1], info->mac[2],
169+
info->mac[3], info->mac[4], info->mac[5],
170+
(unsigned long)s_filtered);
171+
}
172+
return;
173+
}
174+
}
175+
135176
if (s_cb_count <= 3 || (s_cb_count % 100) == 0) {
136-
ESP_LOGI(TAG, "CSI cb #%lu: len=%d rssi=%d ch=%d",
177+
ESP_LOGI(TAG, "CSI cb #%lu: len=%d rssi=%d ch=%d mac=%02X:%02X:%02X:%02X:%02X:%02X",
137178
(unsigned long)s_cb_count, info->len,
138-
info->rx_ctrl.rssi, info->rx_ctrl.channel);
179+
info->rx_ctrl.rssi, info->rx_ctrl.channel,
180+
info->mac[0], info->mac[1], info->mac[2],
181+
info->mac[3], info->mac[4], info->mac[5]);
139182
}
140183

141184
uint8_t frame_buf[CSI_MAX_FRAME_SIZE];

firmware/esp32-csi-node/main/csi_collector.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,28 @@
2222
/** Maximum number of channels in the hop table (ADR-029). */
2323
#define CSI_HOP_CHANNELS_MAX 6
2424

25+
/** Length of a MAC address in bytes. */
26+
#define CSI_MAC_LEN 6
27+
2528
/**
2629
* Initialize CSI collection.
2730
* Registers the WiFi CSI callback.
2831
*/
2932
void csi_collector_init(void);
3033

34+
/**
35+
* Set a MAC address filter for CSI collection.
36+
*
37+
* When set, only CSI frames from the specified transmitter MAC are processed;
38+
* all others are silently dropped. This prevents signal mixing in multi-AP
39+
* environments.
40+
*
41+
* Pass NULL to disable filtering (accept CSI from all transmitters).
42+
*
43+
* @param mac 6-byte MAC address to accept, or NULL to disable filtering.
44+
*/
45+
void csi_collector_set_filter_mac(const uint8_t *mac);
46+
3147
/**
3248
* Serialize CSI data into ADR-018 binary frame format.
3349
*

firmware/esp32-csi-node/main/main.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ void app_main(void)
134134
/* Initialize CSI collection */
135135
csi_collector_init();
136136

137+
/* Apply MAC address filter if configured (Issue #98) */
138+
if (s_cfg.filter_mac_enabled) {
139+
csi_collector_set_filter_mac(s_cfg.filter_mac);
140+
} else {
141+
ESP_LOGI(TAG, "No MAC filter — accepting CSI from all transmitters");
142+
}
143+
137144
ESP_LOGI(TAG, "CSI streaming active → %s:%d",
138145
s_cfg.target_ip, s_cfg.target_port);
139146

firmware/esp32-csi-node/main/nvs_config.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "nvs_config.h"
1010

1111
#include <string.h>
12+
#include <stdio.h>
1213
#include "esp_log.h"
1314
#include "nvs_flash.h"
1415
#include "nvs.h"
@@ -51,6 +52,29 @@ void nvs_config_load(nvs_config_t *cfg)
5152
cfg->tdm_slot_index = 0;
5253
cfg->tdm_node_count = 1;
5354

55+
/* MAC filter: default disabled (all zeros) */
56+
memset(cfg->filter_mac, 0, 6);
57+
cfg->filter_mac_enabled = 0;
58+
59+
/* Parse compile-time Kconfig MAC filter if set (format: "AA:BB:CC:DD:EE:FF") */
60+
#ifdef CONFIG_CSI_FILTER_MAC
61+
{
62+
const char *mac_str = CONFIG_CSI_FILTER_MAC;
63+
unsigned int m[6];
64+
if (mac_str[0] != '\0' &&
65+
sscanf(mac_str, "%x:%x:%x:%x:%x:%x",
66+
&m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) == 6) {
67+
for (int i = 0; i < 6; i++) {
68+
cfg->filter_mac[i] = (uint8_t)m[i];
69+
}
70+
cfg->filter_mac_enabled = 1;
71+
ESP_LOGI(TAG, "Kconfig MAC filter: %02X:%02X:%02X:%02X:%02X:%02X",
72+
cfg->filter_mac[0], cfg->filter_mac[1], cfg->filter_mac[2],
73+
cfg->filter_mac[3], cfg->filter_mac[4], cfg->filter_mac[5]);
74+
}
75+
}
76+
#endif
77+
5478
/* Try to override from NVS */
5579
nvs_handle_t handle;
5680
esp_err_t err = nvs_open("csi_cfg", NVS_READONLY, &handle);
@@ -152,6 +176,27 @@ void nvs_config_load(nvs_config_t *cfg)
152176
}
153177
}
154178

179+
/* MAC filter (stored as a 6-byte blob in NVS key "filter_mac") */
180+
uint8_t mac_blob[6];
181+
size_t mac_len = 6;
182+
if (nvs_get_blob(handle, "filter_mac", mac_blob, &mac_len) == ESP_OK && mac_len == 6) {
183+
/* Check it's not all zeros (which would mean "no filter") */
184+
uint8_t is_zero = 1;
185+
for (int i = 0; i < 6; i++) {
186+
if (mac_blob[i] != 0) { is_zero = 0; break; }
187+
}
188+
if (!is_zero) {
189+
memcpy(cfg->filter_mac, mac_blob, 6);
190+
cfg->filter_mac_enabled = 1;
191+
ESP_LOGI(TAG, "NVS override: filter_mac=%02X:%02X:%02X:%02X:%02X:%02X",
192+
mac_blob[0], mac_blob[1], mac_blob[2],
193+
mac_blob[3], mac_blob[4], mac_blob[5]);
194+
} else {
195+
cfg->filter_mac_enabled = 0;
196+
ESP_LOGI(TAG, "NVS override: filter_mac disabled (all zeros)");
197+
}
198+
}
199+
155200
/* Validate tdm_slot_index < tdm_node_count */
156201
if (cfg->tdm_slot_index >= cfg->tdm_node_count) {
157202
ESP_LOGW(TAG, "tdm_slot_index=%u >= tdm_node_count=%u, clamping to 0",

firmware/esp32-csi-node/main/nvs_config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ typedef struct {
3535
uint32_t dwell_ms; /**< Dwell time per channel in ms. */
3636
uint8_t tdm_slot_index; /**< This node's TDM slot index (0-based). */
3737
uint8_t tdm_node_count; /**< Total nodes in the TDM schedule. */
38+
39+
/* MAC address filter for CSI source selection (Issue #98) */
40+
uint8_t filter_mac[6]; /**< Transmitter MAC to accept (all zeros = no filter). */
41+
uint8_t filter_mac_enabled; /**< 1 = filter active, 0 = accept all. */
3842
} nvs_config_t;
3943

4044
/**

0 commit comments

Comments
 (0)