前言
最近在做一个物联网项目,涉及到智能开关的开发。目前已经实现简单的TCP通信远程控制,但是考虑到后期的设备管理以及设备通信所需要的技术和服务器的维护成本,我决定将设备接入云平台。本文将详细阐述如何利用华为云的物联网平台(IoTDA)实现设备的云上管理与通信。
前提条件
- 搭建Arduino+ESP8266的开发环境。可以参考: 软件开发人员从0到1实现物联网项目:初步实现智能开关
- 开通华为云物联网平台服务,并注册一个设备。可以参考官网文档。
准备工作
设备侧选择用MQTT协议,因为对(IoTDA)SDK的编译环境不太了解,这里通过API的方式接入华为云的物联网平台。
获取IoTDA接入信息
首先需要拿到两个信息:
-
华为云物联网平台的接入信息,也就是URL和PORT。接入信息通过下图可以看到。
-
华为云物联网平台的设备连接鉴权信息。可以参考设备连接鉴权 。简单来讲,就是通过注册设备时的设备ID和密钥生成设备连接鉴权所需的参数(ClientId、Username、Password)。生成参数的链接
安装代码库
其次,设备侧作为MQTT的客户端,需要编写相关代码。基于Arduino IDE开发,可以使用现成的库减少工作量。
- 安装
PubSubClient
库,用于连接和交互MQTT代理服务器(IoTDA)。 - 安装
ArduinoJson
库,用于解析和生成 JSON 数据。
功能实现
基于IoTDA平台实现智能开关,代码要实现以下功能:
- 设备进行WiFi连接与网络通信。
- 设备通过MQTT协议接入IoTDA平台。
- 设备接收IoTDA平台下发的消息。
- 设备根据消息执行相应的控制操作。
WiFi连接
c
#include <ESP8266WiFi.h>
const char* ssid = "xxx"; //wifi名称
const char* password = "xxx"; //wifi密码
void setup_wifi() {
delay(10);
// We start by connecting to WiFi
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
对以上的代码进行解释:
ESP8266WiFi.h
库提供了与 WiFi 网络连接相关的功能,WiFi.begin(ssid, password);
这行代码就可以自动连接WiFi。
接入IoTDA
设备要接入IoTDA,就需要上面准备的接入信息。在代码中定义如下:
c
const char* mqttServer = "xxx.myhuaweicloud.com";
const int mqttPort = 1883;
const char* mqttClientId = "xxx";
const char* mqttUser ="xxx";
const char* mqttPassword = "xxx";
#define deviceId "xxx"
#define mqttTopic "$oc/devices/" deviceId "/sys/messages/down"
mqttTopic
是在接入成功后,设备侧订阅的Topic,用来接收平台下发的消息。更多的topic定义参考IoTDA平台预置的topic。
接下来就需要通过PubSubClient
库进行接入IoTDA平台了
c
WiFiClient espClient;
PubSubClient client(espClient);
void reconnect() {
client.setServer(mqttServer, mqttPort);
client.setCallback(callback);
while (!client.connected()) {
Serial.println("Attempting MQTT connection...");
if (client.connect(mqttClientId, mqttUser, mqttPassword)) {
Serial.println("connected");
client.subscribe(mqttTopic);
} else {
Serial.print(client.state());
delay(5000);
}
}
}
接收IoTDA消息&控制开关
client.connect
成功后会订阅Topic,当在IoTDA平台下发消息时,就会在 callback
函数进行 digitalWrite(RELAY_PIN, state);
控制开关了。
c
void callback(char* topic, byte* payload, unsigned int length) {
StaticJsonDocument<128> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.c_str());
return;
}
const char* stateKey = "state";
if (doc.containsKey(stateKey)) {
bool state = doc[stateKey];
digitalWrite(RELAY_PIN, state);
Serial.print("Switch state: ");
Serial.println(state ? "ON" : "OFF");
}
}
完整代码
c
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
const char* ssid = "xxx";
const char* password = "xxx.";
const char* mqttServer = "xxx.myhuaweicloud.com";
const int mqttPort = 1883;
const char* mqttClientId = "xxx";
const char* mqttUser ="xxx";
const char* mqttPassword = "xxx";
#define RELAY_PIN 0
#define deviceId "xxx"
#define mqttTopic "$oc/devices/" deviceId "/sys/messages/down"
WiFiClient espClient;
PubSubClient client(espClient);
void callback(char* topic, byte* payload, unsigned int length) {
StaticJsonDocument<128> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.c_str());
return;
}
const char* stateKey = "state";
if (doc.containsKey(stateKey)) {
bool state = doc[stateKey];
digitalWrite(RELAY_PIN, state);
Serial.print("Switch state: ");
Serial.println(state ? "ON" : "OFF");
}
}
void reconnect() {
while (!client.connected()) {
Serial.println("Attempting MQTT connection...");
if (client.connect(mqttClientId, mqttUser, mqttPassword)) {
Serial.println("connected");
client.subscribe(mqttTopic);
} else {
Serial.print(client.state());
delay(5000);
}
}
}
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void setup() {
Serial.begin(115200);
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, HIGH);
setup_wifi();
client.setServer(mqttServer, mqttPort);
client.setCallback(callback);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
}
IoTDA平台下发消息
将编写完成的代码烧录到ESP-01S模块后,就可以通过IoTDA平台进行消息下发了。消息下发位置见下图
消息下发格式:
json
{
"state":true
}
当state
为true
时,表示电平状态为HIGH
,电路呈开启状态,此时电路不通电。
而当state
为false
时,电平状态则为LOW
,电路闭合,此时电路处于通电状态。
通过state
数值的变化,从而达到远程控制开关。
问题记录
在串口调试时,如果mqtt连接返回-1状态码,意味着连接失败。
那么需要将 PubSubClient.h
库中的两个宏定义MQTT_KEEPALIVE
、MQTT_MAX_PACKET_SIZE
的数值调大。本文中的代码已分别调整为60和2048。
总结
借助华为云的物联网平台IoTDA,可以帮助物联网行业的用户快速完成设备联网及行业应用集成,大大提高了开发人员的工作效率。而且按需计费每月前一百万条消息是免费的,很大程度上降低了设备上云的成本。