传统的公司会议室预约管理系统存在诸多问题,如:
诸如会议室等隐私场所,不能通过单纯的安装监控进行管理,实际管理中却又想得知是否有人在使用。为解决此类问题,会议室使用小助手横空出世。
本设备既可以单独使用,借助腾讯云平台也可以对接其他会议室管理系统使用,具有以下亮点:
内置TencentOS Tiny开源物联网操作系统。
OV5640图像传感器是一种低电压、高性能、1/4英寸、500万像素、的CMOS图像传感器。该传感器支持输出最大为500万像素的图像 (2592x1944分辨率),支持使用VGA时序输出图像数据,输出图像的数据格式支持YUV(422/420)、YCbCr422、RGB565以及JPEG格式,若直接输出JPEG格式的图像时可大大减少数据量,方便网络传输。它还可以对采集得的图像进行补偿,支持伽玛曲线、白平衡、饱和度、色度等基础处理。根据不同的分辨率配置,传感器输出图像数据的帧率从15-60帧可调,工作时功率在150mW-200mW之间。
ESP8266模块小熊派开发板套件提供的用于通过Wi-Fi传输数据的通信扩展板,该拓展板采用的是乐鑫ESP8266 Wi-Fi通信模组,支持常见的IPv4/TCP/UDP/HTTP/FTP等通信协议。
振动传感器、光敏传感器、热释电传感器......数字信号输出的均可以。
TencentOS tiny是腾讯面向物联网领域开发的实时操作系统,现已捐赠给开放原子开源基金会进行孵化,具有低功耗,低资源占用,模块化,安全可靠等特点,可有效提升物联网终端产品开发效率。TencentOS tiny 提供精简的 RTOS 内核,内核组件可裁剪可配置,可快速移植到多种主流 MCU (如NXP Arm Cortex-M 全系列)及模组芯片上。而且,基于RTOS内核提供了丰富的物联网组件,内部集成主流物联网协议栈(如 CoAP/MQTT/TLS/DTLS/LoRaWAN/NB-IoT 等),可助力物联网终端设备及业务快速接入腾讯云物联网平台。
TencentOS Tiny 内核具有超低资源占用的特点,RAM 0.8KB,ROM 1.8KB;在类似烟感和红外等实际场景下,TencentOS tiny 的资源占用仅为:RAM 2.69KB、ROM 12.38KB。
完整包含 MCU 和外围设备功耗管理,用户可以根据业务场景选择可参考的低功耗方案,有效降低设备耗电,延长设备寿命。
TencentOS tiny 提供多种编译器快速移植指南和移植工具,可实现向新硬件开发板的一键移植,省时省力,有效提升开发效率。
TencentOS tiny 可以自动获取故障现场信息,并保持在端侧存储设备中,触发重启后会自动上传故障信息,可有效解决远程物联网设备故障信息获取难题,提升故障分析解决效率。
TencentOS tiny 提供了多个等级的 IoT 安全方案。您可以根据业务场景和成本要求选择合适的安全解决方案,方便客户在安全需求和成本控制之间进行有效平衡。
TensorFlow Lite for Microcontrollers 是 TensorFlow Lite 的一个实验性移植版本,它适用于微控制器和其他一些仅有数千字节内存的设备。
适用于微控制器的 TensorFlow Lite 专门用于在微控制器和其他只有几千字节内存的设备上运行机器学习模型。核心运行时可以放入 Arm Cortex M3 上 16 KB 的存储空间中,并且可以运行许多基本模型。它不需要操作系统支持、任何标准 C/C++ 库或动态内存分配。
它可以直接在“裸机”上运行,不需要操作系统支持、任何标准 C/C++ 库和动态内存分配。核心运行时(core runtime)在 Cortex M3 上运行时仅需 16KB,加上足以用来运行语音关键字检测模型的操作,也只需 22KB 的空间。
功能和组件
MQTT、cJSON由TencentOS提供,用于MQTT通信实现,JSON数据打包解包。
int main(void)
{
board_init();
person_detect_init();
PRINTF("Welcome to TencentOS tiny(%s)\r\n", TOS_VERSION);
tos_knl_init();
user_variable_init();
tos_task_create(&mqttclient_task, "mqttclient_task", mqttclient_entry, NULL, 4, mqttclient_task_stk, MQTTCLIENT_TASK_STK_SIZE, 0);
tos_task_create(&detection_task, "detection_task", detection_entry, NULL, 3, detection_task_stk, DETECTION_TASK_STK_SIZE, 0);
tos_task_create(&codec_task, "codec_task", codec_entry, NULL, 4, codec_task_stk, CODEC_TASK_STK_SIZE, 0);
tos_task_create(&default_task, "default_task", default_entry, NULL, 4, default_task_stk, DEFAULT_TASK_STK_SIZE, 0);
tos_knl_start();
while (1);
}
void default_entry(void *arg)
{
uint8_t sensor1 = 0,sensor1_past = 0;
uint8_t sensor2 = 0,sensor2_past = 0;
uint8_t interval_time = 0;
uint8_t trigger_flag = 0;
k_event_flag_t default_flag_match = 0;
while (1)
{
//获取外部传递信息,不等待并清空标志
default_flag_match = 0;
tos_event_pend(&event_default, 0xFFFFFFFF, &default_flag_match, TOS_TIME_NOWAIT, TOS_OPT_EVENT_PEND_ANY);
sensor1_past = sensor1;
sensor2_past = sensor2;
if(GPIO_PinRead(BOARD_INITPINS_E53_GPIO1_GPIO, BOARD_INITPINS_E53_GPIO1_GPIO_PIN) != 0) sensor1 = 1;
else sensor1 = 0;
if(GPIO_PinRead(BOARD_INITPINS_E53_GPIO3_GPIO, BOARD_INITPINS_E53_GPIO3_GPIO_PIN) != 0) sensor2 = 1;
else sensor2 = 0;
if(((user_configure.sensor1_mode != MODE_SWITCH) || (sensor1 == 0)) &&
((user_configure.sensor2_mode != MODE_SWITCH) || (sensor2 == 0)))
{
//传感器处于触发模式,且发生了1-0电平跳变
if(((user_configure.sensor1_mode == MODE_TRIGGER) && (sensor1 == 0) && (sensor1_past == 1))||
((user_configure.sensor2_mode == MODE_TRIGGER) && (sensor2 == 0) && (sensor2_past == 1)))
{
trigger_flag = 1;
}
//定时触发识别
if(user_configure.interval_mode == MODE_ON)
{
interval_time ++;
if(interval_time >= (user_configure.interval_time * 1000 / DEFAULT_DELAY_TIME_MS))
{
interval_time = 0;
trigger_flag = 1;
}
}
//声音触发识别
if(user_configure.sound_judge_mode == MODE_ON)
{
uint8_t room_status = user_get_room_status();
uint8_t sound_state = user_get_sound_state();
if((default_flag_match & EVENT_DEFAULT_SOUND) &&
(((room_status == 0) && (sound_state == 1)) || ((room_status == 1) && (sound_state == 0))))
{
trigger_flag = 1;
}
}
}
if(trigger_flag == 1)
{
trigger_flag = 0;
tos_event_post(&event_detection, EVENT_PERSON_DETECTION_MODEL); //启动模型
}
tos_task_delay(DEFAULT_DELAY_TIME_MS);
}
}
void detection_entry(void *arg)
{
uint32_t cameraReceivedFrameAddr;
void *lcdFrameAddr;
uint8_t person_count = 0;
k_event_flag_t detection_match_flag;
tos_completion_create(&completion_display);
APP_InitDisplay();
CAMERA_RECEIVER_Start(&cameraReceiver);
while (1)
{
//外部传递标志,不等待(测试用)
detection_match_flag = 0;
tos_event_pend(&event_detection, EVENT_PERSON_DETECTION_MODEL, &detection_match_flag, TOS_TIME_NOWAIT, TOS_OPT_EVENT_PEND_ANY | TOS_OPT_EVENT_PEND_CLR);
if(detection_match_flag & EVENT_PERSON_DETECTION_MODEL) //运行模型
{
for(uint8_t i=0;i<10;i++)
{
uint8_t person_flag;
//获取摄像头缓冲区
while (kStatus_Success != CAMERA_RECEIVER_GetFullBuffer(&cameraReceiver, &cameraReceivedFrameAddr))
{
tos_task_delay(10);
}
//转换图像为模型可处理的大小
input_convert((uint16_t*)cameraReceivedFrameAddr,model_buffer);
person_flag = person_detect(model_buffer); //运行模型
person_count += person_flag;
tos_completion_pend(&completion_display); //等待上一帧显示完成
lcdFrameAddr = s_lcdBuffer[s_lcdActiveFbIdx ^ 1]; //切换显示缓冲区
Camera_PXP_LCD(cameraReceivedFrameAddr, lcdFrameAddr); //将摄像头缓冲区数据搬移到显示缓冲区上
CAMERA_RECEIVER_SubmitEmptyBuffer(&cameraReceiver, (uint32_t)cameraReceivedFrameAddr); //释放摄像头缓冲区
//显示识别框
draw_identify_rectangular((uint16_t *)lcdFrameAddr, (person_flag == 0)?0xffff:0xf800);
//显示新的一帧
tos_completion_reset(&completion_display);
g_dc.ops->setFrameBuffer(&g_dc, 0, lcdFrameAddr);
}
if(person_count >= 1) room_status = 1;
else room_status = 0;
tos_event_post(&event_mqttclient, EVENT_MQTT_TX_ROOM_STATUS);
person_count = 0;
}
else //不运行模型,正常显示
{
/* 低功耗:挂起任务 */
//获取摄像头缓冲区
while (kStatus_Success != CAMERA_RECEIVER_GetFullBuffer(&cameraReceiver, &cameraReceivedFrameAddr))
{
tos_task_delay(10);
}
tos_completion_pend(&completion_display); //等待上一帧显示完成
lcdFrameAddr = s_lcdBuffer[s_lcdActiveFbIdx ^ 1]; //切换显示缓冲区
Camera_PXP_LCD(cameraReceivedFrameAddr, lcdFrameAddr); //将摄像头缓冲区数据搬移到显示缓冲区上
CAMERA_RECEIVER_SubmitEmptyBuffer(&cameraReceiver, (uint32_t)cameraReceivedFrameAddr); //释放摄像头缓冲区
//显示新的一帧
tos_completion_reset(&completion_display);
g_dc.ops->setFrameBuffer(&g_dc, 0, lcdFrameAddr);
tos_task_delay(100);
}
}
}
void mqttclient_entry(void)
{
int error;
mqtt_client_t *client = NULL;
mqtt_message_t msg;
k_event_flag_t match_flag;
char host_ip[20];
k_event_flag_t mqtt_match_flag;
memset(&msg, 0, sizeof(msg));
esp8266_sal_init(HAL_UART_PORT_2);
esp8266_join_ap(WIFI_SSID, WIFI_PASSWORD);
mqtt_log_init();
client = mqtt_lease();
tos_event_create(&report_result_event, (k_event_flag_t)0u);
tos_sal_module_parse_domain(MQTT_PRODUCT_ID ".iotcloud.tencentdevices.com",host_ip,sizeof(host_ip));
mqtt_set_port(client, "1883");
mqtt_set_host(client, host_ip);
mqtt_set_client_id(client, MQTT_CLIENT_ID);
mqtt_set_user_name(client, MQTT_USR_NAME);
mqtt_set_password(client, MQTT_PASSWORD);
mqtt_set_clean_session(client, 1);
error = mqtt_connect(client);
MQTT_LOG_D("mqtt connect error is %#0x", error);
error = mqtt_subscribe(client, "$thing/down/property/" MQTT_SUBSCRIBE_TOPIC, QOS0, tos_topic_handler);
MQTT_LOG_D("mqtt subscribe error is %#0x", error);
while (1)
{
//死等外部传递标志
mqtt_match_flag = 0;
tos_event_pend(&event_mqttclient, 0xFFFFFFFF, &mqtt_match_flag, TOS_TIME_FOREVER, TOS_OPT_EVENT_PEND_ANY | TOS_OPT_EVENT_PEND_CLR);
if(mqtt_match_flag & EVENT_MQTT_TX_ROOM_STATUS)
{
uint8_t room_state = user_get_room_status();
memset(&msg, 0, sizeof(msg));
snprintf(report_buf, sizeof(report_buf), REPORT_DATA_TEMPLATE, room_state);
msg.qos = QOS0;
msg.payload = (void *) report_buf;
error = mqtt_publish(client, "$thing/up/property/" MQTT_PUBLISH_TOPIC, &msg);
MQTT_LOG_D("mqtt publish error is %#0x", error);
tos_event_pend(&report_result_event,
report_success|report_fail,
&match_flag,
TOS_TIME_FOREVER,
TOS_OPT_EVENT_PEND_ANY | TOS_OPT_EVENT_PEND_CLR);
if (match_flag == report_success)
{
printf("report to Tencent IoT Explorer success\r\n");
}
else if (match_flag == report_fail)
{
printf("report to Tencent IoT Explorer fail\r\n");
}
}
tos_task_delay(2000);
}
}
static void tos_topic_handler(void* client, message_data_t* msg)
{
(void) client;
cJSON* cjson_root = NULL;
cJSON* cjson_status = NULL;
cJSON* cjson_params = NULL;
char* status = NULL;
k_event_flag_t event_flag = report_fail;
MQTT_LOG_I("-----------------------------------------------------------------------------------");
MQTT_LOG_I("%s:%d %s()...\ntopic: %s, qos: %d. \nmessage:\n\t%s\n", __FILE__, __LINE__, __FUNCTION__,
msg->topic_name, msg->message->qos, (char*)msg->message->payload);
MQTT_LOG_I("-----------------------------------------------------------------------------------\n");
cjson_root = cJSON_Parse((char*)msg->message->payload);
if (cjson_root == NULL) {
printf("report reply message parser fail\r\n");
event_flag = report_fail;
goto exit;
}
cjson_params = cJSON_GetObjectItem(cjson_root, "params");
if (cjson_params != NULL)
{
cJSON* cjson_interval_mode = cJSON_GetObjectItem(cjson_params, "interval_mode");
if(cjson_interval_mode != NULL) user_configure.interval_mode = cjson_interval_mode->valueint;
cJSON* cjson_sound_judge_mode = cJSON_GetObjectItem(cjson_params, "sound_judge_mode");
if(cjson_sound_judge_mode != NULL) user_configure.sound_judge_mode = cjson_sound_judge_mode->valueint;
cJSON* cjson_sensor1_mode = cJSON_GetObjectItem(cjson_params, "sensor1_mode");
if(cjson_sensor1_mode != NULL) user_configure.sensor1_mode = cjson_sensor1_mode->valueint;
cJSON* cjson_sensor2_mode = cJSON_GetObjectItem(cjson_params, "sensor2_mode");
if(cjson_sensor2_mode != NULL) user_configure.sensor2_mode = cjson_sensor2_mode->valueint;
cJSON* cjson_interval_time = cJSON_GetObjectItem(cjson_params, "interval_time");
if(cjson_interval_time != NULL) user_configure.interval_time = cjson_interval_time->valueint;
cJSON* cjson_sound_judge_time = cJSON_GetObjectItem(cjson_params, "sound_judge_time");
if(cjson_sound_judge_time != NULL) user_configure.sound_judge_time = cjson_sound_judge_time->valueint;
}
/* 提取status状态 */
cjson_status = cJSON_GetObjectItem(cjson_root, "status");
status = cJSON_GetStringValue(cjson_status);
if (cjson_status == NULL || status == NULL) {
printf("report reply status parser fail\r\n");
event_flag = report_fail;
goto exit;
}
/* 判断status状态 */
if (strstr(status,"success")) {
event_flag = report_success;
}else {
event_flag = report_fail;
}
exit:
cJSON_Delete(cjson_root);
cjson_root = NULL;
status = NULL;
tos_event_post(&report_result_event, event_flag);
return;
}
void codec_entry(void *arg)
{
sai_transfer_t xfer;
uint8_t sound_flag = 0;
uint8_t count = 0;
tos_completion_create(&completion_codec_rx);
tos_task_delay(DEMO_CODEC_INIT_DELAY_MS);
while(1)
{
if(user_configure.sound_judge_mode == MODE_ON)
{
//启动音频采集
xfer.data = Buffer;
xfer.dataSize = BUFFER_SIZE;
if (kStatus_Success == SAI_TransferReceiveEDMA(DEMO_SAI, &rxHandle, &xfer))
{
tos_completion_reset(&completion_codec_rx);
//等音频采集完成
tos_completion_pend(&completion_codec_rx);
//判断是否较大声音
if(sound_flag == 0)
{
for(uint16_t i=0;i<BUFFER_SIZE;i+=2)
{
if( Buffer[i+1] > 0)
{
sound_flag = 1;
break;
}
}
}
count ++;
}
if(count >= (user_configure.sound_judge_time * 5))
{
//更新当前环境声音状态
sound_state = sound_flag;
tos_event_post(&event_default, EVENT_DEFAULT_SOUND);
count = 0;
sound_flag = 0;
PRINTF("flag:%d\r\n",sound_flag);
}
}
else
{
tos_task_delay(300);
}
}
}
云端采用腾讯云IoT explorer平台,设备通过esp8266模块连接wifi入网。
客户端腾讯连连微信小程序,开发使用了标准面板,可以说超级超级简单了,而且效果还不错。
在开发中感觉TencentOS tiny特点,就是简单,无论是API的设计,还是部署的复杂度上都大大降低,源码的可读性也比较高,配套资料也很齐全,新手也能快速凭借文档入门。
腾讯云IoT explorer平台可以说是做到一站式开发,让我这个没有接触过云的新手也能快速上手,而且效果还不错,进阶开发也提供了相对应的接口,挺好的,之后的量产阶段并没有体验,不过看起来也是十分完善。
这个项目定位就是接入现有的会议室系统,让整个系统能够更好的形成闭环,因此在界面设计的时候就比较草率,不过好像也能考虑单独使用啊。
本来也想对手上的项目进一步优化,但是无奈年后实在没时间,只能暂时到此了,后期自行设计板卡时,会增加电子开关关断外部外设,追加蓝牙通信方式,增加深度休眠功能,充分发挥MCU低功耗特点,让设备也能部署于供电不方便的地方。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。