AT581X Radar Presence Sensor
The At581x component provides a driver for the AT581X (AirTouch) 5.8 GHz
microwave-radar human-presence / motion sensor. This is the radar found on
modules such as the MoreSense MS58-3909S68U4, including the one on the
ESP32-S3-BOX-3 sensor / dock board.
The chip is configured over I2C (detection distance / sensitivity, RF frequency,
gain, power consumption, and timing), while presence/motion is reported on a
dedicated active-high output GPIO. To react to presence, attach an interrupt
(e.g. espp::Interrupt) to the radar’s output GPIO.
API Reference
Header File
Classes
-
class At581x : public espp::BasePeripheral<>
Driver for the AT581X (AirTouch) 5.8 GHz microwave radar human-presence / motion sensor.
The AT581X is configured over I2C, while presence/motion is reported on a dedicated active-high output GPIO (which stays asserted for the configured “trigger keep” time after the last detection). This driver handles the I2C configuration of the chip (detection distance/sensitivity, RF frequency, gain, power, and timing); to react to presence, attach an interrupt (e.g. `espp::Interrupt`) to the radar’s output GPIO. See the example.
The AT581X is the radar found on modules such as the MoreSense `MS58-3909S68U4` used on the ESP32-S3-BOX-3 sensor / dock board (I2C on SDA=GPIO41, SCL=GPIO40 there).
The register protocol implemented here follows the AT581X datasheet and the reference drivers from Espressif and ESPHome.
AT581X Example
// Make the I2C bus the radar is on (SDA=41, SCL=40 on the ESP32-S3-BOX-3 sensor dock). espp::I2c i2c({ .port = RADAR_I2C_PORT, .sda_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SDA_GPIO, .scl_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO, }); std::error_code ec; auto radar_dev = i2c.add_device<uint8_t>({.device_address = espp::At581x::DEFAULT_ADDRESS, .timeout_ms = static_cast<int>(i2c.config().timeout_ms), .scl_speed_hz = i2c.config().clk_speed, .log_level = espp::Logger::Verbosity::WARN}, ec); if (!radar_dev) { logger.error("Could not add AT581X I2C device: {}", ec.message()); return; } // Create the radar driver. auto_init writes the configuration and resets the RF frontend. espp::At581x radar({ .write = espp::make_i2c_addressed_write(radar_dev), .read_register = espp::make_i2c_addressed_read_register(radar_dev), .sensing_distance = g_sensing_distance.load(), // 0..1023, larger = farther / more sensitive .trigger_keep_time_ms = 1000, .log_level = espp::Logger::Verbosity::INFO, }); // The AT581X reports presence on an active-high output GPIO. If it is wired up, attach an // interrupt to it so we get notified on presence/motion transitions. std::unique_ptr<espp::Interrupt> presence_interrupt; if (CONFIG_EXAMPLE_RADAR_OUTPUT_GPIO >= 0) { presence_interrupt = std::make_unique<espp::Interrupt>(espp::Interrupt::Config{ .interrupts = {{ .gpio_num = CONFIG_EXAMPLE_RADAR_OUTPUT_GPIO, .callback = [](const espp::Interrupt::Event &event) { g_presence = event.active; if (event.active) { g_presence_count++; } logger.info("Radar presence {}", event.active ? "DETECTED" : "cleared"); }, .active_level = espp::Interrupt::ActiveLevel::HIGH, .interrupt_type = espp::Interrupt::Type::ANY_EDGE, .pulldown_enabled = true, }}, .task_config = {.name = "radar presence", .stack_size_bytes = 4096}, .log_level = espp::Logger::Verbosity::WARN, }); logger.info("Watching radar output on GPIO {}", CONFIG_EXAMPLE_RADAR_OUTPUT_GPIO); } else { logger.warn("No radar output GPIO configured (CONFIG_EXAMPLE_RADAR_OUTPUT_GPIO); presence " "interrupt disabled. Set it to receive presence events."); }
Public Types
-
typedef std::function<bool(uint8_t)> probe_fn
Function to probe the peripheral
- Param address:
The address to probe
- Return:
True if the peripheral is found at the given address
Public Functions
-
inline explicit At581x(const Config &config)
Construct an AT581X driver.
- Parameters:
config – The configuration for the driver.
-
inline bool initialize(std::error_code &ec)
Initialize the AT581X by writing the current configuration and resetting it.
- Parameters:
ec – Set on error.
- Returns:
True on success.
-
inline bool write_config(std::error_code &ec)
Write the full configuration to the chip and reset its RF frontend so it takes effect.
- Parameters:
ec – Set on error.
- Returns:
True on success.
-
inline bool reset(std::error_code &ec)
Reset the AT581X RF frontend (required for new config to take effect).
- Parameters:
ec – Set on error.
- Returns:
True on success.
-
inline bool set_rf_enabled(bool enable, std::error_code &ec)
Turn the RF / analog frontend on or off (for power saving).
- Parameters:
enable – True to enable RF, false to disable.
ec – Set on error.
- Returns:
True on success.
-
inline bool set_sensing_distance(int distance, std::error_code &ec)
Set the detection distance / sensitivity (0..1023, larger = farther) and re-apply.
- Parameters:
distance – The new sensing distance.
ec – Set on error.
- Returns:
True on success.
-
inline bool set_gain(int gain, std::error_code &ec)
Set the gain stage index (0..12, higher = more gain) and re-apply.
- Parameters:
gain – The new gain index.
ec – Set on error.
- Returns:
True on success.
-
inline bool set_trigger_keep_time(int trigger_keep_time_ms, std::error_code &ec)
Set how long the output stays asserted after detection (ms) and re-apply.
- Parameters:
trigger_keep_time_ms – The new trigger-keep time in ms.
ec – Set on error.
- Returns:
True on success.
-
inline int get_sensing_distance() const
Get the current configured sensing distance (0..1023).
-
inline int get_gain() const
Get the current configured gain index (0..12).
-
inline bool probe(std::error_code &ec) const
Probe the peripheral
Note
This function is thread safe
Note
If the probe function is not set, this function will return false and set the error code to operation_not_supported
Note
This function is only available if UseAddress is true
- Parameters:
ec – The error code to set if there is an error
- Returns:
True if the peripheral is found
-
inline void set_address(uint8_t address)
Set the address of the peripheral
Note
This function is thread safe
Note
This function is only available if UseAddress is true
- Parameters:
address – The address of the peripheral
-
inline void set_probe(const probe_fn &probe)
Set the probe function
Note
This function is thread safe
Note
This should rarely be used, as the probe function is usually set in the constructor. If you need to change the probe function, consider using the set_config function instead.
Note
This function is only available if UseAddress is true
- Parameters:
probe – The probe function
-
inline void set_write(const write_fn &write)
Set the write function
Note
This function is thread safe
Note
This should rarely be used, as the write function is usually set in the constructor. If you need to change the write function, consider using the set_config function instead.
- Parameters:
write – The write function
-
inline void set_read(const read_fn &read)
Set the read function
Note
This function is thread safe
Note
This should rarely be used, as the read function is usually set in the constructor. If you need to change the read function, consider using the set_config function instead.
- Parameters:
read – The read function
-
inline void set_read_register(const read_register_fn &read_register)
Set the read register function
Note
This function is thread safe
Note
This should rarely be used, as the read register function is usually set in the constructor. If you need to change the read register function, consider using the set_config function instead.
- Parameters:
read_register – The read register function
-
inline void set_write_then_read(const write_then_read_fn &write_then_read)
Set the write then read function
Note
This function is thread safe
Note
This should rarely be used, as the write then read function is usually set in the constructor. If you need to change the write then
- Parameters:
write_then_read – The write then read function
-
inline void set_separate_write_then_read_delay(const std::chrono::milliseconds &delay)
Set the delay between the write and read operations in write_then_read
Note
This function is thread safe
Note
This should rarely be used, as the delay is usually set in the constructor. If you need to change the delay, consider using the set_config function instead.
Note
This delay is only used if the write_then_read function is not set to a custom function and the write and read functions are separate functions.
- Parameters:
delay – The delay between the write and read operations in write_then_read
-
inline void set_config(const Config &config)
Set the configuration for the peripheral
Note
This function is thread safe
Note
The configuration should normally be set in the constructor, but this function can be used to change the configuration after the peripheral has been created - for instance if the peripheral could be found on different communications buses.
- Parameters:
config – The configuration for the peripheral
-
inline void set_config(Config &&config)
Set the configuration for the peripheral
Note
This function is thread safe
Note
The configuration should normally be set in the constructor, but this function can be used to change the configuration after the peripheral has been created - for instance if the peripheral could be found on different communications buses.
- Parameters:
config – The configuration for the peripheral
-
inline const Config &config() const
Get the configuration for the peripheral
- Returns:
The configuration for the peripheral
-
inline uint8_t address() const
Get the address of the peripheral
- Returns:
The address of the peripheral
-
inline const std::string &get_name() const
Get the name of the component
Note
This is the tag of the logger
- Returns:
A const reference to the name of the component
-
inline void set_log_tag(const std::string_view &tag)
Set the tag for the logger
- Parameters:
tag – The tag to use for the logger
-
inline espp::Logger::Verbosity get_log_level() const
Get the log level for the logger
See also
See also
- Returns:
The verbosity level of the logger
-
inline void set_log_level(espp::Logger::Verbosity level)
Set the log level for the logger
See also
See also
- Parameters:
level – The verbosity level to use for the logger
-
inline void set_log_verbosity(espp::Logger::Verbosity level)
Set the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls set_log_level
- Parameters:
level – The verbosity level to use for the logger
-
inline espp::Logger::Verbosity get_log_verbosity() const
Get the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls get_log_level
- Returns:
The verbosity level of the logger
-
inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)
Set the rate limit for the logger
See also
Note
Only calls to the logger that have _rate_limit suffix will be rate limited
- Parameters:
rate_limit – The rate limit to use for the logger
Public Static Functions
-
static inline std::span<const uint16_t> allowed_frequencies_mhz()
The RF frequencies (MHz) accepted by frequency_mhz / set_frequency.
-
static inline std::span<const uint8_t> allowed_power_ua()
The power-consumption values (µA) accepted by power_consumption_ua.
Public Static Attributes
-
static constexpr uint8_t DEFAULT_ADDRESS = 0x28
Default I2C address of the AT581X.
-
struct Config
Configuration for the AT581X driver.
Public Members
-
uint8_t device_address = DEFAULT_ADDRESS
I2C address of the device.
-
BasePeripheral::write_fn write
Function for writing to the device.
-
BasePeripheral::read_register_fn read_register
Function for reading a register.
-
int frequency_mhz = 5800
RF frequency in MHz. Must be one of allowed_frequencies_mhz().
-
int sensing_distance = 823
Detection distance, 0..1023. Larger = farther/more sensitive.
-
int gain = 3
Gain stage index, 0..12. Higher = more gain.
-
int power_consumption_ua = 70
Power draw in µA. Must be one of allowed_power_ua().
-
int trigger_base_time_ms = 500
Base detection window in ms.
-
int trigger_keep_time_ms = 1500
How long the output stays asserted after detection, ms.
-
int protect_time_ms = 1000
Protection (re-trigger lockout) time in ms.
-
int poweron_selfcheck_time_ms = 2000
Power-on self-check time in ms (0..65535).
-
bool auto_init = true
If true, write the configuration to the chip on construction.
-
uint8_t device_address = DEFAULT_ADDRESS
-
typedef std::function<bool(uint8_t)> probe_fn