资讯详情

2021爱智先行者—人体存在感传感器

【本文正在参与"2021爱智先行者-征文大赛"活动】https://mp.weixin.qq.com/s/I2s99dZpbP0QpMiKFneYJA


上次我说在 智能照明开关 我说在屏住大动作,当然不仅是这个东西,所以今天我们将继续!在 智能照明开关和光传感器 之前,已经简单地完成了一个小场景的设备建设,但这样一个有趣的想法怎么能只做这么简单的事情呢?那当然不是啊,你必须做点什么!做大事!

之前的场景仅仅具备检查有没有关灯,并且远程关灯的功能,家里有没有其他人在家,是没办法感知到的,还是不够智能。摄像头又太复杂太贵,于是我准备寻找一个好使的传感器来感知,人的存在。

硬件选择

首先是翼辉 边缘计算机 Spirit 1 边缘计算机,这个环境是基于这个东西的。

还有祖传的安信可 ESP32S 。

传感器存在于人体内

人体存在传感器方面困扰了我好一阵子,所以准备专门讲一下这个传感器,我尝试了市面上很多人体传感器,常见的大都是CW 多普勒系统相似bis0001芯片 放大信号进行检测,或者青蛙眼,只能检测到运动,而且是大规模的运动(具体点:2米远我甩手没用,要摇身体。淘宝买贼贵的 );或者误报率很高,很难判断。没有办法简单满足我的需求。玩游戏的时候传感器检测不到我。关灯不尴尬吗?

但最后,我找到了一件好事:阶跃时进 HS2BC3A 这是一个毫米波传感器。这个东西很有趣。采用类似雷达的原理向检测区域发射 24GHz 的 FMCW 无线电波,过传感器系统中的毫米波 接收区域内所有运动、微动和极弱微动的目标反射的无线电波MMIC 电路转换为电信号,信号处理(呼吸信号 提取算法)由数字信号算法处理单元处理,解算目标信息(存在、微动、运动、静态等)。

其实这个东西也是青蛙眼,也是通过检测运动来判断的,但是他的精度可以检测到人呼吸引起的运动。众所周知,人不呼吸就会死,所以这个问题就不复存在了,不仅仅是呼吸,还有人的很多大大小小的动作(呼吸都能捕捉到,更别说动手指了),很多参数可以通过串口配置。我实际上用得很好。

HS2BC3A 可以说老少皆宜,有简单IO数字输出满足基本使用需求,串口可以进行复杂的配置和详细的数据输出。串口的详细输出甚至可以跟踪最多8个目标,报告目标的数量距离和信噪比(与数据可靠性有关)。还可以修改模块检测距离、灵敏度、输出模式、输出延迟时间(确定目标延迟和目标丢失延迟)、积极获取数据等功能。需要注意的是,串口的配置并不影响IO关闭串口的主动报告不会影响口输出IO口输出。

因为这个模块太过敏感(HS2BC3A 检测范围为100°×100°,近距离检测范围会更大)。在测试和调试过程中,建议将灵敏度和检测距离调整到最低,然后将输出延迟时间设置为最短,将模块放置在高于头顶的位置,方便调试代码,否则会一直检测到调试人员的存在,默认持续15。S如果我不能检测到信号,我就会判断没有人,这导致我在测试时无法切换到无人状态。有一段时间,我怀疑设备是否坏了 。

在这里插入图片描述

在实际使用中,官方默认配置非常有用(我安装在天花板上,大约2-3米高),一个传感器可以完全覆盖主卧室和客厅。手册上写着最远的7米,最多可以设置到9米,但远程探测角度会变窄。

但官方手册说,如果环境中有干扰源,可以通过降低灵敏度来避免,或者通过详细的串口获取详细的数据:点云目标输出 $JYRPO ,该信息包括目标序号、目标距离、目标信噪比等信息,其中信噪比与可靠性有关。信噪比越大,目前检测到的目标可靠性越高,手动筛选数据就越方便。

在这里插入图片描述

代码解析

获取代码

为了方便解释逻辑,我会打乱代码的顺序,可能会被切割。想直接拿代码跑的朋友可以直接去 灵感桌面的秘密宝库 获取代码,或者直接 clone:

https://gitee.com/inspiration-desktop/DEV-lib-arduino.git

这一次,由于篇幅有限,我不会重复获得代码。代码在 human_body_induction 文件夹中,如果有不会用的朋友,可以参考上一篇文:2021爱智先锋-智能灯光开关-CSDN社区

设备控制命令:

通过 Spirit 1 向传感器设备发送的应用程序或调试工具 嗅探器 命令:

{  "method": "get", "obj": ["rtgy"]}

协议的初始化过程:

基于官方 demo 不需要修改,主要是设备初始化、管脚配置和协议初始化。

/* * 初始化传感器 */void sensor_init(){    / 初始化 GOIP 口为输入模式,接收传感器发送的信息   pinMode(sensor_in,INPUT);    / 创建传感器任务,并将周期性传感器数据发送给 EdgerOS    xTaskCreate(periodic_sensor_task,"periodic_sensor_task",ESP_TASK_STACK_SIZE,NULL,ESP_TASK_PRIO,NULL);}void setup()      byte mac     Serial.begin(115200)Serial.setDebugOutput(true);    Serial.println();    // 初始化传感器    sensor_init();     / 清除按键状态机的状态    button.reset();      / 创建按键扫描线程,长按 IO0 按钮,松开后ESP32 将进入 SmartConfig 模式   sddc_printf("长按键进入 Smartconfig...\n");    button.attachLongPressStop(esp_io0_key_task);    xTaskCreate(esp_tick_task,"button_tick",ESP_TASK_STACK_SIZE,NULL,ESP_TASK_PRIO,NULL);      WiFi  连接网络    WiFi.begin(ssid,password);    while (WiFi.status() != WL_CONNECTED)                                                                                                                                                                                                                           delay(500);        Serial.print(".");    IP    Serial.println("");    Serial.println("WiFi connected");    Serial.print("'ip :");    Serial.print(WiFi.localIP();     Serial.println("' to connect");    sddc     初始化协议sddc_lib_main(&sys_cfg);     mac    WiFi.macAddress(mac);    sddc_printf("MAC addr: x:x:x:x:x:x\n",                          mac[5],mac[4],mac[3],mac[2],mac[1],mac[0]);   / 使用网卡  mac 唯一的地址设置设备标识 UID    sddc_set_uid(G_sddc,mac);}void loop()      / 操作SDDC    while (1)            sddc_printf("SDDC running...\n");        sddc_run(G_sddc);        sddc_printf("SDDC quit!\n");    SDDC    sddc_destroy(G_sddc);}

配置设备信息

这部分代码可以配置 WiFi 名字和 WiFi 密码,引脚,设备在 Spirit 1 显示的信息:

#define SDDC_CFG_PORT        U       SDDC 协议使用的端口号#define PIN_INPUT                 0          选择 IO0 进行控制#define ESP_TASK_STACK_SIZE     #define ESP_TASK_PRIO       static const int sensor_in  = 34;            数据输入引脚static const char* ssid     = "EOS-Tenda";      WiFi 名static const char* password = "1234567890";     WiFi 密码static int rtgy_state       = 1;static int xTicksToDelay    = 1000;                // 周期延时时间  OneButton button(PIN_INPUT, true);/* *  系统对象状态获取注册 */DEV_STATE_GET  dev_state_get_reg[] = {        {"rtgy",   DEV_IO_TYPE,  get_sensor_state},};/* *  当前设备的信息定义 */DEV_INFO    dev_info = {            .name     = "人体感应模块",            .type     = "device.rtgy",            .excl     = SDDC_FALSE,            .desc     = "ESP-32S",            .model    = "IDRTGY01B",            .vendor   = "inspiration-desktop",};/* *   系统注册对象汇聚 */SDDC_CONFIG_INFO sys_cfg = {        .token             = "1234567890",            // 设备密码        .devinfo           = &dev_info,                       .io_dev_reg        = io_dev,        .io_dev_reg_num    = ARRAY_SIZE(io_dev),        .num_dev_reg       = num_dev,        .num_dev_reg_num   = ARRAY_SIZE(num_dev),        .state_get_reg     = dev_state_get_reg,        .state_get_reg_num = ARRAY_SIZE(dev_state_get_reg),        .dis_dev_reg       = dis_dev,        .dis_dev_num       = ARRAY_SIZE(dis_dev),};

回调函数注册

这是收到命令后回调函数注册的位置,在这里注册的函数才能被 SDK 正确的调用,执行正确的动作。

具体 SDK 的解析可以参考 同人逼死官方系列!基于sddc 协议的SDK框架 sddc_sdk_lib 解析 和 同人逼死官方系列!从 DDC 嗅探器到 sddc_sdk_lib 的数据解析

/*  *  数字量设备对象函数与处理方法注册 */NUM_DEV_REGINFO num_dev[] = {//        {"set_num_demo", demo},                          // 字符串为输入命令,demo为命令处理函数};/* *  显示设备对象函数与处理方法注册 */DIS_DEV_REGINFO dis_dev[] = {//        {"set_dis_demo", demo},                          // 字符串为输入命令,demo为命令处理函数};/* * IO设备对象设置函数与处理方法注册 */IO_DEV_REGINFO io_dev[] = {//        {"set_io_demo", demo},                           // 字符串为输入命令,demo为命令处理函数          {"SW_ctrl", SW_ctrl},};/* *  系统对象状态获取注册 */DEV_STATE_GET  dev_state_get_reg[] = {//        {"demo", DEV_NUM_TYPE, num_get_demo},           // demo为输入命令,字符串为命令处理函数//        {"demo", DEV_IO_TYPE, io_get_demo},//        {"demo", DEV_DISPLAY_TYPE, dis_get_demo},        {"rtgy",   DEV_IO_TYPE,  get_sensor_state},};

数据获取与上报流程

这里是我们自己编写的处理流程 ,可以根据你的需求自己更改,收到 set 或者 get 后根据前面的注册的函数,进入对应的处理函数。

 /*  * 周期上报函数  */static void periodic_sensor_task(void *arg){    int newval = 0;    int oldval = 0;    int i = 0;    // 监控开启和关闭状态    while(1)    {        newval = digitalRead(sensor_in);        if (newval == 0) {            i++;        } else {            i = 0;              rtgy_state = 1;        }        if( i > 15)        {            if (rtgy_state != 0){                rtgy_state = 0;                report_sensor_state();            }            i = 0;         }                 // 任务创建之后,设定延时周期        delay(xTicksToDelay);    }}/*  *  主动数据上报函数 */static void report_sensor_state(){      int sensorValue = 0;    cJSON *value;    cJSON *root;    char  *msg;         value = cJSON_CreateArray();    root = cJSON_CreateObject();    sddc_return_if_fail(value);    sddc_return_if_fail(root);          sddc_return_if_fail(value);          // 获取传感器数据    cJSON_AddItemToArray(value, cJSON_CreateString("rtgy"));   // 这里的字符串要和系统对象状态获取注册结构体里的对应    cJSON_AddItemToObject(root, "obj", value);          // 发送数据给 EdgerOS    msg = cJSON_Print(root);    printf("触发上报: %s\n",msg);    object_report(root);          cJSON_Delete(value);    cJSON_free(msg);}/*  *  单次获取数据 */sddc_bool_t get_sensor_state(char *objvalue, int value_len){    if(rtgy_state)    {        strncpy(objvalue, "ON", value_len);    }else    {        strncpy(objvalue, "OFF", value_len);    }        return SDDC_TRUE;}

总结

这只是最简单的通过读的应用,本来考虑使用串口进行配置与获取详细数据的,但是在具体实现的时候遇到一点BUG,就先用IO凑合一下,之后有时间再把复杂功能完善。

标签: 多普勒传感器参数

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台