目录
介绍
这篇文章的目的是解释如何使用ESP32通过MQTT发送JSON消息。
为此,我们将使用两个处理低级细节的库,并在易于使用的界面中向我们展示JSON编码和MQTT发布功能。这些库是PubSubClient,为MQTT提供相关的功能,以及ArduinoJson,为JSON提供相关的功能。
这两个库都可以与ESP8266和ESP32配合使用,并有一些示例可以帮助我们入门,我建议您尝试一下。此外,我一直在以前的帖子中介绍它们在两个设备中的使用情况,这些信息在相关帖子部分列出。
在这个教程中,我们将使用CloudMQTT,它提供免费计划,允许我们创建帐户并测试实例。如果您还没有注册,请注册并创建一个实例。我们将需要后者的实例信息(地址,端口,用户名和密码)。
代码
我们需要做的第一件事是安装ArduinoJson.h和PubSubClient.h库,以便能够访问所需的所有类。我们还需要包含WiFi.h库,这样我们就可以连接到WiFi网络,从而能够将消息发布到MQTT主题。
为了使代码更易读和易于修改,我们将声明一些全局变量来保存连接到WiFi网络和MQTT代理所需的凭据。对于与MQTT代理的连接,我们需要有关所创建实例的信息,该信息可在CloudMQTT实例信息页面中找到:
#include <ArduinoJson.h>
#include <WiFi.h>
#include <PubSubClient.h>
const char* ssid = yourNetworkName";
const char* password = "yourNetworkPassword";
const char* mqttServer = "m11.cloudmqtt.com";
const int mqttPort = 12948;
const char* mqttUser = "yourInstanceUsername";
const char* mqttPassword = "yourInstancePassword";
之后,我们将声明一个WiFiClient类的对象。我们还将声明类PubSubClient的对象,该对象 接收构造函数的输入以前声明的WiFiClient实例。请注意,我们不会在代码中使用WiFiClient,因为它将由PubSubClient库替代使用。因此,我们不需要担心实现细节:
WiFiClient espClient;
PubSubClient client(espClient);
声明全局变量后,我们将定义setup()函数。我们要做的第一件事就是打开一个串行连接,,设置好波特率。然后,我们连接到WiFi网络:
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to the WiFi network");
接下来,我们需要指定要连接的MQTT代理的地址和端口。为此,我们 在PubSubClient对象上调用setServer方法,将地址和端口作为输入传递。这些参数是指定为全局变量并从CloudMQTT实例页面信息获取的参数。
现在,我们将通过调用connect方法连接到MQTT服务器。该方法接收一个字符串作为输入,该字符串对应于客户端的唯一标识符(我们将使用“ESP32Client”)。它还接收连接到代理所需的用户名和密码,可在CloudMQTT实例页面信息中获得。
此方法调用将在连接成功时返回true,否则返回false。我们将在循环中进行连接尝试:
while (!client.connected()) {
Serial.println("Connecting to MQTT...");
if (client.connect("ESP32Client", mqttUser, mqttPassword )) {
Serial.println("connected");
} else {
Serial.print("failed with state ");
Serial.print(client.state());
delay(2000);
}
}
在此之后,我们应该已经连接到MQTT代理,我们可以开始向主题发布消息。不过,我们将在主循环函数上执行此操作,因此我们在setup函数上不需要更多代码。
现在,在主循环函数中,我们声明了一个StaticJsonBuffer类的对象,它将用于创建要通过MQTT发送的JSON消息。我们需要指定缓冲区的容量(以字节为单位)作为模板参数。然后,我们通过调用createObject方法从我们创建的StaticJsonBuffer对象获得对JsonObject的引用。
StaticJsonBuffer<300> JSONbuffer;
JsonObject& JSONencoder = JSONbuffer.createObject();
我们将创建一个JSON消息,如下所示。请注意,我们将创建一个包含静态内容的消息,仅用于保持代码简单,但我们可以从传感器或其他动态源获取值。
{
"device": "ESP32",
"sensorType" : "Temperature",
"Value" : [20,21,23]
}
接下来,我们将值添加到JsonObject中:
JSONencoder["device"] = "ESP32";
JSONencoder["sensorType"] = "Temperature";
JsonArray& values = JSONencoder.createNestedArray("values");
values.add(20);
values.add(21);
values.add(23);
现在,我们将使用 JsonObject引用上的printTo方法将JSON消息打印到char缓冲区 。此方法以紧凑格式打印消息,因此可以减少内存占用。
char JSONmessageBuffer[100];
JSONencoder.printTo(JSONmessageBuffer, sizeof(JSONmessageBuffer));
接下来,我们将JSON消息发送到名为“ esp / test ” 的主题。我们通过调用 PubSubClient对象上的publish方法来实现。此方法将接收主题名称和我们要发布的消息作为输入。
从这里可以看出,如果发布成功,则此方法返回“true”,否则返回“false”。我们将使用此信息进行错误处理。
最后,我们在同一个对象引用上调用loop方法。这应该定期调用,以便我们的程序可以处理传入的消息并维护与MQTT服务器的连接。由于我们将在Arduino主循环函数中发送消息,每个消息之间有一个小延迟,我们在每次迭代结束时调用循环方法。
Serial.println(JSONmessageBuffer);
if (client.publish("esp/test", JSONmessageBuffer) == true) {
Serial.println("Success sending message");
} else {
Serial.println("Error sending message");
}
client.loop();
下面是最终完整代码,以及一些其他打印来帮助调试:
#include <ArduinoJson.h>
#include <WiFi.h>
#include <PubSubClient.h>
const char* ssid = "yourNetworkName";
const char* password = "yourNetworkPassword";
const char* mqttServer = "m11.cloudmqtt.com";
const int mqttPort = 12948;
const char* mqttUser = "yourInstanceUsername";
const char* mqttPassword = "yourInstancePassword";
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
Serial.begin(115200);
Serial.println();
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to the WiFi network");
client.setServer(mqttServer, mqttPort);
while (!client.connected()) {
Serial.println("Connecting to MQTT...");
if (client.connect("ESP32Client", mqttUser, mqttPassword )) {
Serial.println("connected");
} else {
Serial.print("failed with state ");
Serial.print(client.state());
delay(2000);
}
}
}
void loop() {
StaticJsonBuffer<300> JSONbuffer;
JsonObject& JSONencoder = JSONbuffer.createObject();
JSONencoder["device"] = "ESP32";
JSONencoder["sensorType"] = "Temperature";
JsonArray& values = JSONencoder.createNestedArray("values");
values.add(20);
values.add(21);
values.add(23);
char JSONmessageBuffer[100];
JSONencoder.printTo(JSONmessageBuffer, sizeof(JSONmessageBuffer));
Serial.println("Sending message to MQTT topic..");
Serial.println(JSONmessageBuffer);
if (client.publish("esp/test", JSONmessageBuffer) == true) {
Serial.println("Success sending message");
} else {
Serial.println("Error sending message");
}
client.loop();
Serial.println("-------------");
delay(10000);
}
测试结果
为了测试代码,我们将使用MQTTLens软件来订阅ESP32将要发布的主题。因此,打开应用程序并订阅“ esp / test ”主题。
之后,将代码上传到ESP32并打开串行监视器。你可以看到类似于图1的输出。
图1 – 程序的输出。
现在,回到MQTTLens。您可以看到类似于图2的输出,并打印JSON消息。MQTTLens识别JSON格式,虽然我们以紧凑格式发送消息,但它允许我们以缩进的用户友好格式查看它。
图2 – ESP32发送的JSON消息。