本文分享自华为云社区《基于 STM32+NBIOT + 华为云 IOT 设计的智能井盖》,作者:DS 小龙哥 。

https://bbs.huaweicloud.com/blogs/400975

一、概述

智能井盖是一种通过物联网技术实现对井盖状态监测和管理的设备。当前介绍基于 STM32 微控制器,BC26 NBIOT 模组以及华为云 IOT 平台设计一款智能井盖系统。该系统通过光线传感器、霍尔传感器、温湿度传感器等设备实现井盖状态的实时监测,通过 NBIOT 网络将数据上传到华为云 IOT 平台,再通过云平台下发控制指令实现远程管理。

应用场景
智能井盖系统可以广泛应用于城市管理、交通建设等领域,其中具体应用场景包括:
(1)实时监测井盖状态,及时发现井盖开放或异常情况,提高城市管理的效率和安全性;
(2)提供实时环境监测数据,帮助提升城市环境监测能力;
(3)利用 NBIOT 网络和华为云平台的远程控制功能,可以实现智能井盖的开关控制和监管,避免人工操作不便和监管不到位引起的危险。
image-20230426095637493.png
image-20230426095643915.png

二、硬件设计
本系统的硬件设计主要包括传感器模块和控制模块两部分,其中传感器模块主要负责采集井盖状态信息,控制模块则负责数据处理和通信。

  【1】传感器模块
(1)光线传感器:光线传感器用于感知井盖上方光线强度,判断井盖是否露出地面。当井盖被遮住时,光线传感器输出低电平;当井盖暴露在外时,光线传感器输出高电平。
(2)霍尔传感器:霍尔传感器用于感知井盖状态(开 / 关),当井盖开启时,霍尔传感器输出高电平;当井盖关闭时,霍尔传感器输出低电平。
(3)温度传感器和湿度传感器:温度传感器和湿度传感器用于感知井盖下方的环境温湿度,实时反馈给系统,便于监测井盖下方环境状况。

  【2】控制模块
(1)STM32 微控制器:使用 STM32F103C8T6 微控制器,主要负责传感器数据采集、处理和控制模块与 NBIOT 模组之间的通信。
(2)BC26 NBIOT 模组:使用 BC26 NBIOT 模组,通过 NBIOT 网络将采集到的井盖状态数据上传到华为云 IOT 平台,同时支持远程控制井盖开关。
(3)LED 指示灯:采用不同颜色的 LED 指示灯,将井盖状态(开 / 关、异常、低电量)实时反馈给用户。
软件设计 软件设计主要包括 STM32 微控制器程序设计和华为云 IOT 平台开发两部分。
STM32 微控制器程序设计: 主要包括三个模块:传感器采集模块、数据处理模块和通信模块。其中传感器采集模块负责采集传感器数据并进行处理;数据处理模块根据采集的数据进行逻辑处理,判断井盖状态;通信模块负责与 NBIOT 模组之间的通信,将处理后的数据上传至华为云 IOT 平台。

三、华为云 IOT 平台开发
在华为云 IOT 平台上,需要进行设备接入、数据模型定义、规则引擎配置和应用开发等四个核心模块的开发。其中,设备接入模块包括设备注册、获取设备证书、建立连接等步骤,以保障设备与云平台之间的安全通信;数据模型定义模块需要根据实际需求定义相应的数据模型,包括上传数据格式、设备属性和服务等。规则引擎配置模块需要完成实时消息推送、远程控制和告警等功能。应用开发模块则是将完整的智能井盖系统进行打包,为用户提供统一的操作接口。
华为云官网: https://www.huaweicloud.com/
打开官网,搜索物联网,就能快速找到 设备接入IoTDA。
image-20221204193824815.png

3.1 物联网平台介绍
华为云物联网平台(IoT 设备接入云服务)提供海量设备的接入和管理能力,将物理设备联接到云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云其他产品,帮助我们快速构筑物联网解决方案。
使用物联网平台构建一个完整的物联网解决方案主要包括 3 部分:物联网平台、业务应用和设备。
物联网平台作为连接业务应用和设备的中间层,屏蔽了各种复杂的设备接口,实现设备的快速接入;同时提供强大的开放能力,支撑行业用户构建各种物联网解决方案。
设备可以通过固网、2G/3G/4G/5G、NB-IoT、Wifi 等多种网络接入物联网平台,并使用 LWM2M/CoAP、MQTT、HTTPS 协议将业务数据上报到平台,平台也可以将控制命令下发给设备。
业务应用通过调用物联网平台提供的 API,实现设备数据采集、命令下发、设备管理等业务场景。
zh-cn_image_0000001130147031.png

3.2 开通物联网服务
地址: https://www.huaweicloud.com/product/iothub.html
image-20221204194233414.png
开通标准版免费单元。
image-20230420181306316.png
image-20230420181322092.png
开通之后,点击总览,查看接入信息。 我们当前设备准备采用 MQTT 协议接入华为云平台,这里可以看到 MQTT 协议的地址和端口号等信息。
image-20230423111235524.png
总结:
  1. 端口号:   MQTT (1883)| MQTTS (8883)   
  2. 接入地址: a3433ab133.iot-mqtts.cn-north-4.myhuaweicloud.com
根据域名地址得到 IP 地址信息:
  1. Microsoft Windows [版本 10.0.19044.2846]
  2. (c) Microsoft Corporation。保留所有权利。
  3. C:\Users\11266>ping a3433ab133.iot-mqtts.cn-north-4.myhuaweicloud.com
  4. 正在 Ping a3433ab133.iot-mqtts.cn-north-4.myhuaweicloud.com [121.36.42.100] 具有 32 字节的数据:
  5. 来自 121.36.42.100 的回复: 字节=32 时间=37ms TTL=31
  6. 来自 121.36.42.100 的回复: 字节=32 时间=37ms TTL=31
  7. 来自 121.36.42.100 的回复: 字节=32 时间=36ms TTL=31
  8. 来自 121.36.42.100 的回复: 字节=32 时间=37ms TTL=31
  9. 121.36.42.100 的 Ping 统计信息:
  10.      数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
  11. 往返行程的估计时间(以毫秒为单位):
  12.      最短 = 36ms,最长 = 37ms,平均 = 36ms
  13. C:\Users\11266>
image-20230423111213624.png
MQTT 协议接入端口号有两个,1883 是非加密端口,8883 是证书加密端口,单片机无法加载证书,所以使用 1883 端口比较合适。 接下来的 ESP8266 就采用 1883 端口连接华为云物联网平台。

3.3 创建产品

(1)创建产品
点击右上角创建产品。
image-20230420181503524.png

(2)填写产品信息
根据自己产品名字填写,设备类型选择自定义类型。

(3)添加自定义模型
产品创建完成之后,点击进入产品详情页面,翻到最下面可以看到模型定义。
image-20230420181615129.png
模型简单来说: 就是存放设备上传到云平台的数据。比如:环境温度、环境湿度、环境烟雾浓度、火焰检测状态图等等,这些我们都可以单独创建一个模型保存。

3.4 添加设备
产品是属于上层的抽象模型,接下来在产品模型下添加实际的设备。添加的设备最终需要与真实的设备关联在一起,完成数据交互。

(1)注册设备
点击右上角注册设备。
image-20230421091842025.png

(2)根据自己的设备填写
在弹出的对话框里填写自己设备的信息。根据自己设备详细情况填写。

(3)保存设备信息
创建完毕之后,点击保存并关闭,得到创建的设备密匙信息。该信息在后续生成 MQTT 三元组的时候需要使用。

3.5 MQTT 协议主题订阅与发布
(1)MQTT 协议介绍
当前的设备是采用 MQTT 协议与华为云平台进行通信。
MQTT 是一个物联网传输协议,它被设计用于轻量级的发布 / 订阅式消息传输,旨在为低带宽和不稳定的网络环境中的物联网设备提供可靠的网络服务。MQTT 是专门针对物联网开发的轻量级传输协议。MQTT 协议针对低带宽网络,低计算能力的设备,做了特殊的优化,使得其能适应各种物联网应用场景。目前 MQTT 拥有各种平台和设备上的客户端,已经形成了初步的生态系统。
MQTT 是一种消息队列协议,使用发布 / 订阅消息模式,提供一对多的消息发布,解除应用程序耦合,相对于其他协议,开发更简单;MQTT 协议是工作在 TCP/IP 协议上;由 TCP/IP 协议提供稳定的网络连接;所以,只要具备 TCP 协议栈的网络设备都可以使用 MQTT 协议。 本次设备采用的 ESP8266 就具备 TCP 协议栈,能够建立 TCP 连接,所以,配合 STM32 代码里封装的 MQTT 协议,就可以与华为云平台完成通信。
华为云的 MQTT 协议接入帮助文档在这里: https://support.huaweicloud.com/devg-iothub/iot_02_2200.html
zh-cn_image_0269115067.png
业务流程:
zh-cn_image_0263925108.png

(2)华为云平台 MQTT 协议使用限制
描述限制
支持的 MQTT 协议版本3.1.1
与标准 MQTT 协议的区别支持 Qos 0 和 Qos 1 支持 Topic 自定义不支持 QoS2 不支持 will、retain msg
MQTTS 支持的安全等级采用 TCP 通道基础 + TLS 协议(最高 TLSv1.3 版本)
单帐号每秒最大 MQTT 连接请求数无限制
单个设备每分钟支持的最大 MQTT 连接数1
单个 MQTT 连接每秒的吞吐量,即带宽,包含直连设备和网关3KB/s
MQTT 单个发布消息最大长度,超过此大小的发布请求将被直接拒绝1MB
MQTT 连接心跳时间建议值心跳时间限定为 30 至 1200 秒,推荐设置为 120 秒
产品是否支持自定义 Topic支持
消息发布与订阅设备只能对自己的 Topic 进行消息发布与订阅
每个订阅请求的最大订阅数无限制
  
(3)主题订阅格式
帮助文档地址:https://support.huaweicloud.com/devg-iothub/iot_02_2200.html
image-20221207153310037.png
对于设备而言,一般会订阅平台下发消息给设备 这个主题。
设备想接收平台下发的消息,就需要订阅平台下发消息给设备 的主题,订阅后,平台下发消息给设备,设备就会收到消息。

(4)主题发布格式
对于设备来说,主题发布表示向云平台上传数据,将最新的传感器数据,设备状态上传到云平台。
这个操作称为:属性上报。
帮助文档地址:https://support.huaweicloud.com/usermanual-iothub/iot_06_v5_3010.html
image-20221207153637391.png

3.6 MQTT 三元组
MQTT 协议登录需要填用户 ID,设备 ID,设备密码等信息,就像我们平时登录 QQ,微信一样要输入账号密码才能登录。MQTT 协议登录的这 3 个参数,一般称为 MQTT 三元组。
接下来介绍,华为云平台的 MQTT 三元组参数如何得到。

(1)MQTT 服务器地址
要登录 MQTT 服务器,首先记得先知道服务器的地址是多少,端口是多少。
帮助文档地址:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/home
image-20230411141412090.png
MQTT 协议的端口支持 1883 和 8883,它们的区别是:8883 是加密端口更加安全。但是单片机上使用比较困难,所以当前的设备是采用 1883 端口进连接的。
根据上面的域名和端口号,得到下面的 IP 地址和端口号信息: 如果设备支持填写域名可以直接填域名,不支持就直接填写 IP 地址。 (IP 地址就是域名解析得到的)
  1. 华为云的MQTT服务器地址:121.36.42.100
  2. 华为云的MQTT端口号:1883
(2)生成 MQTT 三元组
华为云提供了一个在线工具,用来生成 MQTT 鉴权三元组: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/
打开这个工具,填入设备的信息(也就是刚才创建完设备之后保存的信息),点击生成,就可以得到 MQTT 的登录信息了。
下面是打开的页面:
image-20221207154917230.png

3.7 参考案例
华为云平台部署开发也可以参考这里:
https://bbs.huaweicloud.com/blogs/381072
【基于华为云 IOT 平台实现多节点温度采集 (STM32+NBIOT)】

四、读取烟雾气体浓度

【1】MQ2 传感器
以下是一个读取 MQ2 传感器数据,并转换为烟雾浓度的示例代码,
  1. #include "stm32f10x.h"
  2. #include <stdio.h>
  3. int main(void)
  4. {
  5.      // 初始化ADC
  6.      ADC_InitTypeDef ADC_InitStructure;
  7.      RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
  8.      ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  9.      ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  10.      ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
  11.      ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  12.      ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  13.      ADC_InitStructure.ADC_NbrOfChannel = 1;
  14.      ADC_Init(ADC1, &ADC_InitStructure);
  15.      // 配置ADC通道1的GPIO引脚
  16.      GPIO_InitTypeDef GPIO_InitStructure;
  17.      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  18.      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  19.      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  20.      GPIO_Init(GPIOA, &GPIO_InitStructure);
  21.      // 启动ADC校准
  22.      ADC_Cmd(ADC1, ENABLE);
  23.      ADC_ResetCalibration(ADC1);
  24.      while (ADC_GetResetCalibrationStatus(ADC1));
  25.      ADC_StartCalibration(ADC1);
  26.      while (ADC_GetCalibrationStatus(ADC1));
  27.      // 读取ADC值
  28.      ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_28Cycles5);
  29.      ADC_SoftwareStartConvCmd(ADC1, ENABLE);
  30.      while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
  31.      uint16_t adc_value = ADC_GetConversionValue(ADC1);
  32.      // 计算烟雾浓度
  33.      float voltage = (float)adc_value / 4096.0f * 3.3f;
  34.      float density = (voltage - 0.4f) / 0.4f * 10000.0f;
  35.      // 打印出烟雾浓度
  36.      printf("MQ2 Smoke Density: %.2f ppm\n", density);
  37. }
  38. float adc_average()
  39. {
  40.      const int num_discarded = 3;  // 剔除的最大/最小值数量
  41.      float samples[20];   // 存储采样结果的数组
  42.      
  43.      // 采集数据
  44.      for (int i = 0; i < num_samples; i++) {
  45.          samples[i] = ADC_GET();
  46.      }
  47.      
  48.      // 对采样结果进行排序(升序)
  49.      for (int i = 0; i < num_samples - 1; i++) {
  50.          for (int j = i + 1; j < num_samples; j++) {
  51.              if (samples[i] > samples[j]) {
  52.                  float temp = samples[i];
  53.                  samples[i] = samples[j];
  54.                  samples[j] = temp;
  55.              }
  56.          }
  57.      }
  58.      
  59.      // 计算剩下的平均值
  60.      float sum = 0;
  61.      for (int i = num_discarded; i < num_samples - num_discarded; i++) {
  62.          sum += samples[i];
  63.      }
  64.      return sum / (num_samples - 2 * num_discarded);  // 返回计算结果
  65. }
【2】MQ4 传感器
以下是基于 HAL 库的 STM32F103ZET6 读取 MQ4 烟雾传感器的代码:
  1. #include "gpio.h"
  2. /* MQ4传感器的引脚定义 */
  3. #define MQ4_PORT        GPIOA
  4. #define MQ4_PIN         GPIO_PIN_0
  5. /* MQ4传感器的校准电压 */
  6. #define MQ4_RL_VALUE    10      // RL值为10kΩ
  7. #define MQ4_CALCULATE_RO_CLEAN(adcValue)     ((float)(RL_VALUE*(4096-adcValue)/adcValue))
  8. /* 获取MQ4传感器的数据 */
  9. float get_mq4_value()
  10. {
  11.      uint32_t adc_value = HAL_ADC_GetValue(&hadc1);
  12.      float ro = MQ4_CALCULATE_RO_CLEAN(adc_value);
  13.      float sensor_volt = HAL_ADC_GetValue(&hadc2) * (3.3 /4096.0);
  14.      float sensor_rsr = (3.3 - sensor_volt) / sensor_volt * ro;
  15.      float mq4_ppm = pow(10, ((log10(sensor_rsr / 2.5) - 0.3420) / (-0.6162)));
  16.      return mq4_ppm;
  17. }
  18. /* 主函数 */
  19. int main()
  20. {
  21.      HAL_Init();
  22.      MX_GPIO_Init();
  23.      MX_ADC1_Init();
  24.      MX_ADC2_Init();
  25.    
  26.      /* 读取MQ4传感器数据 */
  27.      float mq4_value = get_mq4_value();
  28.      printf("MQ4传感器值:%.2f PPM\r\n", mq4_value);
  29.      while (1);
  30. }
在该示例代码中,我们用到了 ADC1 和 ADC2 来分别读取 MQ4 传感器的数据引脚和校准电压。函数 get_mq4_value () 中使用了 MQ4 传感器的电路计算公式,将读取的传感器数据转化成对应的 PPM 值。

五、总结

当前文章介绍基于 STM32 微控制器、BC26 NBIOT 模组和华为云 IOT 平台,实现了一款智能井盖系统。该系统通过多种传感器实现了井盖状态的实时监测和数据上传,在应用上具有重要的应用场景和实际应用价值。整体介绍了系统硬件和软件设计的各个环节,对相关产品的开发提供了一定的参考价值和设计思路。