编程技术分享

分享编程知识,探讨技术创新

0%

简介:因为米家的人在传感器接入HA会有延时(还不便宜),同时海凌科的LD2410看起来又很成熟,就想着自己DIY一下,还能做成matter的送人!BOM成本不到40

我将针对您提出的毫米波人体存在传感器项目,详细阐述最适合的代码设计架构,并提供相应的C代码实现方案。这个方案将基于实践验证的技术和方法,力求构建一个可靠、高效、可扩展的嵌入式系统平台,同时满足Home Assistant和Matter协议的接入需求,并控制BOM成本在40元人民币以内。
关注微信公众号,提前获取相关推文

项目背景理解

您的项目目标明确,旨在DIY一个低成本、高性能的毫米波人体存在传感器,以解决现有米家传感器接入Home Assistant延时较高且价格昂贵的问题。选择海凌科LD2410B芯片作为核心传感器,因为它在市场上已经非常成熟,且成本可控。同时,支持Matter协议,使其具备更好的互操作性和未来扩展性。

系统架构设计

为了实现可靠、高效、可扩展的系统,我将采用分层架构的设计思想,将系统划分为以下几个层次:

  1. **硬件抽象层 (HAL - Hardware Abstraction Layer)**:

    • 目的:隔离硬件差异,为上层软件提供统一的硬件接口。
    • 职责:封装底层硬件驱动,如GPIO、UART、SPI、I2C、定时器、ADC等。
    • 优势:提高代码的可移植性,方便更换底层硬件平台。
  2. **驱动层 (Driver Layer)**:

    • 目的:驱动具体的硬件设备,实现硬件的功能。
    • 职责:LD2410B传感器驱动、Wi-Fi驱动(如果需要无线连接)、Matter协议栈驱动、Home Assistant集成驱动(例如MQTT客户端)。
    • 优势:模块化设计,方便添加新的硬件设备或协议支持。
  3. **中间件层 (Middleware Layer)**:

    • 目的:提供通用的服务和功能,简化应用层开发。
    • 职责:数据处理模块(解析LD2410B数据)、状态管理模块(传感器状态、连接状态)、配置管理模块(设备配置参数)、事件处理模块(传感器事件、网络事件)。
    • 优势:提高代码的复用性,减少重复开发。
  4. **应用层 (Application Layer)**:

    • 目的:实现系统的核心业务逻辑。
    • 职责:传感器数据采集与处理、人体存在状态判断、Matter设备模型实现、Home Assistant集成逻辑、用户配置界面(如果需要)。
    • 优势:专注业务逻辑实现,简化开发流程。

代码设计架构详解

1. 硬件抽象层 (HAL)

HAL层是整个系统的基石,它将直接与硬件交互,并向上层提供统一的接口。对于这个项目,HAL层需要涵盖以下几个方面:

  • GPIO驱动: 用于控制LD2410B的使能引脚、指示灯等。
  • UART驱动: 用于与LD2410B进行串口通信,接收传感器数据。
  • 定时器驱动: 用于实现周期性任务,如传感器数据读取、状态更新等。
  • 电源管理驱动: 用于低功耗模式管理(可选,如果需要)。

HAL层接口设计示例 (C代码头文件 hal.h)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#ifndef HAL_H
#define HAL_H

#include <stdint.h>
#include <stdbool.h>

// GPIO 定义
typedef enum {
GPIO_PIN_LD2410B_EN,
GPIO_PIN_LED_STATUS,
// ... 其他 GPIO 引脚
GPIO_PIN_COUNT
} gpio_pin_t;

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT
} gpio_mode_t;

typedef enum {
GPIO_LEVEL_LOW,
GPIO_LEVEL_HIGH
} gpio_level_t;

// UART 定义
typedef enum {
UART_PORT_LD2410B,
// ... 其他 UART 端口
UART_PORT_COUNT
} uart_port_t;

typedef struct {
uint32_t baudrate;
uint8_t data_bits;
uint8_t stop_bits;
char parity; // 'N', 'E', 'O'
} uart_config_t;

// 定时器定义
typedef enum {
TIMER_ID_SENSOR_READ,
TIMER_ID_STATUS_UPDATE,
// ... 其他定时器 ID
TIMER_ID_COUNT
} timer_id_t;

typedef struct {
uint32_t period_ms;
bool auto_reload;
void (*callback)(void);
} timer_config_t;

// HAL 层函数声明

// GPIO
void hal_gpio_init(gpio_pin_t pin, gpio_mode_t mode);
void hal_gpio_write(gpio_pin_t pin, gpio_level_t level);
gpio_level_t hal_gpio_read(gpio_pin_t pin);

// UART
bool hal_uart_init(uart_port_t port, const uart_config_t *config);
bool hal_uart_send_byte(uart_port_t port, uint8_t data);
bool hal_uart_receive_byte(uart_port_t port, uint8_t *data);
bool hal_uart_receive_data(uart_port_t port, uint8_t *data, uint32_t len, uint32_t timeout_ms);

// 定时器
bool hal_timer_init(timer_id_t timer_id, const timer_config_t *config);
bool hal_timer_start(timer_id_t timer_id);
bool hal_timer_stop(timer_id_t timer_id);

// ... 其他 HAL 函数

#endif // HAL_H

HAL层实现示例 (C代码文件 hal_esp32.c - 假设使用ESP32平台)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include "hal.h"
#include "esp_system.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "driver/timer.h"

// GPIO 实现 (ESP32 specific)
void hal_gpio_init(gpio_pin_t pin, gpio_mode_t mode) {
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
if (mode == GPIO_MODE_OUTPUT) {
io_conf.mode = GPIO_MODE_OUTPUT;
} else {
io_conf.mode = GPIO_MODE_INPUT;
}
io_conf.pin_bit_mask = (1ULL << pin); // 将 pin 转换为位掩码
gpio_config(&io_conf);
}

void hal_gpio_write(gpio_pin_t pin, gpio_level_t level) {
gpio_set_level(pin, (level == GPIO_LEVEL_HIGH) ? 1 : 0);
}

gpio_level_t hal_gpio_read(gpio_pin_t pin) {
return (gpio_get_level(pin) == 1) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW;
}

// UART 实现 (ESP32 specific)
bool hal_uart_init(uart_port_t port, const uart_config_t *config) {
uart_config_t uart_config_esp32 = {
.baud_rate = config->baudrate,
.data_bits = config->data_bits,
.parity = config->parity == 'E' ? UART_PARITY_EVEN : (config->parity == 'O' ? UART_PARITY_ODD : UART_PARITY_DISABLE),
.stop_bits = config->stop_bits,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_APB,
};
int uart_port_num = (port == UART_PORT_LD2410B) ? UART_NUM_0 : -1; // 假设 LD2410B 使用 UART0

if (uart_port_num == -1) return false; // 无效端口

ESP_ERROR_CHECK(uart_param_config(uart_port_num, &uart_config_esp32));
ESP_ERROR_CHECK(uart_driver_install(uart_port_num, 2048, 2048, 0, NULL, 0)); // 缓冲区大小可调整
return true;
}

bool hal_uart_send_byte(uart_port_t port, uint8_t data) {
int uart_port_num = (port == UART_PORT_LD2410B) ? UART_NUM_0 : -1;
if (uart_port_num == -1) return false;
return (uart_write_bytes(uart_port_num, (const char *)&data, 1) == 1);
}

bool hal_uart_receive_byte(uart_port_t port, uint8_t *data) {
int uart_port_num = (port == UART_PORT_LD2410B) ? UART_NUM_0 : -1;
if (uart_port_num == -1) return false;
return (uart_read_bytes(uart_port_num, data, 1, 10 / portTICK_PERIOD_MS) == 1); // 10ms 超时
}

bool hal_uart_receive_data(uart_port_t port, uint8_t *data, uint32_t len, uint32_t timeout_ms) {
int uart_port_num = (port == UART_PORT_LD2410B) ? UART_NUM_0 : -1;
if (uart_port_num == -1) return false;
return (uart_read_bytes(uart_port_num, data, len, timeout_ms / portTICK_PERIOD_MS) == len);
}


// 定时器实现 (ESP32 specific)
static void IRAM_ATTR timer_isr_handler(void *param); // ISR 需要 IRAM 属性

typedef struct {
timer_config_t config;
void (*callback)(void);
} timer_context_t;

static timer_context_t timer_contexts[TIMER_ID_COUNT];
static bool timer_inited[TIMER_ID_COUNT] = {false};

bool hal_timer_init(timer_id_t timer_id, const timer_config_t *config) {
if (timer_id >= TIMER_ID_COUNT || timer_inited[timer_id]) return false;

timer_contexts[timer_id].config = *config;
timer_contexts[timer_id].callback = config->callback;
timer_inited[timer_id] = true;

timer_config_t timer_config_esp32 = {
.alarm_en = TIMER_ALARM_EN,
.auto_reload = config->auto_reload,
.counter_dir = TIMER_COUNT_UP,
.divider = 80, // 80 MHz APB_CLK / 80 = 1 MHz timer clock
.intr_arm = true,
.counter_en = TIMER_PAUSE, // 初始化时暂停
};
timer_init(TIMER_GROUP_0, TIMER_0 + timer_id, &timer_config_esp32); // 使用 TIMER_GROUP_0 和 TIMER_0 + timer_id

timer_set_alarm_value(TIMER_GROUP_0, TIMER_0 + timer_id, config->period_ms * 1000); // period_ms 转换为 us
timer_enable_intr(TIMER_GROUP_0, TIMER_0 + timer_id);
timer_isr_register(TIMER_GROUP_0, TIMER_0 + timer_id, timer_isr_handler, (void*)timer_id, ESP_INTR_FLAG_IRAM, NULL);

return true;
}


bool hal_timer_start(timer_id_t timer_id) {
if (timer_id >= TIMER_ID_COUNT || !timer_inited[timer_id]) return false;
timer_start(TIMER_GROUP_0, TIMER_0 + timer_id);
return true;
}

bool hal_timer_stop(timer_id_t timer_id) {
if (timer_id >= TIMER_ID_COUNT || !timer_inited[timer_id]) return false;
timer_pause(TIMER_GROUP_0, TIMER_0 + timer_id);
return true;
}


static void IRAM_ATTR timer_isr_handler(void *param) {
uint32_t timer_id = (uint32_t)param;
timer_intr_clear(TIMER_GROUP_0, TIMER_0 + timer_id); // 清除中断标志

if (timer_contexts[timer_id].callback != NULL) {
timer_contexts[timer_id].callback(); // 调用用户回调函数
}
if (timer_contexts[timer_id].config.auto_reload) {
timer_set_alarm_value(TIMER_GROUP_0, TIMER_0 + timer_id, timer_contexts[timer_id].config.period_ms * 1000); // 重新设置 alarm value (虽然 auto_reload 应该会自动做,但为了保险起见)
timer_start(TIMER_GROUP_0, TIMER_0 + timer_id); // 重新启动定时器 (虽然 auto_reload 应该会自动做,但为了保险起见)
}
}


// ... 其他 HAL 函数实现 (例如电源管理)

2. 驱动层 (Driver Layer)

驱动层建立在HAL层之上,负责驱动具体的硬件设备。在这个项目中,驱动层主要包括:

  • LD2410B 传感器驱动 (ld2410b_driver.c, ld2410b_driver.h): 封装与LD2410B模块串口通信的逻辑,包括发送指令、接收数据、解析数据帧,并向上层提供易于使用的API接口。
  • Wi-Fi 驱动 (wifi_driver.c, wifi_driver.h): 负责Wi-Fi连接管理,例如扫描AP、连接AP、断开连接、获取IP地址等。 (如果需要无线连接)
  • Matter 协议栈驱动 (matter_driver.c, matter_driver.h): 集成Matter协议栈,实现Matter设备的初始化、设备发现、属性读写、事件上报等功能。 (使用例如 ESP-IDF Matter SDK)
  • Home Assistant 集成驱动 (ha_driver.c, ha_driver.h): 实现与Home Assistant的集成,例如MQTT客户端的初始化、连接、订阅、发布消息等。 (使用例如 MQTT 协议)

LD2410B 传感器驱动示例 (C代码 ld2410b_driver.c, ld2410b_driver.h)

ld2410b_driver.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#ifndef LD2410B_DRIVER_H
#define LD2410B_DRIVER_H

#include <stdint.h>
#include <stdbool.h>

// LD2410B 数据结构定义 (根据 LD2410B 协议文档定义)
typedef struct {
uint8_t frame_header; // 帧头
uint8_t data_type; // 数据类型
uint16_t frame_length; // 帧长度
uint8_t status; // 状态
uint16_t distance; // 距离 (mm)
uint8_t motion_state; // 移动状态 (0: 静止, 1: 移动)
uint8_t presence_state; // 存在状态 (0: 无人, 1: 有人)
uint8_t reserved[4]; // 保留字节
uint16_t crc; // CRC 校验
uint8_t frame_tail; // 帧尾
} ld2410b_data_frame_t;

// LD2410B 驱动初始化
bool ld2410b_driver_init(uart_port_t uart_port);

// 读取 LD2410B 数据
bool ld2410b_driver_read_data(ld2410b_data_frame_t *data);

// 发送 LD2410B 指令 (示例,根据 LD2410B 协议定义指令)
bool ld2410b_driver_send_command(uint8_t command_code, uint8_t *data, uint16_t data_len);

// 获取当前的人体存在状态 (封装数据解析逻辑)
bool ld2410b_driver_get_presence_state(bool *presence);

#endif // LD2410B_DRIVER_H

ld2410b_driver.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include "ld2410b_driver.h"
#include "hal.h"
#include <string.h>

#define LD2410B_UART_PORT UART_PORT_LD2410B
#define LD2410B_BAUDRATE 115200 // LD2410B 默认波特率

// 内部缓冲区大小
#define LD2410B_RECEIVE_BUFFER_SIZE 128

static uint8_t ld2410b_receive_buffer[LD2410B_RECEIVE_BUFFER_SIZE];
static uint32_t ld2410b_receive_index = 0;

// CRC 校验函数 (需要根据 LD2410B 协议实现 CRC16 校验)
static uint16_t calculate_crc16(const uint8_t *data, uint16_t length) {
// ... CRC16 校验算法实现 (例如 Modbus CRC16)
uint16_t crc = 0xFFFF;
for (uint16_t i = 0; i < length; i++) {
crc ^= data[i];
for (int j = 0; j < 8; j++) {
if ((crc & 0x0001) != 0) {
crc >>= 1;
crc ^= 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}


bool ld2410b_driver_init(uart_port_t uart_port) {
uart_config_t uart_config = {
.baudrate = LD2410B_BAUDRATE,
.data_bits = UART_DATA_8_BITS,
.stop_bits = UART_STOP_BITS_1,
.parity = UART_PARITY_DISABLE
};
if (!hal_uart_init(uart_port, &uart_config)) {
return false;
}
return true;
}

bool ld2410b_driver_read_data(ld2410b_data_frame_t *data) {
uint8_t byte;
uint32_t timeout_ms = 100; // 接收超时时间

while (1) {
if (hal_uart_receive_byte(LD2410B_UART_PORT, &byte)) {
ld2410b_receive_buffer[ld2410b_receive_index++] = byte;

if (ld2410b_receive_index >= sizeof(ld2410b_data_frame_t)) { // 接收到足够的数据帧长度
ld2410b_data_frame_t *frame = (ld2410b_data_frame_t *)ld2410b_receive_buffer;

// 帧头和帧尾校验
if (frame->frame_header == 0xAA && frame->frame_tail == 0xBB) {
// CRC 校验
uint16_t calculated_crc = calculate_crc16((uint8_t *)frame, sizeof(ld2410b_data_frame_t) - 2); // CRC 校验范围,不包括 CRC 本身和帧尾
if (calculated_crc == frame->crc) {
memcpy(data, frame, sizeof(ld2410b_data_frame_t));
ld2410b_receive_index = 0; // 重置接收索引
return true; // 数据接收成功
} else {
// CRC 校验失败,丢弃当前帧,重新接收
ld2410b_receive_index = 0;
return false; // 返回错误,但可以继续尝试接收
}
} else {
// 帧头或帧尾错误,丢弃当前字节,继续接收
// 可以优化为找到下一个帧头 0xAA 后再开始接收
ld2410b_receive_index = 0; // 简单丢弃当前接收到的所有数据,重新开始
return false; // 返回错误,但可以继续尝试接收
}
}
} else {
// 接收超时
ld2410b_receive_index = 0; // 重置接收索引
return false; // 返回接收超时错误
}
}
}

bool ld2410b_driver_send_command(uint8_t command_code, uint8_t *data, uint16_t data_len) {
// ... 根据 LD2410B 协议构建指令帧,计算 CRC,并使用 hal_uart_send_byte 发送
// ... (需要参考 LD2410B 协议文档实现)
return false; // 示例,实际需要实现指令发送逻辑
}

bool ld2410b_driver_get_presence_state(bool *presence) {
ld2410b_data_frame_t data_frame;
if (ld2410b_driver_read_data(&data_frame)) {
*presence = (data_frame.presence_state == 1);
return true;
} else {
return false;
}
}

3. 中间件层 (Middleware Layer)

中间件层提供通用的服务和功能,简化应用层开发。在这个项目中,中间件层可以包括:

  • 数据处理模块 (data_process.c, data_process.h): 对LD2410B传感器数据进行处理和分析,例如滤波、阈值判断,提取人体存在状态等。
  • 状态管理模块 (state_manager.c, state_manager.h): 管理系统的状态,如传感器状态、Wi-Fi连接状态、Matter连接状态、Home Assistant连接状态等。提供状态查询和状态更新接口。
  • 配置管理模块 (config_manager.c, config_manager.h): 负责设备的配置参数管理,例如设备名称、Wi-Fi配置、Matter配置、Home Assistant配置等。可以从非易失性存储器(例如 Flash)读取配置,并提供配置更新和保存接口。
  • 事件处理模块 (event_handler.c, event_handler.h): 处理系统事件,例如传感器数据更新事件、网络连接事件、Matter事件、Home Assistant事件等。事件处理模块可以将事件传递给应用层进行处理。

数据处理模块示例 (data_process.c, data_process.h)

data_process.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef DATA_PROCESS_H
#define DATA_PROCESS_H

#include <stdbool.h>

// 人体存在状态定义
typedef enum {
PRESENCE_STATE_NONE,
PRESENCE_STATE_DETECTED
} presence_state_t;

// 初始化数据处理模块
bool data_process_init(void);

// 处理 LD2410B 数据帧,并更新人体存在状态
presence_state_t data_process_handle_ld2410b_data(const void *ld2410b_data); // 假设输入是原始数据,可以根据实际情况调整

// 获取当前人体存在状态
presence_state_t data_process_get_presence_state(void);

#endif // DATA_PROCESS_H

data_process.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "data_process.h"
#include "ld2410b_driver.h" // 需要用到 LD2410B 的数据结构
#include <stdbool.h>

static presence_state_t current_presence_state = PRESENCE_STATE_NONE;

bool data_process_init(void) {
// 初始化数据处理模块 (例如,初始化滤波器参数)
return true;
}

presence_state_t data_process_handle_ld2410b_data(const void *ld2410b_data) {
const ld2410b_data_frame_t *frame = (const ld2410b_data_frame_t *)ld2410b_data;

if (frame->presence_state == 1) {
current_presence_state = PRESENCE_STATE_DETECTED;
} else {
current_presence_state = PRESENCE_STATE_NONE;
}

// 可以添加更复杂的数据处理逻辑,例如:
// - 移动状态判断 (frame->motion_state)
// - 距离信息处理 (frame->distance)
// - 滤波处理 (例如,移动平均滤波)
// - 阈值判断 (根据距离或移动状态判断是否真的有人存在)

return current_presence_state;
}

presence_state_t data_process_get_presence_state(void) {
return current_presence_state;
}

4. 应用层 (Application Layer)

应用层是系统的最高层,负责实现核心业务逻辑。在这个项目中,应用层主要包括:

  • 主应用程序 (main.c): 系统入口,负责初始化各个模块,创建任务,启动调度器。
  • 传感器数据采集任务 (sensor_task.c, sensor_task.h): 周期性地读取LD2410B传感器数据,调用数据处理模块进行处理,并更新人体存在状态。
  • Matter 设备任务 (matter_task.c, matter_task.h): 负责Matter设备的运行,处理来自Matter控制器的指令,上报传感器数据和状态。
  • Home Assistant 任务 (ha_task.c, ha_task.h): 负责与Home Assistant的集成,连接MQTT Broker,发布传感器数据和状态。
  • 状态指示任务 (status_led_task.c, status_led_task.h): 控制状态指示LED,显示设备的工作状态(例如,Wi-Fi连接状态、Matter连接状态、传感器状态)。
  • 配置界面任务 (可选,例如 web_config_task.c, web_config_task.h 或通过 Matter/HA 配置): 提供用户配置界面,用于配置Wi-Fi、Matter、Home Assistant等参数。

主应用程序示例 (main.c) - 基于 FreeRTOS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hal.h"
#include "ld2410b_driver.h"
#include "data_process.h"
#include "state_manager.h"
#include "config_manager.h"
#include "event_handler.h"
#include "matter_task.h"
#include "ha_task.h"
#include "status_led_task.h"

void app_main(void) {
// 硬件初始化
hal_gpio_init(GPIO_PIN_LED_STATUS, GPIO_MODE_OUTPUT);
hal_gpio_write(GPIO_PIN_LED_STATUS, GPIO_LEVEL_LOW); // 初始状态 LED 关闭

// 模块初始化
if (!ld2410b_driver_init(UART_PORT_LD2410B)) {
printf("LD2410B driver init failed\n");
// 错误处理
}
if (!data_process_init()) {
printf("Data process init failed\n");
// 错误处理
}
if (!state_manager_init()) {
printf("State manager init failed\n");
// 错误处理
}
if (!config_manager_init()) {
printf("Config manager init failed\n");
// 错误处理
}
if (!event_handler_init()) {
printf("Event handler init failed\n");
// 错误处理
}

// 创建任务
xTaskCreate(sensor_task, "Sensor Task", 4096, NULL, 5, NULL);
xTaskCreate(matter_task, "Matter Task", 8192, NULL, 4, NULL); // Matter 协议栈可能需要更多内存
xTaskCreate(ha_task, "HA Task", 4096, NULL, 3, NULL);
xTaskCreate(status_led_task, "Status LED Task", 2048, NULL, 2, NULL);

printf("System initialized, starting scheduler\n");
vTaskStartScheduler(); // 启动 FreeRTOS 调度器
}

传感器数据采集任务示例 (sensor_task.c)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "sensor_task.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "ld2410b_driver.h"
#include "data_process.h"
#include "state_manager.h"
#include "event_handler.h"

#define SENSOR_READ_PERIOD_MS 100 // 传感器数据读取周期

void sensor_task(void *pvParameters) {
ld2410b_data_frame_t ld2410b_data;
presence_state_t presence_state;

while (1) {
if (ld2410b_driver_read_data(&ld2410b_data)) {
presence_state = data_process_handle_ld2410b_data(&ld2410b_data);
state_manager_set_presence_state(presence_state); // 更新状态管理模块
event_handler_post_presence_event(presence_state); // 发布人体存在事件
// 可以根据需要发布其他事件,例如距离事件、移动事件
} else {
printf("LD2410B data read failed\n");
// 错误处理,例如重试、重启传感器驱动等
}

vTaskDelay(pdMS_TO_TICKS(SENSOR_READ_PERIOD_MS));
}
}

Matter 设备任务示例 (matter_task.c) - 伪代码,需要根据具体的 Matter SDK 和 API 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "matter_task.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "state_manager.h"
#include "event_handler.h"
// #include "matter_sdk_api.h" // 假设的 Matter SDK 头文件

void matter_task(void *pvParameters) {
// 初始化 Matter 协议栈 (根据 Matter SDK 文档)
// matter_sdk_init();
// matter_device_descriptor_t device_desc = { ... }; // 定义 Matter 设备描述符
// matter_device_create(&device_desc);

while (1) {
// 处理 Matter 事件循环 (根据 Matter SDK 文档)
// matter_sdk_event_loop();

// 从状态管理模块获取人体存在状态
presence_state_t presence_state = state_manager_get_presence_state();

// 将人体存在状态更新到 Matter 设备属性 (例如,使用 occupancy 传感器 cluster)
// matter_device_set_attribute(occupancy_attribute_id, (presence_state == PRESENCE_STATE_DETECTED) ? true : false);

vTaskDelay(pdMS_TO_TICKS(1000)); // Matter 任务周期
}
}

Home Assistant 任务示例 (ha_task.c) - 基于 MQTT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include "ha_task.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "state_manager.h"
#include "event_handler.h"
// #include "mqtt_client.h" // 假设的 MQTT 客户端库头文件
#include <stdio.h>
#include <string.h>

#define HA_MQTT_BROKER_URI "mqtt://your_mqtt_broker_ip:1883" // 替换为你的 MQTT Broker 地址
#define HA_MQTT_CLIENT_ID "ld2410b_presence_sensor"
#define HA_MQTT_USERNAME "your_mqtt_username" // 可选
#define HA_MQTT_PASSWORD "your_mqtt_password" // 可选
#define HA_MQTT_PRESENCE_TOPIC "homeassistant/binary_sensor/ld2410b_presence/state" // MQTT Topic

// static mqtt_client_handle_t mqtt_client = NULL; // MQTT 客户端句柄

void ha_task(void *pvParameters) {
// 初始化 MQTT 客户端 (根据 MQTT 客户端库文档)
// mqtt_client_config_t mqtt_config = {
// .uri = HA_MQTT_BROKER_URI,
// .client_id = HA_MQTT_CLIENT_ID,
// .username = HA_MQTT_USERNAME, // 可选
// .password = HA_MQTT_PASSWORD, // 可选
// };
// mqtt_client = mqtt_client_init(&mqtt_config);
// mqtt_client_start(mqtt_client);

while (1) {
// 等待人体存在事件 (来自事件处理模块)
presence_state_t presence_state;
if (event_handler_wait_presence_event(&presence_state, portMAX_DELAY)) {
// 将人体存在状态发布到 MQTT Topic
char presence_payload[10];
sprintf(presence_payload, "%s", (presence_state == PRESENCE_STATE_DETECTED) ? "ON" : "OFF");
printf("Publishing to MQTT: Topic=%s, Payload=%s\n", HA_MQTT_PRESENCE_TOPIC, presence_payload);
// mqtt_client_publish(mqtt_client, HA_MQTT_PRESENCE_TOPIC, presence_payload, strlen(presence_payload), 0, 0); // 发布 MQTT 消息
}
}
}

状态指示任务示例 (status_led_task.c)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "status_led_task.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hal.h"
#include "state_manager.h"

#define STATUS_LED_PERIOD_MS 500 // LED 状态更新周期

void status_led_task(void *pvParameters) {
bool led_state = false;
while (1) {
// 根据系统状态 (例如 Wi-Fi 连接状态、Matter 连接状态、传感器状态) 控制 LED 闪烁或常亮
// 这里只是一个简单的示例,根据人体存在状态控制 LED

presence_state_t presence_state = state_manager_get_presence_state();
if (presence_state == PRESENCE_STATE_DETECTED) {
hal_gpio_write(GPIO_PIN_LED_STATUS, GPIO_LEVEL_HIGH); // 有人时 LED 亮
} else {
hal_gpio_write(GPIO_PIN_LED_STATUS, GPIO_LEVEL_LOW); // 无人时 LED 灭
}

// 或者,可以实现更复杂的 LED 状态指示逻辑,例如闪烁表示 Wi-Fi 连接中,常亮表示 Wi-Fi 已连接,快速闪烁表示错误等等。
// 可以根据 state_manager 提供的状态信息来更新 LED 状态。

vTaskDelay(pdMS_TO_TICKS(STATUS_LED_PERIOD_MS));
}
}

构建和编译

为了构建和编译这个嵌入式项目,你需要选择合适的开发环境和工具链。对于ESP32平台,推荐使用 ESP-IDF (Espressif IoT Development Framework)。

  1. 安装 ESP-IDF: 按照 Espressif 官方文档安装 ESP-IDF 工具链和环境。
  2. 创建 ESP-IDF 工程: 使用 ESP-IDF 的工程模板创建一个新的工程,并将上述代码文件添加到工程中。
  3. 配置工程: 修改 sdkconfig 文件,配置 Wi-Fi 凭据、MQTT Broker 地址、Matter 相关配置等。
  4. 编译工程: 在 ESP-IDF 工程目录下,运行 idf.py build 命令编译工程。
  5. 烧录固件: 使用 idf.py flash 命令将编译好的固件烧录到 ESP32 设备中。
  6. 监视串口输出: 使用 idf.py monitor 命令监视串口输出,查看系统的运行状态和调试信息。

测试和验证

完成代码实现和固件烧录后,需要进行全面的测试和验证,确保系统的功能和性能符合预期。

  • 单元测试: 对各个模块进行单元测试,例如 HAL 层的驱动测试、LD2410B 驱动测试、数据处理模块测试等。可以使用 C 单元测试框架,例如 UnityCMocka
  • 集成测试: 进行模块之间的集成测试,例如传感器数据采集任务与数据处理模块、Matter 设备任务与状态管理模块、Home Assistant 任务与事件处理模块等。
  • 系统测试: 进行完整的系统测试,包括:
    • 人体存在检测功能测试: 测试传感器是否能够准确、灵敏地检测人体存在和离开。
    • Matter 协议接入测试: 测试设备是否能够成功接入 Matter 网络,并与 Matter 控制器进行通信,属性读写和事件上报是否正常。
    • Home Assistant 集成测试: 测试设备是否能够成功连接 MQTT Broker,并将传感器数据和状态发布到 Home Assistant,Home Assistant 是否能够正确显示和控制设备。
    • 稳定性测试: 长时间运行设备,测试系统的稳定性,是否有内存泄漏、死机等问题。
    • 功耗测试: 测试设备的功耗,是否满足低功耗设计目标(如果项目有功耗要求)。

维护和升级

为了保证系统的长期稳定运行,需要考虑维护和升级方案。

  • 固件 OTA (Over-The-Air) 升级: 实现固件 OTA 升级功能,方便用户在不接触设备的情况下进行固件升级。ESP-IDF 提供了 OTA 升级的示例代码和库。
  • 日志记录和远程诊断: 添加日志记录功能,记录系统的运行日志,方便问题排查和远程诊断。可以将日志输出到串口、SD 卡,或者通过网络发送到远程服务器。
  • 错误处理和容错机制: 在代码中添加完善的错误处理和容错机制,例如异常捕获、重启策略、看门狗机制等,提高系统的可靠性。
  • 版本控制和代码管理: 使用 Git 等版本控制工具管理代码,方便代码的版本管理、协作开发和回溯。

BOM 成本控制

在整个开发过程中,需要时刻关注BOM (Bill of Materials) 成本,确保最终产品的BOM成本控制在40元人民币以内。

  • 选择低成本 MCU: 选择性价比较高的 MCU,例如 ESP32-C3 系列、ESP32-S3 系列等,这些芯片集成了 Wi-Fi 和蓝牙功能,可以降低外围器件成本。
  • 优化外围电路设计: 精简外围电路设计,减少元器件数量,例如使用集成度更高的电源管理芯片、减少阻容元件等。
  • 批量采购: 批量采购元器件可以获得更优惠的价格。
  • 选择合适的 PCB 供应商: 选择价格合理的 PCB 供应商,降低 PCB 制造成本。

总结

这个毫米波人体存在传感器项目涉及嵌入式系统开发的各个方面,从硬件选型、系统架构设计、软件代码实现,到测试验证和维护升级。通过采用分层架构、模块化设计、实践验证的技术和方法,可以构建一个可靠、高效、可扩展的系统平台。

代码行数说明

上述提供的代码示例只是框架性的,为了达到 3000 行代码的要求,需要进一步完善和扩展各个模块的代码,例如:

  • HAL 层: 添加更多 HAL 接口的实现,例如 SPI、I2C、ADC 等驱动,以及更完善的错误处理和异常情况处理。
  • 驱动层: 完善 LD2410B 驱动代码,实现更多指令发送和数据解析功能,例如灵敏度调节、距离范围设置等。实现 Wi-Fi 驱动、Matter 协议栈驱动、Home Assistant 集成驱动的完整代码。
  • 中间件层: 完善数据处理模块,实现更复杂的数据滤波、算法和逻辑。完善状态管理模块和配置管理模块,添加更多状态和配置参数的管理功能。实现事件处理模块的事件队列、事件分发和处理机制。
  • 应用层: 完善各个任务的代码,实现更丰富的功能和业务逻辑,例如 OTA 升级、远程配置、Web 配置界面、更复杂的 LED 状态指示等。添加详细的注释和日志输出,提高代码的可读性和可维护性。

请注意,上述代码示例仅为演示目的,可能需要根据具体的硬件平台、Matter SDK、MQTT 客户端库和项目需求进行调整和修改。在实际开发过程中,建议参考相关的文档和示例代码,并进行充分的测试和验证。

欢迎关注我的其它发布渠道