抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

什么是MQTT

MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议

优点:

轻巧、开放、简单、规范,易于实现。

这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。

基本原理

在MQTT协议通讯中,有两个最为重要的角色。它们分别是服务端客户端

服务端

是消息传输的枢纽

客户端

客户端有多个,它们之间不能直接相互通信,中间使用服务端来处理消息分配。

客户端要获取某个主题信息,要向服务端进行主题 “订阅”,订阅后可以向服务端发送与主题相关的信息,其他客户端再从服务端获取信息。

image-20210810220807339

客户端连接服务端

分2个步骤

  1. 客户端向服务端发送请求数据包“CONNECT”(也叫报文),其中包含了连接请求的信息
  2. 服务端收到请求后,发送数据包“CONNACK”进行确认

CONNECT报文

这是该报文的信息:

MQTTCONNECT报文内容

clientID

clientId是MQTT客户端的唯一标识,MQTT服务端以此来识别客户端

cleanSession – 清除会话

表示如果客户端未能正确接收到数据时,服务端是否要对数据进行保存;

取值true表示保存,false表示不保存

注意:如果数据很重要,客户端没有正确接收到会造成严重后果,建议取值true,让服务端先把会话保留,待客户端能正常接收再发送。

反之如果数据不会重要,可以取值为false

keepAlive —— 间隔时间

用于服务端每隔多久就了解一下客户端是否与其保持连接的情况

CONNACK – 确认连接请求

MQTTCONNACK信息内容

sessionPresent

CONNACK报文的sessionPresent与CONNECT报文的cleanSession相互配合。

cleanSession=true时,sessionPresent应该配置为false,即不需要保存信息;反之sessionPresent应该配置为true,即需要保存会话信息。

总之,sessionPresent作用是客户端发送连接请求时,服务端告知客户端有没有保存报文信息。这个被服务端保存的报文信息是来自于上一次客户端连接时,服务端曾经发送此报文给客户端,但是发送后没有收到客户端接收确认。

returnCode —— 返回码

当服务端收到了客户端的连接请求后,会向客户端发送returnCode(返回码),用以说明连接情况

返回码 返回码描述
0 成功连接
1 连接被服务端拒绝,原因是不支持客户端的MQTT协议版本
2 连接被服务端拒绝,原因是不支持客户端标识符的编码。 可能造成此原因的是客户端标识符编码是UTF-8,但是服务端不允许使用此编码。
3 连接被服务端拒绝,原因是服务端不可用。 即,网络连接已经建立,但MQTT服务不可用。
4 连接被服务端拒绝,原因是用户名或密码无效。
5 连接被服务端拒绝,原因是客户端未被授权连接到此服务端。

ESP8266连接MQTT服务端

http://test.ranye-iot.net 是国内一个MQTT服务器平台地址,TCP端口为1883

接下来使用PubSubClient库(去GitHub下载zip包后通过ArduinoIDE添加即可)来实现MQTT物联网应用。

  1. 配置WiFi

  2. 创建mqtt客户端对象(间接创建)

    1
    2
    WiFiClient wifiClient;
    PubSubClient mqttClient(wifiClient);
  3. 配置客户端要连接哪个服务端(绑定服务端)

    1
    2
    3
    String mqttServer = "test.ranye-iot.net";
    const int port = 1883;
    mqttClient.setServer(mqttServer, port); //连接服务端
  4. 连接服务端

    1
    2
    3
    4
    //用设备的mac地址来生成唯一标识该设备的clientID
    String clientID = "esp8266-" + WiFi.macAddress();

    mqttClient.connect(clientId.c_str()) //连接成功返回true
  5. 连接成功后保持 “心跳”,否则…(可以尝试继续连接)

    1
    2
    3
    4
    5
    6
    7
    void loop() { 
    if (mqttClient.connected()) { // 如果开发板成功连接服务器
    mqttClient.loop(); // 保持客户端心跳
    } else { // 如果开发板未能成功连接服务器
    connectMQTTServer(); // 则尝试连接服务器
    }
    }

完整code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "rbook";
const char* password = "12345678";
const char* mqttServer = "test.ranye-iot.net";

// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

void setup() {
Serial.begin(9600);

//设置ESP8266工作模式为无线终端模式
WiFi.mode(WIFI_STA);

// 连接WiFi
connectWifi();

// 设置MQTT服务器和端口号
mqttClient.setServer(mqttServer, 1883);

// 连接MQTT服务器
connectMQTTServer();
}

void loop() {
if (mqttClient.connected()) { // 如果开发板成功连接服务器
mqttClient.loop(); // 保持客户端心跳
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}

void connectMQTTServer(){
// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
String clientId = "esp8266-" + WiFi.macAddress();

// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}

// ESP8266连接wifi
void connectWifi(){

WiFi.begin(ssid, password);

//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
}

评论