目录
原创不易,点个赞或者点个关注激励笔者分享更多优质原创内容吧!
相信很多人和笔者一样,参加各种比赛时,都选择了有关华为IoT平台的命题。可惜全网搜寻一番,发现硬件设备与华为云IoT连接的教程几乎没有,大多数是腾讯云、阿里云或者ONENET平台的。经过自己的摸索,终于连接上华为云IoT平台实现相关功能,所以今天的这一篇教程便是ESP32连接华为云IoT平台的教程。
相比较于以前做过的STM32+ESP8266实现上华为云,ESP32上云可以说是十分十分简单了。
首先STM32与8266使用的是串口通信发送AT指令的方案,连接服务器的过程总是不太灵敏,且对于待发送或者接收的JSON包难以编码和解析。
而ESP32自带WiFi模块,使用其PubSubClient库可以方便的做到与服务器连接以及通信,至于JSON格式的处理可以使用ArduinoJSON库。
关于应用侧和华为云IoT平台的连接,数据流转等功能的使用教程,笔者的一位朋友已经在着手撰写,后面将在这篇文章更新其文章链接,望大家多多催更:D
此处为更新的文章:
【教程】应用侧连接华为云IoT平台_叫我胡萝北的博客-CSDN博客
Arduino IDE
ESP32
PubSubClient库
ArduinoJSON库(V5和V6的代码语法会有一些差别,笔者使用的是V5版本的库)
这是ArduinoJSON库的官方网站ArduinoJson: Efficient JSON serialization for embedded C++
科普一下,此处的DA是Device Access的意思,表示设备接入



总览:呈现了平台的接入地址以及一些新手引导,建议新手可以根据引导观看,倒数第四个“产品文档”项也提供了软硬件与华为云IoT平台的交互的详细文档,建议新手仔细阅读
产品:顾名思义,这是定义和查看你拥有的产品的地方
设备:一个产品名下可以拥有很多设备,在这里可以定义设备,并对其参数进行修改或进行命令的下发
监控运维:包含 “在线调试” 和 “消息跟踪” 等功能,在线调试可以进行命令下发,参数设置等操作;而消息跟踪则是跟踪硬件设备一段时间内和平台的交互

PS:这里要来定义产品的服务,比如一个物联网的智能配送车就可以有一个订单服务
而订单服务下面又可以分为很多种属性,比如订单下发的命令,订单当前状态的查看,订单配送的命令



依次点击设备-> 所有设备-> 注册设备
选择所属的产品




这四个头文件都是必须的
#include <Wire.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
将其中的参数按照实际情况修改
/*MQTT连接配置*/
/*-----------------------------------------------------*/
const char* ssid = "ESP32连接的WiFi名称";
const char* password = "WiFi密码";
const char* mqttServer = "华为云MQTT接入地址";
const int mqttPort = 1883;
//以下3个参数可以由HMACSHA256算法生成,为硬件通过MQTT协议接入华为云IoT平台的鉴权依据
const char* clientId = "";
const char* mqttUser = "";
const char* mqttPassword = "";
WiFiClient espClient; //ESP32WiFi模型定义
PubSubClient client(espClient);
const char* topic_properties_report = "属性上报topic";
//接收到命令后上发的响应topic
char* topic_Commands_Response = "$oc/devices/设备ID/sys/commands/response/request_id=";
/*******************************************************/
PS:
接入地址
华为云MQTT接入地址以及端口号1883可以在华为云IoTDA的 总览-> 平台接入地址 查看
鉴权信息
clientId、mqttUser、mqttPassword是设备接入华为云IoT平台时要验证的鉴权信息,可以通过HMACSHA256算法实时在程序中生成,也可以通过产品文档中提供的参数生成工具直接生成(参数生成工具生成的不校验时间戳)
鉴权信息生成工具的使用
使用刚刚保存的设备id 和 密钥,填入,生成鉴权信息


topic
至于属性上报和命令响应等topic定义,在华为云IoTDA-> 产品-> 选择要查看的产品-> topic管理处查看
依据要使用的功能在程序中定义不同的topic,注意{device_id}应该整个替换为设备id,大括号也要替换
代码
/*
* 作用: ESP32的WiFi初始化以及与MQTT服务器的连接
* 参数: 无
* 返回值:无
*/
void MQTT_Init()
{
//WiFi网络连接部分
WiFi.begin(ssid, password); //开启ESP32的WiFi
while (WiFi.status() != WL_CONNECTED) { //ESP尝试连接到WiFi网络
delay(3000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to the WiFi network");
//MQTT服务器连接部分
client.setServer(mqttServer, mqttPort); //设置连接到MQTT服务器的参数
client.setKeepAlive (60); //设置心跳时间
while (!client.connected()) { //尝试与MQTT服务器建立连接
Serial.println("Connecting to MQTT...");
if (client.connect(clientId, mqttUser, mqttPassword )) {
Serial.println("connected");
} else {
Serial.print("failed with state ");
Serial.print(client.state());
delay(6000);
}
}
//接受平台下发内容的初始化
client.setCallback(callback); //可以接受任何平台下发的内容
}
如果你的产品下定义了属性,那么可以通过ESP32硬件向服务器上报属性。
比如你的产品上有一LED指示灯,那么在产品中我们可以定义一个服务用于管理灯,该服务下包含一属性,如灯的亮灭状态,灯的颜色。在用户改变灯的亮灭或者颜色等属性后ESP32可以将灯的最新属性数据上报给华为云IoT平台,此即为属性上报。
在产品-> 选择你的产品-> Topic管理处可以看到属性上报时要public的Topic。

"{device_id}" 依据你的设备改变。
const char* topic_properties_report = "$oc/devices/Doge1_1/sys/properties/report";
属性上报的相关代码
注释里有对代码的详细解释,请仔细阅读注释
/*
* 作用: 垃圾桶容量上报到MQTT服务器任务
* 参数: (int)垃圾桶容量,1代表垃圾桶满了,0代表垃圾桶未满
* 返回值:无
* 命名说明:Capacity:容量
*/
void TASK_Capacity_Report(int capacity)
{
//以下部分代码调用了ArduinoJson库将属性上报消息打包为JSON格式
//此部分代码可以通过ArduinoJson库的官方网站直接生成
StaticJsonBuffer<300> JSONbuffer; //定义静态的JSON缓冲区用于存储JSON消息
JsonObject& root = JSONbuffer.createObject();
JsonArray& services = root.createNestedArray("services");
JsonObject& service_1 = services.createNestedObject();
JsonObject& properties_1_1 = service_1.createNestedObject("properties");
service_1["service_id"] = "ash_bin_Service";
properties_1_1["ash_bin_capacity"] = capacity;
// root.prettyPrintTo(Serial);//调试用,将JSON打印到串口
//以下将生成好的JSON格式消息格式化输出到字符数组中,便于下面通过PubSubClient库发送到服务器
char JSONmessageBuffer[100];
root.printTo(JSONmessageBuffer, sizeof(JSONmessageBuffer));
Serial.println("Sending message to MQTT topic..");
Serial.println(JSONmessageBuffer);
//以下代码将打包好的JSON数据包通过PubSubClient库发送到服务器
if (client.publish(topic_properties_report, JSONmessageBuffer) == true) {
Serial.println("Success sending message");
} else {
Serial.println("Error sending message");
}
//由于本函数是放在loop中不断循环执行,所以添加client.loop()进行保持硬件的活跃度
//避免由于长时间未向服务器发送消息而被服务器踢下线
client.loop();
Serial.println("-------------");
}
| 产品文档_设备接入IoTDA_设备属性上报_华为云 | 描述了设备属性上报的Json消息格式 |
| ArduinoJson 6_JSON消息代码生成助手 | 如果你使用的是V6以上的ArduinoJson库版本,请使用这个网站生成代码 |
| ArduinoJson 5_JSON消息代码生成助手 | 如果你使用的是V5的ArduinoJson库版本,请使用这个网站生成代码 |
注意,属性上报可以是设备主动上报消息也可以是接到命令后被动上报。两次主动上报间应有一定的时间间隔,否则会导致服务器接收的消息过多,此时我们可以设置一定时器定时上报,或者当该属性改变时再主动上报给服务器。
如果你的产品模型中定义了命令以及响应的命令参数,那么华为云IoT平台侧可以向设备下发命令,设备接收到命令后必须依据特定的JSON格式向华为云IoT平台侧发送一命令响应,华为云IoT平台侧接收到该命令响应后才视为命令下发成功。
//接受平台下发内容的初始化
client.setCallback(callback); //可以接受任何平台下发的内容
当接受到平台侧下发给设备的任何消息或者命令时,程序就会调用括号中的回调函数(callback),我们在此回调函数中处理接收到的消息,执行相关操作。
下面贴上我一个项目中的回调函数代码
读者可以根据自己的需要修改其中的参数。
注释里有对代码的详细解释,请仔细阅读注释。
//监听华为云IoT平台下发指令并处理
void callback(char *topic, byte *payload, unsigned int length)
{
char *pstr = topic; //指向topic字符串,提取request_id用
/*串口打印出收到的平台消息或者命令*/
Serial.println();
Serial.println();
Serial.print("Message arrived [");
Serial.print(topic); //将收到消息的topic展示出来
Serial.print("] ");
Serial.println();
payload[length] = '\0'; //在收到的内容后面加上字符串结束符
char strPayload[255] = {0};
strcpy(strPayload, (const char*)payload);
Serial.println((char *)payload); //打印出收到的内容
Serial.println(strPayload);
/*request_id解析部分*///后文有详细解释为什么要提取下发命令的request_id
char arr[100]; //存放request_id
int flag = 0;
char *p = arr;
while(*pstr) //以'='为标志,提取出request_id
{
if(flag) *p ++ = *pstr;
if(*pstr == '=') flag = 1;
pstr++;
}
*p = '\0';
Serial.println(arr);
// strcat(topic_Commands_Response, arr);
// topic_Commands_Response.concat(arr);
/*将命令响应topic与resquest_id结合起来*/
char topicRes[200] = {0};
strcat(topicRes, topic_Commands_Response);
strcat(topicRes, arr);
Serial.println(topicRes);
/*payload解析*///这是对接收到的平台下发的消息或者命令进行解析
//解析程序同样可以由ArduinoJson库官方网站的ArduinoJson助手生成
const size_t capacity_Payload_Receive = JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(5) + 150;
DynamicJsonBuffer jsonBuffer_Payload(capacity_Payload_Receive);
JsonObject& root_Payload = jsonBuffer_Payload.parseObject(strPayload);
//以下就是根据不同的命令或者消息进行不同的响应,此部分代码请自行修改
if (root_Payload.success()){ //判断json解析是否成功
if(!strcmp(root_Payload["command_name"], "user_order")) //如果收到的内容是“用户下单”
{
JsonObject& paras_Payload = root_Payload["paras"];
const char* paras_address = paras_Payload["address"]; // "88—902"
const char* paras_user = paras_Payload["user"]; // "wksgogogo"
const char* paras_number = paras_Payload["number"]; // "3333"
const char* paras_day = paras_Payload["day"]; // "2022-07-21"
const char* paras_time = paras_Payload["time"]; // "12:01"
Serial.println("__________JSON Received Parse__________");
Serial.println(paras_address);
Serial.println(paras_user);
Serial.println(paras_number);
Serial.println(paras_day);
Serial.println(paras_time);
Info_UserOrder_Structure OrderInfo;
strcpy(OrderInfo.userName, paras_user);
strcpy(OrderInfo.address, paras_address);
strcpy(OrderInfo.orderNum, paras_number);
strcpy(OrderInfo.day, paras_day);
strcpy(OrderInfo.time, paras_time);
//响应函数会在下文贴出
Command_Response(topicRes, "user_order", SUCCESS);
OrderInfo_Save(OrderInfo); //订单信息存储
}
if(!strcmp(root_Payload["command_name"], "open")) //如果收到的内容是“开锁”
{
const char* paras_user = root_Payload["paras"]["user"];
Serial.println("__________JSON Received Parse__________");
Serial.println(paras_user);
EOF_ELock_Unlock(paras_user);
Command_Response(topicRes, "open", SUCCESS);
}
}
命令响应代码
读者可以根据自己的需要修改其中的参数;
注释里有对代码的详细解释,请仔细阅读注释;
此部分代码的原理与属性上报的代码原理基本相同。
void Command_Response(char *topic, char *responseName, uint8_t response_Result)
{
/*发送命令响应部分*/
/*构建JSON内容*/
const size_t capacity = JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3);
DynamicJsonBuffer jsonBuffer(capacity);
JsonObject& root = jsonBuffer.createObject();
if(response_Result == SUCCESS){
root["result_code"] = 0;
JsonObject& paras = root.createNestedObject("paras");
paras["status"] = 200;
paras["msg"] = "success";
}
else if(response_Result == FAIL){
root["result_code"] = 1;
JsonObject& paras = root.createNestedObject("paras");
paras["status"] = 400;
paras["msg"] = "fail";
}
if(!strcmp(responseName, "user_order")){
root["response_name"] = "user_order";
}
else if(!strcmp(responseName, "open")){
root["response_name"] = "open";
}
root.printTo(Serial); //串口打印出构建好的JSON内容
Serial.println();
char JSONmessageBuffer[300];
root.printTo(JSONmessageBuffer, sizeof(JSONmessageBuffer)); //将构建的JSON消息复制到char数组中
Serial.println("Sending response to HuaWei Cloud..");
Serial.println(JSONmessageBuffer);
if (client.publish(topic, JSONmessageBuffer) == true) {
Serial.println("Success sending response command message");
} else {
Serial.println("Error sending response command message");
}
Serial.println("-------------");
}
华为云产品文档中对于命令下发与命令响应的解释
平台命令下发_设备接入 IoTDA_API参考_设备侧MQTT/MQTTS接口参考_设备命令_华为云
Topic
下行: $oc/devices/{device_id}/sys/commands/request_id={request_id}
上行:$oc/devices/{device_id}/sys/commands/response/request_id={request_id}
- {request_id}用于唯一标识这次请求。设备侧收到下行请求的topic带该参数时,上行响应的topic需要将该参数值返回给平台。
- 设备侧订阅带{request_id}结尾的topic时,可以使用通配#,设备侧订阅平台命令下发的topic为:$oc/devices/{device_id}/sys/commands/#
也就是说,接收华为云IoT平台下发命令时,我们可以不关心Topic中的{request_id}而使用通配符#代替,但是设备接收到命令后必须响应此命令,此时{request_id}不可省略。所以在上文中的callback函数中我们才需要解析出平台下发命令的Topic中的{request_id},并将其接到定义的命令响应Topic中,通过此Topic进行命令的响应。
下面是宏定义的命令响应Topic,在其后面接上命令下发的{request_id}后才可以用此Topic向平台响应命令。
char* topic_Commands_Response = "$oc/devices/{device_id}/sys/commands/response/request_id=";
命令响应Topic中的{request_id}与要响应的命令Topic中的{request_id}一致。
以上便是ESP32与华为云IoT平台的连接过程,希望对你有所帮助。
笔者道行不深,但努力学习,还请多多指教。
需要相关代码,请留言评论,笔者会在后续更新。
如果教程中还有其他不懂的,请评论或者私信我,笔者会尽可能回答。
我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以
我使用的是Firefox版本36.0.1和Selenium-Webdrivergem版本2.45.0。我能够创建Firefox实例,但无法使用脚本继续进行进一步的操作无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055)错误。有人能帮帮我吗? 最佳答案 我遇到了同样的问题。降级到firefoxv33后一切正常。您可以找到旧版本here 关于ruby-无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055),我们在StackOverflow上找到一个类
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
system-view进入系统视图quit退到系统视图sysname交换机命名vlan20创建vlan(进入vlan20)displayvlan显示vlanundovlan20删除vlan20displayvlan20显示vlan里的端口20Interfacee1/0/24进入端口24portlink-typeaccessvlan20把当前端口放入vlan20undoporte1/0/10删除当前VLAN端口10displaycurrent-configuration显示当前配置02配置交换机支持TELNETinterfacevlan1进入VLAN1ipaddress192.168.3.100
1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主
1.1.1 YARN的介绍 为克服Hadoop1.0中HDFS和MapReduce存在的各种问题⽽提出的,针对Hadoop1.0中的MapReduce在扩展性和多框架⽀持⽅⾯的不⾜,提出了全新的资源管理框架YARN. ApacheYARN(YetanotherResourceNegotiator的缩写)是Hadoop集群的资源管理系统,负责为计算程序提供服务器计算资源,相当于⼀个分布式的操作系统平台,⽽MapReduce等计算程序则相当于运⾏于操作系统之上的应⽤程序。 YARN被引⼊Hadoop2,最初是为了改善MapReduce的实现,但是因为具有⾜够的通⽤性,同样可以⽀持其他的分布式计算模
require"socket"server="irc.rizon.net"port="6667"nick="RubyIRCBot"channel="#0x40"s=TCPSocket.open(server,port)s.print("USERTesting",0)s.print("NICK#{nick}",0)s.print("JOIN#{channel}",0)这个IRC机器人没有连接到IRC服务器,我做错了什么? 最佳答案 失败并显示此消息::irc.shakeababy.net461*USER:Notenoughparame
考虑一下:现在这些情况:#output:http://domain.com/?foo=1&bar=2#output:http://domain.com/?foo=1&bar=2#output:http://domain.com/?foo=1&bar=2#output:http://domain.com/?foo=1&bar=2我需要用其他字符串输出URL。我如何保证&符号不会被转义?由于我无法控制的原因,我无法发送&。求助!把我的头发拉到这里:\编辑:为了澄清,我实际上有一个像这样的数组:@images=[{:id=>"fooid",:url=>"http://