# Touch Sensor FSM
## Version 0.9.1~1 SDK integration
Version 0.9.1~1 adds an ESP-IDF 6.2 development snapshot to its
SDK/target matrix, including ESP32-S31. Published version 0.9.1 contains only
IDF 5.5, 6.0 and 6.1 libraries and cannot be used with IDF 6.2.
The validated 6.2 development snapshot is ESP-IDF commit
`383dda6c182e3dabf6355160166fe007df775629`; this does not establish
compatibility with every 6.2 development commit.
Use a library built for your SDK minor version and target; renaming an older
library directory does not establish compatibility. Future SDK minor versions
remain unsupported until matching libraries are supplied.
`touch_sensor_fsm` provides configurable touch-state detection for one to
three sample configurations per channel. It accepts samples from polling or
user-push applications and reports debounced active/inactive state changes.
The current package requires ESP-IDF 5.5, 6.0, 6.1, or the pinned 6.2 snapshot and
`touch_sensor_lowlevel` 1.0.1 or later. ESP-IDF 5.5 and 6.0 cover ESP32-S2, ESP32-S3,
ESP32-P4, and Linux; ESP-IDF 6.1 and the pinned 6.2 snapshot also cover ESP32-H4 and ESP32-S31 when those
targets are available in the SDK.
Registry version 0.9.1~1 contains precompiled libraries for the supported ESP-IDF
version and target combinations. Select the matching ESP-IDF version and target;
a missing library causes configuration to fail.
Each library is accompanied by its build provenance and full effective
sdkconfig. Keep these files with the library when integrating or reporting an issue.
## Features
- Single- and multi-frequency touch detection.
- Startup initialization from measured samples or optional preset values.
- Configurable active/inactive thresholds and debounce.
- Polling callbacks and user-push input, including ISR-safe sample submission.
- Runtime state, sample, threshold, and signal-quality information.
- Deep copies of channel, active/inactive threshold, and optional initialization arrays.
## Add the Component
```bash
idf.py add-dependency "espressif/touch_sensor_fsm^0.9.1~1"
```
The component declares its compatible `touch_sensor_lowlevel` 1.x dependency.
## Polling Mode
Discover the effective sample count and scan period from lowlevel before
resolving the FSM defaults. Start lowlevel successfully before starting the FSM.
```c
#include "esp_check.h"
#include "touch_sensor_fsm.h"
#include "touch_sensor_lowlevel.h"
static void polling_cb(fsm_handle_t handle, uint32_t channel,
uint32_t *raw_data, void *user_data)
{
(void)handle;
(void)user_data;
ESP_ERROR_CHECK(touch_sensor_lowlevel_get_data(channel, raw_data));
}
void app_main(void)
{
uint32_t channels[] = {10};
uint32_t thresholds_active[] = {70};
uint32_t thresholds_inactive[] = {0}; /* 0 selects active / 2. */
touch_lowlevel_config_t lowlevel_config = {
.channel_num = 1,
.channel_list = channels,
.channel_type = NULL,
.sample_period_ms = 20,
.sample_cfg_num = 0,
};
ESP_ERROR_CHECK(touch_sensor_lowlevel_create(&lowlevel_config));
ESP_ERROR_CHECK(touch_sensor_lowlevel_start());
touch_lowlevel_info_t lowlevel_info = {0};
ESP_ERROR_CHECK(touch_sensor_lowlevel_get_info(&lowlevel_info));
fsm_config_t fsm_config;
ESP_ERROR_CHECK(touch_sensor_fsm_get_default_config(
lowlevel_info.sample_cfg_num, lowlevel_info.sample_period_ms,
&fsm_config));
fsm_config.channel_num = lowlevel_info.channel_num;
fsm_config.channel_list = (uint32_t *)lowlevel_info.channel_list;
fsm_config.freq_num = lowlevel_info.sample_cfg_num;
fsm_config.threshold_active = thresholds_active;
fsm_config.threshold_inactive = thresholds_inactive;
fsm_config.sample_period_ms = lowlevel_info.sample_period_ms;
fsm_config.polling_cb = polling_cb;
fsm_handle_t fsm = NULL;
ESP_ERROR_CHECK(touch_sensor_fsm_create(&fsm_config, &fsm));
ESP_ERROR_CHECK(touch_sensor_fsm_control(fsm, FSM_CTRL_START, NULL));
while (true) {
ESP_ERROR_CHECK(touch_sensor_fsm_handle_events(fsm));
vTaskDelay(pdMS_TO_TICKS(20));
}
}
```
Keep the first configured electrode untouched and the board stable while
lowlevel starts and FSM initialization collects its initial samples. A
lowlevel startup error is returned by `touch_sensor_lowlevel_start()`; do not
start the FSM until that call succeeds. The FSM also needs valid no-touch
samples before normal interaction.
## User-Push Mode
Use user-push mode when samples arrive from an application callback or ISR:
```c
fsm_config_t config = DEFAULTS_TOUCH_SENSOR_FSM_CONFIG();
config.mode = FSM_MODE_USER_PUSH;
config.channel_num = 1;
config.channel_list = (uint32_t[]){10};
config.freq_num = 3;
config.threshold_active = (uint32_t[]){70};
config.threshold_inactive = NULL; /* defaults to active / 2 */
fsm_handle_t fsm = NULL;
ESP_ERROR_CHECK(touch_sensor_fsm_create(&config, &fsm));
ESP_ERROR_CHECK(touch_sensor_fsm_control(fsm, FSM_CTRL_START, NULL));
uint32_t samples[] = {12300, 11800, 11500};
ESP_ERROR_CHECK(touch_sensor_fsm_update_data(fsm, 10, samples, 3, false));
ESP_ERROR_CHECK(touch_sensor_fsm_handle_events(fsm));
```
Pass `true` as the final argument of `touch_sensor_fsm_update_data()` only from
ISR context. Continue calling `touch_sensor_fsm_handle_events()` from a task.
Each update must contain exactly `freq_num` values for the configured channel.
## Configuration
Start with `DEFAULTS_TOUCH_SENSOR_FSM_CONFIG()` or
`touch_sensor_fsm_get_default_config()` and override only the values required
by the application.
- `channel_num`, `channel_list`, and `threshold_active` are required. Every
active threshold must be nonzero and below `UINT32_MAX`.
- `freq_num` must be in the range `1..FSM_MAX_FREQ`.
- Polling mode requires `polling_cb`.
- `threshold_active` and `threshold_inactive` use the amplitude-count units
returned by `touch_sensor_fsm_get_data()`. The inactive array is optional;
NULL or a zero entry selects `threshold_active / 2`.
- The smoothing coefficient uses integer permille values in the range
`0..1000`. Fields documented as automatic use their mode-specific default
when set to zero.
- `sample_period_ms` defaults to 20 ms. It is the polling rate limit in
polling mode and the time base for startup windows. Set it to zero when the
period is unknown.
- `debounce_active` and `debounce_inactive` control the number of consecutive
samples required for a state change. Both default to one frame; zero also
selects that one-frame default.
- `freq_sensitivity` is optional per-channel/per-frequency sensitivity data in
the layout documented by `fsm_config_t`. Values are 100..1000; `NULL` uses
the default equal sensitivity.
- Configuration arrays are copied during creation and may be released after
`touch_sensor_fsm_create()` returns.
`touch_sensor_fsm_get_default_config(freq_num, sample_period_ms, &config)`
returns the scalar defaults for a topology. Set the channel and active
threshold arrays before passing that structure to `touch_sensor_fsm_create()`.
## Runtime Information
Call `touch_sensor_fsm_get_state()` for the current public state and
`touch_sensor_fsm_get_data()` for the signed touch amplitude in
normalized counts. Negative values are preserved by this FSM API; wrapper
components may clamp them when exposing a non-negative business amplitude.
The conversion saturates to the `int32_t` range at the API boundary.
Both functions remain available with diagnostics disabled.
Internal snapshots and effective-configuration queries are not public APIs.
The 0.9.1~1 registry library rejects `CONFIG_TOUCH_SENSOR_FSM_DEBUG=y` and does
not ship the private header or diagnostic symbols.
The state callback receives `FSM_STATE_ACTIVE` or `FSM_STATE_INACTIVE` after
debounce. Its data argument is the current non-negative touch amplitude.
## Migration from versions before 0.9.0
Diagnostic types, queries and CSV macros have moved out of the public header.
Callers using `get_info()` only for amplitude must migrate to `get_data()`.
Use the public state/data queries in registry-library applications. Recompile
callers when migrating from the earlier structures.
The removed `baseline_coef`, `baseline_recover_coef`, `long_press_samples`,
`baseline_hold`, `baseline_locked`, `press_timer`, `baseline_recovering`,
`long_press_absorb_count`, and outlier diagnostic fields have no replacement.
Delete reads and configuration assignments for those fields. Keep limiter
overrides at their default values unless required by an existing configuration.
The old `threshold` plus `hysteresis` pair is not a field rename. For example,
old `threshold = 70, hysteresis = 10` compared at active 80 and inactive 60.
The new `threshold_active = 70` with a NULL inactive array compares at active
70 and inactive 35. To preserve the old comparison boundaries, configure
`threshold_active = 80` and `threshold_inactive = 60` explicitly.
The noise-window and noise-margin settings are no longer configurable. Remove
assignments to those former fields.
## Lifecycle
```text
create -> start -> handle/update -> stop -> delete
```
Stop the FSM with `FSM_CTRL_STOP` before calling
`touch_sensor_fsm_delete()`. Stop and delete the lowlevel driver separately
after the FSM no longer uses it. If creation or startup fails, delete any
successfully created instance before retrying.
## Linux Integration
The Linux library supports the user-push API for host integration tests. Supply
complete input frames and an explicit sample period. Hardware validation must
use the intended physical target.
idf.py add-dependency "espressif/touch_sensor_fsm^0.9.1~1"