共计 5694 个字符,预计需要花费 15 分钟才能阅读完成。
【本文正在参加 ”2021 爱智先行者 - 征文大赛 ” 流动】https://mp.weixin.qq.com/s/I2…
上次说了在 智能灯光开关 的我说了在憋大招,那必定不会只有这点货色,那么明天咱们就来持续!之前 智能灯光开关和光照传感器 曾经简略的实现了一个小场景的设施搭建,然而这么好玩的点子怎么就只做这么简略的货色呢?那当然不行了啊,得搞事件!搞大事件!
之前的场景仅仅具备查看有没有关灯,并且近程关灯的性能,家里有没有其他人在家,是没方法感知到的,还是不够智能。摄像头又太简单太贵,于是我筹备寻找一个好使的传感器来感知,人的存在。
硬件抉择
首先,就是翼辉的 边缘计算机 Spirit 1 边缘计算机,这套环境都是建设在这个玩意的根底上。
还有祖传的安信可 ESP32S。
人体存在传感器
人体存在传感器方面困扰了我好一阵子,所以筹备专门讲一下这个传感器,我尝试了市面上很多人体传感器,常见的大都是 CW 多普勒体制 + 相似 bis0001 芯片 把信号放大进行检测,要么是青蛙眼,只能检测到静止,而且是大幅度静止(具体点:2 米间隔我甩手没用,得晃动身材。淘宝买到贼贵的那种);要么是误报率极高,判断难度很高。都没方法简略的满足我的需要,我在打游戏的时候传感器检测不到我,把我灯关了岂不是很难堪?
不过最初我还是找到了一个好货色:阶跃时进的 HS2BC3A 这是一个毫米波传感器。这玩意可有意思了,采纳相似雷达的原理,向检测区域发射 24GHz 的 FMCW 无线电波,并接管区域内的所有静止、微动、极弱微动的指标反射的无线电波,经传感器零碎中的毫米波 MMIC 电路转换为电信号,并由数字信号算法处理单元进行信号处理(呼吸信号 提取算法),解算出指标信息(存在、微动、静止、静止等状态)。
这玩意实际上也是一个青蛙眼,也是通过检测到静止来判断,然而他精度能检测到人呼吸产生的静止,家喻户晓,人不呼吸就会死,所以这个问题也就不复存在了,而且不只是呼吸,包含人的很多大小动作也能被他精确的捕捉到(呼吸都能捕捉到,别说动动手指什么的了),而且还能够通过串口配置很多参数。我理论用起来成果十分棒。
HS2BC3A 能够说是老少皆宜,有简略的 IO 数字量输入满足根本应用需要,还有串口能够进行简单的配置和具体数据输入,串口具体输入甚至能追踪最多 8 个指标,报告指标的数量间隔与信噪比(与数据可靠性相干)。还能够批改模块探测间隔,灵敏度,输入模式,输入延时工夫(确定指标延时与指标失落延时),被动获取数据等性能,须要留神的一点是,串口的配置不影响 IO 口输入,敞开了串口上的被动上报并不会影响 IO 口输入。
因为这个模块太过敏感(HS2BC3A 探测范畴是 100°×100°,近距离探测范畴还会更大一些),在测试和调试的时候,倡议将灵敏度和探测间隔调整到最低,而后将输入延时工夫设置为最短,将模块搁置到高于头顶的地位,不便调试代码,要不然会始终检测到调试人员的存在,加上默认继续 15S 检测不到信号才会判断没人,导致我测试的时候始终没方法切换到无人状态,一度让我狐疑,是不是设施坏了。
理论应用的时候官网默认配置就挺好用(我装置在天花板上,大略 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[6];
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: %02x:%02x:%02x:%02x:%02x:%02x\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 680U // SDDC 协定应用的端口号
#define PIN_INPUT 0 // 抉择 IO0 进行管制
#define ESP_TASK_STACK_SIZE 4096
#define ESP_TASK_PRIO 25
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 对付一下,之后有工夫再把简单功能完善。