How `motion_off_hits` Affects False Negative Rates in Espectre

Increasing motion_off_hits reduces false negative rates by requiring multiple consecutive low-motion evaluations before transitioning from MOTION to IDLE, preventing premature state changes during brief motion dips.

The motion_off_hits parameter is a critical configuration option in the Espectre Wi-Fi sensing framework (francescopace/espectre) that controls the persistence of motion detection states. It directly influences the trade-off between missing ongoing motion (false negatives) and incorrectly reporting motion after it has ceased (false positives).

Understanding the motion_off_hits Mechanism

The CSIManager State Filter

At the core of Espectre's motion detection pipeline is the CSIManager class, which implements an edge-driven state filter to stabilize binary sensor outputs. This filter prevents rapid state flickering by requiring a specific number of consecutive evaluations before confirming a state transition.

In components/espectre/csi_manager.cpp, the filter logic determines the required threshold based on the current pending state:

uint8_t required_hits = (pending_motion_state_ == MotionState::MOTION) 
    ? motion_on_hits_ 
    : motion_off_hits_;

if (pending_state_hits_ >= required_hits) {
    // State transition confirmed
}

When the system is in MOTION state and evaluates a low-motion reading, it increments pending_state_hits_ and checks against motion_off_hits_. Only when the consecutive hit count meets or exceeds this threshold does the effective state transition to IDLE.

Parameter Configuration and API

The parameter defaults to 3 and can be configured through multiple interfaces. The public API in components/espectre/espectre.h exposes a setter:

void set_motion_off_hits(uint8_t hits) { 
    this->motion_off_hits_ = hits; 
}

This value is also honored in the Micro-Espectre Python runtime policy, where micro-espectre/src/runtime_policy.py enforces a minimum value of 1:

self.motion_off_hits = max(1, int(motion_off_hits))

Impact on False Negative Detection

The Trade-off Between Latency and Reliability

A false negative occurs when the binary sensor reports IDLE despite genuine motion still occurring. The motion_off_hits parameter indirectly governs this by determining how quickly the system may revert to IDLE after motion metrics temporarily dip below the detection threshold.

  • Low values (1-2): The state can flip to IDLE after just one or two low-motion evaluations. This reduces latency for returning to idle but increases the risk of false negatives during continuous motion that momentarily dips below the threshold due to noise or brief RF fluctuations.

  • High values (5-10): Requires many consecutive low-motion evaluations before confirming IDLE. This keeps the sensor in MOTION longer, significantly decreasing the probability of missing ongoing motion, but may prolong false positive reports when motion has actually stopped.

Environment-Specific Tuning Recommendations

The optimal setting depends on your environment's motion dynamics:

  • Short, intermittent motion bursts: Use lower motion_off_hits values (2-3) to avoid lingering MOTION states, accepting a slight increase in false negatives.
  • Continuous motion tracking: Use higher values (5-8) to maintain MOTION state during brief signal dips, ensuring reliable capture of sustained activity.

Implementation Details

Core Logic in csi_manager.cpp

The hit counter logic is implemented in components/espectre/csi_manager.cpp (lines 111-118). When processing CSI packets, the manager evaluates motion states and maintains the pending_state_hits_ counter. The system only publishes a state change to the binary sensor when the accumulated hits meet the required threshold defined by motion_off_hits.

Configuration in ESP-Home and Micro-Espectre

In ESP-Home YAML configurations, the parameter is set at the component level:

espectre:
  name: living_room_sensor
  motion_on_hits: 3
  motion_off_hits: 5    # Wait for 5 consecutive low-motion evaluations

  evaluation_interval: 25

For Micro-Espectre Python deployments, configure via the RuntimeMotionPolicy:

from micro_espectre.runtime_policy import RuntimeMotionPolicy

policy = RuntimeMotionPolicy(
    evaluation_interval=25,
    motion_on_hits=3,
    motion_off_hits=4     # Require 4 OFF-hits before IDLE

)

Practical Configuration Examples

ESP-Home YAML Configuration

Configure the hit filter in your ESP-Home configuration to balance responsiveness and stability:

espectre:
  name: my_espectre
  motion_on_hits: 3          # Default activation threshold

  motion_off_hits: 5         # Extended persistence to reduce false negatives

  evaluation_interval: 25

The component reads this value in espectre.cpp and passes it to CSIManager during initialization.

Micro-Espectre Python Runtime

Adjust the parameter at runtime in Python micro-controller deployments:

from micro_espectre.runtime_policy import RuntimeMotionPolicy

# Create policy with reduced false-negative rate

policy = RuntimeMotionPolicy(
    evaluation_interval=25,
    motion_on_hits=3,
    motion_off_hits=6
)

# Apply to Spectre instance

spectre = Spectre()
spectre.set_runtime_policy(policy)

Direct C++ Integration

For custom components using the native API:

#include "espectre.h"

class MotionController : public Component {
  Spectre *spectre_;
 public:
  void setup() override {
    spectre_ = new Spectre();
    spectre_->set_motion_off_hits(4);   // Line 103 in espectre.h
    spectre_->init();
  }
};

Summary

Frequently Asked Questions

What is the default value for motion_off_hits in Espectre?

The default value is 3, as defined in the component initialization and documented in the README. This means the system requires three consecutive low-motion evaluations before transitioning from MOTION to IDLE, providing a balance between responsiveness and stability for most environments.

Does motion_off_hits affect how quickly motion is detected?

No, motion_off_hits only governs the transition from MOTION to IDLE. The transition from IDLE to MOTION is controlled by the separate motion_on_hits parameter. These two parameters operate independently, allowing you to tune activation and deactivation persistence separately based on your specific use case requirements.

How does motion_off_hits interact with packet loss or RF noise?

Espectre processes CSI packets at rates up to 100 packets per second. Brief RF fluctuations or occasional missed packets can cause single low-motion evaluations even during genuine motion. The motion_off_hits filter smooths out these transient drops, preventing the binary sensor from flickering and ensuring downstream automations (such as Home Assistant triggers) receive stable state signals rather than noisy transitions.

Can I change motion_off_hits at runtime, or only at compile time?

You can configure motion_off_hits at runtime through multiple mechanisms. In ESP-Home, set it via YAML configuration. In Micro-Espectre, instantiate a new RuntimeMotionPolicy with updated values and apply it to the running Spectre instance. In custom C++ components, call set_motion_off_hits() on the Spectre object at any point after initialization but before the main loop begins processing CSI data.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →