三木社区

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 527|回复: 0
打印 上一主题 下一主题

MQTT之 PubSubClient 库

[复制链接]

1657

主题

1684

帖子

5684

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5684
跳转到指定楼层
楼主
发表于 2018-12-19 11:30:46 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
转发地址:
  1. https://www.jianshu.com/p/7f54b92d7a7b
复制代码

我在MQTT简介中的ESP8266的示例显得有点复杂,虽说Adafruit的库貌似很强大,但从软件角度来看却显得非常的臃肿,简洁至尚才是写代码的王道。
幸亏MQTT的库有非常的多,这里我会采用一款更简单好用的库PubSubClient来做一个最简单的MQTT客户端。
PubSubClient可以在Arduino IDE的库管理器中找到:


然后我们来写个例子,当ESP8266收到来自home/devices/onoff/主题中值为1的信息就点亮板载的LED,收到0就熄灭LED。
首先定义基本的变量:
  1. #include <ESP8266WiFi.h>
  2. #include <PubSubClient.h>

  3. const char* ssid = "网络SSID";
  4. const char* password = "密码";
  5. const char* mqtt_server = "broker.mqtt-dashboard.com"; // 使用HIVEMQ 的信息中转服务
  6. const char* TOPIC = "home/devices/onoff/";                     // 订阅信息主题
  7. const char* client_id = "clientId-ApjJZcy9Dh";                   // 标识当前设备的客户端编号

  8. WiFiClient espClient;                                                         // 定义wifiClient实例
  9. PubSubClient client(espClient);                                         // 定义PubSubClient的实例
  10. long lastMsg = 0;
复制代码
然后是初始化
  1. void setup() {
  2.   pinMode(BUILTIN_LED, OUTPUT);     // 定义板载LED灯为输出方式
  3.   Serial.begin(115200);
  4.   setup_wifi();                                          //执行Wifi初始化,下文有具体描述
  5.   client.setServer(mqtt_server, 1883);    //设定MQTT服务器与使用的端口,1883是默认的MQTT端口
  6.   client.setCallback(callback);                 //设定回调方式,当ESP8266收到订阅消息时会调用此方法
  7. }
复制代码
接下来是初始化WIFI
  1. void setup_wifi() {

  2.   delay(10);
  3.   // 板子通电后要启动,稍微等待一下让板子点亮
  4.   Serial.println();
  5.   Serial.print("Connecting to ");
  6.   Serial.println(ssid);

  7.   WiFi.begin(ssid, password);

  8.   while (WiFi.status() != WL_CONNECTED) {
  9.     delay(500);
  10.     Serial.print(".");
  11.   }

  12.   Serial.println("");
  13.   Serial.println("WiFi connected");
  14.   Serial.println("IP address: ");
  15.   Serial.println(WiFi.localIP());
  16. }
复制代码
连接成功后会在串口监视器中输出当前ESP8266的IP地址。接下来就是编写回调的逻辑,就是当收到一特定的信息时如何让ESP来执行一个具体动作:
  1. void callback(char* topic, byte* payload, unsigned int length) {
  2.   Serial.print("Message arrived [");
  3.   Serial.print(topic);   // 打印主题信息
  4.   Serial.print("] ");
  5.   for (int i = 0; i < length; i++) {
  6.     Serial.print((char)payload[i]); // 打印主题内容
  7.   }
  8.   Serial.println();

  9.   if ((char)payload[0] == '1') {
  10.     digitalWrite(BUILTIN_LED, HIGH);   // 亮灯
  11.   } else {
  12.     digitalWrite(BUILTIN_LED, LOW);   // 熄灯
  13.   }
  14. }
复制代码
  1. 注意 void callback(char* topic, byte* payload, unsigned int length)的参数是固定的,是一个方法接口除了方法名可改,类型与参数个数都不能错。
复制代码

payload传递过来的内容是个二进制的流,所以可以任何的内容,当然上述代码中我们也可以将其转化为整数来用,另外还可以直接传递JSON格式的数据,这样能让数据更加可读。但在单片机的设备间通信还是以“小”为原则, 能传整数的时候绝不传字符串,这样可以大大减轻整体网络通信的负担同时也可以降低单片机运行的功耗。
接下来我们再写一个方法来防止MQTT服务下线,当断开服务时可以尝试重新连接:


  1. void reconnect() {
  2.   while (!client.connected()) {
  3.     Serial.print("Attempting MQTT connection...");
  4.     // Attempt to connect
  5.     if (client.connect(client_id)) {
  6.       Serial.println("connected");
  7.       // 连接成功时订阅主题
  8.       client.subscribe(TOPIC);
  9.     } else {
  10.       Serial.print("failed, rc=");
  11.       Serial.print(client.state());
  12.       Serial.println(" try again in 5 seconds");
  13.       // Wait 5 seconds before retrying
  14.       delay(5000);
  15.     }
  16.   }
  17. }
复制代码

最后就是主循环,主循环的逻辑非常简单:
  • MQTT是否连接,没有就重试
  • 每隔2秒向home/status/主题发一个设备上线的信息
具体代码如下:
  1. void loop() {

  2.   if (!client.connected()) {
  3.     reconnect();
  4.   }
  5.   client.loop();

  6.   long now = millis();
  7.   if (now - lastMsg > 2000) {
  8.     lastMsg = now;
  9.     client.publish("home/status/", "{device:client_id,'status':'on'}");
  10.   }
  11. }

复制代码
以下是本例的全部代码:
  1. #include <ESP8266WiFi.h>
  2. #include <PubSubClient.h>

  3. const char* ssid = "网络SSID";
  4. const char* password = "密码";
  5. const char* mqtt_server = "broker.mqtt-dashboard.com"; // 使用HIVEMQ 的信息中转服务
  6. const char* TOPIC = "home/devices/onoff/";                     // 订阅信息主题
  7. const char* client_id = "clientId-ApjJZcy9Dh";                   // 标识当前设备的客户端编号

  8. WiFiClient espClient;                                                         // 定义wifiClient实例
  9. PubSubClient client(espClient);                                         // 定义PubSubClient的实例
  10. long lastMsg = 0;                                                               // 记录上一次发送信息的时长

  11. void setup() {
  12.   pinMode(BUILTIN_LED, OUTPUT);                               // 定义板载LED灯为输出方式
  13.   Serial.begin(115200);
  14.   setup_wifi();                                                                    //执行Wifi初始化,下文有具体描述
  15.   client.setServer(mqtt_server, 1883);                              //设定MQTT服务器与使用的端口,1883是默认的MQTT端口
  16.   client.setCallback(callback);                                          //设定回调方式,当ESP8266收到订阅消息时会调用此方法
  17. }

  18. void setup_wifi() {

  19.   delay(10);
  20.   // 板子通电后要启动,稍微等待一下让板子点亮
  21.   Serial.println();
  22.   Serial.print("Connecting to ");
  23.   Serial.println(ssid);

  24.   WiFi.begin(ssid, password);

  25.   while (WiFi.status() != WL_CONNECTED) {
  26.     delay(500);
  27.     Serial.print(".");
  28.   }

  29.   Serial.println("");
  30.   Serial.println("WiFi connected");
  31.   Serial.println("IP address: ");
  32.   Serial.println(WiFi.localIP());
  33. }

  34. void callback(char* topic, byte* payload, unsigned int length) {
  35.   Serial.print("Message arrived [");
  36.   Serial.print(topic);   // 打印主题信息
  37.   Serial.print("] ");
  38.   for (int i = 0; i < length; i++) {
  39.     Serial.print((char)payload[i]); // 打印主题内容
  40.   }
  41.   Serial.println();

  42.   if ((char)payload[0] == '1') {
  43.     digitalWrite(BUILTIN_LED, HIGH);   // 亮灯
  44.   } else {
  45.     digitalWrite(BUILTIN_LED, LOW);   // 熄灯
  46.   }
  47. }

  48. void reconnect() {
  49.   while (!client.connected()) {
  50.     Serial.print("Attempting MQTT connection...");
  51.     // Attempt to connect
  52.     if (client.connect(client_id)) {
  53.       Serial.println("connected");
  54.       // 连接成功时订阅主题
  55.       client.subscribe(TOPIC);
  56.     } else {
  57.       Serial.print("failed, rc=");
  58.       Serial.print(client.state());
  59.       Serial.println(" try again in 5 seconds");
  60.       // Wait 5 seconds before retrying
  61.       delay(5000);
  62.     }
  63.   }
  64. }

  65. void loop() {

  66.   if (!client.connected()) {
  67.     reconnect();
  68.   }
  69.   client.loop();

  70.   long now = millis();
  71.   if (now - lastMsg > 2000) {
  72.     lastMsg = now;
  73.     client.publish("home/status/", "{device:client_id,'status':'on'}");
  74.   }
  75. }
复制代码





回复

使用道具 举报

Archiver|手机版|小黑屋|三木电子社区 ( 辽ICP备11000133号-4 )

辽公网安备 21021702000620号

GMT+8, 2026-3-6 00:10 , Processed in 0.028785 second(s), 23 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表