# Easydetek Radar Module Communication API Guide
English | [简体中文](README.md)
The `ed_radar` component provides an **object-oriented abstraction layer** for multiple Easydetek radar modules (such as `EDQ152`, `EDV163`, `EDV11P`, `EDV151`, etc.). Through this layer, application developers no longer need to worry about model-specific packet framing/parsing details or local structure differences — all modules are controlled through a unified interface, with strict incompatibility interception between models.
Serial-protocol references for each model are available in the [`docs/`](docs/) directory.
---
## 0. Using as an ESP Component Registry Component
This component is a standard ESP-IDF component (IDF v5.0+) with no private dependencies, and can be referenced directly through the component manager.
### 0.1 Referencing in a Project
Once published to the registry, run in the project root directory:
```bash
idf.py add-dependency "fuhua817/ed_radar^1.0.0"
```
Or add it manually to the project's `main/idf_component.yml` (pulling directly from Git is also supported):
```yaml
dependencies:
ed_radar:
git: https://github.com/fuhua817/ed_radar.git
version: "*"
```
### 0.2 Configuring the Component
```bash
idf.py menuconfig
# -> Component config -> ED Radar Configuration
```
Here you can select the radar model (EDQ152 / EDV163 / EDV163_ASCII / EDV11P / EDV151), UART port number, TX/RX/EN pins, baud rate, and protocol distance unit. When EDV163_ASCII is selected, an extra `RADAR_PIN_PRESENCE` option appears for the presence-status input pin (that model reads presence via a GPIO level; set it to -1 if unused).
### 0.3 Uploading the Component to the ESP Component Registry
1. Register an account at [components.espressif.com](https://components.espressif.com) (the namespace is your account name; `fuhua817` for this project);
2. Configure the API token (generate a token in the website account settings and save it to the local configuration — it will not enter the repository):
```bash
compote config set --api-token <your-token> --default-namespace fuhua817
```
3. Run in the ESP-IDF terminal:
```bash
compote component upload --namespace fuhua817 --name ed_radar
```
Before uploading, make sure `version` and `url` in `idf_component.yml` are correct. Increment `version` for every new release.
---
## 1. Architecture and Design Philosophy
The component adopts **polymorphism** and the **facade pattern**:
- **`ed_radar_obj_t` (base handle)**: the abstract handle exposed to the application layer; internally it holds the real device context of each model via `void *ctx`.
- **Down-cast interception**: inside each generic API, dispatch is performed with a `switch` on the device model specified at creation time (`obj->config.type`). If the physical model supports the feature (e.g. XYZ range configuration), the call is translated into the underlying structure and issued; if the action is incompatible (e.g. asking an EDQ152 to set XYZ installation height), a warning is logged and the call is intercepted (returning `ED_FAIL`) to prevent memory or logic errors.
---
## 2. Basic Usage Flow
### 2.1 Including the Header
The application layer only needs to include the top-level header — no need to touch the underlying protocol headers (unless down-cast configuration is required):
```c
#include "ed_radar.h"
```
### 2.2 Creating a Radar Instance
Create a radar handle through the `ed_radar_config_t` configuration structure (pass `NULL` or `&ED_RADAR_CONFIG_DEFAULT` to use the Kconfig defaults); the underlying layer automatically allocates the required memory and parsing structures.
```c
// Example 1: use Kconfig defaults (model selected in menuconfig)
ed_radar_obj_t *my_radar = ed_radar_creat(&ED_RADAR_CONFIG_DEFAULT);
// Example 2: custom configuration, creating an EDV11P instance
ed_radar_config_t cfg = {
.type = ED_RADAR_DEVICE_EDV11P,
.uart_num = 1,
.pin_tx = 5,
.pin_rx = 4,
.pin_en = 7,
.baudrate = 921600,
.buffer_size = 1024,
};
ed_radar_obj_t *my_radar = ed_radar_creat(&cfg);
if (my_radar == NULL) {
// handle instantiation failure
}
```
### 2.3 Data Pump (RX/TX Task)
Simply call the parsing core in a loop inside a dedicated data-receiving task. The underlying driver internally fetches bytes from the UART via `ed_radar_receive_data()`, assembles frames, and dispatches them to the model-specific parser. The application layer must **not** read the serial port itself first (otherwise it steals the data and the parser never receives complete frames):
```c
while (1) {
// internally fetches data and dispatches to the model-specific parser
ed_radar_data_handle(my_radar);
vTaskDelay(pdMS_TO_TICKS(10));
}
```
### 2.4 Reading the Presence Result
Once the data pump is running, query the detection result at any time:
```c
uint8_t has_person = ed_radar_get_presence(my_radar); // 1 = person present, 0 = no person
```
- EDQ152: present as soon as any channel reports delayed presence;
- EDV11P: derived from the presence flag and per-area target counts;
- EDV151: present when the vital-signs sleep state is anything other than "leave bed";
- Other models always return 0 (for EDV163, down-cast as in Section 5 to read channel data).
For debugging or frame tapping, you can register a raw UART receive callback (invoked in the data-pump task context — do not block inside it):
```c
ed_radar_register_raw_rx_callback(my_raw_rx_cb, user_ctx);
```
---
## 3. Standard Generic API Overview
All radars are aligned to these methods as much as possible; simply pass unified space-based physical parameters (millimeters, mm). **Calls unsupported by the current instance log a `not supported` warning and return `ED_FAIL`** — see the support matrix at the end of this section.
> Most set APIs have a matching get API (e.g. `ed_radar_get_detect_range`, `ed_radar_get_sensitivity`). The get series is currently only wired up for EDV11P — see the matrix.
### Getting Device Information
```c
ed_err_t ed_radar_get_module_info(ed_radar_obj_t *obj);
```
### Detection Result
```c
// Read the presence result: 1 = person present, 0 = no person
// EDQ152: delayed-presence state; EDV11P: presence flag plus per-area targets;
// EDV151: vital-signs sleep state (not "leave bed" means present);
// EDV163 / EDV163_ASCII: not wired up yet, always returns 0 (use Section 5 down-casting)
uint8_t ed_radar_get_presence(ed_radar_obj_t *obj);
```
### System-Level Control
```c
ed_err_t ed_radar_factory_reset(ed_radar_obj_t *obj);
ed_err_t ed_radar_set_work_mode(ed_radar_obj_t *obj, uint8_t mode); // EDV11P only
ed_err_t ed_radar_set_radar_onoff(ed_radar_obj_t *obj, uint8_t onoff); // EDQ152 / EDV163
ed_err_t ed_radar_set_report_freq(ed_radar_obj_t *obj, uint16_t period_ms); // EDV11P only
```
### Core Spatial and Boundary Control
These interfaces typically serve 2D/3D spatial radars (such as EDV163/EDV11P):
```c
// Set the actual radar installation height
ed_err_t ed_radar_set_install_height(ed_radar_obj_t *obj, uint16_t mm);
// Trigger automatic height measurement (radar calibrates its own height)
ed_err_t ed_radar_trigger_auto_measure_height(ed_radar_obj_t *obj);
// Limit the radar detection height range
ed_err_t ed_radar_set_detect_height(ed_radar_obj_t *obj, uint16_t min_mm, uint16_t max_mm);
// Set spatial XYZ-axis detection boundaries (unified ed_radar_detect_range_t)
ed_err_t ed_radar_set_detect_range(ed_radar_obj_t *obj, ed_radar_detect_range_t *range);
// Set shield / induction areas
ed_err_t ed_radar_set_shield_areas(ed_radar_obj_t *obj, uint8_t count, ed_radar_area_t *areas);
ed_err_t ed_radar_set_induct_areas(ed_radar_obj_t *obj, uint8_t count, ed_radar_area_t *areas);
```
### Delay and Sensitivity
```c
// Set the delay time for determining target absence
ed_err_t ed_radar_set_detect_delay_time(ed_radar_obj_t *obj, uint16_t delay_seconds);
// Set overall motion/presence detection sensitivity (internally expanded
// to all channels for multi-channel radars)
ed_err_t ed_radar_set_sensitivity(ed_radar_obj_t *obj, uint8_t motion, uint8_t presence);
```
### RF Parameters (EDV11P only)
```c
// Set transmit power level (0-100)
ed_err_t ed_radar_set_tx_power(ed_radar_obj_t *obj, uint8_t level);
```
### Model Support Matrix
✅ = supported; `—` = the call is intercepted and returns `ED_FAIL`.
| API | EDQ152 | EDV163 | EDV163_ASCII* | EDV11P | EDV151** |
| --- | :-: | :-: | :-: | :-: | :-: |
| `ed_radar_data_handle` (data pump) | ✅ | ✅ | — | ✅ | ✅ |
| `ed_radar_get_presence` | ✅ | — | — | ✅ | ✅ |
| `ed_radar_get_module_info` | ✅ | ✅ | — | ✅ | ✅ |
| `ed_radar_factory_reset` | ✅ | ✅ | — | ✅ | ✅ |
| `ed_radar_set_radar_onoff` | ✅ | ✅ | — | — | ✅ |
| `ed_radar_set_detect_delay_time` | ✅ | ✅ | — | ✅ | — |
| `ed_radar_set_install_height` | — | ✅ | — | ✅ | — |
| `ed_radar_set_detect_height` | — | ✅ | — | ✅ | — |
| `ed_radar_set_detect_range` | — | ✅ | — | ✅ | ✅ (1-D distance) |
| `ed_radar_set_shield_areas` | — | ✅ | — | ✅ | — |
| `ed_radar_set_sensitivity` | ✅ | — | — | ✅ | — |
| `ed_radar_trigger_auto_measure_height` | — | ✅ | — | ✅ | — |
| `ed_radar_set_trigger_mode` | — | ✅ | — | — | — |
| `ed_radar_set_work_mode` | — | — | — | ✅ | — |
| `ed_radar_set_report_freq` | — | — | — | ✅ | ✅ |
| `ed_radar_set_induct_areas` | — | — | — | ✅ | — |
| `ed_radar_set_tx_power` | — | — | — | ✅ | — |
| `ed_radar_set_trigger_enable` | ✅ | — | — | — | — |
| `ed_radar_set_energy_notify_onoff` | ✅ | — | — | — | — |
| `ed_radar_force_no_person` | ✅ | — | — | — | — |
| `ed_radar_set_presence_switch` | — | — | — | ✅ | — |
| Self-learning series (enter/exit/restore/status) | ✅ | — | — | ✅ | — |
> \* **EDV163_ASCII** currently only supports instance creation (`ed_radar_creat`) at the abstraction layer. After creation, down-cast via `ed_radar_get_ctx()` and call the model-specific APIs in `radar/edv163_ascii.h`: run `edv163_ascii_data_handle()` in the data-pump loop, and read presence with `edv163_ascii_get_delay_presence()` (based on the `RADAR_PIN_PRESENCE` GPIO level).
>
> \*\* **EDV151** is a vital-signs monitoring radar (sleep/heart rate/breathing). Its RF is **off by default** — call `ed_radar_set_radar_onoff(obj, 1)` after creation to start vital-signs reporting. Sleep reports and other specific capabilities are available via down-casting through `radar/edv151.h` (see `docs/EDV151-N-H01串口通用协议.md`).
---
## 4. Model-Specific APIs (Silent Interception)
Some radars have unique features (such as the environment learning mode), which you can call freely at this layer. If the current instance does not support a call, a `not supported` warning is logged and `ED_FAIL` is returned.
### Environment Self-Learning Series (EDQ152 / EDV11P)
```c
ed_err_t ed_radar_enter_self_learning(ed_radar_obj_t *obj, uint16_t duration_seconds);
ed_err_t ed_radar_exit_self_learning(ed_radar_obj_t *obj, uint8_t save);
ed_err_t ed_radar_restore_self_learning(ed_radar_obj_t *obj);
ed_err_t ed_radar_get_self_learning_status(ed_radar_obj_t *obj, uint16_t *remaining_seconds);
```
### Special Forced Control
```c
// EDQ152: force a frame reporting "no person"
ed_err_t ed_radar_force_no_person(ed_radar_obj_t *obj);
// EDQ152: external trigger enable
ed_err_t ed_radar_set_trigger_enable(ed_radar_obj_t *obj, uint8_t enable);
// EDQ152: raw energy-map output switch
ed_err_t ed_radar_set_energy_notify_onoff(ed_radar_obj_t *obj, uint8_t onoff);
// EDV163: trigger mode (0-3)
ed_err_t ed_radar_set_trigger_mode(ed_radar_obj_t *obj, uint8_t mode);
// EDV11P: dedicated presence-detection switch
ed_err_t ed_radar_set_presence_switch(ed_radar_obj_t *obj, uint8_t enable);
// EDV11P: target-missing delay (equivalent alias of ed_radar_set_detect_delay_time)
ed_err_t ed_radar_set_target_missing_delay(ed_radar_obj_t *obj, uint16_t delay_s);
```
---
## 5. Advanced Development: Model-Specific Down-Casting
Abstraction requires compromise. For example, `ed_radar_set_detect_range` requires generic XYZ parameters, but **independent fine-grained per-channel configuration on a multi-sector radar (EDQ152)** cannot be covered by the abstraction layer.
`ed_radar_obj_t` is an **opaque handle** to the application layer (its internal members are not directly accessible). In this case, use `ed_radar_get_ctx()` to safely retrieve the `ctx` pointer and call the driver-specific header:
```c
#include "ed_radar.h"
#include "radar/edq152.h" // include the driver-specific header
// type is the device model used when creating the instance (i.e. ed_radar_config_t.type)
void custom_edq152_setup(ed_radar_obj_t *obj, uint32_t type)
{
// check whether the instance is an EDQ152
if (type == ED_RADAR_DEVICE_EDQ152) {
edq152_channel_range_t ch_range = {0};
// exclusive customization: set channel 1 to 0m - 3m
ch_range.channel = 1;
ch_range.min_motion_dist = 0;
ch_range.max_motion_dist = 3000;
// retrieve the real handle via ed_radar_get_ctx() and safely cast to edq152_t
edq152_set_detect_distance((edq152_t*)ed_radar_get_ctx(obj), ch_range);
}
}
```
With this approach, we achieve **high reuse and unification of 95% of the core-layer code**, while preserving **safe penetration to the remaining 5% of model-specific capabilities at the application layer**.
idf.py add-dependency "fuhua817/ed_radar^1.1.0"