本帖最后由 lazy 于 2025-9-14 03:02 编辑
一、项目介绍
空闲时间越来越少了。最近看到 “申请升级版雷达模组Rd-03_V2” 的活动,就申请了一块。正好手上也有一个RD03D,可以检测x轴的正负值,就想着做一个回家离家检测。
主要的原理就是,通过判断 RD03D 雷达的 正负值,来判断是从 哪个方向 到 哪个方向。
比如,从正方向到负方向。就是离家。 从负方向到正方向就是 回家。
由于RD03D 距离的单位是mm 所以后续都用毫米标识,设置Y轴值 在 1000 mm 内,并且 计算出, 然后 获取 当前 检测到 移动 人的坐标。 再通过Y轴的值 计算出 当前 X轴的坐标值是否在 30度夹角内。如果在,就会触发 回家 、离家检测。 并进行 提醒。
离家提醒:出门三件套,一个不能少。手机揣兜里,钥匙挂腰间,门锁咔嚓响~!
回家提醒:忙了一天辛苦了,欢迎回来。
主打的就是一个情绪价值。
其实,开始构思的时候是想着,使用opus 数据进行语音播报的。使用 M61-32SU 模块请求服务器音频数据,然后再播放出来。研究好久没搞定。浪费了很多时间。
最后换成了熟悉的DY-SV17F。
单纯做这个的话 M61-32SU 有些功能浪费,但是对其他开发板又不是很熟悉,所以就用这个了。 期待安信可可以出一些小小的板子。
原设想:
M61-32SU 获取天气信息,出门的时候,判断天气状态,然后通过 tts 合成 提醒音频。
例如:
今天天气预报说有小雨,记得带伞呀。
今天高温,记得防暑。
等等。
语音播报这块就没搞定。遂猝。
另外可以参考 WT_0213 的这篇文章,将回家、离家接入HomeAssistant。
Ai-M61+VC02语音控制HA设备
之前我也是有HomeAssistant的,后来做了小米的定制 ,HA设备就刷机了。导致想用的时候环境搭建不起来了。 后续看看,能不能做个小板子。找个低功耗的。整个完整版。
二、模块
M61-32SU 介绍:
这个看上去和M61-32S 功能基本一样的,就是天线 M61-32SU 需要外接。
它长这样子
管脚定义和M61-32S没什么区别。
RD03D 数据介绍:
判断最高位是否为 1 使用 x & 0x8000 就可以了。
去掉符号位取出绝对值数据 x & 0x7FFF 就可以了。
获取 x,y 的值,带符号位
- int16_t x = UART_RECEIVE_BUFFER[4] + (UART_RECEIVE_BUFFER[5] << 8);
- int16_t y = UART_RECEIVE_BUFFER[6] + (UART_RECEIVE_BUFFER[7] << 8);
复制代码
文档地址:
Rd-03D快速入门文档
DY-SV17F模块
接口定义
工作模式(这里选择了第二种,还不清楚M61-32SU怎么使用多个UART,看到代码中有个UART2,但是M61-32SU好像是没有。)
IO0 低电平重复播放第一个音频文件,IO1低电平重复播放第二个音频文件。
通过BUSY 引脚可以判断播放状态,高电平 正在播放, 低电平播放完成。
文档:
三、代码
雷达数据检测
- #include "rd03d.h"
- #include <math.h>
- #include "sv17f.h"
- struct bflb_device_s *gpio;
- struct bflb_device_s *rd03d;
- uint8_t UART_RECEIVE_BUFFER[30];
- // 缓存数据总数,默认Rd03D 数据,是30位 AA FF 03 00
- int BUFFER_LEN = 30;
- int trendStatus = 0; // 0: 无趋势, 1: 回家, -1: 离家
- float angle = 30.0;
- void radar_init(void)
- {
- gpio = bflb_device_get_by_name("gpio");
- // 初始化led针脚gpio
- bflb_gpio_init(gpio, GPIO_PIN_14, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
- bflb_gpio_reset(gpio, GPIO_PIN_14);
- bflb_gpio_uart_init(gpio, GPIO_PIN_23, GPIO_UART_FUNC_UART1_TX);
- bflb_gpio_uart_init(gpio, GPIO_PIN_24, GPIO_UART_FUNC_UART1_RX);
- rd03d = bflb_device_get_by_name("uart1");
- struct bflb_uart_config_s conf = {
- .baudrate = 256000,
- .data_bits = UART_DATA_BITS_8,
- .stop_bits = UART_STOP_BITS_1,
- .parity = UART_PARITY_NONE,
- .flow_ctrl = UART_FLOWCTRL_NONE,
- .rx_fifo_threshold = 7,
- .tx_fifo_threshold = 7
- };
- bflb_uart_init(rd03d, &conf);
- xTaskCreate(sv17fTask, (char *)"sv17f", 1024, NULL, 2, NULL);
- }
- float calculate_opposite_side(float adjacent, float angle_degrees)
- {
- float angle_radians = angle_degrees * (M_PI / 180.0); // 角度转弧度
- return adjacent * tan(angle_radians);
- }
- void rd03dTask(void *pvParameters)
- {
- int index = 0;
- while (1) {
- // 获取单个数据
- int ch = bflb_uart_getchar(rd03d);
- // 如果ch不等于-1说明有数据
- if (ch != -1) {
- // 如果当前数据是雷达指令头
- if (ch == 0xAA) {
- // 重置数据缓存
- memset(UART_RECEIVE_BUFFER, 0, BUFFER_LEN);
- index = 0;
- }
- // 如果当前数据量小于指令数据
- if (index < BUFFER_LEN) {
- UART_RECEIVE_BUFFER[index++] = ch;
- // 如果是数据帧结束标记
- if (ch == 0xCC) {
- // 检测坐标变化
- int16_t x = UART_RECEIVE_BUFFER[4] + (UART_RECEIVE_BUFFER[5] << 8);
- int16_t y = UART_RECEIVE_BUFFER[6] + (UART_RECEIVE_BUFFER[7] << 8);
- int16_t real_x = x & 0x7FFF;
- int16_t real_y = y & 0x7FFF;
- float px = calculate_opposite_side(real_y, angle);
- // 一般门宽度一米二左右,所以y值小于1000说明在门附近
- if (real_y <= 1000 && real_x <= px) {
- // printf("real_x: %d, real_y:%d, px:%.2f, trendStatus: %d\r\n", real_x, real_y, px, trendStatus);
- if (trendStatus == 0) {
- trendStatus = 1;
- // 判断趋势
- if (x & 0x8000) {
- printf("离家\r\n");
- bflb_gpio_reset(gpio, GPIO_PIN_14);
- sv17f_send_message("leave");
- } else {
- printf("回家\r\n");
- bflb_gpio_set(gpio, GPIO_PIN_14);
- sv17f_send_message("home");
- }
- }
- } else {
- if (real_x == 0 || real_x > px || real_y < 60 || real_y > 1000) {
- // printf("未检测到人员\r\n");
- if (trendStatus != 0) {
- trendStatus = 0;
- bflb_gpio_reset(gpio, GPIO_PIN_14);
- printf("未检测到人员\r\n");
- }
- }
- }
- }
- }
- }
- vTaskDelay(1/portTICK_PERIOD_MS);
- }
- }
复制代码
播放音频文件
- #include "sv17f.h"
- #include "bflb_gpio.h"
- extern struct bflb_device_s *gpio;
- QueueHandle_t sound_queue;
- void sv17fTask(void *pvParameters)
- {
- // 初始化led针脚gpio
- bflb_gpio_init(gpio, GPIO_PIN_26, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
- bflb_gpio_init(gpio, GPIO_PIN_28, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
- bflb_gpio_init(gpio, GPIO_PIN_29, GPIO_INPUT | GPIO_FLOAT | GPIO_SMT_EN | GPIO_DRV_0);
- bflb_gpio_set(gpio, GPIO_PIN_26);
- bflb_gpio_set(gpio, GPIO_PIN_28);
- bflb_gpio_reset(gpio, GPIO_PIN_29);
- sound_queue = xQueueCreate(1, 1024*2);
- char* sound_data = NULL;
- while (1)
- {
- sound_data = pvPortMalloc(1024*2);
- memset(sound_data, 0, 1024*2);
- if(xQueueReceive(sound_queue, sound_data, 100/portTICK_PERIOD_MS) == pdTRUE){
- printf("sound_data: %s\n", sound_data);
- if (strcmp(sound_data, "home") == 0) {
- sv17f_back_home();
- } else if (strcmp(sound_data, "leave") == 0) {
- sv17f_leave_home();
- }
- }
- vPortFree(sound_data);
- }
-
- }
- void sv17f_back_home(void)
- {
- printf("back home\n");
- bflb_gpio_reset(gpio, GPIO_PIN_28);
- vTaskDelay(100/portTICK_PERIOD_MS);
- while (bflb_gpio_read(gpio, GPIO_PIN_29) == 1)
- {
- printf("waiting for home\n");
- vTaskDelay(1/portTICK_PERIOD_MS);
- }
- printf("home\n");
- bflb_gpio_set(gpio, GPIO_PIN_28);
-
- }
- void sv17f_leave_home (void)
- {
- printf("leave home\n");
- bflb_gpio_reset(gpio, GPIO_PIN_26);
- vTaskDelay(100/portTICK_PERIOD_MS);
- while (bflb_gpio_read(gpio, GPIO_PIN_29) == 1)
- {
- printf("waiting for leave\n");
- vTaskDelay(1/portTICK_PERIOD_MS);
- }
- printf("leave\n");
- bflb_gpio_set(gpio, GPIO_PIN_26);
- }
- void sv17f_send_message(const char *message)
- {
- printf("send message: %s\n", message);
- if (sound_queue != NULL) {
- xQueueSend(sound_queue, message, portMAX_DELAY);
- }
- }
复制代码
四、设备展示
外壳之前都是用的嘉立创或者朋友代打的。这次,找了一个元器件外壳。由于研究 opus 数据传输浪费了很多时间,导致最后外壳有点草率了。搞个防水盒也是可以的。不过开始想的设备构造和这个不太一样。
程序源码:
程序固件:
|