• [技术干货] 基于STM32+华为云设计的智慧烟感系统
    一、概述当前基于STM32和华为云,设计了一种智慧烟感系统,该系统可以检测烟雾,同时将检测到的数据上传到云端进行处理和分析。系统可用于家庭、办公室等需要安装烟雾报警器场所。二、系统设计2.1 系统硬件设计【1】硬件平台该系统主要使用STM32F103ZET6微控制器作为核心控制器,并搭配数码显示器、蜂鸣器、MQ4烟雾传感器、WiFi模块等作为辅助组件。【2】系统连接STM32F103ZET6微控制器通过GPIO连接数码显示器、蜂鸣器、MQ4烟雾传感器等外设。通过UART连接WiFi模块,将读取到的数据上传到云端。【3】系统工作流程系统在启动时,首先进行硬件初始化和WiFi连接操作。之后,通过烟雾传感器实时检测室内环境中的烟雾浓度,当烟雾浓度超出一定阈值时,蜂鸣器会发出警报,同时将警报信息上传到云端。在云端,系统可以对上传的数据进行处理和分析,如通过分析历史数据预测烟雾浓度的变化趋势等。2.2 系统软件设计【1】软件平台系统的软件平台主要由STM32的HAL库和华为云平台的API组成。【2】软件流程系统在启动时,首先进行硬件初始化和WiFi连接操作。之后,通过HAL库读取MQ4烟雾传感器的数据,并根据数据判断是否触发烟雾警报。如果触发了烟雾警报,则蜂鸣器发出警报,并通过WiFi模块将警报信息上传到云端。在云端,系统通过华为云平台的API接口获取上传的数据,并进行处理和分析。三、华为云IOT平台开发在华为云IOT平台上,需要进行设备接入、数据模型定义、规则引擎配置和应用开发等四个核心模块的开发。其中,设备接入模块包括设备注册、获取设备证书、建立连接等步骤,以保障设备与云平台之间的安全通信;数据模型定义模块需要根据实际需求定义相应的数据模型,包括上传数据格式、设备属性和服务等。规则引擎配置模块需要完成实时消息推送、远程控制和告警等功能。应用开发模块则是将完整的智能井盖系统进行打包,为用户提供统一的操作接口。华为云官网: cid:link_6打开官网,搜索物联网,就能快速找到 设备接入IoTDA。3.1 物联网平台介绍华为云物联网平台(IoT 设备接入云服务)提供海量设备的接入和管理能力,将物理设备联接到云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云其他产品,帮助我们快速构筑物联网解决方案。使用物联网平台构建一个完整的物联网解决方案主要包括3部分:物联网平台、业务应用和设备。物联网平台作为连接业务应用和设备的中间层,屏蔽了各种复杂的设备接口,实现设备的快速接入;同时提供强大的开放能力,支撑行业用户构建各种物联网解决方案。设备可以通过固网、2G/3G/4G/5G、NB-IoT、Wifi等多种网络接入物联网平台,并使用LWM2M/CoAP、MQTT、HTTPS协议将业务数据上报到平台,平台也可以将控制命令下发给设备。业务应用通过调用物联网平台提供的API,实现设备数据采集、命令下发、设备管理等业务场景。3.2 开通物联网服务地址: cid:link_4开通标准版免费单元。开通之后,点击总览,查看接入信息。 我们当前设备准备采用MQTT协议接入华为云平台,这里可以看到MQTT协议的地址和端口号等信息。总结: 端口号:   MQTT (1883)| MQTTS (8883)  接入地址: a3433ab133.iot-mqtts.cn-north-4.myhuaweicloud.com根据域名地址得到IP地址信息: Microsoft Windows [版本 10.0.19044.2846]  (c) Microsoft Corporation。保留所有权利。  ​  C:\Users\11266>ping a3433ab133.iot-mqtts.cn-north-4.myhuaweicloud.com  ​  正在 Ping a3433ab133.iot-mqtts.cn-north-4.myhuaweicloud.com [121.36.42.100] 具有 32 字节的数据:  来自 121.36.42.100 的回复: 字节=32 时间=37ms TTL=31  来自 121.36.42.100 的回复: 字节=32 时间=37ms TTL=31  来自 121.36.42.100 的回复: 字节=32 时间=36ms TTL=31  来自 121.36.42.100 的回复: 字节=32 时间=37ms TTL=31  ​  121.36.42.100 的 Ping 统计信息:      数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),  往返行程的估计时间(以毫秒为单位):      最短 = 36ms,最长 = 37ms,平均 = 36ms  ​  C:\Users\11266>MQTT协议接入端口号有两个,1883是非加密端口,8883是证书加密端口,单片机无法加载证书,所以使用1883端口比较合适。 接下来的ESP8266就采用1883端口连接华为云物联网平台。3.3 创建产品(1)创建产品点击右上角创建产品。​(2)填写产品信息根据自己产品名字填写,设备类型选择自定义类型。(3)添加自定义模型产品创建完成之后,点击进入产品详情页面,翻到最下面可以看到模型定义。​模型简单来说: 就是存放设备上传到云平台的数据。比如:环境温度、环境湿度、环境烟雾浓度、火焰检测状态图等等,这些我们都可以单独创建一个模型保存。3.4 添加设备产品是属于上层的抽象模型,接下来在产品模型下添加实际的设备。添加的设备最终需要与真实的设备关联在一起,完成数据交互。(1)注册设备点击右上角注册设备。(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协议接入帮助文档在这里: cid:link_2、业务流程:(2)华为云平台MQTT协议使用限制描述限制支持的MQTT协议版本3.1.1与标准MQTT协议的区别支持Qos 0和Qos 1支持Topic自定义不支持QoS2不支持will、retain msgMQTTS支持的安全等级采用TCP通道基础 + TLS协议(最高TLSv1.3版本)单帐号每秒最大MQTT连接请求数无限制单个设备每分钟支持的最大MQTT连接数1单个MQTT连接每秒的吞吐量,即带宽,包含直连设备和网关3KB/sMQTT单个发布消息最大长度,超过此大小的发布请求将被直接拒绝1MBMQTT连接心跳时间建议值心跳时间限定为30至1200秒,推荐设置为120秒产品是否支持自定义Topic支持消息发布与订阅设备只能对自己的Topic进行消息发布与订阅每个订阅请求的最大订阅数无限制(3)主题订阅格式帮助文档地址:cid:link_2​对于设备而言,一般会订阅平台下发消息给设备 这个主题。设备想接收平台下发的消息,就需要订阅平台下发消息给设备 的主题,订阅后,平台下发消息给设备,设备就会收到消息。(4)主题发布格式对于设备来说,主题发布表示向云平台上传数据,将最新的传感器数据,设备状态上传到云平台。这个操作称为:属性上报。帮助文档地址:cid:link_13.6 MQTT三元组MQTT协议登录需要填用户ID,设备ID,设备密码等信息,就像我们平时登录QQ,微信一样要输入账号密码才能登录。MQTT协议登录的这3个参数,一般称为MQTT三元组。接下来介绍,华为云平台的MQTT三元组参数如何得到。(1)MQTT服务器地址要登录MQTT服务器,首先记得先知道服务器的地址是多少,端口是多少。帮助文档地址:cid:link_0MQTT协议的端口支持1883和8883,它们的区别是:8883 是加密端口更加安全。但是单片机上使用比较困难,所以当前的设备是采用1883端口进连接的。根据上面的域名和端口号,得到下面的IP地址和端口号信息: 如果设备支持填写域名可以直接填域名,不支持就直接填写IP地址。 (IP地址就是域名解析得到的) 华为云的MQTT服务器地址:121.36.42.100  华为云的MQTT端口号:1883(2)生成MQTT三元组华为云提供了一个在线工具,用来生成MQTT鉴权三元组: cid:link_3打开这个工具,填入设备的信息(也就是刚才创建完设备之后保存的信息),点击生成,就可以得到MQTT的登录信息了。下面是打开的页面:3.7 参考案例华为云平台部署开发也可以参考这里:cid:link_5【基于华为云IOT平台实现多节点温度采集(STM32+NBIOT)】四、烟感核心代码【1】MQ2传感器以下是一个读取MQ2传感器数据,并转换为烟雾浓度的示例代码, #include "stm32f10x.h"  #include <stdio.h>  ​  int main(void)  {      // 初始化ADC      ADC_InitTypeDef ADC_InitStructure;      RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);      ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;      ADC_InitStructure.ADC_ScanConvMode = DISABLE;      ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;      ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;      ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;      ADC_InitStructure.ADC_NbrOfChannel = 1;      ADC_Init(ADC1, &ADC_InitStructure);  ​      // 配置ADC通道1的GPIO引脚      GPIO_InitTypeDef GPIO_InitStructure;      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;      GPIO_Init(GPIOA, &GPIO_InitStructure);  ​      // 启动ADC校准      ADC_Cmd(ADC1, ENABLE);      ADC_ResetCalibration(ADC1);      while (ADC_GetResetCalibrationStatus(ADC1));      ADC_StartCalibration(ADC1);      while (ADC_GetCalibrationStatus(ADC1));  ​      // 读取ADC值      ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_28Cycles5);      ADC_SoftwareStartConvCmd(ADC1, ENABLE);      while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));      uint16_t adc_value = ADC_GetConversionValue(ADC1);  ​      // 计算烟雾浓度      float voltage = (float)adc_value / 4096.0f * 3.3f;      float density = (voltage - 0.4f) / 0.4f * 10000.0f;  ​      // 打印出烟雾浓度      printf("MQ2 Smoke Density: %.2f ppm\n", density);  }  ​  ​  ​  ​  float adc_average()  {      const int num_discarded = 3;  // 剔除的最大/最小值数量      float samples[20];   // 存储采样结果的数组            // 采集数据      for (int i = 0; i < num_samples; i++) {          samples[i] = ADC_GET();     }            // 对采样结果进行排序(升序)      for (int i = 0; i < num_samples - 1; i++) {          for (int j = i + 1; j < num_samples; j++) {              if (samples[i] > samples[j]) {                  float temp = samples[i];                  samples[i] = samples[j];                  samples[j] = temp;             }         }     }            // 计算剩下的平均值      float sum = 0;      for (int i = num_discarded; i < num_samples - num_discarded; i++) {          sum += samples[i];     }      return sum / (num_samples - 2 * num_discarded);  // 返回计算结果  }【2】MQ4传感器以下是基于HAL库的STM32F103ZET6读取MQ4烟雾传感器的代码: #include "gpio.h"  ​  /* MQ4传感器的引脚定义 */  #define MQ4_PORT       GPIOA  #define MQ4_PIN         GPIO_PIN_0  ​  /* MQ4传感器的校准电压 */  #define MQ4_RL_VALUE   10     // RL值为10kΩ  #define MQ4_CALCULATE_RO_CLEAN(adcValue)     ((float)(RL_VALUE*(4096-adcValue)/adcValue))  ​  /* 获取MQ4传感器的数据 */  float get_mq4_value()  {     uint32_t adc_value = HAL_ADC_GetValue(&hadc1);     float ro = MQ4_CALCULATE_RO_CLEAN(adc_value);     float sensor_volt = HAL_ADC_GetValue(&hadc2) * (3.3 /4096.0);     float sensor_rsr = (3.3 - sensor_volt) / sensor_volt * ro;     float mq4_ppm = pow(10, ((log10(sensor_rsr / 2.5) - 0.3420) / (-0.6162)));     return mq4_ppm;  }  ​  /* 主函数 */  int main()  {     HAL_Init();     MX_GPIO_Init();     MX_ADC1_Init();     MX_ADC2_Init();         /* 读取MQ4传感器数据 */     float mq4_value = get_mq4_value();  ​     printf("MQ4传感器值:%.2f PPM\r\n", mq4_value);  ​     while (1);  }在该示例代码中,我们用到了ADC1和ADC2来分别读取MQ4传感器的数据引脚和校准电压。函数get_mq4_value()中使用了MQ4传感器的电路计算公式,将读取的传感器数据转化成对应的PPM值。五、系统优化设计为了提高系统的准确性和实用性,采取以下优化方案:【1】使用多个传感器在实际应用中,可以通过使用多个烟雾传感器来提高系统的准确性。安装多个传感器,可以检测到更多的区域,从而更准确地判断室内是否有烟雾浓度超标的情况。【2】使用专业的云平台为了更好地管理和分析数据,可以选择一个专业的云平台,如华为云。使用云平台,可以更方便地对数据进行分析和处理,并实现更多的智能化管理功能。【3】提高警报效果为了更好地提高警报效果,除了使用蜂鸣器外,还可以考虑使用其他类型的报警器,如闪光灯、震动器等。这些报警器可以在不同场合下提供更好的报警效果。【4】引入云服务将数据上传到云端,不仅可以对数据进行分析和处理,还可以通过云平台提供的服务,如语音警报、邮件通知等方式及时向用户通报警情,让用户第一时间采取措施。【5】实现远程控制若在系统中加入控制模块,用户就可以在远程控制平台上通过云端控制系统,如关闭报警器、打开风扇等。对于需要海量数据处理和智能控制的复杂系统,基于云服务的远程控制就显得尤为重要。六、总结本文基于STM32和华为云平台,设计了一种智慧烟感系统。该系统可以检测烟雾浓度并上传数据到云端,通过云端进行分析和处理,实现报警和远程控制功能。通过不断地优化系统,可以不断提高系统的准确性和实用性,将其应用到更广泛的领域中。
  • [问题求助] 设备单独注册可以激活 进行批量注册无法激活
    这是用批量注册的设备无法激活试过很多遍了这是单独注册设备可以正常激活不知道是我有什么疏忽还是有什么地方搞错了,希望有大佬可以解答。
  • [问题求助] 小熊派串口通信
    整个项目是实例中的智慧农业项目,我想用在此项目中使用usart2 ,但是小熊派串口2的串口通信无法进入USART2_IRQHandler回调函数串口接受中断的回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){  /* Prevent unused argument(s) compilation warning */  printf("test\r\n");  if (huart == &huart2)  {    if (Rx2cnt >= 200) //溢出判断    {      Rx2cnt = 0;      memset(Rx2buf, 0x00, sizeof(Rx2buf));      printf("over array\r\n");    }    else    {      Rx2buf[Rx2cnt++] = a_ch; //接收数据转存      if ((Rx2buf[Rx2cnt - 1] == 0x0A) && (Rx2buf[Rx2cnt - 2] == 0x0D)) //判断结束位      {        printf("[Rx2]:%s\r\n", Rx2buf); //将收到的信息发送出去        while (HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX)          ;        Rx2cnt = 0;        memset(Rx2buf, 0x00, sizeof(Rx2buf)); //清空数组      }    }    HAL_UART_Receive_IT(&huart2, (uint8_t *)&a_ch, 1); //再开启接收中断  }}
  • [问题求助] 应用侧发送POST获取Token失败
    在保证账号信息无误的情况下,返回码为401,该怎么处理呢
  • [互动交流] 盘古加持特定智能手机与智能家居智能设备问题
    中午我在思考一个问题,既然华为自己的盘古大模型开发出来之后,有没有机会能服务到我们的c端用户呢?眼看鸿蒙3.0已经逐渐走向成熟,但是我还是感觉给用户带来的震撼不够。用户需要ai加持,这是必然的趋势不是吗?可能在语言回复功能上胡说八道的可能性还是会有,但是我觉得可以先从特定的几个方向研究,比如已经成熟的AI1.0的小艺小艺,我觉得可以适当的加持部分功能,比如天气预警,事务提醒,外卖点餐等,以及多维度得思维辨析,都是可能带来不一样得感觉的。这是我对移动端得感想,再来谈谈我对智能家居得看法。我还记得华为得万物互联当时给我带来的震撼,无与伦比,这真的是对于真正互联网技术得顶尖理解,才能做出这样得伟大壮举,但是,大模型时代已经到来,万物互联的时代真正要来临了。可以单卡即可运行得模型并非没有,在微调得当得情况之下,难道,智能家居与大模型结合得方法还会没有吗?智能加持得智能床,随时传输给大模型数据,实时关注身体健康,可以及时预警。智能加持得智能房间,随时关注天气变化,实时帮助我们开关窗帘,提醒出门带伞等等等等,能大模型加持得地方太多太多,根本想不过来。如果可以,我更希望大模型对于教育不均衡地区得孩子们带来的意义更大,这就是科技得初衷不是吗?
  • [技术干货] AI语音识别-我给浏览器加了个语音搜索功能
    1. 前言随着物联网的发展,语音识别技术受到越来越多的关注,语音识别技术正积极推动信息通信领域的革命,语音拨号,语音邮件,语音输入乃至语音操控等以语音识别为基础的人机交互日益普及.尽管生物识别方式不断增多,语音识别方式仍是主流方式.与其他生物识别技术相比,语音识别技术不仅具有非接触,非侵入性,使用方便,不会遗失和忘记,不需记忆等特点。本篇文章就采用华为云提供的在线语音识别服务给浏览器设计一个语音自动搜索的功能,编程语言采用C++,软件框架采用QT设计,浏览器内核采用QWebEngineView,在QT5.7以后,QT里就不支持webkit了,目前自带的浏览器内核是QWebEngineView,只能使用MSVC编译编译,mingw要使用浏览器可以单独下载webkit的库,或者使用COM组件调用IE浏览器,当前文章里使用的浏览器是QWebEngineView,编译器采用VS2017,32bit。语音采集功能使用QT的QAudioInput类来实现,采集声卡的PCM数据,保存起来,通过华为云的语音识别HTTP接口完成文字识别,得到文字后再通过浏览器进行搜索文字相关内容。实现效果如下:点击界面上的 ”开始语音采集“按钮,就可以说话,说完点击停止采集,然后调用华为云的语音识别接口进行语音识别,在下面的显示框上显示识别到的文字,然后再完成浏览器自动搜索。2. 创建语音服务器2.1 使用语音服务登录华为云官网: cid:link_4选择产品-人工智能-语音交互服务-一句话识别。短语音识别地址: cid:link_2短语音识别是将口述音频转换为文本,通过API调用识别不超过一分钟的不同音频源发来的音频流或音频文件。适用于语音搜索、人机交互等语音交互识别场景。 支持免费试用。免费试用每日500次。2.2 HTTP接口使用介绍文档地址: cid:link_1在线调试接口地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=SIS&api=RecognizeShortAudio几个重要的参数:本地音频采集的频率、通道数都得与参数匹配。2.3 接口地址总结 请求地址: "https://{endpoint}/v1/{project_id}/asr/short-audio"  ​  请求数据:  {      "config": {          "audio_format": "ulaw8k8bit",          "property": "chinese_8k_common",          "add_punc": "yes",          "digit_norm": "yes",          "need_word_info": "yes"     },      "data": "/+MgxAAUeHpMAUkQAANhuRAC..."  }  ​  请求头里要带: X-Auth-Token 参数请求数据里的参数在前面截图里介绍了,data就是音频文件的base64编码数据。请求地址里的endpoint字段、project_id字段、还有X-Auth-Token字段只要是访问华为云的任何API接口都需要填,获取方法看这里: cid:link_3 翻到2.3小节。识别成功返回的数据: {    "trace_id": "567e8537-a89c-13c3-a882-826321939651",    "result": {      "text": "欢迎使用语音云服务。",      "score": 0.9,      "word_info": [       {          "start_time": 150,          "end_time": 570,          "word": "欢迎"       },       {          "start_time": 570,          "end_time": 990,          "word": "使用"       },       {          "start_time": 990,          "end_time": 1380,          "word": "语音"       },       {          "start_time": 1380,          "end_time": 1590,          "word": "云"       },       {          "start_time": 1590,          "end_time": 2070,          "word": "服务"       }     ]   }  }其中的text字段就是识别的文本数据。3. 项目代码示例下面列出核心的代码,主要是就是字符串拼接格式,拼接完发送http请求即可。3.1 语音转文字请求代码 //语音转文本  void Widget::audio_to_text(QByteArray data)  {      function_select=0;  ​      QString requestUrl;      QNetworkRequest request;  ​      //存放文件的BASE64编码      QString base64_Data;  ​      //设置请求地址      QUrl url;  ​      //一句话识别的请求地址      requestUrl = QString("https://sis-ext.%1.myhuaweicloud.com/v1/%2/asr/short-audio")             .arg(SERVER_ID)             .arg(PROJECT_ID);      qDebug()<<"requestUrl:"<<requestUrl;            //设置数据提交格式      request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));  ​      //将图片进行Base64编码      base64_Data = QString(data.toBase64());  ​      //设置token      request.setRawHeader("X-Auth-Token",Token);  ​      //构造请求      url.setUrl(requestUrl);      request.setUrl(url);  ​     //设置采样率     QString post_param=QString               ("{"                     ""config": {"                         ""audio_format": "%1","                         ""property": "%2","                         ""add_punc": "yes","                         ""digit_norm": "yes","                         ""need_word_info": "yes""                     "},"                     ""data": "%3""                 "}").arg("pcm16k16bit").arg("chinese_16k_common").arg(base64_Data);  ​  /*  chinese_16k_common 支持采样率为16k的中文普通话语音识别。  pcm16k16bit 16k16bit单通道录音数据。  */      //发送请求      manager->post(request, post_param.toUtf8());  }3.2 更新token代码 /*  功能: 获取token  */  void Widget::GetToken()  {      //表示获取token      function_select=3;  ​      QString requestUrl;      QNetworkRequest request;  ​      //设置请求地址      QUrl url;  ​      //获取token请求地址      requestUrl = QString("https://iam.%1.myhuaweicloud.com/v3/auth/tokens")                   .arg(SERVER_ID);  ​      //自己创建的TCP服务器,测试用      //requestUrl="http://10.0.0.6:8080";  ​      //设置数据提交格式      request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=UTF-8"));  ​      //构造请求      url.setUrl(requestUrl);  ​      request.setUrl(url);  ​      QString text =QString("{"auth":{"identity":{"methods":["password"],"password":"      "{"user":{"domain": {"      ""name":"%1"},"name": "%2","password": "%3"}}},"      ""scope":{"project":{"name":"%4"}}}}")             .arg(MAIN_USER)             .arg(IAM_USER)             .arg(IAM_PASSWORD)             .arg(SERVER_ID);  ​      //发送请求      manager->post(request, text.toUtf8());  }3.3 华为云返回的结果处理 //解析反馈结果  void Widget::replyFinished(QNetworkReply *reply)  {      QString displayInfo;      int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();  ​      //读取所有数据      QByteArray replyData = reply->readAll();  ​      qDebug()<<"状态码:"<<statusCode;      qDebug()<<"反馈的数据:"<<QString(replyData);  ​      //更新token      if(function_select==3)     {          displayInfo="token 更新失败.";          //读取HTTP响应头的数据          QList<QNetworkReply::RawHeaderPair> RawHeader=reply->rawHeaderPairs();          qDebug()<<"HTTP响应头数量:"<<RawHeader.size();          for(int i=0;i<RawHeader.size();i++)         {              QString first=RawHeader.at(i).first;              QString second=RawHeader.at(i).second;              if(first=="X-Subject-Token")             {                  Token=second.toUtf8();                  displayInfo="token 更新成功.";  ​                  //保存到文件                  SaveDataToFile(Token);                  break;             }         }          qDebug()<<displayInfo;          return;     }  ​      //判断状态码      if(200 != statusCode)     {          //解析数据          QJsonParseError json_error;          QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);          if(json_error.error == QJsonParseError::NoError)         {              //判断是否是对象,然后开始解析数据              if(document.isObject())             {                  QString error_str="";                  QJsonObject obj = document.object();                  QString error_code;                  //解析错误代码                  if(obj.contains("error_code"))                 {                      error_code=obj.take("error_code").toString();                      error_str+="错误代码:";                      error_str+=error_code;                      error_str+="\n";                 }                  if(obj.contains("error_msg"))                 {                      error_str+="错误消息:";                      error_str+=obj.take("error_msg").toString();                      error_str+="\n";                 }                  //显示错误代码                  qDebug()<<error_str;             }           }          return;     }  ​      //语音识别      if(function_select==0)     {          //解析数据          QJsonParseError json_error;          QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);          if(json_error.error == QJsonParseError::NoError)         {              //判断是否是对象,然后开始解析数据              if(document.isObject())             {                  QString error_str="";                  QJsonObject obj = document.object();                  QString error_code;  ​                  if(obj.contains("result"))                 {                      QJsonObject obj2=obj.take("result").toObject();                      if(obj2.contains("text"))                     {                          QString text=obj2.take("text").toString();                          qDebug()<<"识别的文本:"<<text;                          ui->lineEdit_text_display->setText(text);  ​                          //浏览器搜索                          QString url="https://www.baidu.com/s?ie=UTF-8&wd="+text;  ​                          m_webView->load(QUrl(url));  ​                     }                 }                  //显示错误代码                  qDebug()<<error_str;             }           }     }  }
  • [问题求助] 模组无法进行通信
    NB-IoT联网异常,已经确认卡插的方向是正常的,程序也能烧录到板子上,使用的平台是VSCode编译器+IoT Link Studio插件。然后模组测试结果出现以下问题,该怎么解决呢?
  • [问题求助] 上传到物联网平台的数据如何能保存历史数据?
    上传到物联网平台的数据如何能保存历史数据可以查看?
  • [问题求助] 华为云的物联网有有没有可视化方案?
    华为云的物联网平台有没有可视化方案? 
  • [问题求助] 设备激活
    这种情况是这么用应用侧激活啊 用的协议是coap协议
  • [问题求助] vscode1.49版本
    有那位大佬有vscode1.49版本的安装包啊 小熊派开发需要这个版本
  • [技术干货] vscode 版本
    小熊派开发需要vscode 1.49版本,vscode官网已经找不到1.49版本了 不知道哪位大佬还有1.49版本的安装包啊
  • [问题求助] 如何获取物联网平台的 IP 和端口号
    IP 和端口号,可以从平台上的“对接信息”获取,请问这个“对接信息”从那里找到啊
  • [技术干货] 基于luatos的4G(LTE Cat.1)模组接入华为云物联网平台完成设备通信与控制(Air780e)
    一、简介1、项目介绍之前发布的文章有esp8266的wifi模块和BC20的NB模块与华为云物联网通信为主,本期文章采用了合宙的4G LTE Cat.1模块,编程语言用的是lua,整体来说代码比较简洁,实现对华为云物联网平台的设备通信与控制,即采用一个变量作为模拟属性定时上报,并以一个LED灯作为受控设备进行云端命令的控制,本期内容为我们在户外的项目应用中提供一些参考方案,实现手机远程控制户外设备与数据通信的功能。准备:Air780e开发板usb-typec数据线4G SIM手机卡(可上网)2、Air780E模组Air780E 是合宙通信推出的 LTE Cat.1bis通信模块,采用移芯EC618平台,支持4G全网通 支持双卡单待、支持SPI LCD、支持USB 2.0, 仅CDC功能、支持I2S数字语音接口、支持摄像头等配置,支持AT指令开发、CSDK开发和luatos的lua脚本语言的多种开发,开发板目前淘宝官网是39.9,性价比还是可以的3、luatosLua可以说是目前嵌入式方案中,资源占用最小、运行效率最高、语法最简洁的一门脚本语言。对于编程小白来说,它适合作为你的编程入门语言,因为语法简单。对于会c语言的老手来说,它与c可以完美契合,再加上LuatOS本身就是开源,你可以轻松地使用c为其添加一套c库接口,享受它的高效。在本次教程中,我们便使用lua语言跑luatos实现对华为云物联网平台的设备通信与控制。4、华为云物联网平台物联网平台的相关配置在这里就不和大家一一重复了,创建服务、设备、属性、命令等过程大家可以参考以前发布的文章(文末有链接),主要需要提前准备的数据有华为云物联网平台的设备三元组、相关MQTT发布订阅主题等,参考如下:local mqtt_host = "iot-mqtts.cn-north-4.myhuaweicloud.com" local mqtt_port = 1883 local mqtt_isssl = false local client_id = "61fb2d7fde9933029bfuniot_esp8266_test01_0_0_2023021710" local user_name = "61fb2d7fde9933029bfuniot_esp8266_test01" local password = "eaff6453a8b5b774b11funiot99aa367b52df8funiot5333funiotf76bea151" local mqtt_hw = nil local devdata_topic="$oc/devices/61fb2d7fde9933029funiot_esp8266_test01/sys/properties/report" --订阅属性上报主题 local cmdrec_topic="$oc/devices/61fb2d7fde9933029befuniot_esp8266_test01/sys/commands/#" --订阅命令下发主题 local service_id="Dev_data" --服务ID local command_name="Control" --控制命令二、完整开发流程1. 下载软件包与编译烧录工具1) LuatOS软件包:Air780e使用LuatOS-SoC@EC618下载链接:LuatOS-SoC@EC618 V11032) 调试与烧录工具:Luatools下载链接:cid:link_12. 编写luatos脚本-- 获取更多资料,可以关注公众号"IOT趣制作" -- LuaTools需要PROJECT和VERSION这两个信息 PROJECT = "led" VERSION = "1.0.0" -- 引入必要的库文件(lua编写), 内部库不需要require sys = require("sys") log.info("main", "mqtt_huaweiiot") print(_VERSION) if wdt then --添加硬狗防止程序卡死,在支持的设备上启用这个功能 wdt.init(9000)--初始化watchdog设置为9s sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗 end --用户代码开始--------------------------------------------------- --根据自己华为云物联网平台的配置修改以下参数,下列参数仅作参考 local mqtt_host = "iot-mqtts.cn-north-4.myhuaweicloud.com" local mqtt_port = 1883 local mqtt_isssl = false local client_id = "61fb2d7fde9933029bfuniot_esp8266_test01_0_0_2023021710" local user_name = "61fb2d7fde9933029bfuniot_esp8266_test01" local password = "eaff6453a8b5b774b11funiot99aa367b52df8funiot5333funiotf76bea151" local mqtt_hw = nil local devdata_topic="$oc/devices/61fb2d7fde9933029funiot_esp8266_test01/sys/properties/report" --订阅属性上报主题 local cmdrec_topic="$oc/devices/61fb2d7fde9933029befuniot_esp8266_test01/sys/commands/#" --订阅命令下发主题 local service_id="Dev_data" --服务ID local command_name="Control" --控制命令 local LED_PIN=27 --LED引脚编号 gpio.setup(LED_PIN,0, gpio.PULLUP) --设置LED上拉输出 sys.taskInit(function() while 1 do --网络相关 mobile.simid(2) LED = gpio.setup(27, 0, gpio.PULLUP) device_id = mobile.imei() sys.waitUntil("IP_READY", 30000) --mqtt客户端创建 mqtt_hw = mqtt.create(nil,mqtt_host, mqtt_port, mqtt_isssl, ca_file) mqtt_hw:auth(client_id,user_name,password) mqtt_hw:keepalive(60) -- 默认值240s mqtt_hw:autoreconn(true, 3000) -- 自动重连机制 --注册mqtt回调 mqtt_hw:on(function(mqtt_client, event, data, payload) -- 用户自定义代码 log.info("mqtt", "event", event, mqtt_client, data, payload) if event == "conack" then --连接响应成功 sys.publish("mqtt_conack")--订阅主题 mqtt_client:subscribe(pub_devdata_topic) mqtt_client:subscribe(pub_cmdrec_topic) elseif event == "recv" then log.info("mqtt", "downlink", "topic", data, "payload", payload) print("payload:",payload) --解析json --例如:{"paras":{"led":1},"service_id":"Dev_data","command_name":"Control"} local mycmd=json.decode(payload) if mycmd then -- 若解码失败, 会返回nil print("service_id :",mycmd["service_id"]) print("command_name is",mycmd["command_name"]) print("paras->led is",mycmd["paras"]["led"]) if mycmd["service_id"]==service_id and mycmd["command_name"]==command_name then if mycmd["paras"]["led"]==1 then print("led turn on") gpio.set(LED_PIN, gpio.HIGH) elseif mycmd["paras"]["led"]==0 then print("led turn off") gpio.set(LED_PIN, gpio.LOW) end end end elseif event == "sent" then log.info("mqtt", "sent", "pkgid", data) -- elseif event == "disconnect" then -- 非自动重连时,按需重启mqtt_hw -- mqtt_client:connect() end end) --连接mqtt mqtt_hw:connect() sys.waitUntil("mqtt_conack") while true do -- mqtt_hw自动处理重连 local ret, topic, data, qos = sys.waitUntil("mqtt_pub", 30000) if ret then if topic == "close" then break end mqtt_hw:publish(topic, data, qos) end end mqtt_hw:close() mqtt_hw = nil end end) --定时上报属性 sys.taskInit(function() local topic = devdata_topic --上报的topic local temp=0 --温度属性值 local data = "{\"services\":[{\"service_id\":\"Dev_data\",\"properties\":{\"temp\": "..tostring(temp).."}}]}" local qos = 1 local temp=0 while true do sys.wait(5000) if mqtt_hw and mqtt_hw:ready() then -- mqtt_hw:subscribe(topic) local pkgid = mqtt_hw:publish(topic, data, qos) temp=temp+1 data = "{\"services\":[{\"service_id\":\"Dev_data\",\"properties\":{\"temp\": "..tostring(temp).."}}]}" -- 也可以通过sys.publish发布到指定task去 -- sys.publish("mqtt_pub", topic, data, qos) end end end) -- 用户代码已结束--------------------------------------------- -- 结尾总是这一句 sys.run() -- sys.run()之后后面不要加任何语句!!!!!3. 编译烧录打开Luatools,点击项目管理按钮,选择下载好的固件(后缀名为soc的文件)和编写好的main.lua文件,勾选USB BOOT下载,点击下载底层和脚本按钮下载即可,具体操作流程如下图所示: 4. 运行结果命令接收的同时开发板的LED灯光会对应的完成开关亮灭5.Android APP这里我们同样可以采用之前的Android APP完成与物联网平台的通信,实现手机远程控制户外设备与数据通信的功能链接:【华为云IOT的Android应用开发-详细教程】:https://bbs.huaweicloud.com/forum/thread-186368-1-1.html6.其他相关参考文章【stm32+AT指令+ESP8266接入华为云物联网平台并完成属性上报与下发的命令处理】https://bbs.huaweicloud.com/forum/thread-195143-1-1.html【esp8266接入华为云物联网平台完成属性上报、命令处理】https://bbs.huaweicloud.com/forum/thread-187353-1-1.html
  • [技术干货] 采用华为云IOT平台设计的高速公路多节点温度采集系统(STM32+NBIOT)
    一、前言当前的场景是,在高速公路上部署温度采集设备,在高速路地表安装温度检测传感器,检测当前路段的路面实际温度。一段高速路上有多个地点需要采集温度数据。 采集温度数据需要上传到云平台进行数据存储,并且通过可视化界面展示温度变化曲线,支持查询最近几天的温度信息。二、设计思路(1)云平台选型:使用华为云物联网云平台。(2)云数据存储: 使用OBS存储,存放设备上传的历史数据。(3)设备选项:NBIOT模块+温度采集模块,实现温度采集上报。(4)数据可视化:采用华为云IoT应用侧接口,获取传感器设备上传到云端的数据,在本地设计界面进行可视化显示温度数据。下面是温度数据可视化展示效果:本篇文章主要介绍设备上云的详细流程,介绍华为云物联网云端产品、设备创建流程,数据转存方式,应用侧开发接口等等。硬件选型:(1)STM32开发板: STM32F103C8T6(2)NBIOT模块--BC26BC26模块是一款高性能、低功耗、多频段LTE Cat NB1无线通信模块。(3)温度采集模块pt100是铂热电阻,它的阻值会随着温度的变化而改变。PT后的100即表示它在0℃时阻值为100欧姆,在100℃时它的阻值约为138.5欧姆。其工作原理:当PT100在0℃时,其电阻为100欧姆。它的电阻会随着温度的升高而上升,并且它的电阻会匀速增加。热电阻是一种常用于中低温的温度传感器。它的工作原理是基于电阻的热效应,即电阻的阻值随温度的变化而变化。铂金热敏电阻的热阻精度最高,具有抗振动、稳定性好、耐高压等特点。因此,它被制成各种标准温度计进行测量和校准。三、华为云IOT平台3.1 创建产品官网地址: cid:link_12(1)设备接入IOTDA在产品页面找到iot物联网,选择设备接入IOTDA设备接入服务(IoT Device Access)是华为云的物联网平台,提供海量设备连接上云、设备和云端双向消息通信、批量设备管理、远程控制和监控、OTA升级、设备联动规则等能力,并可将设备数据灵活流转到华为云其他服务,帮助物联网行业用户快速完成设备联网及行业应用集成,基础版每月一百万条消息免费。在页面上选择免费试用。点击后,会进入到设备接入控制台页面。(2)设备接入地址在基础版详情页面,点击右边的按需计费详情,可以查看物联网服务器接入的IP地址,端口号、接入方式。如果是设备接入,可以选择MQTT或者MQTTS协议,在单片机上只有MQTT协议验证比较方便,通过MQTT三元组即可完成设备连接,当前我这里的设备选择是MQTT协议连接华为云平台。 域名:a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com  IP地址: 121.36.42.100  端口号: 1883(3)创建产品在左边选项卡里点击产品,进入产品页面,点击右上角创建产品。根据自己的产品信息,填写表单:(4)完成产品创建点击查看详情,可以进入到创建成功的产品页面。(4)定义产品模型产品创建之后,接着需要在平台上构建一款设备的抽象模型,使平台理解该款设备支持的功能。比如:温度采集设备肯定会向云平台上传采集的温度信息;在产品模型里就可以定义一个温度的属性字段。选择下面的自定义模型。添加服务ID。添加属性。新增属性字段。当前在多节点温度采集的设备里,主要是采集温度上传,这里就新增一个温度的属性。产品模型创建完成。3.2 创建设备(1)创建单个设备设备注册的方式有很多:【1】创建支持单个设备手动创建。【2】如果设备特别多可以选择批量注册。【3】通过API接口进行动态注册。当前为了演示流程,这里选择第一种方式,手动创建单个设备。在设备页面,选择所有设备选项,点击右边的注册设备按钮。(2)单设备注册填写信息点击注册设备之后,会弹出一个表单填写信息。其中产品就选择刚才创建的产品,设备标识码这一项一般是填设备的ID(设备的唯一标识符,方便绑定设备)。目前还没有对接硬件,我这里就填dev1,方便接下来的测试。 下面的设备ID这一项如果不填,会自动生成,可以不管;最后输入密匙(这个密匙的作用:通过对每个设备进行身份验证,可以安全地将每个设备连接到平台,并且安全地管理这些设备),填好之后点击确定。(3)设备创建完成创建好之后,保存生成的设备ID和密匙。得到的密匙和ID的格式文本如下: {      "device_id": "6353a8163ec34a6d03c8dfe5_dev1",      "secret": "12345678"  }这个设备密匙和ID,后面生成MQTT登录参数时需要用到。(4)创建多个温度设备节点由于本项目是实现多节点温度上传到云平台,每个节点都是一个独立的温度采集设备,为了方便演示效果,还需要多创建几个设备。接下来的创建流程,和刚才第一个设备一样,这里就不再截图演示了。点击创建设备按钮,继续注册。目前一共创建了4个设备,其中main_dev设备是用来作为显示终端,在本地用显示屏显示其他温度采集节点采集的温度信息。 剩下3个设备dev1,dev2,dev3是表示3个独立的温度采集节点。这4个设备的密匙和ID信息如下: {      "device_id": "6353a8163ec34a6d03c8dfe5_dev1",      "secret": "12345678"  }  ​  ​  {      "device_id": "6353a8163ec34a6d03c8dfe5_dev2",      "secret": "12345678"  }  ​  ​  {      "device_id": "6353a8163ec34a6d03c8dfe5_dev3",      "secret": "12345678"  }  ​  ​  {      "device_id": "6353a8163ec34a6d03c8dfe5_main_dev",      "secret": "12345678"  }3.3 模拟设备上云测试目前设备创建之后,这些设备都还没有激活。接下来会使用MQTT客户端来模拟真实设备上云,上传温度数据。这里的上云,包括数据交互都采用MQTT客户端来模拟实现,不涉及到实际的硬件,只要模拟能测试成功,并且能得到自己想要的结果,那硬件就没有问题了。(1)生成MQTT鉴权三元组设备要连接华为云平台的方式,在第一节创建产品的时候就已经介绍了,本次项目里的设备是采用MQTT协议接入云平台。 在完成设备模拟上云之前,需要先生成设备的MQTT协议鉴权三元组。华为云提供了一个在线工具,用来生成MQTT鉴权三元组: cid:link_8工具打开的界面如下效果:前面两行就是填设备创建后生成的设备ID和设备密匙,填好之后,生成下面3行信息,生成的3行就是MQTT协议登录需要用的参数。按照格式分别生成4个设备的鉴权信息:得到的三元组如下: ClientId  6353a8163ec34a6d03c8dfe5_dev1_0_0_2022102209  Username  6353a8163ec34a6d03c8dfe5_dev1  Password  c58c45d514832b119b4302eafb3e74854849ca94079e9aed75efedf176e9c388  ​  ClientId  6353a8163ec34a6d03c8dfe5_dev2_0_0_2022102209  Username  6353a8163ec34a6d03c8dfe5_dev2  Password  c58c45d514832b119b4302eafb3e74854849ca94079e9aed75efedf176e9c388  ​  ClientId  6353a8163ec34a6d03c8dfe5_dev3_0_0_2022102209  Username  6353a8163ec34a6d03c8dfe5_dev3  Password  c58c45d514832b119b4302eafb3e74854849ca94079e9aed75efedf176e9c388  ​  ClientId  6353a8163ec34a6d03c8dfe5_main_dev_0_0_2022102209  Username  6353a8163ec34a6d03c8dfe5_main_dev  Password  c58c45d514832b119b4302eafb3e74854849ca94079e9aed75efedf176e9c388(2)MQTT客户端模拟设备登录得到MQTT三元组之后,接下来用MQTT客户端模拟设备登录云平台。按照软件的输入框提示,输入对应的信息,点击登录。 其中的IP地址和端口号在第一小节的产品创建里就介绍过了。 后面输入的这3行就是上一步生成的MQTT鉴权三元组。登录成功。然后打开云平台的控制台,查看设备在线情况:可以看到dev1已经在线了, 刚才模拟的设备就是dev1。(3)主题订阅与发布目前设备已经成功登录,接下来要解决的问题就是数据传输问题了。MQTT协议里要理解的两个概念就是主题订阅,主题发布。 设备上传数据到平台,属于 主题发布。 设备想要知道其他设备的数据或者云平台下发的指令,需要进行主题订阅。帮助文档的地址: cid:link_6MQTT消息由固定报头(Fixed header)、可变报头(Variable header)和有效载荷(Payload)三部分组成。其中固定报头(Fixed header)和可变报头(Variable header)格式的填写请参考MQTT标准规范,有效载荷(Payload)的格式由应用定义,即设备和物联网平台之间自己定义。常见MQTT消息类型主要有CONNECT、SUBSCRIBE、PUBLISH。 CONNECT:指客户端请求和服务端连接。有效载荷(Payload)的主要参数,参考设备连接鉴权填写。  SUBSCRIBE:指客户端订阅请求。有效载荷(Payload)中的主要参数“Topic name”,参考Topic定义中订阅者为设备的Topic。  PUBLISH:平台发布消息。  可变报头(Variable header)中的主要参数“Topic name”,指设备上报到物联网平台时发布者为设备的Topic。详细请参考Topic定义。  有效载荷(Payload)中的主要参数为完整的数据上报和命令下发的消息内容,目前是一个JSON对象。  ​  上行Topic是指设备向平台发送请求,或上报数据,或回复响应。  下行Topic是指平台向设备下发指令,或回复响应。  设备与平台建立连接后,需要订阅下行Topic,否则无法收到平台下发的指令或回复的响应。应用侧接口的调用,需要设备侧的配合,例如应用侧下发命令,设备侧需要先订阅“平台命令下发”的下行Topic,否则设备无法收到平台命令,应用下发命令的接口也会报超时。在产品页面,可以看到主题的格式,以及对于主题的用途:**【1】订阅主题对于设备而言,一般会订阅平台下发消息给设备 这个主题。设备想接收平台下发的消息,就需要订阅平台下发消息给设备 的主题,订阅后,平台下发消息给设备,设备就会收到消息。主题的格式如下: $oc/devices/{device_id}/sys/messages/down        以dev1设备1为例,最终的格式:  $oc/devices/6353a8163ec34a6d03c8dfe5_dev1/sys/messages/down【2】发布主题对于设备,发布主题,也就显示向云平台上传数据。发布的主题格式如下: $oc/devices/{device_id}/sys/properties/report  ​  以dev1设备1为例最终的格式:  $oc/devices/6353a8163ec34a6d03c8dfe5_dev1/sys/properties/report发布主题时,需要上传数据,这个数据格式是JSON格式。上传的JSON数据格式如下: {    "services": [     {        "service_id": <填服务ID>,        "properties": {          "<填属性名称1>": <填属性值>,          "<填属性名称2>": <填属性值>,          ..........       }     }   ]  }根据JSON格式,一次可以上传多个属性字段。 这个JSON格式里的,服务ID,属性字段名称,属性值类型,在前面创建产品的时候就已经介绍了,不记得可以翻到前面去查看。根据这个格式,组合温度节点一次上传的数据: {"services": [{"service_id": "temp","properties":{"temp":24.6}}]}(4)MQTT客户端模拟设备上报数据打开MQTT客户端填入订阅主题,发布主题,和需要发布的数据,然后分别点击订阅主题按钮,发布主题按钮。 右边提示成功之后,就可以打开云平台看上传的效果了。 (这里是以dev1,设备1为例)打开云平台看到dev1已经在线。点击dev1,进去查看设备上传的数据。 可以看到,刚才上传的数据已经收到了。到此,设备上传数据到云平台已经完成。四、设备数据转存如果设备上传的数据需要进行保存,后续进行分析做其他用途,可以利用数据转发服务,让平台将设备上报数据推送给自己的应用服务器,由应用服务器进行保存;也可以选择让平台将设备上报数据转发给OBS对象存储服务,由OBS进行存储,进行永久保存,非常方便。如果存储在OBS里,自己设计应用侧界面时,也可以直接拉取OBS里的数据下来进行显示,处理,分析。4.1 创建OSB存储桶地址: cid:link_11对象存储服务(Object Storage Service,OBS)是一个基于对象的存储服务,提供了海量、安全、高可靠、低成本的数据存储能力。(1)选择管理控制台(2)创建桶填充桶信息: 我这里选择的是 华北-北京一我这里因为要长期使用,这里选择1年的购买权。(3)创建成功4.2 配置数据转发规则(1)创建规则选择左侧导航栏的规则>数据转发,单击右上角的创建规则。填充规则转发的信息:参数名参数说明规则名称自定义,如: led_obs。规则描述自定义,如数据转发至OBS服务。数据来源选择“设备属性”。触发事件自动匹配“设备属性上报”。资源空间和上一步创建的产品所在的资源空间保持一致。(2)设置转发目标单击添加,设置转发目标。这里的区域选择--华北-北京一。 因为前面的OBS桶创建的时候,设置区域设置的是北京一,然后点击授权。授权之后,选择刚才创建的OBS桶,设置存储数据的目录和文件名字。存储的数据可以直接转存JSON数据到OBS存储桶,也可以存放成CSV文件到存储桶。如果选择存储JSON,就是直接将设备上传的数据存放到OBS存储里,如果选择存储CSV文件,可以自己选择需要存储的字段。 下面我演示一下存储成CSV文件时如何进行设置。 (选择JSON文件不需要进行任何设置,直接将设备上传的数据JSON存储进去了)。下面设置转发的字段: (如果提示没有授权,点击授权即可)这个转发字段就是表示需要存放的数据是那些,对于路灯而言,肯定是需要存储上报的温度、湿度、电量、光照强度的属性的。 这些属性在创建产品的时候设置,设备上报的也是这些属性。这是设备上传一次云平台的完整JSON数据格式: {    "resource": "device.property",    "event": "report",    "event_time": "20221018T131627Z",    "request_id": "5ee95a0c-262d-43c3-8d31-af453f9952ef",    "notify_data": {      "header": {        "app_id": "7211833377cf435c8c0580de390eedbe",        "device_id": "634e3e423ec34a6d03c84bfb_1126626497",        "node_id": "1126626497",        "product_id": "634e3e423ec34a6d03c84bfb",        "gateway_id": "634e3e423ec34a6d03c84bfb_1126626497"     },      "body": {        "services": [         {            "service_id": "temp",            "properties": {              "temp": 28,           },            "event_time": "20221018T131627Z"         }       ]     }   }  }当前设备上传到云端服务器有一个温度属性字段temp,如果想转发设备上传的这个温度字段,就可以这样写: notify_data.body.services[0].properties.temp后面的存储目标字段,为了好区分,直接填temp即可。如果设备还上传了其他属性字段也想转发,按照上面格式设置即可。如果存储数据时,想知道这个数据是那个产品,那个设备上传的,也可以将设备ID和产品ID转发存储起来:格式如下。 notify_data.header.app_id    notify_data.header.device_id下面是我设置好,转发存储的字段:确定后,点击设置完成。(3)测试规则设置好之后,在设置转发目标的页面点击测试。输入数据模板,然后点击连通性测试。如果测试结果显示成功,说明整体流程没有问题了。(4)启用规则设置完成后,点击启用规则。(6)数据上报测试为了验证转发规则是否生效,接下来使用MQTT客户端多上报几次数据到云平台。上传之后,打开OBS存储桶的控制台页面。打开转发规则存储的OBS桶。找到存储数据的文件,点击下载。下载下来,打开可以看到存储的数据:到此,数据转发,存储已经成功了。五、应用侧-可视化大屏开发对于数据的可视化显示,华为云提供了(Data Lake Visualization)一站式数据可视化平台,数据可视化服务(DLV)可以从OBS文件读取数据呈现为可视化报表,实现数据的可视化显示。下面是华为云的DLV大屏从OBS读取数据显示的流程:当前我这里的需求是需要在本地自己设计界面显示数据,没有采用华为云的DLV大屏,如果自己本地软件需要显示设备上传的数据,就需要使用华为云物联网的平台的应用侧API接口,读取设备上传的数据进行,本地进行显示;如果需要历史数据,可以读取OBS存储桶里存储的数据进行显示。5.1 应用侧接口帮助文档地址: cid:link_45.2 查询设备影子数据接口应用侧接口可以查询发送指令给设备查询属性,也可以读取设备的影子数据。【1】查询设备查询: 这个是实时查询,相当于应用侧接口发送指令给在线设备,设备收到指令,将当前最新的数据再上传。这个需要保证设备在线,离线是无法调用的。【2】影子数据:影子数据相当于保存设备上传的最新一次数据。 读取读取影子设备数据,是不需要设备在线。(1)接口URI地址请求方法GETURI/v5/iot/{project_id}/devices/{device_id}/shadow传输协议HTTPS(2)请求参数说明名称必选/可选类型位置说明X-Auth-Token必选StringHeader参数说明:用户Token。通过调用IAM服务 获取IAM用户Token接口获取,接口返回的响应消息头中“X-Subject-Token”就是需要获取的用户Token。Instance-Id可选StringHeader参数说明:实例ID。物理多租下各实例的唯一标识,一般华为云租户无需携带该参数,仅在物理多租场景下从管理面访问API时需要携带该参数。project_id必选StringPath参数说明:项目ID。获取方法请参见 获取项目ID。device_id必选StringPath参数说明:设备ID,用于唯一标识一个设备。在注册设备时直接指定,或者由物联网平台分配获得。由物联网平台分配时,生成规则为"product_id" + " " + "node_id"拼接而成。取值范围:长度不超过128,只允许字母、数字、下划线( )、连接符(-)的组合。(3)响应参数名称类型说明device_idString设备ID,用于唯一标识一个设备。在注册设备时直接指定,或者由物联网平台分配获得。由物联网平台分配时,生成规则为"product_id" + "_" + "node_id"拼接而成。shadowList[DeviceShadowData]()设备影子数据结构体。  名称类型说明service_idString设备的服务ID,在设备关联的产品模型中定义。desiredDeviceShadowProperties Object用户最近一次对设备下发的预期数据,Json格式,里面是一个个键值对,每个键都是产品模型中属性的参数名(property_name)。reportedDeviceShadowProperties Object设备最近一次上报的属性数据,Json格式,里面是一个个键值对,每个键都是产品模型中属性的参数名(property_name)。versionLong设备影子的版本,携带该参数时平台会校验值必须等于当前影子版本,初始从0开始。  名称类型说明propertiesObject设备影子的属性数据,Json格式,里面是一个个键值对,每个键都是产品模型中属性的参数名(property_name),目前如样例所示只支持一层结构。注意:JSON结构的key当前不支持特殊字符:点(.)、dollar符号($)、空char(十六进制的ASCII码为00),key为以上特殊字符无法正常刷新设备影子event_timeString事件操作时间,格式:yyyyMMdd'T'HHmmss'Z',如20151212T121212Z。(4)请求示例 GET https://{Endpoint}/v5/iot/{project_id}/devices/{device_id}/shadow  Content-Type: application/json  X-Auth-Token: ********  Instance-Id: ********(5)响应示例Status Code: 200 OK Content-Type: application/json  ​  {   "device_id" : "40fe3542-f4cc-4b6a-98c3-61a49ba1acd4",   "shadow" : [ {     "service_id" : "WaterMeter",     "desired" : {       "properties" : {         "temperature" : "60"       },       "event_time" : "20151212T121212Z"     },     "reported" : {       "properties" : {         "temperature" : "60"       },       "event_time" : "20151212T121212Z"     },     "version" : 1   } ]  }(6)错误码HTTP状态码错误码错误码英文描述错误码中文描述处理建议403IOTDA.000021Operation not allowed. User not found by IAM token or the authorized user has not subscribed IOTDA service.没有找到IAM Token所对应的用户信息或该用户没有订阅设备接入服务(IOTDA)请排查IAM Token所在用户是否订阅了设备接入服务(IOTDA)。404IOTDA.014000The device does not exist.设备不存在请排查请求参数是否有误并确认是否有在平台注册该设备。500IOTDA.000020Decrypt IAM token failed.5.3 接口调试在线调试地址:https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=IoTDA&api=ShowDeviceShadow下面是调试影子 数据查询接口,查询设备影子数据: 右边返回的是设备上传的最新数据。5.5 接口总结 请求地址:  https://iotda.cn-north-4.myhuaweicloud.com/v5/iot/{project_id}/devices/{device_id}/shadow  ​  请求方式: GET  ​  请求头:  {   "X-Auth-Token": "这个需要自己获取",     "Content-Type": "application/json"  }5.5 如何获取X-Subject-Token使用API访问华为云的所有服务接口,都需要填X-Subject-Token参数,下面介绍步骤:(1)创建一个新的IAM帐户鼠标悬停在右上角的用户名称上,弹出下拉框,选择统一身份认证。(2)选择创建用户(3)使用调试接口测试获取oken调试接口地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=IAM&api=KeystoneCreateUserTokenByPassword右边响应头里的X-Subject-Token就是获取的token。(4)上面的这些账户名称从哪里获取?(5)请求地址和数据格式获取X-Subject-Token请求的地址: cid:link_10请求头数据:  {   "User-Agent": "API Explorer",   "X-Auth-Token": "******",   "Content-Type": "application/json;charset=UTF-8"   }请求体数据:  {     "auth": {       "identity": {         "methods": [           "password"         ],         "password": {           "user": {             "domain": {                 "name": "xxxxx"  //这里填当前主账户名称             },             "name": "xxxx",  //这个新建的子账户名称             "password": "xxxxx"    //这个是新建的子账户密码           }         }       },       "scope": {         "project": {           "name": "cn-north-4"         }       }     }   }(6)代码实现  /*   功能: 获取token   */   void Widget::GetToken()   {       //表示获取token       function_select=3;         QString requestUrl;       QNetworkRequest request;         //设置请求地址       QUrl url;         //获取token请求地址       requestUrl = QString("https://iam.%1.myhuaweicloud.com/v3/auth/tokens")                   .arg(SERVER_ID);         //自己创建的TCP服务器,测试用       //requestUrl="http://10.0.0.6:8080";         //设置数据提交格式       request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=UTF-8"));         //构造请求       url.setUrl(requestUrl);         request.setUrl(url);         QString text =QString("{"auth":{"identity":{"methods":["password"],"password":"       "{"user":{"domain": {"       ""name":"%1"},"name": "%2","password": "%3"}}},"       ""scope":{"project":{"name":"%4"}}}}")               .arg(MAIN_USER)               .arg(IAM_USER)               .arg(IAM_PASSWORD)               .arg(SERVER_ID);         //发送请求       manager->post(request, text.toUtf8());   }5.6 查询设备影子数据代码(1)这是请求代码 //查询设备属性  void Widget::Get_device_properties()  {      //表示获取token      function_select=0;  ​      QString requestUrl;      QNetworkRequest request;  ​      //设置请求地址      QUrl url;  ​      //获取token请求地址      requestUrl = QString("https://iotda.%1.myhuaweicloud.com/v5/iot/%2/devices/%3/shadow")                   .arg(SERVER_ID)             .arg(PROJECT_ID)             .arg(device_id);  ​      //设置数据提交格式      request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));  ​      //设置token      request.setRawHeader("X-Auth-Token",Token);  ​      //构造请求      url.setUrl(requestUrl);  ​      request.setUrl(url);  ​      //发送请求      manager->get(request);  }  ​  //更新设备属性  void Widget::on_pushButton_update_device_clicked()  {      Get_device_properties();  }(2)这是请求一次返回的JSON数据 {    "device_id": "6353a8163ec34a6d03c8dfe5_dev1",    "shadow": [     {        "service_id": "temp",        "desired": {          "properties": null,          "event_time": null       },        "reported": {          "properties": {            "temp": 45.6         },          "event_time": "20221024T013607Z"       },        "version": 49     }   ]  }5.7 界面设计最终效果六、硬件部分6.1 MQTT版本注意:华为云、OneNet、腾讯IOT 等平台规定接入的MQTT协议版本必须是3.1.1 。在BC26里,需要执行这行代码配置MQTT协议版本为3.1.1 AT+QMTCFG="version",0,46.2 BC26上云配置代码 #include "BC26.h"  ​  //BC26复位  //引脚是PC0  //高电平有效  void BC26_Reset(void)  {      //开时钟      RCC->APB2ENR|=1<<4;  ​      //配置GPIO口      GPIOC->CRL&=0xFFFFFFF0;      GPIOC->CRL|=0x00000003;            //开始复位      GPIOC->ODR|=1<<0;      DelayMs(2000);      GPIOC->ODR&=~(1<<0);  }  ​  ​  /*  函数功能:向BC26模块发送指令  函数参数:   char *cmd 发送的命令    char *check_data 检测返回的数据  返回值: 0表示成功 1表示失败  */  u8 BC26_SendCmd(char *cmd,char *check_data)  {     u16 i,j;     for(i=0;i<5;i++) //测试的总次数     {        USART2_RX_FLAG=0;        USART2_RX_CNT=0;    memset(USART2_RX_BUFFER,0,sizeof(USART2_RX_BUFFER));    USARTx_StringSend(USART2,cmd); //发送指令        for(j=0;j<500;j++) //等待的时间(ms单位)       {            if(USART2_RX_FLAG)           {                USART2_RX_BUFFER[USART2_RX_CNT]='\0';                if(strstr((char*)USART2_RX_BUFFER,check_data))               {                    return 0;               }                else break;           }            delay_ms(30); //一次的时间       }     }     return 1;  }  ​  ​  ​  ​  //初始化BC26模块  int BC26_Init(void)  {      if(BC26_SendCmd("AT\r\n","OK"))     {          USART1_Printf("BC26模块不存在.\r\n");          return 1;     }      else     {          USART1_Printf("BC26模块正常!\r\n");     }  ​      if(BC26_SendCmd("AT+CIMI\r\n","OK"))     {          USART1_Printf("模块未插卡.\r\n");          return 3;     }      else     {          USART1_Printf("卡已经插好.\r\n");     }  ​        if(BC26_SendCmd("AT+CGATT=1\r\n","OK"))     {          USART1_Printf("配置:网络激活失败.\r\n");          return 3;     }      else     {          USART1_Printf("配置:网络激活成功.\r\n");     }            if(BC26_SendCmd("AT+CGATT?\r\n","OK"))     {          USART1_Printf("状态:网络激活失败.\r\n");          return 4;     }      else     {          USART1_Printf("状态:网络激活成功.\r\n");     }            if(BC26_SendCmd("AT+CSQ\r\n","OK"))     {          USART1_Printf("查询信号质量失败.\r\n");          return 5;     }      else     {          USART1_Printf("信号质量:%s\r\n",USART2_RX_BUFFER);     }              //   if(BC26_SendCmd("AT+QGNSSC=1\r\n","OK"))  //   {  //       USART1_Printf("激活GPS定位失败.\r\n");  //       return 6;  //   }  //   else  //   {  //       USART1_Printf("激活GPS定位成功.\r\n");  //   }  //      //   if(BC26_SendCmd("AT+QGNSSAGPS=1\r\n","OK"))  //   {  //       USART1_Printf("开启AGPS定位失败.\r\n");  //       return 6;  //   }  //   else  //   {  //       USART1_Printf("开启AGPS定位成功.\r\n");  //   }  //      //   if(BC26_SendCmd("AT+CGPADDR=1\r\n","OK"))  //   {  //       USART1_Printf("激活GPRS场景失败.\r\n");  //       return 7;  //   }  //   else  //   {  //       USART1_Printf("激活GPRS场景成功.\r\n");  //   }  //      //   DelayMs(1000);  //   DelayMs(1000);            if(BC26_SendCmd("AT+CEREG?\r\n","+CEREG: 0,1"))     {          USART1_Printf("网络注册状态:失败.\r\n");          return 9;     }      else     {          USART1_Printf("网络注册状态:成功.\r\n");     }            return 0;  }  ​  ​  //发送使用的缓冲区  char BC26_SEND_BUFF[500];  ​  //MQTT协议登录服务器  int BC26_MQTT_Connect(void)  {      //1. 先关闭之前的连接       USART1_Printf("正在关闭之前的连接...\r\n");      BC26_SendCmd("AT+QMTCLOSE=0\r\n","OK");      DelayMs(4000);            //关闭服务      BC26_SendCmd("AT+QMTCONN?\r\n","OK");      DelayMs(4000);            //2. 连接MQTT服务器      USART1_Printf("正在连接MQTT服务器..\r\n");      sprintf(BC26_SEND_BUFF,"AT+QMTOPEN=0,"%s",%s\r\n",MQTT_SERVER_ADDR,MQTT_SERVER_PORT);      if(BC26_SendCmd(BC26_SEND_BUFF,"OK"))     {          USART1_Printf("MQTT服务器连接失败:%s\r\n",BC26_SEND_BUFF);          return 1;     }      else     {          USART1_Printf("MQTT服务器连接成功.\r\n");     }      DelayMs(3000);                  //3. 登录MQTT服务器      USART1_Printf("正在登录MQTT服务器...\r\n");      sprintf(BC26_SEND_BUFF,"AT+QMTCONN=0,"%s","%s","%s"\r\n",MQTT_CLIENT_ID,MQTT_USERNAME,MQTT_PASSWORD);      if(BC26_SendCmd(BC26_SEND_BUFF,"OK"))     {          USART1_Printf("MQTT服务器登录失败:%s\r\n",BC26_SEND_BUFF);          return 2;     }      else     {          USART1_Printf("MQTT服务器登录成功.\r\n");     }      DelayMs(3000);            //4. 订阅主题      USART1_Printf("正在订阅主题...\r\n");      sprintf(BC26_SEND_BUFF,"AT+QMTSUB=0,1,"%s",2\r\n",MQTT_TOPIC_SUB_GET);      if(BC26_SendCmd(BC26_SEND_BUFF,"OK"))     {          USART1_Printf("MQTT主题订阅失败:%s\r\n",BC26_SEND_BUFF);     }      else     {          USART1_Printf("MQTT主题订阅成功.\r\n");     }      return 0;  }  ​  ​  //MQTT发布主题  int MQTT_PublishTheme(char *text)  {      char send_buf[3];      sprintf(BC26_SEND_BUFF,"AT+QMTPUB=0,0,0,0,"%s"\r\n",MQTT_TOPIC_SUB_SET);      if(BC26_SendCmd(BC26_SEND_BUFF,">"))     {          USART1_Printf("发布主题等待输入失败:%s\r\n",BC26_SEND_BUFF);          return 1;     }      USARTx_StringSend(USART2,text);     //发送主题内容            //发送结束符      send_buf[0] = 0x1a;      send_buf[1] = '\0';      if(BC26_SendCmd(send_buf,"OK"))     {          USART1_Printf("发布主题内容失败:%s\r\n",BC26_SEND_BUFF);          return 2; //发送结束符号     }      else     {          USART1_Printf("发布主题内容成功.\r\n");     }             USART1_Printf("发布主题内容:%s\r\n",text);      return 0;  }  ​  //MQTT响应应用层的属性请求  int MQTT_SendAttribute(char *text,char *request_id)  {      char send_buf[3];      sprintf(BC26_SEND_BUFF,"AT+QMTPUB=0,0,0,0,"%s%s"\r\n",MQTT_TOPIC_SUB_SET_RUN,request_id);      if(BC26_SendCmd(BC26_SEND_BUFF,">"))     {          USART1_Printf("(响应)发布主题等待输入失败:%s\r\n",BC26_SEND_BUFF);          return 1;     }      USARTx_StringSend(USART2,text);     //发送主题内容            //发送结束符      send_buf[0] = 0x1a;      send_buf[1] = '\0';      if(BC26_SendCmd(send_buf,"OK"))     {          USART1_Printf("(响应)发布主题内容失败:%s\r\n",BC26_SEND_BUFF);          return 2; //发送结束符号     }      else     {          USART1_Printf("(响应)发布主题内容成功.\r\n");     }             USART1_Printf("(响应)发布主题内容:%s\r\n",text);      return 0;  }  ​  ​  ​  /*  函数功能: 获取一次GPS经纬度数据  函数参数:          double *Longitude :经度          double *latitude   :纬度  返回值: 0表示定位成功,1表示数据接收失败,2表示定位失败  */  u8 BC26_GetGPS_Data(double *Longitude,double *latitude)  {      /*1. 发送获取GPS数据的指令*/      if(BC26_SendCmd("AT+QGNSSRD="NMEA/RMC"\r\n", "OK\r\n"))return 1;            /*2. 对GPS数据进行解码*/      if(GPS_GNRMC_Decoding((char *)USART2_RX_BUFFER,Longitude,latitude))return 2;            //解码成功      return 0;  }  ​  ​  /*  函数功能: 开启GPS功能  返 回 值:0表示成功 1表示失败  */  u8 BC26_StartGPS(void)  {      //先判断GPS功能是否启动      if(BC26_SendCmd("AT+QGNSSC?\r\n","+QGNSSC: 1"))     {          //没有启动就启动GPS功能          if(BC26_SendCmd("AT+QGNSSC=1\r\n","OK\r\n"))         {              USART1_Printf("开启GPS功能失败.\r\n");              return 1;  //GPS功能启动失败         }          else         {              USART1_Printf("开启GPS功能成功.\r\n");         }     }      return 0;  }6.3 MQTT 3.1协议介绍地址: cid:link_5 规范共分七章:  第一章 - 介绍  第二章 - MQTT控制包格式  第三章 - MQTT控制包  第四章 - 操作行为  第五章 - 安全  第六章 - 使用WebSocket进行网络传输  第七章 - 一致性目标
总条数:799 到第
上滑加载中