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_hitsvalues (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
motion_off_hitscontrols the exit delay from MOTION to IDLE state, requiring multiple consecutive low-motion evaluations before transitioning.- Higher values reduce false negatives by maintaining MOTION state through brief signal dips, though they may increase false positive duration.
- Default value is 3, tunable via ESP-Home YAML, Python runtime policies, or direct C++ API calls.
- Implementation spans
components/espectre/csi_manager.cpp(filter logic),components/espectre/espectre.h(API), andmicro-espectre/src/runtime_policy.py(Python support). - Unit test validation exists in
test/test_csi_manager/test_csi_manager.cpp(lines 408-442), confirming the filter behavior.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →