# Setup Guide Complete guide to install and configure ESPectre with ESPHome. Choose one of the two installation methods below: | Method | Best for | Tools | |--------|----------|-------| | **Option A** | End users, quick setup | Chrome + ESPConnect | | **Option B** | Developers, customization | Python + ESPHome CLI | --- ## Option A: Web Flash (no coding required) ### What You Need **Hardware:** - **ESP32 board** with CSI support: - ✅ Tested: ESP32-S3, ESP32-C6, ESP32-C5, ESP32-C3, ESP32 (original) - ⚠️ Experimental: ESP32-S2 - **USB cable** (USB-C or Micro-USB, depending on your board) - **Wi-Fi router** (2.4 GHz, 802.11b/g/n/ax) **Software:** - Google Chrome browser (required for Web Serial API) ### 1. Download Firmware Go to [Releases](https://github.com/francescopace/espectre/releases/latest) and download the `.bin` file for your chip (e.g., `espectre-2.5.0-esp32c6.bin`). ### 2. Flash Firmware 1. Open [ESPConnect](https://thelastoutpostworkshop.github.io/ESPConnect/) in Chrome 2. Connect your ESP32 via USB 3. Click **Connect** and select the serial port 4. Select the `.bin` file you downloaded 5. Click **Flash** ![ESPConnect](images/ESPConnect.png) ### 3. Configure WiFi After flashing, configure WiFi using one of these methods: | Method | How | |--------|-----| | **BLE** (easiest) | Use ESPHome or Home Assistant Companion app | | **USB** | Go to [web.esphome.io](https://web.esphome.io) → Connect → Configure WiFi | | **Captive Portal** | Connect to "ESPectre Fallback" WiFi → Configure in browser | That's it! The device will be automatically discovered by Home Assistant. --- ## Option B: ESPHome CLI (for developers) ### What You Need **Hardware:** Same as Easy Install above. **Software:** - Python 3.12 (⚠️ Python 3.14 has known issues with ESPHome) - ESPHome 2026.5.0 or newer - Home Assistant (recommended, but optional) ### 1. Install ESPHome ```bash # Create virtual environment (recommended) python3 -m venv venv source venv/bin/activate # On macOS/Linux # venv\Scripts\activate # On Windows # Install ESPHome pip install esphome ``` ### 2. Download a configuration file Download the example configuration for your hardware: | Platform | Configuration File | CPU | WiFi Chip | PSRAM | Status | |----------|-------------------|-----|-----------|-------|--------| | **ESP32-C6** | [espectre-c6.yaml](https://raw.githubusercontent.com/francescopace/espectre/main/examples/espectre-c6.yaml) | RISC-V @ 160MHz | WiFi 6 capable | ❌ | ✅ Tested | | **ESP32-S3** | [espectre-s3.yaml](https://raw.githubusercontent.com/francescopace/espectre/main/examples/espectre-s3.yaml) | Xtensa @ 240MHz | WiFi 4 | ✅ 8MB | ✅ Tested | | **ESP32-C3** | [espectre-c3.yaml](https://raw.githubusercontent.com/francescopace/espectre/main/examples/espectre-c3.yaml) | RISC-V @ 160MHz | WiFi 4 | ❌ | ✅ Tested ² | | **ESP32** | [espectre-esp32.yaml](https://raw.githubusercontent.com/francescopace/espectre/main/examples/espectre-esp32.yaml) | Xtensa @ 240MHz | WiFi 4 | Optional | ✅ Tested ³ | | **ESP32-C5** | [espectre-c5.yaml](https://raw.githubusercontent.com/francescopace/espectre/main/examples/espectre-c5.yaml) | RISC-V @ 240MHz | WiFi 6 capable | ❌ | ✅ Tested ¹ | | **ESP32-S2** | [espectre-s2.yaml](https://raw.githubusercontent.com/francescopace/espectre/main/examples/espectre-s2.yaml) | Xtensa @ 240MHz | WiFi 4 | Optional | ⚠️ Experimental | > **Note**: ESPectre uses WiFi 4 (802.11b/g/n) mode for stable 64 subcarriers and faster calibration, even on WiFi 6 capable chips (C5, C6). This ensures consistent performance across all platforms. > > On ESP32-C5, ESPectre also forces `2.4 GHz only` band mode at runtime to avoid unintended 5 GHz association and keep CSI behavior stable. **Recommendations**: - **ESP32-C6**: Modern RISC-V platform, good performance, compact form factor - **ESP32-S3**: Best for advanced applications, future ML features (more memory) - **ESP32-C3**: Budget-friendly option, compact form factor These files are pre-configured to download the component automatically from GitHub. > ⚠️ **Experimental platform**: ESP32-S2 has CSI support but has not been extensively tested. Please report your results on [GitHub Discussions](https://github.com/francescopace/espectre/discussions)! > > ¹ ESP32-C5: `improv_serial` (USB provisioning) not yet supported by ESPHome. Use BLE or WiFi AP provisioning instead. > > > ³ ESP32 (original/WROOM-32): AGC/FFT gain lock is not available on this platform. Band calibration works but CSI amplitudes may have more variance than newer chips. > > ⁴ **Boards with USB-UART bridges** (CH340, CP2102, CH343): If you don't see logs after flashing, uncomment the `hardware_uart: UART0` line in the `logger:` section of your configuration file to enable logging on UART0. ### 3. Build and flash ```bash esphome run espectre-c6.yaml # replace with your platform's file ``` ### 4. Configure WiFi After flashing, configure WiFi using one of these methods: | Method | How | |--------|-----| | **BLE** (easiest) | Use ESPHome app or Home Assistant Companion app | | **USB** | Go to [web.esphome.io](https://web.esphome.io) → Connect → Configure WiFi | | **Captive Portal** | Connect to "ESPectre Fallback" WiFi → Configure in browser | That's it! The device will be automatically discovered by Home Assistant. --- ## Development Setup For development, contributions, or offline use, use the pre-configured development files. ### 1. Clone the repository ```bash git clone https://github.com/francescopace/espectre.git cd espectre ``` ### 2. Install ESPHome ```bash python3 -m venv venv source venv/bin/activate # On macOS/Linux pip install esphome ``` ### 3. Create secrets file ```bash cat > examples/secrets.yaml << EOF wifi_ssid: "YourWiFiName" wifi_password: "YourWiFiPassword" # Optional: lock to specific AP (useful for mesh networks) # wifi_bssid: "AA:BB:CC:DD:EE:FF" EOF ``` ### 4. Build and flash Use the development configuration files (with debug sensors and local component path): | Platform | Development File | |----------|-----------------| | **ESP32-C6** | `examples/espectre-c6-dev.yaml` | | **ESP32-C5** | `examples/espectre-c5-dev.yaml` | | **ESP32-S3** | `examples/espectre-s3-dev.yaml` | | **ESP32-C3** | `examples/espectre-c3-dev.yaml` | | **ESP32** | `examples/espectre-esp32-dev.yaml` | ```bash # For ESP32-C6 esphome run examples/espectre-c6-dev.yaml # For ESP32-S3 esphome run examples/espectre-s3-dev.yaml # For ESP32 (original) esphome run examples/espectre-esp32-dev.yaml ``` ### Development vs Production Files | File | Component Source | WiFi | Logger | Debug Sensors | |------|-----------------|------|--------|---------------| | `espectre-c6.yaml` | GitHub | Provisioning (BLE/USB/AP) | INFO | ❌ | | `espectre-c6-dev.yaml` | Local | secrets.yaml | DEBUG | ✅ | | `espectre-s3.yaml` | GitHub | Provisioning (BLE/USB/AP) | INFO | ❌ | | `espectre-s3-dev.yaml` | Local | secrets.yaml | DEBUG | ✅ | | `espectre-esp32.yaml` | GitHub | Provisioning (BLE/USB/AP) | INFO | ❌ | | `espectre-esp32-dev.yaml` | Local | secrets.yaml | DEBUG | ✅ | --- ## Docker / Home Assistant Add-on If you run ESPHome in Docker or as a Home Assistant add-on, just download an example file to your config directory. **Example for Docker with bind mount:** ```bash # Your docker-compose.yml mounts /home/user/esphome/config:/config cd /home/user/esphome/config # Download the configuration file curl -O https://raw.githubusercontent.com/francescopace/espectre/main/examples/espectre-c6.yaml # Run ESPHome docker compose exec esphome esphome run espectre-c6.yaml # After flashing, configure WiFi via BLE, USB, or Captive Portal ``` No need to copy any files manually - the component is downloaded automatically from GitHub! --- ## Configuration Parameters ### ESPectre Component All parameters can be adjusted in the YAML file under the `espectre:` section: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `detection_algorithm` | string | mvs | Detection algorithm: `mvs` (variance) or `ml` (neural network) | | `traffic_generator_rate` | int | 100 | Packets/sec for CSI generation (0-1000, 0=disabled) | | `traffic_generator_mode` | string | ping | Traffic generator mode: `ping` (ICMP) or `dns` (UDP queries) | | `publish_interval` | int | auto | Packets between periodic sensor/log updates (default: same as traffic_generator_rate, or 100 if traffic is 0) | | `evaluation_interval` | int | 25 | Packets between internal detector state evaluations | | `motion_on_hits` | int | 3 | Consecutive evaluated hits required before switching the binary sensor to `MOTION` | | `motion_off_hits` | int | 3 | Consecutive evaluated hits required before switching the binary sensor back to `IDLE` | | `segmentation_threshold` | string/float | auto | Threshold: `auto`, `min`, or number (0.0-10.0 for both MVS and ML) | | `segmentation_window_size` | int | 100 | Moving variance window in packets (10-200) | | `selected_subcarriers` | list | auto | Fixed subcarriers (omit for auto-calibration) | | `lowpass_enabled` | bool | false | Enable low-pass filter for noise reduction (MVS and ML) | | `lowpass_cutoff` | float | 11.0 | Low-pass filter cutoff frequency in Hz (5-20) | | `hampel_enabled` | bool | true | Enable Hampel outlier filter (MVS and ML) | | `hampel_window` | int | 7 | Hampel filter window size (3-11) | | `hampel_threshold` | float | 5.0 | Hampel filter sensitivity (MAD multiplier) (1.0-10.0) | | `gain_lock` | string | auto | AGC/FFT gain lock: `auto`, `enabled`, `disabled` | | `ble_channel_enabled` | bool/string | auto | Enable BLE telemetry/control channel: `auto`, `true`, `false` | | `ble_telemetry_interval_ms` | int | 40 | BLE telemetry notify interval in ms (20-500) | For detailed parameter tuning (ranges, recommended values, troubleshooting), see [TUNING.md](TUNING.md). ### Choosing Detection Algorithm | Algorithm | How It Works | Pros | Cons | Best For | |-----------|--------------|------|------|----------| | **MVS** (default) | Variance of spatial turbulence | Low CPU, adaptive threshold | Requires 10s NBVI calibration | General use | | **ML** | Neural network (MLP 9→32→16→1) | Fast boot (~3s), no calibration | Pre-trained weights, fixed subcarriers | Experimental | Both algorithms support optional low-pass and Hampel filters on the turbulence stream. ```yaml espectre: detection_algorithm: mvs # or ml ``` **Threshold ranges (unified for both algorithms):** - Range: 0.0 - 10.0 - MVS default: `auto` (adaptive based on baseline noise) - ML default: 5.0 (equivalent to 0.5 probability) ### Integrated Sensors (Created Automatically) All sensors are created automatically when the `espectre` component is configured. You can optionally customize their names. | Sensor Config | Type | Default Name | Description | |---------------|------|--------------|-------------| | `movement_sensor` | sensor | "Movement Score" | Current motion score on a 0-10 scale (more gradual in ML mode) | | `motion_sensor` | binary_sensor | "Motion Detected" | Edge-driven motion state (on/off), filtered by `evaluation_interval` and hit counters | | `threshold_number` | number | "Threshold" | Detection threshold (adjustable from HA) | | `calibrate_switch` | switch | "Calibrate" | Trigger band recalibration (ON during calibration) | ### Customizing Sensors All sensor entities support standard ESPHome options: | Option | Type | Description | |--------|------|-------------| | `name` | string | Custom entity name | | `internal` | bool | If `true`, hide from Home Assistant (still processes data) | | `icon` | string | Custom MDI icon (e.g., `mdi:motion-sensor`) | | `disabled_by_default` | bool | Entity disabled until manually enabled in HA | The `movement_sensor` also supports ESPHome [sensor filters](https://esphome.io/components/sensor/#sensor-filters) for data transformation. - **MVS mode**: publishes the current moving-variance based metric - **ML mode**: publishes a 0-10 confidence-like score derived from the neural network output - **ML temperature scaling**: the ML score is intentionally softened before the sigmoid so Home Assistant can show intermediate values instead of a nearly binary 0/10 output Common filters: | Filter | Example | Description | |--------|---------|-------------| | `multiply` | `multiply: 10` | Scale values (e.g., 0-10 → 0-100) | | `round` | `round: 1` | Round to N decimal places | | `clamp` | `min_value: 0, max_value: 100` | Limit value range | | `offset` | `offset: -0.5` | Add/subtract constant | | `sliding_window_moving_average` | `window_size: 5` | Smooth noisy readings | See the [ESPHome sensor filters documentation](https://esphome.io/components/sensor/#sensor-filters) for the complete list. **Example:** ```yaml espectre: movement_sensor: name: "Living Room Movement" internal: true # Hide from Home Assistant icon: "mdi:sine-wave" filters: - multiply: 100 # Scale 0-1 to 0-100 - clamp: min_value: 0 max_value: 100 # Cap at 100% - round: 1 # Round to 1 decimal motion_sensor: name: "Living Room Motion" icon: "mdi:motion-sensor" threshold_number: name: "Living Room Threshold" ``` > **Tip:** Use `internal: true` on `movement_sensor` to reduce data sent to Home Assistant while keeping `motion_sensor` for automations. --- ## Home Assistant Integration ESPHome provides **automatic Home Assistant integration**. Once the device is flashed and connected to WiFi: 1. Home Assistant will automatically discover the device 2. Go to **Settings** → **Devices & Services** → **ESPHome** 3. Click **Configure** on the discovered device 4. All sensors will be automatically added ### Entities Created Entity names are based on the device name in your YAML (default: `espectre`): - **binary_sensor.espectre_motion_detected** - Motion state (on/off) - **sensor.espectre_movement_score** - Movement intensity value - **number.espectre_threshold** - Detection threshold (adjustable from Home Assistant) - **switch.espectre_calibrate** - Trigger recalibration (ON during calibration) > **Note:** If you change the device name, replace `espectre` with your device name in automations and dashboards. ### Automation Example ```yaml automation: - alias: "Turn on light on motion" trigger: - platform: state entity_id: binary_sensor.espectre_motion_detected to: "on" action: - service: light.turn_on target: entity_id: light.living_room - alias: "Turn off light after no motion" trigger: - platform: state entity_id: binary_sensor.espectre_motion_detected to: "off" for: minutes: 5 action: - service: light.turn_off target: entity_id: light.living_room ``` **Inactivity alert:** ```yaml automation: - alias: "Inactivity Alert" trigger: - platform: state entity_id: binary_sensor.espectre_motion_detected to: "off" for: hours: 4 condition: - condition: time after: "08:00:00" before: "22:00:00" action: - service: notify.mobile_app data: message: "No movement detected for 4 hours" ``` ### Dashboard Examples Two dashboard examples are available: | Dashboard | Description | |-----------|-------------| | [home-assistant-dashboard.yaml](examples/home-assistant-dashboard.yaml) | Production dashboard with motion sensors | | [home-assistant-dashboard-dev.yaml](examples/home-assistant-dashboard-dev.yaml) | Development dashboard with debug sensors (Free Heap, Loop Time, etc.) | **How to use:** 1. Go to **Settings** → **Dashboards** → **Add Dashboard** 2. Open the new dashboard and click **Edit** (pencil icon) 3. Click the three dots menu → **Raw configuration editor** 4. Replace ALL content with the YAML from the file (delete the default content first) 5. Click **Save** > **Note:** If you changed the device name from `espectre`, replace all occurrences of `espectre_` with your device name (e.g., `espectre_living_room_`). > ⚠️ **Multiple devices?** If you uncommented `name_add_mac_suffix: true` in your YAML, entity names will include the MAC suffix (e.g., `sensor.espectre_a1b2c3_movement_score`). Update the dashboard entities accordingly. **Production dashboard includes:** - **Gauge**: Visual representation of movement score with color-coded severity - **Motion tile**: Current motion state with last changed time - **Threshold control**: Adjustable detection threshold - **History graph**: 24-hour view of movement and threshold **Development dashboard adds:** - **Free Heap**: Available memory (monitor for leaks) - **Max Free Block**: Largest contiguous memory block - **Loop Time**: Main loop execution time --- ## Traffic Generator The traffic generator creates network packets that trigger CSI callbacks from the WiFi driver. Default rate is **100 pps** (packets per second). > ⚠️ **Important**: The traffic generator runs **continuously** while the device is powered on, not just during calibration. Each ESPectre device constantly sends packets to generate CSI data for motion detection. See [Network Impact](#network-impact) below to understand the bandwidth and airtime implications for your WiFi network. ```yaml espectre: traffic_generator_rate: 100 # packets per second (0-1000) traffic_generator_mode: ping # ping (default) or dns ``` ### Traffic Generator Mode Two modes are available: | Mode | Protocol | Description | |------|----------|-------------| | `ping` | ICMP | Sends ICMP echo requests to gateway. Default mode, more reliable on routers that ignore root-domain DNS queries. | | `dns` | UDP | Sends DNS queries to gateway:53. Lower-overhead alternative when DNS works well in your environment. | Both modes generate minimal network traffic (<20 bytes per packet). **Choosing a mode:** - Start with `ping` (default) - more reliable when routers ignore root-domain DNS queries - Try `dns` if you prefer lower-overhead UDP traffic and your router responds consistently - Note: some routers/firewalls may rate-limit or block ICMP ping responses ```yaml espectre: traffic_generator_rate: 100 traffic_generator_mode: dns # Use DNS queries instead of the default ping mode ``` **Community test results** (thanks to [@gasment](https://github.com/francescopace/espectre/issues/48)): | Board | Antenna | Mode | Rate | RSSI | AGC | Result | |-------|---------|------|------|------|-----|--------| | ESP32-S3-N16R8 | built-in | DNS | 100 | -15±5 | 15±5 | ✅ | | ESP32-S3-N16R8 | built-in | PING | 100 | -15±5 | 10±5 | ✅ | | ESP32-S3-N16R8 | external | DNS | 100 | -10±5 | 5±5 | ✅ | | ESP32-S3-N16R8 | external | PING | 100 | -5±5 | 5±5 | ✅ | | ESP32-C6 Super Mini | built-in | DNS | 100 | -20±5 | 15±5 | ✅ | | ESP32-C6 Super Mini | built-in | PING | 100 | -20±5 | 15±5 | ✅ | | ESP32-C3 Core | built-in | DNS | 94 | -20±5 | 15±5 | ✅ | | ESP32-C3 Core | built-in | PING | 94 | -20±5 | 15±5 | ✅ | Both modes work reliably across different boards and antenna configurations. For detailed rate recommendations and Nyquist-Shannon sampling theory, see [TUNING.md](TUNING.md#traffic-generator-rate-0-1000-pps). ### Network Impact Each ESPectre device generates **continuous** WiFi traffic as long as it's powered on. This is required for motion detection to work. Here's the approximate impact per device: | Rate | Packets/sec | Approximate Bandwidth | |------|-------------|----------------------| | 50 pps | 50 | ~4.5 KB/s | | 100 pps (default) | 100 | ~9 KB/s | | 200 pps | 200 | ~18 KB/s | For single devices or small deployments (< 5 devices), the default 100 pps has negligible impact on most home networks (typically <1% of available bandwidth). > ⚠️ **Planning a large deployment?** With many ESPectre devices, the cumulative traffic can consume noticeable WiFi airtime. For example, 10 devices at 100 pps each = 2000 packets/sec (1000 requests + 1000 responses), using approximately 10-15% of airtime. Consider using [External Traffic Mode](#external-traffic-mode) with UDP broadcast to reduce this to ~0.5% airtime. ### External Traffic Mode You can disable the internal traffic generator and rely on external WiFi traffic: ```yaml espectre: traffic_generator_rate: 0 # Disable internal generator publish_interval: 100 # Publish movement score/logs every 100 packets evaluation_interval: 25 # Re-evaluate motion state every 25 packets ``` This is useful when: - You have multiple ESPectre devices and want to reduce total network traffic - You already have continuous WiFi traffic on your network - You want centralized control over traffic generation **Generating external traffic:** When `traffic_generator_rate: 0`, ESPectre opens a UDP listener on **port 5555**. Send UDP packets to this port to generate CSI data. Use the standalone Python script: [`espectre_traffic_generator.py`](examples/espectre_traffic_generator.py) **Configuration:** Edit the script and set your device IP(s): ```python TARGETS = ['192.168.1.255'] # Broadcast address (recommended for multiple devices) # TARGETS = ['192.168.1.100', '192.168.1.101'] # Or list specific device IPs PORT = 5555 RATE = 100 # packets per second (recommended: 100) ``` > ⚠️ **ESP32 (original) limitation:** The CSI driver on ESP32 has a known issue where it doesn't detect traffic that was already flowing when CSI was enabled. You must start the external traffic generator **after** the ESP32 connects to WiFi. Additionally, broadcast mode is not supported - use unicast (specific device IP) instead. ESP32-C3, ESP32-C5, ESP32-C6, and ESP32-S3 don't have these limitations. See [espressif/esp-csi#247](https://github.com/espressif/esp-csi/issues/247) for details. **Usage:** ```bash python3 espectre_traffic_generator.py run # Foreground (Ctrl+C to stop) python3 espectre_traffic_generator.py start # Background daemon python3 espectre_traffic_generator.py stop # Stop daemon python3 espectre_traffic_generator.py status # Check if running ``` **Home Assistant (Docker) note:** The script starts the background process using `subprocess.Popen` (no `fork()`), which avoids the deprecation warning emitted by Python in multi-threaded Home Assistant environments. Run on any device on the network: Raspberry Pi, NAS, Home Assistant server, etc. **Home Assistant integration:** Copy the script to `/config/python_scripts/espectre_traffic_generator.py` and add to `configuration.yaml` (see [command_line integration docs](https://www.home-assistant.io/integrations/command_line/)): ```yaml command_line: - switch: name: "ESPectre Traffic Generator" command_on: "python3 /config/python_scripts/espectre_traffic_generator.py start" command_off: "python3 /config/python_scripts/espectre_traffic_generator.py stop" command_state: "python3 /config/python_scripts/espectre_traffic_generator.py status" value_template: '{{ "Running" in value }}' unique_id: espectre_traffic_generator ``` This creates a `switch.espectre_traffic_generator` entity you can toggle from dashboards or use in automations.
Why UDP instead of ping? You might wonder why we don't just use `ping -b` (broadcast ping) for external traffic. While ping works for CSI generation, UDP has significant advantages: | Aspect | Ping broadcast | UDP broadcast | |--------|----------------|---------------| | Response traffic | Yes (ICMP Echo Reply from each device) | None | | Network overhead | N+1 packets (1 request + N replies) | 1 packet | | Root required | Yes (for broadcast and high-rate) | No | For 10 devices at 100 pps, ping broadcast generates 1100 packets/sec (100 requests + 1000 replies), while UDP broadcast generates only 100 packets/sec total.
### Network Impact: Internal vs External Traffic **Packet sizes by mode:** | Mode | Payload | IP packet | On-wire (with MAC) | |------|---------|-----------|-------------------| | DNS (default) | 17 bytes | 45 bytes | ~75 bytes | | Ping | 8 bytes | 28 bytes | ~58 bytes | | External UDP | 3 bytes | 31 bytes | ~61 bytes | *On-wire size includes WiFi MAC header (~30 bytes). Actual size varies with encryption and PHY rate.* **Comparison for 10 devices at 100 pps:** | Approach | Total packets/sec | Total airtime | |----------|-------------------|---------------| | Internal traffic generator (DNS) | 1000 + 1000 replies | ~14% | | Internal traffic generator (Ping) | 1000 + 1000 replies | ~11% | | **UDP broadcast (recommended)** | **100** | **~0.5%** | UDP broadcast is the most efficient option for multi-device deployments: one packet reaches all devices, with no response traffic.
What is airtime? Airtime is the percentage of time the WiFi channel is occupied by transmissions. Since WiFi is a shared medium, only one device can transmit at a time. Each packet occupies the channel for: - **Transmission time**: packet size / PHY rate (e.g., 60 bytes at 54 Mbps ≈ 9 µs) - **Protocol overhead**: preamble, inter-frame spacing, ACK (~40 µs) At 100 pps with ~50 µs per packet: **airtime = 0.5%** High airtime (>30-50%) causes network congestion, increased latency, and packet loss. The UDP broadcast approach keeps airtime minimal even with many ESPectre devices.
--- ## Auto-Calibration (MVS only) > ⚠️ **CRITICAL**: The room must be **still** during the first ~13 seconds after boot. Movement during calibration will result in poor detection accuracy! Auto-calibration applies only to MVS mode. ML mode uses fixed subcarriers from pre-trained weights and skips this phase. ESPectre automatically calibrates in two phases: 1. **Gain Lock** (~3 seconds, 300 packets): Stabilizes AGC/FFT for consistent amplitudes 2. **NBVI Band Calibration** (~10 seconds, 10 × `window_size` packets): Selects optimal 12-subcarrier band and calculates adaptive threshold With default `segmentation_window_size: 100`, the calibration collects 1000 packets. If you change the window size, the calibration buffer adjusts automatically. Room must be quiet during the entire ~10 second calibration. **Sensor placement:** Position the sensor 3-8 meters from your access point for optimal performance. See [Sensor Placement](TUNING.md#sensor-placement) in the Tuning Guide for details. **Gain lock modes:** The `gain_lock` parameter (`auto`/`enabled`/`disabled`) controls AGC stabilization. See [Gain Lock](TUNING.md#gain-lock) in the Tuning Guide. **Runtime recalibration:** You can trigger recalibration from Home Assistant using the Calibrate switch (`switch.espectre_calibrate`). The switch is automatically disabled during calibration to prevent accidental interruption. For a complete reset, erase flash and re-flash. --- ## Custom Hardware Configuration ESPectre now provides example configurations for all ESP32 variants with CSI support. If you need to customize further, use these guidelines: ### Automatic sdkconfig options ESPectre automatically sets all required and recommended sdkconfig options. You don't need to manually configure anything in most cases. The component automatically configures: | Option | Value | Purpose | |--------|-------|---------| | `CONFIG_ESP_WIFI_CSI_ENABLED` | `y` | Enable CSI (mandatory) | | `CONFIG_PM_ENABLE` | `n` | Disable power management | | `CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE` | `n` | Disable disconnected PM | | `CONFIG_ESP_WIFI_AMPDU_TX_ENABLED` | `n` | More CSI callbacks | | `CONFIG_ESP_WIFI_AMPDU_RX_ENABLED` | `n` | More CSI callbacks | | `CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM` | `128` | Larger RX buffer | | `CONFIG_FREERTOS_HZ` | `1000` | 1ms tick for precise timing | ### Platform-specific options (optional) You only need to add sdkconfig options for platform-specific features: ```yaml esp32: variant: ESP32C6 # or ESP32S3, etc. framework: type: esp-idf version: 5.5.1 sdkconfig_options: # CPU frequency (platform-dependent) CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ: "160" # 160 for C6, 240 for S3 # PSRAM (if available on your board) # CONFIG_ESP32S3_SPIRAM_SUPPORT: y ``` **Reference:** For advanced sdkconfig tuning see official Espressif documentation: [ESP32 WiFi](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/wifi.html#how-to-configure-parameters). --- ## Flash Size and Partitions > **Minimum requirement: 4MB flash.** ESPectre fits comfortably in 4MB with full OTA support. Larger flash (8MB/16MB) is only useful for additional components (like `bluetooth_proxy`) or future ML features planned for v3.x. ### ESPectre Flash Footprint ESPectre itself is very lightweight. The actual code overhead is minimal: | Configuration | Flash | RAM | Notes | |---------------|-------|-----|-------| | ESPHome base + provisioning | 1,464 KB | 49.9 KB | BLE, captive portal, improv | | ESPectre production | 1,535 KB | 50.0 KB | Full featured | | **ESPectre overhead** | **~70 KB** | **88 bytes** | Just the CSI code | The ESPectre component adds only **~70KB of Flash** and less than **100 bytes of RAM**. The majority of flash usage comes from standard ESPHome components (WiFi, API, OTA, provisioning). ### Custom Partition Table ESPectre includes a custom partition table (`partitions.csv`) that is automatically applied during compilation. This partition table: - Supports **OTA updates** (dual app partitions) - Includes **SPIFFS** for calibration buffer (320KB, used during boot only) - App partition size: **~1.8MB** per slot ``` # ESPectre Partition Table (4MB flash) # Name, Type, SubType, Offset, Size nvs, data, nvs, 0x9000, 0x5000 otadata, data, ota, 0xe000, 0x2000 app0, app, ota_0, 0x10000, 0x1D0000 # ~1.8MB app1, app, ota_1, 0x1E0000, 0x1D0000 # ~1.8MB spiffs, data, spiffs, 0x3B0000, 0x50000 # 320KB ``` ### Combining with Other Components If you want to add ESPectre to an existing ESPHome configuration with other heavy components, be aware that ESPectre's partition table may override your existing partitions. **To use your own partition table**, you can override it in your YAML using an **absolute path**: ```yaml esphome: name: my-device platformio_options: board_build.partitions: /path/to/partitions_custom.csv ``` Then create the `partitions_custom.csv` file at that location. **Example for 4MB flash** (no OTA, ~3.7MB for app): ``` # Name, Type, SubType, Offset, Size nvs, data, nvs, 0x9000, 0x5000, phy_init, data, phy, 0xe000, 0x1000, app0, app, factory, 0x10000, 0x3C0000, spiffs, data, spiffs, 0x3D0000,0x30000, ``` **Example for 8MB flash** (no OTA, ~7.7MB for app): ``` # Name, Type, SubType, Offset, Size nvs, data, nvs, 0x9000, 0x5000, phy_init, data, phy, 0xe000, 0x1000, app0, app, factory, 0x10000, 0x7C0000, spiffs, data, spiffs, 0x7D0000, 0x30000, ``` **Notes**: - SPIFFS is required. ESPectre uses it as a temporary buffer during calibration. Removing SPIFFS will cause the component to fail during initialization. - If you remove OTA partitions, you must also remove the `ota:` section from your YAML (OTA updates won't work without the partitions). --- ## BLE Control API (Optional) ESPectre can expose a BLE telemetry/control channel for custom integrations, even without Home Assistant. For the complete protocol reference (UUIDs, commands, limits, and frame examples), see [docs/game/README.md#communication-protocol](docs/game/README.md#communication-protocol). Minimal setup: ```yaml espectre: ble_channel_enabled: auto ble_telemetry_interval_ms: 40 esp32_ble_server: id: espectre_ble_server services: - uuid: "d33ff46b-2203-4775-bc6f-b3a2c36af8f0" advertise: true characteristics: - id: espectre_ble_telemetry uuid: "119d5cac-48da-4bd9-bfc3-169805868258" notify: true - id: espectre_ble_sysinfo uuid: "c8c89ffa-c401-461f-9ffc-942fa04adfe3" read: true notify: true value: "" - id: espectre_ble_control uuid: "33ed9214-a8d7-40e8-82d1-c82747dcdc71" write: true write_no_response: true ``` Use this channel with any standard BLE client by implementing the same UUID/profile contract. If you do not use BLE integrations, you can remove the `esp32_ble_server:` section entirely. With `ble_channel_enabled: auto` (default), ESPectre will keep the BLE channel disabled when no BLE server is configured. This can slightly reduce runtime overhead (RAM/CPU usage and BLE radio activity), which may be useful on constrained boards or when you only use Home Assistant entities. --- ## Troubleshooting ### WiFi protocol/bandwidth shows "unavailable" On some targets or band modes, the WiFi driver may not expose protocol/bandwidth values through all read APIs. In this case, ESPectre logs: ``` WiFi Protocol: unavailable (...) WiFi Bandwidth: unavailable (...) ``` This is expected for unsupported read paths and does not necessarily indicate a WiFi connection failure. ### CSI packet length warnings (`wrong SC count`) ESPectre expects HT20 CSI payloads mapped to `128 bytes` (64 subcarriers). Runtime normalization already handles common alternate lengths: - `256 -> 128` (double HT-LTF / STBC-like packet) - `228 -> 114 -> 128` (double short HT estimate, then remap) - `114 -> 128` (57-subcarrier short HT estimate remapped to 64) If you still see repeated `Filtered ... wrong SC count` warnings, packets are likely arriving in another unsupported format. In that case, keep an eye on the logged metadata (`ch`, `bb`/`sig_mode`, `est_len`) and open an issue with logs and target chip/AP details. ### No motion detection 1. **Verify traffic generator is enabled** (`traffic_generator_rate > 0`) 2. Check WiFi is connected (look for IP address in logs) 3. Wait for band calibration to complete (~13 seconds after boot) 4. Adjust `segmentation_threshold` (try 0.5-2.0 for more sensitivity) ### False positives 1. Increase `segmentation_threshold` (try 2.0-5.0) 2. Check for interference sources (fans, AC, moving curtains) 3. Increase `segmentation_window_size` for more stable detection ### Calibration fails (MVS only) Applies only when `detector_algorithm: mvs` (default). The `ml` detector does not use NBVI calibration. 1. Ensure room is quiet during calibration (first ~13 seconds after boot) 2. Check traffic generator is running 3. Verify WiFi connection is stable **Note:** If band selection fails, the system automatically falls back to the shared default subcarriers `[12, 14, 16, 18, 20, 24, 28, 36, 40, 44, 48, 52]` with a default threshold of `1.0`. Motion detection still works but may be less optimal. Look for the log message `⚠ Fallback calibration: using default subcarriers`. ### SPIFFS partition not found If you see `SPIFFS partition could not be found` in logs, ESPectre's partition table was not applied correctly. This commonly happens when: - Combining ESPectre with other components like `bluetooth_proxy` or `esp32_ble_tracker` - Using a custom YAML instead of the provided examples - Another component is overriding the partition table **Solution:** 1. First, try a full flash erase and reflash: ```bash # Erase flash completely (replace /dev/ttyUSB0 with your port) esptool.py --port /dev/ttyUSB0 erase_flash # Then reflash esphome run your-config.yaml ``` Or use the ESPHome dashboard: click the three dots menu → "Install" → "Erase device before installing". 2. If the problem persists, create a custom partition table that includes SPIFFS. See the "Combining with Other Components" section above for examples. ### Unstable detection with mesh networks If you have a mesh WiFi network, the sensor may roam between access points causing CSI inconsistencies. Lock it to a specific AP using the BSSID. **For development files** (`espectre-*-dev.yaml`): 1. Add `wifi_bssid` to your `secrets.yaml`: ```yaml wifi_bssid: "AA:BB:CC:DD:EE:FF" ``` 2. Uncomment the `bssid` line in your config file: ```yaml wifi: networks: - ssid: !secret wifi_ssid password: !secret wifi_password bssid: !secret wifi_bssid ``` **For production files** (`espectre-*.yaml` with provisioning): Add the BSSID directly after configuring WiFi, or use the ESPHome dashboard to edit the configuration. To find your AP's BSSID: - Check your router's admin page - Use a WiFi analyzer app on your phone - Look in ESPectre logs after connection (shows connected BSSID) ### ESP32-C3 Super Mini issues If you're using an ESP32-C3 Super Mini (popular budget boards from AliExpress/Temu): 1. **No logs visible via USB**: ESPHome defaults to native USB Serial/JTAG on C3, but boards with external USB-UART bridges (like CH343, CP2102) need explicit UART0 configuration. Add this to your YAML: ```yaml logger: hardware_uart: UART0 ``` 2. **Calibration takes very long or fails**: Set `traffic_generator_rate: 94` or lower. Values of 95+ cause calibration to hang for 90+ minutes. 3. **Flash fails or board doesn't respond**: Some cheap clones don't support QIO flash mode. Add this to your YAML: ```yaml esphome: platformio_options: board_build.flash_mode: dio ``` ### Flash failed 1. Hold BOOT button on ESP32 2. Press RESET button 3. Release BOOT button 4. Run flash command again ### View logs ```bash # Via USB esphome logs .yaml # Via network (after first flash) esphome logs .yaml --device espectre.local ``` --- ## Next Steps - **Tuning Guide**: [TUNING.md](TUNING.md) - Optimize for your environment - **Main Documentation**: [README.md](README.md) - Full project overview --- ## License GPLv3 - See [LICENSE](LICENSE) for details.