授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力。希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石。。。

快速导航
单片机菜鸟的博客快速索引(快速找到你要的)

共同学习成长QQ群 622368884,不喜勿加,里面有一大群志同道合的探路人

如果觉得有用,麻烦点赞收藏,您的支持是博主创作的动力。

文章目录

  • 1.前言
  • 2. Adafruit_MQTT
    • 2.1 Adafruit_MQTT 源码地址
    • 2.2 Adafruit_MQTT 是什么
    • 2.3 安装 Adafruit_MQTT 库
    • 2.4. 如何引入 Adafruit_MQTT 库
  • 3. Adafruit_MQTT 源码解析
    • 3.1 如何连接到MQTT服务器
      • 3.1.1 Adafruit_MQTT_Client —— 创建MQTT客户端
      • 3.1.2 will —— 配置遗嘱消息
      • 3.1.3 connect —— 建立mqtt连接
      • 3.1.4 connected —— 判断连接状态
      • 3.1.5 disconnect —— 断开mqtt服务
    • 3.2 如何订阅主题
      • 3.2.1 Adafruit_MQTT_Subscribe —— 创建主题
      • 3.2.2 subscribe —— 订阅主题消息
      • 3.2.3 unsubscribe—— 取消订阅主题消息
    • 3.3 如何发布主题消息
      • 3.3.1 Adafruit_MQTT_Publish—— 构建发布主题
      • 3.3.2 publish —— 发布消息
    • 3.4 保持连接(心跳)
      • 3.4.1 ping —— 保持心跳
    • 3.5 主题消息获取和处理
      • 3.5.1 readSubscription —— 获取主题消息
      • 3.5.2 processPackets —— 获取主题消息
      • 3.5.2 处理主题消息内容
    • 3.6 辅助信息以及打印信息
      • 3.6.1 connectErrorString —— 获取连接错误对应的信息
      • 3.6.1 MQTT_DEBUG 和 MQTT_ERROR
  • 4.实例操作
    • 4.1 官方实例 —— mqtt_esp8266
      • 4.1.1 实例源码
    • 4.2 官方实例2 —— mqtt_esp8266_callback
      • 4.2.1 实例源码
    • 4.3 玩转OneNET物联网平台之MQTT服务④ —— 远程控制LED(数量无限制)+ Android App控制
      • 4.3.1 实例源码
  • 5.总结

1.前言

在前面的博文中,博哥介绍了 PubSubClient 这个使用率很高的Arduino MQTT库,具体可以参考 玩转PubSubClient MQTT库。

然而,博哥这里重点讲解另一款使用率也非常高的Arduino MQTT库 —— Adafruit_MQTT。

当然,博哥这里不会重点讲解MQTT协议(毕竟以前的博文已经花了很大篇幅讲解),需要了解的同学请仔细阅读 玩转PubSubClient MQTT库。

2. Adafruit_MQTT

2.1 Adafruit_MQTT 源码地址

  • 直接点击 github 下载代码

2.2 Adafruit_MQTT 是什么

一句话概括:

  • Arduino library for MQTT support, including access to Adafruit IO. Works with the Adafruit FONA, Arduino Yun, ESP8266 Arduino platforms, and anything that supports Arduino’s Client interface (like Ethernet shield).

翻译为:

  • 支持MQTT库的Arduino库,包括访问Adafruit IO。适用于Adafruit FONA, Arduino Yun, ESP8266 Arduino平台和任何支持Arduino的客户端接口(如以太网)

2.3 安装 Adafruit_MQTT 库

方法一

  • 可以通过Arduino IDE库管理器安装Adafruit_MQTT,搜索“Adafruit_MQTT”,然后点击下载

方法二

  • 直接通过github下载源码,放到你的Arduino libraries文件夹(我个人是比较这种方法)

2.4. 如何引入 Adafruit_MQTT 库

非常简单,直接引入:

#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

至于怎么样去彻底了解这个库的使用方法,请看博主下面的源码分析。

3. Adafruit_MQTT 源码解析

对于只是想知道怎么使用的小白同学,可以直接看实例使用;
对于希望深入学习源码的同学,请务必了解基本的MQTT协议,博主建议对照 玩转PubSubClient MQTT库 学习。

最重要的源码在这几个文件:

  • Adafruit_MQTT主要是整个mqtt协议
  • Adafruit_MQTT_Client 主要是实现mqtt 客户端的功能(包括设置Client端来源、连接服务器、发送数据等)
  • 一般情况下我们都是连接Mqtt服务器,所以可以说两者缺一不可

博主建议:先了解有什么方法,然后在后面例子讲解中去感受方法的使用

在使用MQTT协议的时候,我们重点需要了解三部曲:

  • 如何连接到MQTT服务器
  • 如何订阅主题
  • 如何发布主题消息

先大概看看 Adafruit_MQTT 源码结构(Adafruit_MQTT.h文件):

  • 类Adafruit_MQTT 负责整体MQTT协议
  • 类Adafruit_MQTT_Publish 负责发布消息相关
  • 类Adafruit_MQTT_Subscribe 负责订阅主题相关
    三者职责分离,不失为一种好设计。

再来看看 Adafruit_MQTT_Client 源码结构(Adafruit_MQTT_Client.h文件):

  • 类 Adafruit_MQTT_Client 主要是继承了 Adafruit_MQTT ,并且实现 Adafruit_MQTT 中定义的 几个方法

接下来,我们开始进入具体方法的学习.

3.1 如何连接到MQTT服务器

3.1.1 Adafruit_MQTT_Client —— 创建MQTT客户端

函数说明

/**** 函数1:创建MQTT客户端* @param client 来源客户端,比如Wificlient eth以太网* @param server  mqtt服务器地址* @param port    mqtt服务器端口* @param cid     客户端id,如果是8266,可以设置为芯片id之类的,每个端都是独一无二* @param user    mqtt服务器账号* @param pass    mqtt服务器密码*/
Adafruit_MQTT_Client(Client *client, const char *server, uint16_t port,const char *cid, const char *user, const char *pass):Adafruit_MQTT(server, port, cid, user, pass),client(client)
{}/**** 函数2:创建MQTT客户端* @param client 来源客户端,比如Wificlient eth以太网* @param server  mqtt服务器地址* @param port    mqtt服务器端口* @param user    mqtt服务器账号* @param pass    mqtt服务器密码*/
Adafruit_MQTT_Client(Client *client, const char *server, uint16_t port,const char *user="", const char *pass=""):Adafruit_MQTT(server, port, user, pass),client(client)
{}

这里用到了 Adafruit_MQTT类的初始化构造方法:

/**** 函数1:配置mqtt信息* @param server  mqtt服务器地址* @param port    mqtt服务器端口* @param cid     客户端id,如果是8266,可以设置为芯片id之类的,每个端都是独一无二* @param user    mqtt服务器账号* @param pass    mqtt服务器密码*/
Adafruit_MQTT::Adafruit_MQTT(const char *server,uint16_t port,const char *cid,const char *user,const char *pass) {servername = server;portnum = port;clientid = cid;username = user;password = pass;// 重置订阅主题for (uint8_t i=0; i<MAXSUBSCRIPTIONS; i++) {subscriptions[i] = 0;}will_topic = 0;will_payload = 0;will_qos = 0;will_retain = 0;packet_id_counter = 0;
}/**** 函数2:配置mqtt信息* @param server  mqtt服务器地址* @param port    mqtt服务器端口* @param user    mqtt服务器账号* @param pass    mqtt服务器密码*/
Adafruit_MQTT::Adafruit_MQTT(const char *server,uint16_t port,const char *user,const char *pass) {servername = server;portnum = port;clientid = "";// 默认cid是空字符串username = user;password = pass;// 重置订阅主题for (uint8_t i=0; i<MAXSUBSCRIPTIONS; i++) {subscriptions[i] = 0;}will_topic = 0;will_payload = 0;will_qos = 0;will_retain = 0;packet_id_counter = 0;}

3.1.2 will —— 配置遗嘱消息

函数说明

/*** 配置遗嘱消息* @param topic 遗嘱主题* @param payload 遗嘱内容* @param qos 遗嘱质量等级* @param retain*/
bool Adafruit_MQTT::will(const char *topic, const char *payload, uint8_t qos, uint8_t retain) {if (connected()) {DEBUG_PRINT(F("Will defined after connect"));return false;}will_topic = topic;will_payload = payload;will_qos = qos;will_retain = retain;return true;}

3.1.3 connect —— 建立mqtt连接

函数说明

/*** 功能描述: 连接服务器* @param user mqtt服务器账号* @param pass mqtt服务器密码*/
int8_t Adafruit_MQTT::connect(const char *user, const char *pass)
{username = user;password = pass;return connect();
}/*** 功能描述:连接服务器*/
int8_t Adafruit_MQTT::connect() {// 判断是否连接到服务器if (!connectServer())return -1;// 构建连接报文.uint8_t len = connectPacket(buffer);// 发送连接报文if (!sendPacket(buffer, len))return -1;// 读取响应报文信息并且做校验len = readFullPacket(buffer, MAXBUFFERSIZE, CONNECT_TIMEOUT_MS);if (len != 4)return -1;if ((buffer[0] != (MQTT_CTRL_CONNECTACK << 4)) || (buffer[1] != 2))return -1;if (buffer[3] != 0)return buffer[3];// 判断是否注册了订阅主题 subscriptions 由类 Adafruit_MQTT_Subscribe负责/*** #if defined  (__AVR_ATmega32U4__) || defined(__AVR_ATmega328P__)#define MAXSUBSCRIPTIONS 5#define SUBSCRIPTIONDATALEN 20#else#define MAXSUBSCRIPTIONS 15#define SUBSCRIPTIONDATALEN 100#endif*/for (uint8_t i=0; i<MAXSUBSCRIPTIONS; i++) {// Ignore subscriptions that aren't defined.if (subscriptions[i] == 0) continue;boolean success = false;for (uint8_t retry=0; (retry<3) && !success; retry++) { // retry until we get a suback    // 构建订阅主题报文.uint8_t len = subscribePacket(buffer, subscriptions[i]->topic, subscriptions[i]->qos);// 发送报文if (!sendPacket(buffer, len))return -1;if(MQTT_PROTOCOL_LEVEL < 3) // older versions didn't subackbreak;// Check for SUBACK if using MQTT 3.1.1 or higher// TODO: The Server is permitted to start sending PUBLISH packets matching the// Subscription before the Server sends the SUBACK Packet. (will really need to use callbacks - ada)//Serial.println("\t**looking for suback");// 等待订阅响应if (processPacketsUntil(buffer, MQTT_CTRL_SUBACK, SUBACK_TIMEOUT_MS)) {success = true;break;}}if (! success) return -2; // failed to sub for some reason}return 0;
}

这里用到几个方法:

  • connectServer —— 连接服务器
  • connectPacket —— 构建连接报文
  • sendPacket —— 发送数据包
  • readFullPacket —— 读取响应报文信息
  • subscribePacket —— 订阅报文

其中,connectServer 方法:

/*** 功能描述:是否连接到服务器 (servername、portnum)* @return bool true or false*/
bool Adafruit_MQTT_Client::connectServer() {// Grab server name from flash and copy to buffer for name resolution.memset(buffer, 0, sizeof(buffer));strcpy((char *)buffer, servername);DEBUG_PRINT(F("Connecting to: ")); DEBUG_PRINTLN((char *)buffer);// Connect and check for success (0 result).int r = client->connect((char *)buffer, portnum);DEBUG_PRINT(F("Connect result: ")); DEBUG_PRINTLN(r);return r != 0;
}

connectPacket 方法用来构建连接报文

/*** 构建连接报文*/
uint8_t Adafruit_MQTT::connectPacket(uint8_t *packet) {uint8_t *p = packet;uint16_t len;// fixed header, connection messsage no flagsp[0] = (MQTT_CTRL_CONNECT << 4) | 0x0;p+=2;// fill in packet[1] last#if MQTT_PROTOCOL_LEVEL == 3p = stringprint(p, "MQIsdp");
#elif MQTT_PROTOCOL_LEVEL == 4p = stringprint(p, "MQTT");
#else#error "MQTT level not supported"
#endifp[0] = MQTT_PROTOCOL_LEVEL; //mqtt协议版本p++;// always clean the sessionp[0] = MQTT_CONN_CLEANSESSION;// set the will flags if needed 设置遗嘱消息if (will_topic && pgm_read_byte(will_topic) != 0) {p[0] |= MQTT_CONN_WILLFLAG;if(will_qos == 1)p[0] |= MQTT_CONN_WILLQOS_1;else if(will_qos == 2)p[0] |= MQTT_CONN_WILLQOS_2;if(will_retain == 1)p[0] |= MQTT_CONN_WILLRETAIN;}if (pgm_read_byte(username) != 0)p[0] |= MQTT_CONN_USERNAMEFLAG;if (pgm_read_byte(password) != 0)p[0] |= MQTT_CONN_PASSWORDFLAG;p++;p[0] = MQTT_CONN_KEEPALIVE >> 8;p++;p[0] = MQTT_CONN_KEEPALIVE & 0xFF;p++;if(MQTT_PROTOCOL_LEVEL == 3) {p = stringprint(p, clientid, 23);  // Limit client ID to first 23 characters.} else {if (pgm_read_byte(clientid) != 0) {p = stringprint(p, clientid);} else {p[0] = 0x0;p++;p[0] = 0x0;p++;DEBUG_PRINTLN(F("SERVER GENERATING CLIENT ID"));}}if (will_topic && pgm_read_byte(will_topic) != 0) {p = stringprint(p, will_topic);p = stringprint(p, will_payload);}if (pgm_read_byte(username) != 0) {p = stringprint(p, username);}if (pgm_read_byte(password) != 0) {p = stringprint(p, password);}len = p - packet;packet[1] = len-2;  // don't include the 2 bytes of fixed header dataDEBUG_PRINTLN(F("MQTT connect packet:"));DEBUG_PRINTBUFFER(buffer, len);return len;
}
  • 此方法用到了 will_topic 遗嘱消息,这个消息可由 will 方法设置.

sendPacket 方法:

/*** 通过客户端发送信息,主要是通过client发送内容出去*/
bool Adafruit_MQTT_Client::sendPacket(uint8_t *buffer, uint16_t len) {uint16_t ret = 0;while (len > 0) {if (client->connected()) {// send 250 bytes at most at a time, can adjust this later based on Clientuint16_t sendlen = len > 250 ? 250 : len;//Serial.print("Sending: "); Serial.println(sendlen);ret = client->write(buffer, sendlen);DEBUG_PRINT(F("Client sendPacket returned: ")); DEBUG_PRINTLN(ret);len -= ret;if (ret != sendlen) {DEBUG_PRINTLN("Failed to send packet.");return false;}} else {DEBUG_PRINTLN(F("Connection failed!"));return false;}}return true;
}

readFullPacket方法:

/*** 读取响应内容* @param buffer 内容缓冲区* @param maxsize 最大大小* @param timeout 读取超时时间*/
uint16_t Adafruit_MQTT::readFullPacket(uint8_t *buffer, uint16_t maxsize, uint16_t timeout) {// will read a packet and Do The Right Thing with lengthuint8_t *pbuff = buffer;uint8_t rlen;// read the packet type:rlen = readPacket(pbuff, 1, timeout);if (rlen != 1) return 0;DEBUG_PRINT(F("Packet Type:\t")); DEBUG_PRINTBUFFER(pbuff, rlen);pbuff++;uint32_t value = 0;uint32_t multiplier = 1;uint8_t encodedByte;do {rlen = readPacket(pbuff, 1, timeout);if (rlen != 1) return 0;encodedByte = pbuff[0]; // save the last read valpbuff++; // get ready for reading the next byteuint32_t intermediate = encodedByte & 0x7F;intermediate *= multiplier;value += intermediate;multiplier *= 128;if (multiplier > (128UL*128UL*128UL)) {DEBUG_PRINT(F("Malformed packet len\n"));return 0;}} while (encodedByte & 0x80);DEBUG_PRINT(F("Packet Length:\t")); DEBUG_PRINTLN(value);if (value > (maxsize - (pbuff-buffer) - 1)) {DEBUG_PRINTLN(F("Packet too big for buffer"));rlen = readPacket(pbuff, (maxsize - (pbuff-buffer) - 1), timeout);} else {rlen = readPacket(pbuff, value, timeout);}//DEBUG_PRINT(F("Remaining packet:\t")); DEBUG_PRINTBUFFER(pbuff, rlen);return ((pbuff - buffer)+rlen);
}

subscribePacket 方法:

/**** 构建订阅主题信息* @param packet 构建包* @param topic 主题* @param qos 订阅主题信息等级*/
uint8_t Adafruit_MQTT::subscribePacket(uint8_t *packet, const char *topic,uint8_t qos) {uint8_t *p = packet;uint16_t len;// 订阅命令p[0] = MQTT_CTRL_SUBSCRIBE << 4 | MQTT_QOS_1 << 1;// fill in packet[1] lastp+=2;// packet identifier. used for checking SUBACKp[0] = (packet_id_counter >> 8) & 0xFF;p[1] = packet_id_counter & 0xFF;p+=2;// increment the packet idpacket_id_counter++;p = stringprint(p, topic);p[0] = qos;p++;len = p - packet;packet[1] = len-2; // don't include the 2 bytes of fixed header dataDEBUG_PRINTLN(F("MQTT subscription packet:"));DEBUG_PRINTBUFFER(buffer, len);return len;
}

3.1.4 connected —— 判断连接状态

函数说明

bool Adafruit_MQTT_Client::connected() {// Return true if connected, false if not connected.return client->connected();
}

3.1.5 disconnect —— 断开mqtt服务

函数说明

bool Adafruit_MQTT::disconnect() {// Construct and send disconnect packet.uint8_t len = disconnectPacket(buffer);if (! sendPacket(buffer, len))DEBUG_PRINTLN(F("Unable to send disconnect packet"));return disconnectServer();}

disconnectServer 方法:

bool Adafruit_MQTT_Client::disconnectServer() {// Stop connection if connected and return success (stop has no indication of// failure).if (client->connected()) {client->stop();}return true;
}

3.2 如何订阅主题

3.2.1 Adafruit_MQTT_Subscribe —— 创建主题

函数说明

// 类定义
class Adafruit_MQTT_Subscribe {public:Adafruit_MQTT_Subscribe(Adafruit_MQTT *mqttserver, const char *feedname, uint8_t q=0);void setCallback(SubscribeCallbackUInt32Type callb);void setCallback(SubscribeCallbackDoubleType callb);void setCallback(SubscribeCallbackBufferType callb);void setCallback(AdafruitIO_MQTT *io, SubscribeCallbackIOType callb);void removeCallback(void);const char *topic;uint8_t qos;uint8_t lastread[SUBSCRIPTIONDATALEN];// Number valid bytes in lastread. Limited to SUBSCRIPTIONDATALEN-1 to// ensure nul terminating lastread.uint16_t datalen;SubscribeCallbackUInt32Type callback_uint32t;SubscribeCallbackDoubleType callback_double;SubscribeCallbackBufferType callback_buffer;SubscribeCallbackIOType     callback_io;AdafruitIO_MQTT *io_mqtt;private:Adafruit_MQTT *mqtt;
};/*** 创建主题对象* @param mqttserver 主题对应的mqtt服务器* @param feed 主题名字* @param q 主题等级*/
Adafruit_MQTT_Subscribe::Adafruit_MQTT_Subscribe(Adafruit_MQTT *mqttserver,const char *feed, uint8_t q) {mqtt = mqttserver;topic = feed;qos = q;datalen = 0;callback_uint32t = 0;callback_buffer = 0;callback_double = 0;callback_io = 0;io_mqtt = 0;
}/*** 以下是三个回调函数 可以设置也可以不设置*/
void Adafruit_MQTT_Subscribe::setCallback(SubscribeCallbackUInt32Type cb) {callback_uint32t = cb;
}void Adafruit_MQTT_Subscribe::setCallback(SubscribeCallbackDoubleType cb) {callback_double = cb;
}void Adafruit_MQTT_Subscribe::setCallback(SubscribeCallbackBufferType cb) {callback_buffer = cb;
}void Adafruit_MQTT_Subscribe::setCallback(AdafruitIO_MQTT *io, SubscribeCallbackIOType cb) {callback_io = cb;io_mqtt= io;
}void Adafruit_MQTT_Subscribe::removeCallback(void) {callback_uint32t = 0;callback_buffer = 0;callback_double = 0;callback_io = 0;io_mqtt = 0;
}

3.2.2 subscribe —— 订阅主题消息

函数说明

/*** 注册订阅主题* @param Adafruit_MQTT_Subscribe 具体主题对象*/
bool Adafruit_MQTT::subscribe(Adafruit_MQTT_Subscribe *sub) {uint8_t i;// see if we are already subscribedfor (i=0; i<MAXSUBSCRIPTIONS; i++) {if (subscriptions[i] == sub) {DEBUG_PRINTLN(F("Already subscribed"));return true;}}if (i==MAXSUBSCRIPTIONS) { // add to subscriptionlistfor (i=0; i<MAXSUBSCRIPTIONS; i++) {if (subscriptions[i] == 0) {DEBUG_PRINT(F("Added sub ")); DEBUG_PRINTLN(i);subscriptions[i] = sub;return true;}}}DEBUG_PRINTLN(F("no more subscription space :("));return false;
}

3.2.3 unsubscribe—— 取消订阅主题消息

函数说明

/*** 取消订阅主题* @param sub 某一个主题*/
bool Adafruit_MQTT::unsubscribe(Adafruit_MQTT_Subscribe *sub) {uint8_t i;// see if we are already subscribedfor (i=0; i<MAXSUBSCRIPTIONS; i++) {if (subscriptions[i] == sub) {DEBUG_PRINTLN(F("Found matching subscription and attempting to unsubscribe."));// Construct and send unsubscribe packet.uint8_t len = unsubscribePacket(buffer, subscriptions[i]->topic);// sending unsubscribe failedif (! sendPacket(buffer, len))return false;// if QoS for this subscription is 1 or 2, we need// to wait for the unsuback to confirm unsubscriptionif(subscriptions[i]->qos > 0 && MQTT_PROTOCOL_LEVEL > 3) {// wait for UNSUBACKlen = readFullPacket(buffer, MAXBUFFERSIZE, CONNECT_TIMEOUT_MS);DEBUG_PRINT(F("UNSUBACK:\t"));DEBUG_PRINTBUFFER(buffer, len);if ((len != 5) || (buffer[0] != (MQTT_CTRL_UNSUBACK << 4))) {return false;  // failure to unsubscribe}}subscriptions[i] = 0;return true;}}// subscription not found, so we are unsubscribedreturn true;}

3.3 如何发布主题消息

3.3.1 Adafruit_MQTT_Publish—— 构建发布主题

函数说明

// 类定义
class Adafruit_MQTT_Publish {public:Adafruit_MQTT_Publish(Adafruit_MQTT *mqttserver, const char *feed, uint8_t qos = 0);bool publish(const char *s);bool publish(double f, uint8_t precision=2);  // Precision controls the minimum number of digits after decimal.// This might be ignored and a higher precision value sent.bool publish(int32_t i);bool publish(uint32_t i);bool publish(uint8_t *b, uint16_t bLen);private:Adafruit_MQTT *mqtt;const char *topic;uint8_t qos;
};// 类实现
/*** @param mqttserver Adafruit_MQTT 实例* @param feed 主题* @param q  主题质量等级*/
Adafruit_MQTT_Publish::Adafruit_MQTT_Publish(Adafruit_MQTT *mqttserver,const char *feed, uint8_t q) {mqtt = mqttserver;topic = feed;qos = q;
}// 以下方法用于发布消息,最终还是调用 Adafruit_MQTT的publish方法
bool Adafruit_MQTT_Publish::publish(int32_t i) {char payload[12];ltoa(i, payload, 10);return mqtt->publish(topic, payload, qos);
}bool Adafruit_MQTT_Publish::publish(uint32_t i) {char payload[11];ultoa(i, payload, 10);return mqtt->publish(topic, payload, qos);
}bool Adafruit_MQTT_Publish::publish(double f, uint8_t precision) {char payload[41];  // Need to technically hold float max, 39 digits and minus sign.dtostrf(f, 0, precision, payload);return mqtt->publish(topic, payload, qos);
}bool Adafruit_MQTT_Publish::publish(const char *payload) {return mqtt->publish(topic, payload, qos);
}//publish buffer of arbitrary length
bool Adafruit_MQTT_Publish::publish(uint8_t *payload, uint16_t bLen) {return mqtt->publish(topic, payload, bLen, qos);
}

3.3.2 publish —— 发布消息

函数说明

bool Adafruit_MQTT::publish(const char *topic, const char *data, uint8_t qos) {return publish(topic, (uint8_t*)(data), strlen(data), qos);
}/*** 发布消息* @param topic 主题* @param data 消息内容* @param blen内容长度* @param qos 消息质量等级* @return 返回是否发送成功*/
bool Adafruit_MQTT::publish(const char *topic, uint8_t *data, uint16_t bLen, uint8_t qos) {// 构建发布报文.uint16_t len = publishPacket(buffer, topic, data, bLen, qos);// 发送报文if (!sendPacket(buffer, len))return false;// If QOS level is high enough verify the response packet.if (qos > 0) {// 读取响应信息len = readFullPacket(buffer, MAXBUFFERSIZE, PUBLISH_TIMEOUT_MS);DEBUG_PRINT(F("Publish QOS1+ reply:\t"));DEBUG_PRINTBUFFER(buffer, len);if (len != 4)return false;if ((buffer[0] >> 4) != MQTT_CTRL_PUBACK)return false;uint16_t packnum = buffer[2];packnum <<= 8;packnum |= buffer[3];// we increment the packet_id_counter right after publishing so inc here too to matchpacknum++;if (packnum != packet_id_counter)return false;}return true;
}

注意:

  • 我们可以调用 Adafruit_MQTT 的publish方法去发布消息
  • 也可以调用 Adafruit_MQTT_Publish 的publish方法去发布消息

3.4 保持连接(心跳)

3.4.1 ping —— 保持心跳

函数说明

/*** 保持心跳*/
bool Adafruit_MQTT::ping(uint8_t num = 1) {//flushIncoming(100);while (num--) {// 构建心跳报文.uint8_t len = pingPacket(buffer);// 发送报文if (!sendPacket(buffer, len))continue;// Process ping reply.len = processPacketsUntil(buffer, MQTT_CTRL_PINGRESP, PING_TIMEOUT_MS);if (buffer[0] == (MQTT_CTRL_PINGRESP << 4))return true;}return false;
}

3.5 主题消息获取和处理

3.5.1 readSubscription —— 获取主题消息

  • 此方法会把有消息响应的主题获取出来

函数说明

/*** 读取最近有响应的主题*/
Adafruit_MQTT_Subscribe *Adafruit_MQTT::readSubscription(int16_t timeout) {uint16_t i, topiclen, datalen;// Check if data is available to read.uint16_t len = readFullPacket(buffer, MAXBUFFERSIZE, timeout); // return one full packetif (!len)return NULL;  // No data available, just quit.DEBUG_PRINT("Packet len: "); DEBUG_PRINTLN(len); DEBUG_PRINTBUFFER(buffer, len);if (len<3) return NULL;if ((buffer[0] & 0xF0) != (MQTT_CTRL_PUBLISH) << 4) return NULL;// Parse out length of packet.topiclen = buffer[3];DEBUG_PRINT(F("Looking for subscription len ")); DEBUG_PRINTLN(topiclen);// Find subscription associated with this packet.for (i=0; i<MAXSUBSCRIPTIONS; i++) {if (subscriptions[i]) {// Skip this subscription if its name length isn't the same as the// received topic name.if (strlen(subscriptions[i]->topic) != topiclen)continue;// Stop if the subscription topic matches the received topic. Be careful// to make comparison case insensitive.if (strncasecmp((char*)buffer+4, subscriptions[i]->topic, topiclen) == 0) {DEBUG_PRINT(F("Found sub #")); DEBUG_PRINTLN(i);break;}}}if (i==MAXSUBSCRIPTIONS) return NULL; // matching sub not found ???uint8_t packet_id_len = 0;uint16_t packetid = 0;// Check if it is QoS 1, TODO: we dont support QoS 2if ((buffer[0] & 0x6) == 0x2) {packet_id_len = 2;packetid = buffer[topiclen+4];packetid <<= 8;packetid |= buffer[topiclen+5];}// zero out the old datamemset(subscriptions[i]->lastread, 0, SUBSCRIPTIONDATALEN);datalen = len - topiclen - packet_id_len - 4;if (datalen > SUBSCRIPTIONDATALEN) {datalen = SUBSCRIPTIONDATALEN-1; // cut it off}// extract out just the data, into the subscription object itselfmemmove(subscriptions[i]->lastread, buffer+4+topiclen+packet_id_len, datalen);subscriptions[i]->datalen = datalen;DEBUG_PRINT(F("Data len: ")); DEBUG_PRINTLN(datalen);DEBUG_PRINT(F("Data: ")); DEBUG_PRINTLN((char *)subscriptions[i]->lastread);if ((MQTT_PROTOCOL_LEVEL > 3) &&(buffer[0] & 0x6) == 0x2) {uint8_t ackpacket[4];// Construct and send puback packet.uint8_t len = pubackPacket(ackpacket, packetid);if (!sendPacket(ackpacket, len))DEBUG_PRINT(F("Failed"));}// return the valid matching subscriptionreturn subscriptions[i];
}

3.5.2 processPackets —— 获取主题消息

  • 此方法加入了延时的操作(延时,意味着阻塞),并且提供消息响应的处理
  • 此方法需要和 Adafruit_MQTT_Subscribe 的callback方法结合使用

函数说明

/*** 处理主题消息* @param timeout 超时时间*/
void Adafruit_MQTT::processPackets(int16_t timeout) {uint32_t elapsed = 0, endtime, starttime = millis();while (elapsed < (uint32_t)timeout) {// 获取主题消息内容Adafruit_MQTT_Subscribe *sub = readSubscription(timeout - elapsed);// 开始处理if (sub) {//Serial.println("**** sub packet received");if (sub->callback_uint32t != NULL) {// huh lets do the callback in integer modeuint32_t data = 0;data = atoi((char *)sub->lastread);//Serial.print("*** calling int callback with : "); Serial.println(data);sub->callback_uint32t(data);} else if (sub->callback_double != NULL) {// huh lets do the callback in doublefloat modedouble data = 0;data = atof((char *)sub->lastread);//Serial.print("*** calling double callback with : "); Serial.println(data);sub->callback_double(data);}else if (sub->callback_buffer != NULL) {// huh lets do the callback in buffer mode//Serial.print("*** calling buffer callback with : "); Serial.println((char *)sub->lastread);sub->callback_buffer((char *)sub->lastread, sub->datalen);}else if (sub->callback_io != NULL) {// huh lets do the callback in io mode//Serial.print("*** calling io instance callback with : "); Serial.println((char *)sub->lastread);((sub->io_mqtt)->*(sub->callback_io))((char *)sub->lastread, sub->datalen);}}// keep track over elapsed timeendtime = millis();if (endtime < starttime) {starttime = endtime; // looped around!")}elapsed += (endtime - starttime);}
}

3.5.2 处理主题消息内容

上面的两个方法是获取整体主题消息,那么要获取消息内容主要有两个方向:

  • Adafruit_MQTT_Subscribe 配置好callback方法,在callback方法中去处理消息内容
  • 利用好 readSubscription 去获取到 Adafruit_MQTT_Subscribe ,再通过 Adafruit_MQTT_Subscribe 的 lastread属性来获取消息内容

接下来看实例讲解吧。

3.6 辅助信息以及打印信息

  • 主要是辅助我们调试代码以及错误提示

3.6.1 connectErrorString —— 获取连接错误对应的信息

函数说明

/*** 获取错误code对应的信息*/
const __FlashStringHelper* Adafruit_MQTT::connectErrorString(int8_t code) {switch (code) {case 1: return F("The Server does not support the level of the MQTT protocol requested");case 2: return F("The Client identifier is correct UTF-8 but not allowed by the Server");case 3: return F("The MQTT service is unavailable");case 4: return F("The data in the user name or password is malformed");case 5: return F("Not authorized to connect");case 6: return F("Exceeded reconnect rate limit. Please try again later.");case 7: return F("You have been banned from connecting. Please contact the MQTT server administrator for more details.");case -1: return F("Connection failed");case -2: return F("Failed to subscribe");default: return F("Unknown error");}
}

3.6.1 MQTT_DEBUG 和 MQTT_ERROR

  • 主要用来是否开启 debug信息或者error信息的打印

源码说明

// Uncomment/comment to turn on/off debug output messages.
//#define MQTT_DEBUG
// Uncomment/comment to turn on/off error output messages.
#define MQTT_ERROR// Set where debug messages will be printed.
#define DEBUG_PRINTER Serial
// If using something like Zero or Due, change the above to SerialUSB// Define actual debug output functions when necessary.
#ifdef MQTT_DEBUG#define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); }#define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); }#define DEBUG_PRINTBUFFER(buffer, len) { printBuffer(buffer, len); }
#else#define DEBUG_PRINT(...) {}#define DEBUG_PRINTLN(...) {}#define DEBUG_PRINTBUFFER(buffer, len) {}
#endif#ifdef MQTT_ERROR#define ERROR_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); }#define ERROR_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); }#define ERROR_PRINTBUFFER(buffer, len) { printBuffer(buffer, len); }
#else#define ERROR_PRINT(...) {}#define ERROR_PRINTLN(...) {}#define ERROR_PRINTBUFFER(buffer, len) {}
#endif
  • 正常情况下,debug不打开,如果开发者需要显示详细内容,可以手动修改库文件或者自己手动加方法处理。

4.实例操作

4.1 官方实例 —— mqtt_esp8266

请把以下代码烧录进esp8266(主要是讲解,需要结合mqtt服务器才能成功展示):

4.1.1 实例源码

/***************************************************Adafruit MQTT Library ESP8266 ExampleMust use ESP8266 Arduino from:https://github.com/esp8266/ArduinoWorks great with Adafruit's Huzzah ESP board & Feather----> https://www.adafruit.com/product/2471----> https://www.adafruit.com/products/2821Adafruit invests time and resources providing this open source code,please support Adafruit and open-source hardware by purchasingproducts from Adafruit!Written by Tony DiCola for Adafruit Industries.MIT license, all text above must be included in any redistribution****************************************************/
#include <ESP8266WiFi.h>
#include "Adafruit_MQTT.h" // https://github.com/adafruit/Adafruit_MQTT_Library
#include "Adafruit_MQTT_Client.h" //https://github.com/adafruit/Adafruit_MQTT_Library/************************* WiFi Access Point *********************************/#define WLAN_SSID       "...your SSID..." // wifi账号
#define WLAN_PASS       "...your password..." // wifi密码/************************* Adafruit.io Setup *********************************/// 下面信息是mqtt服务器的配置信息 不过博哥没有该账号
#define AIO_SERVER      "io.adafruit.com"
#define AIO_SERVERPORT  1883                   // use 8883 for SSL
#define AIO_USERNAME    "...your AIO username (see https://accounts.adafruit.com)..."
#define AIO_KEY         "...your AIO key..."/************ Global State (you don't need to change this!) ******************/// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient client;
// or... use WiFiFlientSecure for SSL
//WiFiClientSecure client;// mqtt客户端对象
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);/****************************** Feeds ***************************************/
// 发布主题消息对象
// Setup a feed called 'photocell' for publishing.
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/photocell");// 订阅主题消息对象
// Setup a feed called 'onoff' for subscribing to changes.
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");/*************************** Sketch Code ************************************/// Bug workaround for Arduino 1.6.6, it seems to need a function declaration
// for some reason (only affects ESP8266, likely an arduino-builder bug).
void MQTT_connect();void setup() {Serial.begin(115200);delay(10);Serial.println(F("Adafruit MQTT demo"));// Connect to WiFi access point.Serial.println(); Serial.println();Serial.print("Connecting to ");Serial.println(WLAN_SSID);WiFi.begin(WLAN_SSID, WLAN_PASS);while (WiFi.status() != WL_CONNECTED) {delay(500);Serial.print(".");}Serial.println();Serial.println("WiFi connected");Serial.println("IP address: "); Serial.println(WiFi.localIP());// Setup MQTT subscription for onoff feed.mqtt.subscribe(&onoffbutton);
}uint32_t x=0;void loop() {// Ensure the connection to the MQTT server is alive (this will make the first// connection and automatically reconnect when disconnected).  See the MQTT_connect// function definition further below.MQTT_connect();// this is our 'wait for incoming subscription packets' busy subloop// try to spend your time hereAdafruit_MQTT_Subscribe *subscription;// 在5s内判断是否有订阅消息进来while ((subscription = mqtt.readSubscription(5000))) {// 判断是否是我们对应的主题if (subscription == &onoffbutton) {Serial.print(F("Got: "));// 打印主题信息内容Serial.println((char *)onoffbutton.lastread);}}// Now we can publish stuff!Serial.print(F("\nSending photocell val "));Serial.print(x);Serial.print("...");// 发布消息if (! photocell.publish(x++)) {Serial.println(F("Failed"));} else {Serial.println(F("OK!"));}// ping the server to keep the mqtt connection alive// NOT required if you are publishing once every KEEPALIVE seconds// 心跳请求if(! mqtt.ping()) {mqtt.disconnect();}}// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {int8_t ret;// Stop if already connected.if (mqtt.connected()) {return;}Serial.print("Connecting to MQTT... ");uint8_t retries = 3;// 连接mqtt服务器while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected// 连接错误时显示连接错误内容Serial.println(mqtt.connectErrorString(ret));Serial.println("Retrying MQTT connection in 5 seconds...");// 断开连接 重试三次mqtt.disconnect();delay(5000);  // wait 5 secondsretries--;if (retries == 0) {// basically die and wait for WDT to reset mewhile (1);}}Serial.println("MQTT Connected!");
}

4.2 官方实例2 —— mqtt_esp8266_callback

  • 此方式主要是讲解如何通过callback方法来获取我们需要的数据

4.2.1 实例源码

/***************************************************Adafruit MQTT Library ESP8266 ExampleMust use ESP8266 Arduino from:https://github.com/esp8266/ArduinoWorks great with Adafruit's Huzzah ESP board:----> https://www.adafruit.com/product/2471Adafruit invests time and resources providing this open source code,please support Adafruit and open-source hardware by purchasingproducts from Adafruit!Written by Tony DiCola for Adafruit Industries.MIT license, all text above must be included in any redistribution****************************************************/
#include <ESP8266WiFi.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"/************************* WiFi Access Point *********************************/#define WLAN_SSID       "network"
#define WLAN_PASS       "password"/************************* Adafruit.io Setup *********************************/#define AIO_SERVER      "io.adafruit.com"
#define AIO_SERVERPORT  1883
#define AIO_USERNAME    "user"
#define AIO_KEY         "key"/************ Global State (you don't need to change this!) ******************/// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient client;// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_USERNAME, AIO_KEY);/****************************** Feeds ***************************************/// Setup a feed called 'time' for subscribing to current time
Adafruit_MQTT_Subscribe timefeed = Adafruit_MQTT_Subscribe(&mqtt, "time/seconds");// Setup a feed called 'slider' for subscribing to changes on the slider
Adafruit_MQTT_Subscribe slider = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/slider", MQTT_QOS_1);// Setup a feed called 'onoff' for subscribing to changes to the button
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff", MQTT_QOS_1);/*************************** Sketch Code ************************************/int sec;
int min;
int hour;int timeZone = -4; // utc-4 eastern daylight time (nyc)void timecallback(uint32_t current) {// adjust to local time zonecurrent += (timeZone * 60 * 60);// calculate current timesec = current % 60;current /= 60;min = current % 60;current /= 60;hour = current % 24;// print hourif(hour == 0 || hour == 12)Serial.print("12");if(hour < 12)Serial.print(hour);elseSerial.print(hour - 12);// print minsSerial.print(":");if(min < 10) Serial.print("0");Serial.print(min);// print secondsSerial.print(":");if(sec < 10) Serial.print("0");Serial.print(sec);if(hour < 12)Serial.println(" am");elseSerial.println(" pm");}void slidercallback(double x) {Serial.print("Hey we're in a slider callback, the slider value is: ");Serial.println(x);
}void onoffcallback(char *data, uint16_t len) {Serial.print("Hey we're in a onoff callback, the button value is: ");Serial.println(data);
}void setup() {Serial.begin(115200);delay(10);Serial.println(F("Adafruit MQTT demo"));// Connect to WiFi access point.Serial.println(); Serial.println();Serial.print("Connecting to ");Serial.println(WLAN_SSID);WiFi.begin(WLAN_SSID, WLAN_PASS);while (WiFi.status() != WL_CONNECTED) {delay(500);Serial.print(".");}Serial.println();Serial.println("WiFi connected");Serial.println("IP address: "); Serial.println(WiFi.localIP());timefeed.setCallback(timecallback);slider.setCallback(slidercallback);onoffbutton.setCallback(onoffcallback);// Setup MQTT subscription for time feed.mqtt.subscribe(&timefeed);mqtt.subscribe(&slider);mqtt.subscribe(&onoffbutton);}uint32_t x=0;void loop() {// Ensure the connection to the MQTT server is alive (this will make the first// connection and automatically reconnect when disconnected).  See the MQTT_connect// function definition further below.MQTT_connect();// this is our 'wait for incoming subscription packets and callback em' busy subloop// try to spend your time here:mqtt.processPackets(10000);// ping the server to keep the mqtt connection alive// NOT required if you are publishing once every KEEPALIVE secondsif(! mqtt.ping()) {mqtt.disconnect();}
}// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {int8_t ret;// Stop if already connected.if (mqtt.connected()) {return;}Serial.print("Connecting to MQTT... ");uint8_t retries = 3;while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connectedSerial.println(mqtt.connectErrorString(ret));Serial.println("Retrying MQTT connection in 10 seconds...");mqtt.disconnect();delay(10000);  // wait 10 secondsretries--;if (retries == 0) {// basically die and wait for WDT to reset mewhile (1);}}Serial.println("MQTT Connected!");
}

4.3 玩转OneNET物联网平台之MQTT服务④ —— 远程控制LED(数量无限制)+ Android App控制

  • 参考 玩转OneNET物联网平台之MQTT服务④ —— 远程控制LED(数量无限制)+ Android App控制
  • 我们这里使用Adafruit_mqtt来代替pubsubclient

4.3.1 实例源码

主工程代码:

#include <ESP8266WiFi.h>
#include <Adafruit_MQTT.h>
#include <Adafruit_MQTT_Client.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
#include <Ticker.h>
#include "H_project.h"#define MAGIC_NUMBER 0xAAint state;
WiFiClient espClient;
// 订阅主题消息对象
Adafruit_MQTT_Client *mqtt = NULL;
Adafruit_MQTT_Subscribe *onoffLED = NULL;//声明方法
void initSystem();
void initOneNetMqtt();
void onoffcallback(char *payload, uint16_t length);
void saveConfig();
void loadConfig();
bool parseRegisterResponse();
void parseOneNetMqttResponse(char* payload);/*** 初始化*/
void setup() {initSystem();initOneNetMqtt();
}void loop() {ESP.wdtFeed();connectToOneNetMqtt();mqtt->processPackets(5000);if(!(mqtt->ping())) {mqtt->disconnect();}
}void initSystem(){int cnt = 0;Serial.begin (115200);Serial.println("\r\n\r\nStart ESP8266 MQTT");Serial.print("Firmware Version:");Serial.println(VER);Serial.print("SDK Version:");Serial.println(ESP.getSdkVersion());wifi_station_set_auto_connect(0);//关闭自动连接ESP.wdtEnable(5000);WiFi.disconnect();delay(100);WiFi.begin(ssid, password);while (WiFi.status() != WL_CONNECTED) {delay(500);cnt++;Serial.print(".");if(cnt>=40){cnt = 0;//重启系统delayRestart(1);}}pinMode(LED_BUILTIN, OUTPUT);loadConfig();//还没有注册if(strcmp(config.deviceid,DEFAULT_ID) == 0){int tryAgain = 0;while(!registerDeviceToOneNet()){Serial.print(".");delay(500);tryAgain++;if(tryAgain == 5){//尝试5次tryAgain = 0;//重启系统delayRestart(1);}}if(!parseRegisterResponse()){//重启系统delayRestart(1);while(1);}}
}void initOneNetMqtt(){if(mqtt != NULL){delete(mqtt);}if(onoffLED != NULL){delete(onoffLED); }/*** 参考OneNet mqtt连接报文* ClientIdentifier: 创建设备时得到的设备ID* UserName: 注册产品时,平台分配的产品ID* UserPassword: 为设备的鉴权信息(即唯一设备编号,SN),或者为apiKey*/mqtt = new Adafruit_MQTT_Client(&espClient, mqttServer, mqttPort, config.deviceid, PRODUCT_ID, API_KEY);onoffLED = new Adafruit_MQTT_Subscribe(mqtt, TOPIC);onoffLED->setCallback(onoffcallback);mqtt->subscribe(onoffLED);
}void onoffcallback(char *payload, uint16_t length) {Serial.print("Message arrived =");for (int i = 0; i < length; i++) {Serial.print((char)payload[i]);}Serial.println();parseOneNetMqttResponse((char *)payload);
}/** 保存参数到EEPROM
*/
void saveConfig()
{Serial.println("Save OneNet config!");Serial.print("deviceId:");Serial.println(config.deviceid);EEPROM.begin(150);uint8_t *p = (uint8_t*)(&config);for (int i = 0; i < sizeof(config); i++){EEPROM.write(i, *(p + i));}EEPROM.commit();
}/** 从EEPROM加载参数
*/
void loadConfig()
{EEPROM.begin(150);uint8_t *p = (uint8_t*)(&config);for (int i = 0; i < sizeof(config); i++){*(p + i) = EEPROM.read(i);}EEPROM.commit();if (config.magic != MAGIC_NUMBER){strcpy(config.deviceid, DEFAULT_ID);config.magic = MAGIC_NUMBER;saveConfig();Serial.println("Restore config!");}Serial.println("-----Read config-----");Serial.print("deviceId:");Serial.println(config.deviceid);Serial.println("-------------------");
}/*** 解析mqtt数据*/
void parseOneNetMqttResponse(char* payload){Serial.println("start parseOneNetMqttResponse");StaticJsonBuffer<100> jsonBuffer;// StaticJsonBuffer 在栈区分配内存   它也可以被 DynamicJsonBuffer(内存在堆区分配) 代替// DynamicJsonBuffer  jsonBuffer;JsonObject& root = jsonBuffer.parseObject(payload);// Test if parsing succeeds.if (!root.success()) {Serial.println("parseObject() failed");return ;}String deviceId = root["Did"];int status = root["sta"];if(strcmp(config.deviceid,deviceId.c_str()) == 0){if (status == 1) {digitalWrite(LED_BUILTIN, LOW);} else {digitalWrite(LED_BUILTIN, HIGH);}}
}/*** 解析注册返回结果*/
bool parseRegisterResponse(){Serial.println("start parseRegisterResponse");StaticJsonBuffer<200> jsonBuffer;// StaticJsonBuffer 在栈区分配内存   它也可以被 DynamicJsonBuffer(内存在堆区分配) 代替// DynamicJsonBuffer  jsonBuffer;JsonObject& root = jsonBuffer.parseObject(response);// Test if parsing succeeds.if (!root.success()) {Serial.println("parseObject() failed");return false;}int errno = root["errno"];if(errno !=0){Serial.println("register failed!");return false;}else{Serial.println("register sucess!");strcpy(config.deviceid, root["data"]["device_id"]);saveConfig();return true;}
}

测试效果与原来PubSubClient完全一致

完整代码在博主个人QQ群

5.总结

此篇可以理解为 玩转PubSubClient MQTT库的兄弟姐妹篇,都是讲解MQTT库的使用;
读者看哪个好用就用哪个。

单片机菜鸟的博客快速索引(快速找到你要的)

如果觉得有用,麻烦点赞收藏,您的支持是博主创作的动力。

共同学习成长QQ群 622368884,不喜勿加,里面有一大群志同道合的探路人

深入学习Arduino Adafruit_MQTT库(初学者不再惧怕Arduino MQTT)相关推荐

  1. arduino下载库出错_【arduino】DIY音乐播放器,arduino播放wav音乐,TRMpcm库测试及使用...

    微信关注 "DLGG创客DIY"设为"星标",重磅干货,第一时间送达. arduino特点库超多,想必大家都领教了,今天来分享一下之前玩过的TRMpcm库. 这 ...

  2. arduino温湿度计库文件_用ARDUINO开发板自制温湿度计(含WEMOS D1开发板环境安装)...

    ARDUINO从入门到创客带师第四弹 自制温湿度计(翻车) 咱想起以前实验室的墙上挂着的介绍说过法拉第之所谓伟大的原因是他不止把成功的经历写在科学日记里,也把翻车失败的经历写进去,因此咱对于咱翻车的经 ...

  3. Linux学习高手写给初学者的经验谈

    Linux学习高手写给初学者的经验谈 2008-04-01 15:02 现在好多的人开始接触电脑的时候,见到的应该是Windows98说实话,98 已经是一个很人性化,封装的很好的一个系统了一个对电脑 ...

  4. arduino下载库出错_arduino的I2C通讯 3:驱动1602液晶屏

    上个推送,我们学习了I2C功能的基础知识.而且知道了使用很多器件都需要安装库.本次,我们一起来做一个实例,用arduino驱动1602液晶屏 1602代表屏幕有16列,2行.传统的驱动方式占用了大量的 ...

  5. Python附带了大量的库 - 初学者如何学起

    Python附带了大量的库 - 称为标准库 - 它们涵盖了从Internet访问到文本处理等所有内容. 一个经常被问到的问题是你应该先学习哪个图书馆; 作为初学者 - 在面对超过100个条目的列表时, ...

  6. 【安卓学习之第三方库】异常上报 库(Crash搜集)

    █ [安卓学习之第三方库]异常上报 库 █ 相关文章: - ● [安卓学习之第三方库]库的使用2-jar类库的使用(以dom4j为例)和升级(以极光推送为例) █ 读前说明: - ● 本文通过学习别人 ...

  7. 5天不再惧怕多线程——第一天 尝试Thread

    5天不再惧怕多线程--第一天 尝试Thread 原本准备在mongodb之后写一个lucene.net系列,不过这几天用到多线程时才发现自己对多线程的了解少之又少,仅仅停留在lock上面, 故这几天看 ...

  8. 目前最好用的大规模强化学习算法训练库是什么?

    点击蓝字  关注我们 本文整理自知乎问答,仅用于学术分享,著作权归作者所有.如有侵权,请联系后台作删文处理. 本文精选知乎问题"目前最好用的大规模强化学习算法训练库是什么?"评论区 ...

  9. 【S操作】轻松优雅库移植解决方案,arduino库移植应对方案

    微信关注 "DLGG创客DIY" 设为"星标",重磅干货,第一时间送达. 为啥要用arduino?最重要的一个原因就是因为arduino完美的生态,即可以找到很 ...

最新文章

  1. 分享一个mysql 复杂查询的例子
  2. awk内建变量示例详解之NR、FNR、NF
  3. linux18.04下安装的jdk11.0.2
  4. V4L2框架分析学习二
  5. lempel ziv matlab,基于Python的LempelZiv算法的熵估计
  6. linux和windows输入法,关于输入法框架,这里有可以跨平台的linux和windows。。。。...
  7. PHP+MySql+PDO实现简单登录、注册
  8. 素数的有关性质(二)欧拉函数的一些定理证明与计算
  9. 第二篇:稳定性之如何有条不紊地应对风险?
  10. 为啥有的人能受穷,却不能吃苦?
  11. 代码安全_弱点(脆弱性)分析 CWE
  12. Qt QT的I/O流 QT输入输出
  13. Oracle物理的体系结构
  14. 一图看懂经典面试题——左外连接,右外连接,内连接的区别
  15. gomarket服务器位置,第一章 昂达V711双核版常见问题解答.pdf
  16. linux 下 g++编译程序时,-I(大写i) 与-L(大写l)-l(小写l) 的作用
  17. java操作excel表格(最简单的教程!一学即会)
  18. 2022考研肖秀荣精讲精练pdf电子版
  19. 第三章 分类模型-随机森林知识点详细总结
  20. Angular快速上手

热门文章

  1. MQTT 协议解析,java使用
  2. 如何储存拨发带分机号的电话
  3. opentext ETX 为全球团队提供远程访问软件
  4. 零售行业交易数据分析(3)——群组/同期群分析(留存率分析)
  5. 如何掌握HEC-RAS建模方法与涉河建设项目防洪评价报告编制
  6. 以ZPW-2000为例的轨道移频电路原理
  7. [JAVA]jdk下载
  8. 什么是pid控制算法_飞行控制PID算法——无人机飞控
  9. 【C语言程序设计】C语言生兔子问题!
  10. linux 命令打开u盘,Linux下U盘使用教程详解