感谢面包板社区和中国移动所提供的中国移动万耦天工物联网开发板评测。前两次介绍了万耦天工STM32F103开发板的开发环境的构建和光敏电阻的数据采集,最后介绍一下如何利用ESP8266模块实现采集到的数据上传中国移动的物联网平台OneNET。

OneNET-中国移动物联网开放平台是由中国移动打造的PaaS物联网开放平台。平台能够帮助开发者轻松实现设备接入与设备连接,提供综合性的物联网解决方案,实现物联网设备的数据获取、数据存储和数据展现。

1. ESP8266模块的使用

ESP8266是乐鑫出品的一款高性能的WIFI转串口芯片,内部集成MCU能实现单片机之间串口通信,是目前使用最广泛的一种WIFI模块之一,可以简单理解为一个WIFI转串口的设备。

我采用的是ATK-ESP8266,这是正点原子团队推出的一款高性能的WiFi转串口模块。ATK-ESP8266 模块可以直接插到万耦天工STM32F103开发板的ATK-MODULE接口上。理论上,其他ESP8266也是可以使用的,只是没有ATK-ESP8266方便。

ATK-ESP8266除了支持乐鑫官方的AT指令集,还提供了对原子云的支持。不过我们不需要修改ATK-ESP8266的固件,原子云的支持对我们的项目没有影响。

ESP8266设备选择连接在开发板的UART3接口上,但是OneOS项目模板默认没有启用UART3设备,所以需要使用CubeMX工具修改配置以支持UART3。配置的过程稍微有点复杂,可以参考许思维老师的博客https://blog.csdn.net/xusiwei1236/article/details/127029518的2.4节(其他内容与本任务不太相同,可以不看)。

2. OneOS代码的配置

在前面两次介绍中我们都是使用的中移物联网官方网站提供的OneOS v3.0.1的代码,这个版本和最新的OneNET平台不兼容,所以首先需要从OneOS的Gitee仓库上下载Dev分支的代码(https://gitee.com/cmcc-oneos/OneOS/tree/dev),这个代码才能和最新的OneNET平台兼容。

2.1 ESP8266串口组件配置

在OneOS中,各种无线模块都属于MoLink组件,按照以下步骤进行配置:



  • 在OneOS_Cube环境下输入menuconfig命令,打开项目配置界面;
  • 进入(Top) → Components→ Network→Molink配置界面;
  • 按空格,使能Enable IoT modules support配置,下方会多出几个配置项;
  • 进入Modules →WiFi Modules Support 配置界面;
  • 按空格,使能ESP8266,打开配置后,进入ESP8266 Config配置界面;
  • 按空格使能Enable ESP8266 Module Object Auto Create选项,这样会在启动时,自动创建module,并根据配置连接WiFi热点;
  • 修改ESP8266 Interface Device Name为uart3;
  • 修改ESP8266 Interface Device Rate为115200(这是ATK-ESP8266的接口速率,如果是其他ESP8266模块,请按照厂商的要求设置此值);
  • 修改ESP8266 Connect AP SSID,输入实际热点名称,例如CU_fjGX;
  • 修改ESP8266 Connect AP Password,输入实际的热点密码,例如12345678;
  • 按空格键使能Enable ESP8266 Module BSD Socket Operates;
  • 按S保存。

配置界面如下所示:

forum.jpg

由于ESP8266模块是通过串口连接的,还需要修改串口的接收缓冲区大小,以避免丢包。设置方法是进入 (Top) →Drivers →Serial,修改Set RX buffer size为2048(如下图)。 forum.jpg

如果采用默认配置(64),经常会产生丢包,最典型的是STM32向ESP8266查询IP地址,ESP8266回回复4行数据,经常只收到2-3行,特别是会丢失最重要的1行数据导致查询IP地址失败。

2.2 OneNET SDK组件的配置

OneOS自带OneNET客户端的SDK,所以使用OneOS接入OneNET平台是非常方便的。

在配置界面中进入 (Top) →Components →Cloud→MQTT Kit,使能Enable onenet mqtt-kit即可,如下图所示。注意,不要使能Enable onenet device auto register选项。

forum.jpg

2.3  调试辅助工具的配置

还有两个调试相关的选项,最好在程序调试的初期打开,方便测试。

一个是使能网络调试命令(ifconfig、ping)。在项目配置界面,执行如下操作:



  • 进入(Top) → Components→ Network→Molink→ Enable IoT modules support → Tools 配置页;
  • 使用空格键使能Enable AT module network debug functions;
  • 按S保存,按Q退出。

配置界面如下图:

forum.jpg

启用该选项后,命令行中将会多出如下命令:



  • ifconfig命令,可用于查看当前网络状态;
  • ping命令,可用于测试与目标主机之间是否可达。

另一个是使能打印原始格式AT命令通信数据。如果想查看ESP8266模组和STM32F103之间的通信具体内容,可以打开Molink的“打印原始格式AT命令通信数据”选项。在项目配置界面,执行以下操作



  • 进入(Top) → Components→ Network→Molink→ Enable IoT modules support → Parser 配置页;
  • 按空格键使能Enable print RAW format AT command communication data;
  • 按S保存,按Q退出。

forum.jpg

启用该选项后,主控芯片与ESP8266模组之间的串口通信原始数据可以显示出来,方便定位问题。我有两个问题都是靠这个打印信息定位的:一个是串口速率设置错误,另一个是发现OneOS v3.0.1版本不能支持最新的OneNET平台。

3. OneNET云端的配置

OneNET是中移动提供的物联网云平台,其网址为:https://open.iot.10086.cn/。首先需要在该平台注册账号,注册后进入其开发者中心进行添加产品和新建设备的工作。

3.1 创建产品

在OneNET的开发者中心,点击左侧的“产品开发”,然后点击右侧的“创建产品”按钮。

forum.jpg

在“创建产品”页面,可以先选择适合的产品品类,比如我们的设备可以选择“智慧城市[产品行业]>环境感知[产品场景]>亮度传感器[产品品类]”。

forum.jpg

“智能化方式”选择“设备接入”。接下来的产品信息可以根据实际情况填写,但是有两个参数特别重要,一个是接入协议需要选择“MQTT”,另一个是数据协议选择“数据流”,如下图所示。

forum.jpg

数据协议默认是OneJSON,这个协议的数据格式和数据流有一些差别,目前OneOS只支持数据流,如果选择OneJSON就需要自己编写MQTT代码。

3.2 添加设备

新建了产品之后还需要添加设备。在前面产品开发的界面里面点击对应产品的“设备管理”按钮,进入“设备管理”界面(如下图),在其中选择“添加设备”按钮就可以添加设备了。设备没有什么特别的参数,只要保证名字不重复就行了。

forum.jpg

3.3 创建数据流模板

如果是OneJSON设备,完成以上两部就结束了。但是数据流设备还需要定义数据流模板。在前面产品开发的界面里面点击对应产品的“产品开发”操作,进入下图界面。

forum.jpg

点击“创建模板”,添加数据流名称,这个名称就是后面用于提交数据的字段名称,我们定义了两个:BrightSensitivity和BatteryPercentage,分别表示亮度和电池容量。

至此,OneNET平台侧的配置就都完成了,开始编写客户端代码。

4. MQTT客户端代码的编写

4.1 OneNET产品和设备参数的设置

首先,我们需要把刚才云平台设置的产品和设备参数输入到C:\OneOS-dev\components\cloud\onenet\mqtt-kit\mqtts_device\onenet_device_sample.h文件中。

在该文件中需要修改三个参数:



  • USER_PRODUCT_ID是产品ID,可以在”产品开发”页面找到;
  • USER_ACCESS_KEY:这个在前面的数据流模板定义的页面可以找到,就是access_key;
  • USER_DEVICE_NAME,就是前面定义的设备名称,如“photo_test”。

修改完这三个参数,编译下载程序后,在shell命令行输入onenet_mqtts_device_start,就可以实现设备连接OneNET云了。

4.2 发送数据到OneNET

我们是在一个定时器中完成对数据的ADC采集的,不宜在定时器中直接调用MQTT库,所以我们创建了一个新的线程专门用于发送数据到云端。在定时器和线程间采用一个信号量进行同步操作,一旦数据采集到就post信号量通知线程发送数据。相关代码如下:

MQTT线程的代码如下:

  1. extern os_msgqueue_dummy_t mqtts_mq;
  2. extern os_semaphore_id sem_static;
  3. extern uint16_t light_value;
  4. const char *base_dp_upload_str = "{"
  5.                                  ""id": %d,"
  6.                                  ""dp": {"
  7.                                  ""BrightSensitivity": [{"
  8.                                  ""v": %d"
  9.                                  "}],"
  10.                                  ""BatteryPercentage": [{"
  11.                                  ""v": %d"
  12.                                  "}]"
  13.                                  "}"
  14.                                  "}";
  15. mq_msg_t mq_msg;
  16. /**
  17. * @brief 向OneNET发布数据
  18. *
  19. */
  20. static void generate_onenet_publish_data_cycle_thread_func(void *arg)
  21. {
  22.     os_err_t rc;
  23.     int      pub_buf_len    = 0;
  24.     int id = 1;
  25.     while(1)
  26.     {
  27.         os_semaphore_wait(sem_static, OS_WAIT_FOREVER);
  28.         LOG_I(DBG_EXT_TAG, "publish data to onenet");
  29.         if (id != 2147483647)
  30.         {
  31.             id++;
  32.         }
  33.         else
  34.         {
  35.             id = 1;
  36.         }
  37.         memset(&mq_msg, 0x00, sizeof(mq_msg));
  38.         mq_msg.topic_type = DATA_POINT_TOPIC;
  39.         snprintf(mq_msg.data_buf, sizeof(mq_msg.data_buf), base_dp_upload_str, id, light_value, 100);
  40. //        os_kprintf("buffer: %s\r\n", mq_msg.data_buf);
  41.         mq_msg.data_len = strlen(mq_msg.data_buf);
  42.         if (OS_FALSE == onenet_mqtts_device_is_connected())
  43.         {
  44.             LOG_E(DBG_EXT_TAG, "onenet mqtts device is disconnected.");
  45.         }
  46.         rc = os_msgqueue_send((os_msgqueue_id)&mqtts_mq, (void *)&mq_msg, sizeof(mq_msg_t), 0);
  47.         if (rc != OS_SUCCESS)
  48.         {
  49.             LOG_E(DBG_EXT_TAG, "mqtts_device_messagequeue_send ERR");
  50.         }
  51.     }
  52. }
  53. os_task_id generate_onenet_publish_data_cycle_thread = NULL;
  54. void generate_onenet_publish_data_cycle(void)
  55. {
  56.     generate_onenet_publish_data_cycle_thread = os_task_create(NULL,
  57.                                                                NULL,
  58.                                                                1024,
  59.                                                                "generate_pubdata",
  60.                                                                generate_onenet_publish_data_cycle_thread_func,
  61.                                                                OS_NULL,
  62.                                                                OS_TASK_PRIORITY_MAX / 2);
  63.     if (NULL == generate_onenet_publish_data_cycle_thread)
  64.     {
  65.         LOG_E(DBG_EXT_TAG, "onenet mqtts client create thread failed");
  66.         OS_ASSERT(OS_NULL != generate_onenet_publish_data_cycle_thread);
  67.     }
  68.     os_task_startup(generate_onenet_publish_data_cycle_thread);
  69. }
       采集的代码如下:
  1. static void timer_periodic_timeout(void *parameter)
  2. {
  3.     uint16_t light;
  4.     light_value = light = Get_Light_Value();        //得到光照值
  5.     os_kprintf("Light: %d\r\n", light);
  6.     uint8_t r, i;
  7.     for(i = 0; i < 3; i++)
  8.     {
  9.         r = light % 10;
  10.         buffer[2 - i] = '0' + r;
  11.         light /= 10;
  12.     }
  13.     if(buffer[0] == '0')
  14.     {
  15.         buffer[0] = ' ';
  16.         if(buffer[1] == '0')
  17.             buffer[1] = ' ';
  18.     }
  19.     buffer[3] = 0;
  20.     lcd_draw_solid_rect(30 + 7 * 12, 170, 200, 24, OS_COLOR_BLACK);
  21.     lcd_show_string(30 + 7 * 12, 170, 200, 24, 24, buffer, OS_COLOR_RED);   
  22.     os_kprintf("semaphore post\r\n");
  23.     if (OS_SUCCESS != os_semaphore_post(sem_static))
  24.     {
  25.         os_kprintf("semaphore post fail");
  26.     }
  27. }

4.3 OneNET平台观察数据

程序运行后,我们进入OneNET开发者中心,就可以看到数据的显示了,效果如下图。


forum.jpg

这里面每次数据的突然变小都是因为我们遮挡光敏传感器引起。


至此,我们的OneOS评测完成了,虽然遇到了各种小坑,但终究顺利地完成了任务。