第二阶段项目文档
项目介绍
我的参赛作品名称是办公室小精灵。我将使用24G毫米波人体检测雷达探测是否有人在工位上,并由ESP32 S3将检测到的人体状态通过BLE发送到电脑。并且通过检测BLE的连通性来判断电脑的状态,进而实现人体靠近自动唤醒电脑的功能。
基于此,本作品实现了两个功能
-
持续通过BLE发送雷达检测状态
-
当有人靠近时,而电脑处于不活跃状态(关机、睡眠),主控板通过继电器唤醒电脑。
-
当ESP32S3主控板能够通过BLE将人体检测状态发送给PC时,可以用来干很多事情,如久坐提醒等。后续如果时间充足,将持续开发相关功能。
市场应用介绍
目前市场上常用的人体检测传感器为红外热释电,体积大不方便开发,使用新兴的毫米波雷达传感器可以检测到更加微小的人体动作,并且在检测的时候均为无感,具有良好的使用前景。
项目设计思路
核心器件为ESP32 - S3与毫米波雷达传感器。基于检测人是否在检测区域、在检测区域的时间等数据,可以做久坐提醒、久坐时间统计等,可用于健康领域。
方案框图
系统逻辑框图
-
目前功能比较基础,主要通过定时器的配合实现
-
ESP32 S3周期性发送信息给PC
-
ESP32 S3周期性检查蓝牙连接情况与周围的人体情况,来决定是否驱动继电器
-
考虑到传感器存在误判的情况,需要对重启次数进行一定的限制
-
两次重启时间需要留有充足的时间,让电脑能够正常启动,并且连接到ESP32S3上
-
主要元器件介绍
ESP32-S3-WROOM-1
ESP32-S3-WROOM-1 是一款通用型 Wi-Fi + 低功耗蓝牙 MCU 模组,搭载 ESP32-S3系列芯片。
ESP32-S3 是一款集成 2.4 GHz Wi-Fi 和 Bluetooth 5 (LE) 的 MCU 芯片,支持远距离模式 (Long Range)。ESP32-S3 搭载 Xtensa® 32 位 LX7 双核处理器,主频高达 240 MHz,内置 512 KB SRAM (TCM),具有 45 个可编程 GPIO 管脚和丰富的通信接口。ESP32-S3 支持更大容量的高速 Octal SPI flash 和片外 RAM,支持用户配置数据缓存与指令缓存。
ESP32-S3芯片具有行业领先的低功耗性能和射频性能,支持 WiFi IEEE802.11b/g/n 协 议和 Bluetooth 5。该芯片搭载 Xtensa-R32 位 LX7 双核处 理器,工作频率高达 240 MHz。支持二次开发,无需使用其它微控制器或处理器。芯片内置 512 KB SRAM,384 KB ROM, 16KB RTC SRAM。芯片支持多种 低功耗工作状态,能够满足各种应用场景的功耗需求。芯片所特有的精细时钟门控功能、动态电压时钟频率调节功能、射频输出功率可调节功能等特性,可以实现通信距离、通信速率和功耗之间的最佳平衡。
模组提供丰富的外设接口,包括 UART,PWM,SPI,I2S,I2C,ADC,LCD, DVP,RMT(TX/RX),脉冲计数器,USB OTG,USB Serial/JTAG,SDIO,DMA 控制器, TWAI 控制器,温度传感器,电容式传感器和多个IO 口。
使用ESP32-S3模组相比于ESP32-S3芯片更有利于降低PCB布线难度,加快开发进度。
安信可24G人体雷达传感器模块 Rd-03
Rd-03是由深圳市安信可科技有限公司开发的雷达模组,搭载砂典微的S3KM1110芯片。S3KM1110芯片是一种基于FMCW雷达收发器技术的集成单片机毫米波传感SoC。它工作在24GHz的K波段,每个单频扫描的调制带宽高达1GHz.利用FMCW调频连续波,对设定空间内的目标进行探测。结合雷达信号处理实现高灵敏度的运动检测和微动检测。
Rd-03模组可感知区域内是否有运动或微动的人体,实现实时检测结果,提供可视化的配置工具,可轻松配置感应距离范围、不同区间的感应灵敏度和无人延时时间等。
PCB绘制打板介绍及遇到的问题和解决方法
绘制打板没有遇到太大的问题,但也存在一些困难。首先是此前没有使用过KiCad,所以需要花时间看视频、查询网络资源进行入门。再然后是KiCad的默认元器件库没有我想要的元器件,导入引脚图与封装花费了一点功夫。
因为我的画板、打板与焊接经验不足,为了能够在有限的时间内完成整个项目,所以我选择只对ESP32S3核心板进行画板和打板,继电器与毫米波雷达通过杜邦线与ESP32S3进行连接,从而降低项目是实施难度。在后续积累了足够的经验与对此项目进行充分的开发后,会将多个模块通过画板、打板的方式集成在一起。
除此之外,没有遇到其他什么困难。
关键代码及说明
本项目目前实现的功能十分简单,所以没有太多的关键代码。
-
依据乐鑫提供的BLE的Demo,在此基础上进行小修小改,实现了BLE连接后的逻辑控制与信息发送。
ESP32 S3使用定时器周期检查相关参数,看看是否需要进行逻辑控制
-
周期性扫描全局参数,进行重启次数清空或者启动重启计数
// 定时扫描参数,决定是否重启PC static void ble_timer2_task(void *arg) { if (enable_reset_pc == 1) //授权可以控制PC { // 有人靠近 且 BLE长时间未连接 且 重启次数小于最大重启次数 if(is_human_nearby == 1 && connect_state == 2 && current_reset_time <= reset_max_time){ if(!timer3_active) // 计算机启动最长时间定时器计时未开始 { if(current_reset_time == 0) //当前重启次数为第一次 { turn_on_pc(); //驱动继电器打开电脑 } xTimerStart(timer3, 0); //开启计时器,倒计时结束驱动继电器打开电脑 current_reset_time++; //重启次数加一 printf("start timer3\r\n"); timer3_active = 1; //计时器开启标志 } // 无人靠近 且 连续四次未检测到人体 或 BLE已连接 或 BLE短时间未连接,则清空重启次数 }else if((is_human_nearby == 0 && no_human_time >5) || connect_state == 0 || connect_state == 1) { current_reset_time = 0; // 重置重启次数 xTimerStop(timer3, 0); // 关闭计时器3 timer3_active = 0; // 关闭计时器3开启标志 printf("reset reset time\r\n"); } } }
-
串口接受与发送信息任务
//串口任务 static void uart_task(void *arg) { // Configure a temporary buffer for the incoming data char *data = (char *) malloc(BUF_SIZE); while (1) { // 从与雷达连接的UART读取数据 int len = uart_read_bytes(LIDAR_UART_PORT_NUM, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS); // 如果接受到的字符串里有"ON",则说明有人靠近 if(strncmp(data, "ON", 2) == 0){ printf("Human is nearby\n"); is_human_nearby = 1; //有人在附近变量置1 no_human_time = 0; //无人靠近次数置0 } // 否则无人靠近 else { printf("Human is not nearby\n"); is_human_nearby = 0; //有人在附近变量置0 no_human_time++; //无人靠近次数置0 } data[0] = '\0'; //清空data vTaskDelay(1000 / portTICK_PERIOD_MS); //延时,方便FreeRTOS调度 } }
-
BLE通信的回调函数
// GAP回调函数 static int blehr_gap_event(struct ble_gap_event *event, void *arg) { switch (event->type) { case BLE_GAP_EVENT_CONNECT: // 连接事件 /* A new connection was established or a connection attempt failed */ MODLOG_DFLT(INFO, "connection %s; status=%d\n", event->connect.status == 0 ? "established" : "failed", event->connect.status); if (event->connect.status != 0) { //连接到一半失败了,重新广播 ble_advertise(); } conn_handle = event->connect.conn_handle; // 1. 修改连接状态 connect_state = 0; // 2. 停止短断开到长断开的定时器 xTimerStop(ble_long_dcnn_timer, 0); break; case BLE_GAP_EVENT_DISCONNECT: // 断开连接事件 MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason); // 1.修改连接状态 connect_state = 1; // 2.关闭发送信息计时器 ble_lidar_timer_stop(); // 3.开启短断开到长断开的定时器 ble_long_dcnn_timer_start(); // 4.恢复广播 ble_advertise(); break; case BLE_GAP_EVENT_ADV_COMPLETE: MODLOG_DFLT(INFO, "adv complete\n"); ble_advertise(); break; //订阅信息 case BLE_GAP_EVENT_SUBSCRIBE: MODLOG_DFLT(INFO, "subscribe event; cur_notify=%d\n value handle; " "val_handle=%d\n", event->subscribe.cur_notify, event->subscribe.attr_handle); if (event->subscribe.attr_handle == esp_send_info_handle) { if(event->subscribe.cur_notify == 1){ printf("PC订阅ESP信息\r\n"); // 开启发送信息 ble_lidar_timer_start(); } else{ printf("PC取消订阅ESP信息\r\n"); ble_lidar_timer_stop(); } } ESP_LOGI("BLE_GAP_SUBSCRIBE_EVENT", "conn_handle from subscribe=%d", conn_handle); break; case BLE_GAP_EVENT_MTU: MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.value); break; } return 0; }
-
-
依据Qt6 BLE的Demo,在此基础上进行小修改,实现了定时扫描蓝牙,自动连接设备等功能。
-
通过BLE 服务UUID判断BLE设备是否为我们的ESP32S3 LIDAR设备
void MainWindow::addLowEnergyService(const QBluetoothUuid &serviceUuid) { qDebug()<<"addLowEnergyService"; // 通过UUID创建服务 QLowEnergyService *service = controller->createServiceObject(serviceUuid); if (!service) { qWarning() << "Cannot create service for uuid"; return; } qDebug()<<service->serviceUuid().toString(); qDebug()<<"this service has "<< service->characteristics().length()<<"characteristics "; // 对比当前扫描到的服务UUID与目标UUID来判断是否为Lidar设备 if (getUUID(service->serviceUuid()) == targetUUID){ qDebug()<<"this device have lidar service"; hasLidarService = true; //如果是,则标志位置1 lidarService = service; //将该服务保存下来 qDebug()<<"lidarService->serviceUuid()"<<lidarService->serviceUuid(); } }
-
扫描BLE服务的特性并保存,用于后续的双方通讯句柄
// 扫描特性 void MainWindow::serviceDetailsDiscovered(QLowEnergyService::ServiceState newState) { qDebug()<<"serviceDetailsDiscovered 356"; if (newState != QLowEnergyService::RemoteServiceDiscovered) { if (newState != QLowEnergyService::RemoteServiceDiscovering) { QMetaObject::invokeMethod(this, "characteristicsUpdated", Qt::QueuedConnection); } return; } auto service = qobject_cast<QLowEnergyService *>(sender()); if (!service) return; // 遍历特性 const QList<QLowEnergyCharacteristic> chars = service->characteristics(); for (const QLowEnergyCharacteristic &ch : chars) { for (QLowEnergyDescriptor &desc : ch.descriptors() ){ qDebug() << desc.uuid(); service->writeDescriptor(desc,QByteArray::fromHex("0100")); // 订阅notify } // 如果是ESP32s3 的notify特性则保存 if (getUUID(ch.uuid()) == chr_esp_notify_pc_UUID){ qDebug()<<"chr_esp_notify_pc get"; chr_esp_notify_pc = ch; }else if (getUUID(ch.uuid()) == chr_pc_write_esp_UUID){ //PC 向 esp32 写信息的句柄 chr_pc_write_esp = ch; // service->writeCharacteristic(chr_pc_write_esp,QByteArray::fromHex("0100")); sendData("0100"); qDebug()<<"chr_pc_write_esp_UUID"; }else if (getUUID(ch.uuid()) == chr_pc_wr_esp_UUID){ //PC 与 esp32 确认信息同步的句柄 chr_pc_wr_esp = ch; qDebug()<<"chr_pc_wr_esp_UUID"; service->readCharacteristic(chr_pc_wr_esp); } } // emit PCStateChanged(true); }
-
-
实现windows11 开机自启动QT6编译的应用,主动连接ESP32S3。该操作可通过可视化的方式完成,具体步骤可查询网络资源。
功能展示及说明
-
本项目实现的功能较为简单,概括的说就是人体靠近,实现电脑唤醒,辅以其他小设计,实现一个简单好用的功能。均为动态场景,通过文字不太好充分展示,大家可以观看相关视频(但不要保有太大期望,就是一个小功能)
-
通过毫米波雷达的调试工具,将毫米波雷达的感受范围、阈值设定到合理的范围内,此处我选择的是70cm的最大触发距离。
-
将毫米波雷达、ESP32S3开发板、继电器通过杜邦线连接,并在毫米波雷达安装在合适的位置上。
-
然后连接继电器与电脑主板的开机线
-
上电即开启功能。
-
PC成功连接上ESP32S3后显示该页面
对本大赛的心得体会
很高兴能够参与到硬禾学堂的FastBond第2季 - How2Make设计大赛阶段二的活动中。在参加完阶段一之后,我也开始抓紧时间投入到了阶段二的过程中,但因为种种原因,实现项目的时间还是比较紧张的,但最终也还是完成了基本的功能。在该活动中,我学习到如何使用KiCad,积累到了一定的打板、画板的经验,有助于我以后的电子创作。再次感谢硬禾学堂,举办了这次活动,让我把心中所想做了出来,后续我将继续完善该项目,也会积极参与到硬禾学堂的其他相关活动。