开篇首先要感谢一下此次 RT-Thread 开源社区以及 CSDN 开源社区的社区之星推荐,对于此次评选我也向大家分享了一段个人的技术成长之路的视频:

 

社区之星成长故事地址:(请复制以下地址粘贴到浏览器观看)

 

https://marketing.csdn.net/p/06e460343ed00f1eb4178b3d64c8003c

 

 

其次,接下来的 2020 年度 CSDN 博客之星的评选,很荣幸我从茫茫大海排名脱颖而出,成为 200 名内定选手,感谢各位一路支持我的小伙伴能给我投上一票,谢谢各位了!

 

CSDN 为我投票的地址:(请复制以下地址粘贴到浏览器)

 

https://bss.csdn.net/m/topic/blog_star2020/detail?username=morixinguan

 

温馨提示:每天都可以投噢!截止日期是 1 月 24 日。

 

 

接下来,由我来分享一下最近这两周参加腾讯连连 IoT 开发大赛的作品:

 

“硬核开发 · 有你更搭”Techo Park- 腾讯连连 IoT 开发大赛正式开启!

 

得不得奖我不是特别关心,当然如果能拿奖那就更好了,最重要的是,我要将它分享出来!

1、产品介绍

达特甲醛探测仪是基于英国达特公司设计开发的 WZ-S 型甲醛检测模组设计开发的,它可以用于检测环境中甲醛的浓度,以提示用户运用科学有效的方法降低甲醛污染,进而使环境的空气质量变得更加清新,也进一步提高用户的健康意识以及对生活品质的追求。

 

 

基于 TencentOS tiny 智能达特甲醛探测系统

 

项目开源地址:https://gitee.com/morixinguan/smart-dart-detect

 

2、产品主要功能视频演示

 

3、产品亮点

  • 支持使用腾讯连连小程序扫描设备上的二维码支持腾讯连连小程序实时查看动态甲醛浓度数据支持腾讯云平台 IoT explorer 实时查看动态甲醛浓度数据支持外部串口调试,系统可动态匹配用户下发的指令灵活做出回应可供用户设置参数(密码权限管理、单位显示、声光报警、数据上传)支持设备状态实时查看(工作状态、网络状态、用户权限、SD 卡容量)支持甲醛浓度数据 SD 卡实时存储,并可以通过 SD 卡进行数据导出查看支持设备恢复出厂设置功能,确保设备在出厂前所有的默认设置参数一致支持腾讯云平台 IoT explorer 实时下发字符串,设备实时通过热敏打印机实时打印字符串内容,并且可控制外设

 

4、产品硬件平台

4.1、智能达特甲醛探测系统硬件组成架构

 

 

该系统由小熊派开发板、达特甲醛模组、ESP8266 模块、热敏打印机模组构成。

 

4.2、小熊派开发板简介

小熊派 BearPi 开发板是南京厚德物联网有限公司为着力于开发者打造的一款简单、丰富、低成本、可扩展、支持多种 IoT 通信的物联网开发板,可广泛应用于智能家居、智慧农业、智慧城市等等领域。

 

 

4.3、达特甲醛模组简介

达特甲醛模组采用的是英国达特 WZ-S 型甲醛模组,它可以用于将环境中甲醛的含量转换成浓度值,然后进行标准化数字输出,也便于系统集成。

 

 

该模组的硬件接口为腾讯团队定制的符合物联网俱乐部定义的标准 E53 接口,如下硬件原理图所示:

 

 

4.4、ESP8266 模块简介

ESP8266 模块小熊派开发板套件提供的用于通过 Wi-Fi 传输数据的通信扩展板,该拓展板采用的是乐鑫 ESP8266 Wi-Fi 通信模组,支持常见的IPv4/TCP/UDP/HTTP/FTP等通信协议。

 

 

该模组的硬件接口为小熊派定制的符合物联网俱乐部定义的标准 WAN 接口,如下硬件原理图所示:

 

 

4.5、热敏打印机模组

热敏打印机模组采用的是深圳市荔璟科技有限公司的 MC-EH205 嵌入式小票打印机,该模组主要应用于各种仪器仪表打印相关凭证及检测数据明细,可广泛应用于邮政快递行业、仓储服务行业、安防安检行业、警用军工行业等等。

 

 

该模组支持串口 TTL 或者 USB 接口通讯,采用开关电源或主板供电,工作电压为 5-9V,工作电流 0.5~1.5A,瞬间电流约等于 2A。

 

 

4.6、STM32CubeMX 配置

 

 

5、产品方案设计

5.1 产品整体架构图

智能达特甲醛探测系统主要是基于 TencentOS tiny 物联网操作系统进行开发,整体架构图如下:

 

 

达特智能系统主要包括硬件组成部分、驱动组成部分、操作系统(TencentOS tiny 物联网操作系统)组成部分以及应用逻辑交互部分等,其整体产品架构设计如下:

 

 

其中,界面交互部分是整个产品最核心的部分,主要由以下逻辑构成:

 

 

5.2 产品核心软件框架说明

5.2.1 产品界面框架设计

关于界面交互,依赖于核心的软件框架,主要是基于事件驱动的方式来实现:

 

 

5.2.2 操作系统任务分配与功能设计

关于产品的操作系统启动流程由以下流程图构成:

 

 

(1)默认任务设计与功能实现

 

功能实现:

 

  • 创建用于任务间、中断和任务间通信的队列、信号量,/默认任务处理/ 创建按键任务、状态栏任务、数据上传任务、传感器任务、串口接口任务。

 

软件逻辑实现:

 

void default_task(void *pdata)
{
    tos_knl_sched_lock();
    /*创建队列,用于接收传感器数据*/
    tos_msg_q_create(&sensor_data_msg, sensor_msg_pool, SENSOR_U3_BUFFER_SIZE);
    tos_msg_q_create(&uploader_data_msg, uploader_msg_pool, NETWORK_BUF);
    tos_msg_q_create(&recv_data_msg, recv_msg_pool, CMD_STR_SIZE);
    /*创建按键任务*/
    osThreadCreate(osThread(KeyTask), NULL);
    /*创建状态栏任务*/
    osThreadCreate(osThread(Status_Task), NULL);
    /*创建数据上传任务*/
    osThreadCreate(osThread(uploader_task), NULL);
    /*创建传感器数据任务*/
    osThreadCreate(osThread(Sensor_Task), NULL);
    /*创建串口接收任务*/
    osThreadCreate(osThread(UartCmdTask), NULL);
    tos_knl_sched_unlock();
}

 

(2)按键任务设计与功能实现

 

功能实现:

 

  • 获取按键事件并转换为具体的事件值根据当前界面的游标以及菜单实现界面切换

 

软件逻辑实现:

 

void KeyTask(void *argument)
{
    __IO uint8_t KeyCode = 255;

    while(1)
    {
        /*获取键值*/
        GetKey(&KeyCode);

        if(255 != KeyCode)
        {
            Menu_Select_Item(Flow_Cursor.flow_cursor, KeyCode);
            KeyCode = 255 ;
        }

        tos_sleep_ms(5);
    }
}

 

(3)状态栏任务设计与功能实现

 

功能实现:

 

  • 获取系统 RTC 时钟日期和时间当系统位于主页面时,1s 钟存储一条甲醛数据实时根据当前系统设置及状态刷新状态栏所有图标显示当配置报警选项将功能打开时,当浓度超过国标规定参数时,设备能够自动闪烁 LED 灯进行报警,否则关闭 LED 灯

 

软件逻辑实现:

 

void Status_Task(void *argument)
{
    LCD_Fill_Para status_bar_fill_para = {0, 240, 0, 27, WHITE} ;
    lcd_model.lcd_driver->lcd_fill(status_bar_fill_para);
    display_runing_time_font(1);

    while(1)
    {
        Get_Date_Time();

        if(0 == plot_handler.plot_mode)
        {
            sprintf(DateTime_Handler_Info.DisPlay_DateBuf, "%02d:%02d:%02d", \
                    DateTime_Handler_Info.hour, DateTime_Handler_Info.minute, DateTime_Handler_Info.sec);
            lcd_model.lcd_driver->lcd_show_ascii_str(datatime_display_para);

            if(MAIN_PAGE == Flow_Cursor.flow_cursor)
                display_runing_time(DateTime_Handler_Info.hour, DateTime_Handler_Info.minute, DateTime_Handler_Info.sec, 1);
        }

        if(MAIN_PAGE == Flow_Cursor.flow_cursor)
        {
            tos_knl_sched_lock();
            save_record_to_flash();
            tos_knl_sched_unlock();
        }

        if(1 == User_Memory_Para.alarm)
        {
            /*如果大于 0.062ppm,则报警灯闪烁,否则熄灭*/
            if(0 == User_Memory_Para.unit)
            {
                if(Formaldehyde_Sensor_interface.Formaldehyde_Value > 0.062)
                    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
                else
                    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
            }
            /*如果大于 0.062*0.74666mg/m3,则报警灯闪烁,否则熄灭*/
            else
            {
                if(Formaldehyde_Sensor_interface.Formaldehyde_Value > 0.046)
                    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
                else
                    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
            }
        }
        else
        {
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
        }

        /*刷新 WIFI 连接状态*/
        if(0 == wifi_connect_flag)
            display_signal_status(0);
        else
            display_signal_status(4);

        /*刷新服务器连接状态*/
        if(0 == server_connect_flag)
            display_server_status(0);
        else
            display_server_status(1);

        /*刷新上传状态*/
        if(0 == User_Memory_Para.upload_flag)
            display_upload_status(0);
        else
            display_upload_status(1);

        /*刷新设备密码状态*/
        if(1 == User_Memory_Para.password_enable_flag)
            display_passwd_icon(0);
        else
            display_passwd_icon(1);

        /*刷新 SD 卡状态*/
        refresh_sd_card_status();
        tos_sleep_ms(1000);
    }
}

 

(4)数据上传任务设计与功能实现

 

功能实现:(注意,需要腾讯云固件支持,后续章节讲解)

 

  • 完成产品与腾讯云 IoT explorer 平台的对接接收腾讯云 IoT explorer 平台下发的指令和字符串,控制打印机打印根据系统配置实时上传当前甲醛浓度值,并通过 IoT explorer 和腾讯连连小程序展示

 

软件逻辑实现:(mqtt_demo_task 代码过长,此处省略,详细请看代码)

 

void uploader_task(void *arg)
{
    mqtt_demo_task();
    while (1)
    {
        printf("This is a mqtt demo!\r\n");
        tos_task_delay(1000);
    }
}

 

(5)传感器任务设计与功能实现

 

功能实现:

 

  • 传感器注册(完成串口接收配置)获取传感器数据并进行解析(根据传感器手册进行数据解析)当浓度超过国标规定参数时,设备能够让数值显示自动飘红,提醒用户注意阈值超出根据数值开关,选择是否在界面上显示最大值和最小值,当打开则显示,关闭则不显示最大最小值根据传感器数据实时显示当前的工作状态和传感器数值,并能根据设置单位选项随意切换浓度匹配算法当网络上传功能打开时,发送信号量通知数据上传任务将传感器浓度值上传到腾讯云 IoT explorer 平台

 

软件逻辑实现:

 

void Sensor_Task(void  *argument)
{
    char ch20_data[20] = {0};
    /*传感器注册*/
    Sensor_Register(&Formaldehyde_Sensor_interface);
    while(1)
    {
        /*获取传感器数据*/
        Formaldehyde_Sensor_interface.get_Formaldehyde_Value(&Formaldehyde_Sensor_interface);

        /*处理传感器数据展示*/
        if(MAIN_PAGE == Flow_Cursor.flow_cursor)
        {
            display_work_status(Formaldehyde_Sensor_interface.Formaldehyde_Value, 1);

            //PPM
            if(0 == User_Memory_Para.unit)
            {
                /* 正常解析之后通过队列发送 */
                memset(ch20_data, 0, 20);

                if(Formaldehyde_Sensor_interface.Formaldehyde_Value > 2)
                {
                    Formaldehyde_Sensor_interface.Formaldehyde_Value = 2 ;
                    sprintf(ch20_data, "ppm:%.3f", Formaldehyde_Sensor_interface.Formaldehyde_Value);
                }
                else
                    sprintf(ch20_data, "ppm:%.3f", Formaldehyde_Sensor_interface.Formaldehyde_Value);

                /*如果网络功能开的状态下才会进行队列发送*/
                if(1 == User_Memory_Para.upload_flag && 1 == wifi_connect_flag && 1 == server_connect_flag)
                    tos_msg_q_post(&uploader_data_msg, ch20_data);

                if(Formaldehyde_Sensor_interface.Formaldehyde_Value > 2)
                {
                    ch20_ppm_value_printf = 2;
                    display_hcho_value(ch20_ppm_value_printf, RED, 1);

                    if(1 == User_Memory_Para.value)
                    {
                        display_hcho_max_value(ch20_ppm_value_printf, GREEN, 1);
                        display_hcho_min_value(ch20_ppm_value_printf, GREEN, 1);
                    }
                    else
                    {
                        display_hcho_max_value(ch20_ppm_value_printf, BLACK, 0);
                        display_hcho_max_value(ch20_ppm_value_printf, BLACK, 0);
                    }
                }
                else
                {
                    if(Formaldehyde_Sensor_interface.Formaldehyde_Value > 0.062)
                    {
                        ch20_ppm_value_printf = Formaldehyde_Sensor_interface.Formaldehyde_Value;
                        display_hcho_value(Formaldehyde_Sensor_interface.Formaldehyde_Value, RED, 1);

                        if(1 == User_Memory_Para.value)
                        {
                            display_hcho_max_value(Formaldehyde_Sensor_interface.Formaldehyde_Max_Value, GREEN, 1);
                            display_hcho_min_value(Formaldehyde_Sensor_interface.Formaldehyde_Min_Value, GREEN, 1);
                        }
                        else
                        {
                            display_hcho_max_value(ch20_ppm_value_printf, BLACK, 0);
                            display_hcho_max_value(ch20_ppm_value_printf, BLACK, 0);
                        }
                    }
                    else
                    {
                        ch20_ppm_value_printf = Formaldehyde_Sensor_interface.Formaldehyde_Value;
                        display_hcho_value(Formaldehyde_Sensor_interface.Formaldehyde_Value, GREEN, 1);

                        if(1 == User_Memory_Para.value)
                        {
                            display_hcho_max_value(Formaldehyde_Sensor_interface.Formaldehyde_Max_Value, GREEN, 1);
                            display_hcho_min_value(Formaldehyde_Sensor_interface.Formaldehyde_Min_Value, GREEN, 1);
                        }
                        else
                        {
                            display_hcho_max_value(ch20_ppm_value_printf, BLACK, 0);
                            display_hcho_max_value(ch20_ppm_value_printf, BLACK, 0);
                        }
                    }
                }
            }
            //mg/m3
            else
            {
                /* 正常解析之后通过队列发送 */
                memset(ch20_data, 0, 20);

                if(Formaldehyde_Sensor_interface.Formaldehyde_Value > 0.046)
                {
                    Formaldehyde_Sensor_interface.Formaldehyde_Value = 0.046 ;
                    sprintf(ch20_data, "mgm3:%.3f", Formaldehyde_Sensor_interface.Formaldehyde_Value);
                }
                else
                    sprintf(ch20_data, "mgm3:%.3f", Formaldehyde_Sensor_interface.Formaldehyde_Value);

                /*如果网络功能开的状态下才会进行队列发送*/
                if(1 == User_Memory_Para.upload_flag && 1 == wifi_connect_flag && 1 == server_connect_flag)
                    tos_msg_q_post(&uploader_data_msg, ch20_data);

                if(Formaldehyde_Sensor_interface.Formaldehyde_Value > 1.493)  //2 * 0.74666
                {
                    ch20_ppm_value_printf = 1.493;
                    display_hcho_value(ch20_ppm_value_printf, RED, 1);

                    if(1 == User_Memory_Para.value)
                    {
                        display_hcho_max_value(ch20_ppm_value_printf, GREEN, 1);
                        display_hcho_min_value(ch20_ppm_value_printf, GREEN, 1);
                    }
                    else
                    {
                        display_hcho_max_value(ch20_ppm_value_printf, BLACK, 0);
                        display_hcho_max_value(ch20_ppm_value_printf, BLACK, 0);
                    }
                }
                else
                {
                    if(Formaldehyde_Sensor_interface.Formaldehyde_Value > 0.046)
                    {
                        ch20_ppm_value_printf = Formaldehyde_Sensor_interface.Formaldehyde_Value;
                        display_hcho_value(ch20_ppm_value_printf, RED, 1);

                        if(1 == User_Memory_Para.value)
                        {
                            display_hcho_max_value(ch20_ppm_value_printf, GREEN, 1);
                            display_hcho_min_value(ch20_ppm_value_printf, GREEN, 1);
                        }
                        else
                        {
                            display_hcho_max_value(ch20_ppm_value_printf, BLACK, 0);
                            display_hcho_max_value(ch20_ppm_value_printf, BLACK, 0);
                        }
                    }
                    else
                    {
                        ch20_ppm_value_printf = Formaldehyde_Sensor_interface.Formaldehyde_Value;
                        display_hcho_value(ch20_ppm_value_printf, GREEN, 1);

                        if(1 == User_Memory_Para.value)
                        {
                            display_hcho_max_value(ch20_ppm_value_printf, GREEN, 1);
                            display_hcho_min_value(ch20_ppm_value_printf, GREEN, 1);
                        }
                        else
                        {
                            display_hcho_max_value(ch20_ppm_value_printf, BLACK, 0);
                            display_hcho_max_value(ch20_ppm_value_printf, BLACK, 0);
                        }
                    }
                }
            }
        }
    }
}

 

(6)串口接收任务设计与功能实现

 

功能实现:

 

  • 命令解析器注册接收来自串口调试助手发送的字符串通过命令解析器对接收的字符串进行匹配执行对应的命令主要会执行如下命令,分别是:

 

命令 1:开灯 / 关灯

 

命令 2:获取当前传感器浓度数据

 

命令 3:获取当前系统版本

 

命令 4:系统复位

 

命令 5:获取系统参数并通过打印机打印出来

 

软件逻辑实现如下:


/*注册命令*/
REGISTER_CMD(reset, cpu_reset_process);
REGISTER_CMD(led_on, led_on_process);
REGISTER_CMD(led_off, led_off_process);
REGISTER_CMD(get_sensor_data, get_sensor_data);
REGISTER_CMD(get_system_para, get_system_para);
REGISTER_CMD(get_system_version, get_system_version);
void UartCmdTask(void  *argument)
{
    k_err_t err;
    void *msg_received;
    uint8_t uart_recv_buf[CMD_STR_SIZE] = {0};
    /*命令初始化*/
    cmd_init();
    HAL_UART_Receive_IT(&huart1, (uint8_t *)&cmd_parse_typedef.Res, 1);
    while(1)
    {
        err = tos_msg_q_pend(&recv_data_msg, &msg_received, TOS_TIME_FOREVER);

        if (err == K_ERR_NONE)
        {
            memcpy(uart_recv_buf, msg_received, CMD_STR_SIZE);
            cmd_parsing((char *)uart_recv_buf);
            memset(&cmd_parse_typedef, 0, sizeof(cmd_parse_typedef));
        }
    }
}

 

6、产品与腾讯云 IoT explorer 平台和腾讯连连对接设计

6.1、为 ESP8266 模块烧写腾讯云定制固件

(1)下载腾讯云官方提供的 ESP8266 固件

 

https://github.com/tencentyun/qcloud-iot-esp-wifi

 

 

(2)下载烧录工具

 

使用乐鑫官方下载工具 ESPFlashDownloadTool,下载链接如下:

 

https://www.espressif.com/zh-hans/support/download/other-tools

 

(3)操作步骤

 

1、确保硬件连接正确,上电即能进入 ESP8266 烧录模式

 

 

2、打开烧录工具

 

 

选择开发者模式

 

 

选择 ESP8266 下载工具

 

 

配置烧录界面面板参数

 

 

点击 START 查看模组信息:

 

 

导入要下载的固件,设置地址从 0x000 开始

 

 

重新点击 START 下载腾讯云固件

 

 

如图所示看见完成即固件更新成功。

 

将 ESP8266 硬件恢复为正常模式,也就是将那些线全部用烙铁取掉,将 ESP8266 模块插在小熊派开发板上,如下图所示,切换开关为 AT PC 模式:

 

 

发送模组信息读取命令查看当前模组信息:

 

 

如果能打印和文档描述信息则表示固件更新正确,然后切换为 AT-MCU 模式。

 

6.2、腾讯云 IoT explorer 与腾讯连连小程序配置

(1)打开腾讯云官网,提前登录成功后,选择物联网开发平台选项

 

 

然后点击立即进入。

 

(2)根据腾讯云开发文档指示创建项目并配置好参数

 

 

详细配置流程请参考:TencentOS 官方公众号文章

 

https://mp.weixin.qq.com/s/t8iVaypA1BtZwt7EEkAWyw

 

(3)根据腾讯云开发文档指示配置腾讯连连小程序面板

 

 

详细配置流程请参考:TencentOS 官方公众号文章

 

https://mp.weixin.qq.com/s/t8iVaypA1BtZwt7EEkAWyw

 

(4)程序连连小程序操作步骤

 

  • 1. 添加家庭

 

手机端在【微信】搜索【腾讯连连】小程序,首次使用需要进入后点击【我的】->【家庭管理】,添加一个你喜欢的名称即可。

 

  • 2. 添加调试设备

 

返回【首页】,点击右上角“加号”图标:

 

 

  • 3. 添加产品二维码

 

 

位于腾讯 IoT explorer 物联网开发平台的设备调试页面,有一个二维码的选项,点击扫码添加,结果如下图所示:

 

 

最后,在确保硬件正常连接的情况下,设备能够按操作视频所述正常工作,部分现象如下图所示:

 

 

7、使用腾讯云 IoT explorer 平台和腾讯连连的感受

(1)操作简单,技术支持到位

腾讯云 IoT explorer 致力于让用户快速将设备联网接入,完善的官方操作指引以及简单的操作界面,用户很快就能学习并应用到实际的产品开发中去,其次是腾讯云 IoT explorer 拥有强大的售后技术支持团队,致力于解决用户使用过程中的任何疑难杂症,保证用户能够简单,高效,快速完成腾讯云平台的对接。

 

(2)完善的开发者解决方案

腾讯云 IoT explorer 还提供多个解决方案,通过提供简单的 API 给用户,用户通过简单调用就可以轻松接入,完善的解决方案注定腾讯云 IoT explorer 未来能够引领物联网行业走向,促进物联网生态快速发展。

 

(3)移动端快速资源整合

腾讯云 Iot explorer 与腾讯连连小程序的默契配合,将物联网网页端平台展现的数据也可以通过手机等移动端随时随地的展示,十分迎合当下信息化时代的发展以及用户需求,该小程序还支持用户二次开发和定制,让用户设备可以有更多的展示空间。

 

8、本次活动收获

本次参加腾讯连连 IoT 开发大赛,让我全方位的学习了 TencetOS tiny 操作系统的使用、腾讯云 IoT 物联网平台的配置和使用以及腾讯小程序腾讯连连的配置和使用,加深了我对物联网产品开发的理解,同时也让我收获了基于腾讯云物联网设备的开发流程和步骤,经过本次的学习和实践,未来在物联网产品的开发上,优先考虑本次实践的方案。