# ESP Wi-Fi Sensing [[中文]](./README_cn.md)
### Overview
`esp_wifi_sensing` is a Wi-Fi sensing FSM component built on CSI-derived motion features. It is designed for motion-triggered interaction, occupancy-aware workflows, and lightweight sensing on ESP devices. The component consumes features from `esp-radar` and provides multi-channel state management, debounce, dynamic baseline tracking, event callbacks, and router-ping-driven sampling.
### Features
- **Multi-channel FSM**: One handle can manage multiple peer MAC/BSSID sensing channels at the same time.
- **Dynamic baseline and debounce**: Built-in smoothing, adaptive noise estimation, hysteresis, and ACTIVE hold window help reduce false triggers.
- **Simplified tuning model**: Main runtime tuning is exposed through `sensitivity`, `active_jitter_min`, and `active_filter_ms`.
- **Event callback integration**: Register ACTIVE / INACTIVE callbacks for lighting, telemetry, or product logic.
- **Ping-assisted sampling**: Optional internal router ping keeps the CSI sampling path active in router-based scenarios.
- **Diagnostics support**: Read channel state, runtime configuration, and detailed diagnostic snapshots for tuning and visualization.
### Closed-source Distribution
- The repository keeps `src/` for local development and CI builds.
- Published component packages exclude `src/` and ship prebuilt static libraries for supported ESP-IDF versions and targets.
- `CMakeLists.txt` supports both modes automatically: compile from source when `src/` exists, or link `libesp_wifi_sensing.a` when only the packaged artifact is present.
### Supported Chips
- ESP32
- ESP32-S2
- ESP32-S3
- ESP32-C3
- ESP32-C5
- ESP32-C6
- ESP32-C61
### Requirements
- ESP-IDF >= 5.4
- `esp-radar` >= 0.3.3
### Core Data Types
#### `esp_wifi_sensing_fsm_handle_t`
Opaque FSM handle. One handle manages multiple sensing channels, and each channel is identified by a peer MAC address.
#### `esp_wifi_sensing_fsm_state_t`
Public channel state returned to applications:
- `ESP_WIFI_SENSING_FSM_STATE_INACTIVE`
- `ESP_WIFI_SENSING_FSM_STATE_DEBOUNCE`
- `ESP_WIFI_SENSING_FSM_STATE_ACTIVE`
#### `esp_wifi_sensing_fsm_event_t`
Event type used by callbacks:
- `ESP_WIFI_SENSING_FSM_EVENT_INACTIVE`
- `ESP_WIFI_SENSING_FSM_EVENT_ACTIVE`
#### `esp_wifi_sensing_fsm_config_t`
Global FSM configuration including maximum channel count, raw sample buffer size, polling interval, internal ping frequency, and the default per-channel configuration.
#### `esp_wifi_sensing_fsm_channel_config_t`
Simplified per-channel tuning parameters:
- `sensitivity`: Unified sensitivity in the recommended range `(0, 1]`. Larger values make detection more sensitive.
- `active_jitter_min`: Minimum raw jitter required to enter or stay in ACTIVE. Set `0` to disable this guard.
- `active_filter_ms`: Minimum hold time after entering ACTIVE.
#### `esp_wifi_sensing_fsm_channel_diag_t`
Per-channel runtime diagnostics including the latest jitter, smoothed value, absolute enter/exit levels, process state, and initialization stage. The scaled values are mainly intended for debugging and visualization.
### Default Macros
```c
#define DEFAULT_ESP_WIFI_SENSING_FSM_CHANNEL_CONFIG() \
{ \
.sensitivity = ESP_WIFI_SENSING_DEFAULT_SENSITIVITY, \
.active_jitter_min = ESP_WIFI_SENSING_DEFAULT_ACTIVE_JITTER_MIN, \
.active_filter_ms = ESP_WIFI_SENSING_ACTIVE_FILTER_MS_DEFAULT, \
}
#define DEFAULT_ESP_WIFI_SENSING_FSM_CONFIG() \
{ \
.max_channel_num = 16, \
.raw_buf_size = 20, \
.polling_interval = 20, \
.ping_frequency_hz = ESP_WIFI_SENSING_PING_FREQUENCY_HZ_DEFAULT, \
.default_channel_config = DEFAULT_ESP_WIFI_SENSING_FSM_CHANNEL_CONFIG(), \
.user_data = NULL, \
}
```
### Recommended Workflow
1. Create the FSM with `esp_wifi_sensing_fsm_create()`.
2. Add one or more channels with `esp_wifi_sensing_fsm_add_channel()`.
3. Optionally register event callbacks and tune channel parameters.
4. Start detection with `esp_wifi_sensing_fsm_control(..., ESP_WIFI_SENSING_FSM_CTRL_START, NULL)`.
5. Optionally call `esp_wifi_sensing_fsm_ping_router_start()` in router-based CSI scenarios.
6. Read state/diagnostics as needed, then stop and delete the FSM when done.
### API Reference
#### Lifecycle
```c
esp_err_t esp_wifi_sensing_fsm_create(const esp_wifi_sensing_fsm_config_t *config,
esp_wifi_sensing_fsm_handle_t *handle);
esp_err_t esp_wifi_sensing_fsm_delete(esp_wifi_sensing_fsm_handle_t handle);
```
Creates and destroys the sensing FSM handle. The current implementation supports one active FSM instance at a time.
#### Channel Management
```c
esp_err_t esp_wifi_sensing_fsm_add_channel(esp_wifi_sensing_fsm_handle_t handle,
const uint8_t peer_mac[6]);
esp_err_t esp_wifi_sensing_fsm_remove_channel(esp_wifi_sensing_fsm_handle_t handle,
const uint8_t peer_mac[6]);
```
Adds or removes sensing channels bound to a peer MAC/BSSID. Each channel keeps its own baseline, runtime state, and diagnostics.
#### Data Feed and State Query
```c
esp_err_t esp_wifi_sensing_fsm_update_data(esp_wifi_sensing_fsm_handle_t handle,
const uint8_t peer_mac[6],
uint32_t raw_data);
esp_err_t esp_wifi_sensing_fsm_get_state(esp_wifi_sensing_fsm_handle_t handle,
const uint8_t peer_mac[6],
esp_wifi_sensing_fsm_state_t *state);
esp_err_t esp_wifi_sensing_fsm_get_channel_diag(esp_wifi_sensing_fsm_handle_t handle,
const uint8_t peer_mac[6],
esp_wifi_sensing_fsm_channel_diag_t *diag);
```
Feeds motion features into the FSM and reads the current state and diagnostics.
- `esp_wifi_sensing_fsm_update_data()` is an advanced API for manual feature injection.
- In the built-in radar integration path, samples are fed automatically.
#### Runtime Configuration and Events
```c
esp_err_t esp_wifi_sensing_fsm_set_channel_config(esp_wifi_sensing_fsm_handle_t handle,
const uint8_t peer_mac[6],
const esp_wifi_sensing_fsm_channel_config_t *config);
esp_err_t esp_wifi_sensing_fsm_get_channel_config(esp_wifi_sensing_fsm_handle_t handle,
const uint8_t peer_mac[6],
esp_wifi_sensing_fsm_channel_config_t *config);
esp_err_t esp_wifi_sensing_fsm_set_amplitude_log_enabled(esp_wifi_sensing_fsm_handle_t handle,
bool enabled);
esp_err_t esp_wifi_sensing_fsm_get_amplitude_log_enabled(esp_wifi_sensing_fsm_handle_t handle,
bool *enabled);
esp_err_t esp_wifi_sensing_fsm_register_event_cb(esp_wifi_sensing_fsm_handle_t handle,
esp_wifi_sensing_fsm_event_t event,
esp_wifi_sensing_fsm_event_cb_t cb,
void *user_data);
esp_err_t esp_wifi_sensing_fsm_unregister_event_cb(esp_wifi_sensing_fsm_handle_t handle,
esp_wifi_sensing_fsm_event_t event);
```
Updates runtime tuning, controls amplitude-log compression in the underlying radar decoder, and registers ACTIVE / INACTIVE callbacks.
- Changing `sensitivity` causes the channel baseline to be relearned.
- Multiple callbacks can be registered for the same event.
#### Control and Processing
```c
esp_err_t esp_wifi_sensing_fsm_control(esp_wifi_sensing_fsm_handle_t handle,
esp_wifi_sensing_fsm_ctrl_t ctrl,
void *ctrl_param);
esp_err_t esp_wifi_sensing_fsm_handle_events(esp_wifi_sensing_fsm_handle_t handle);
```
Starts, stops, resets baseline handling, or manually advances event processing.
- Pass `NULL` for `ctrl_param`.
- `esp_wifi_sensing_fsm_handle_events()` is usually not needed because the component already runs a background task based on `polling_interval`.
#### Ping-driven Sampling
```c
esp_err_t esp_wifi_sensing_fsm_set_ping_frequency_hz(esp_wifi_sensing_fsm_handle_t handle,
uint32_t frequency_hz);
esp_err_t esp_wifi_sensing_fsm_get_ping_frequency_hz(esp_wifi_sensing_fsm_handle_t handle,
uint32_t *frequency_hz);
esp_err_t esp_wifi_sensing_fsm_ping_router_start(esp_wifi_sensing_fsm_handle_t handle);
esp_err_t esp_wifi_sensing_fsm_ping_router_stop(esp_wifi_sensing_fsm_handle_t handle);
```
Configures or controls the internal router ping task used to keep CSI traffic flowing.
- `esp_wifi_sensing_fsm_ping_router_start()` automatically resolves the STA gateway and starts an endless ping session.
- Use this only when the deployment model relies on router traffic to keep CSI samples flowing.
### Usage Example
```c
static void motion_cb(esp_wifi_sensing_fsm_handle_t handle,
const uint8_t peer_mac[6],
esp_wifi_sensing_fsm_event_t event,
uint32_t data,
void *user_data)
{
(void)handle;
(void)peer_mac;
(void)user_data;
printf("event=%d data=%lu\n", event, (unsigned long)data);
}
void sensing_example(const uint8_t peer_mac[6])
{
esp_wifi_sensing_fsm_handle_t handle = NULL;
esp_wifi_sensing_fsm_config_t config = DEFAULT_ESP_WIFI_SENSING_FSM_CONFIG();
ESP_ERROR_CHECK(esp_wifi_sensing_fsm_create(&config, &handle));
ESP_ERROR_CHECK(esp_wifi_sensing_fsm_add_channel(handle, peer_mac));
ESP_ERROR_CHECK(esp_wifi_sensing_fsm_register_event_cb(handle,
ESP_WIFI_SENSING_FSM_EVENT_ACTIVE,
motion_cb,
NULL));
ESP_ERROR_CHECK(esp_wifi_sensing_fsm_control(handle, ESP_WIFI_SENSING_FSM_CTRL_START, NULL));
ESP_ERROR_CHECK(esp_wifi_sensing_fsm_ping_router_start(handle));
/* ... run application logic ... */
ESP_ERROR_CHECK(esp_wifi_sensing_fsm_ping_router_stop(handle));
ESP_ERROR_CHECK(esp_wifi_sensing_fsm_delete(handle));
}
```
See `test_apps` in this component for a more complete demo.
idf.py add-dependency "espressif/esp_wifi_sensing^0.1.0"