0%

使用Arduino+ESP8266实现MQTT发布

分为以下部分:
1:接线并使用AT指令验证连接
2:使用软串口
3:搭建/配置/测试MQTT服务端
4:在Arduino实现MQTT的PUB客户端
5:总结


接线并使用AT指令验证连接

需要各种线+10k电阻*1
ESP8266的3V3/VCC 接到 3.3V
ESP8266的EN 串联一个10k电阻 接到3.3V
ESP8266的RX 接到 Arduino的RX0
ESP8266的TX 接到 Arduino的TX1
ESP8266的GND 接地

1
2
3
4
5
6
7
8
9
//验证程序
const int tx = 1;
const int rx = 0;
void setup() {
pinMode(rx,INPUT_PULLUP);
pinMode(tx,INPUT_PULLUP);
}
void loop() {
}

将Serial Monitor调整为Both NL&CR,115200 baud
输入AT,ESP8266蓝色灯光闪烁,返回OK
输入AT+GMR,返回版本等信息
如下:
在这里插入图片描述
至此,可以验证连接成功。

1
2
3
4
5
6
7
8
一些其他常用的AT命令:
AT+RST 重置wifi模块
AT+CWLAP 扫AP
AT+CWJAP=”SSID”,”PASSWORD” 连接到AP
AT+CWJAP=””,”” 与所有访问点断开连接
AT+CIFSR 显示获得的IP和MAC
AT+UART=9600,8,1,0,0 修改波特率等
AT+CWMODE= 设置工作模式,可有Station\AP\Station+AP三种

使用软串口

修改ESP8266的波特率为9600
假设将2,3分别作为RX,TX
则将ESP8266的RX与3(Arduino的TX)相连,反之亦如此
代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3);
void setup() {
Serial.begin(9600);
mySerial.begin(9600);
mySerial.println("AT+GMR");
}
void loop() {
if(wifiSerial.available()) {
Serial.write(wifiSerial.read());
}
if(Serial.available()){
wifiSerial.write(Serial.read());
}
}

之后打开Serial Monitor即可看到AT+GMR的执行结果了,也可以使用AT指令查看其他信息。


搭建/配置/测试MQTT服务端

服务器使用Ubuntu+mosquitto

1
2
3
4
5
6
7
8
9
10
11
apt-add-repository ppa:mosquitto-dev/mosquitto-ppa
apt-get update
apt-get install mosquitto
cd /etc/mosquitto/conf.d
touch myconfig.conf
vim myconfig.conf
echo allow_anonymous false >> myconfig.conf
echo password_file /etc/mosquitto/pwfile.txt >> myconfig.conf
echo port 1883 >> myconfig.conf
mosquitto_passwd -c /etc/mosquitto/pwfile.txt [username]
service mosquitto start

在云平台调整服务器的安全组,放行出入1883端口的数据
使用python测试服务是否正常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#sub.py
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, rc):
print("Connect result:" + str(rc))
client.subscribe("test_topic")
def on_message(client, userdata, msg):
print(msg.topic+":" +str(msg.payload))
client = mqtt.Client("admin_sub")
client.username_pw_set("[username]","[password]")
client.on_connect = on_connect
client.on_message = on_message
print("Connectting…")
client.connect("[IP]", 1883, 60)
client.loop_forever()
1
2
3
4
5
6
7
8
9
10
11
#pub.py
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, rc):
print("Connect result:" + str(rc))
def on_message(client, userdata, msg):
print(msg.topic + " " + str(msg.payload))
client = mqtt.Client("admin_pub")
client.on_connect = on_connect
client.on_message = on_message
client.connect('[IP]', 1883, 60)
client.publish('test_topic', payload='test_pub_content', qos=0)

可以连接/通讯,如图:
在这里插入图片描述


在Arduino实现MQTT的PUB客户端

网上能找到的资料都需要烧写ESP8266,因为怕写坏自己唯一的板子,所以实现了软串口+TCP协议的MQTT客户端,站在别人肩膀上先实现Pub功能。没有做安全保护0.0。
最终实现效果及代码:
在这里插入图片描述

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include <SoftwareSerial.h>

SoftwareSerial wifiSerial(2, 3);
bool on;

void setup()
{
Serial.begin(9600);
wifiSerial.begin(9600);
while (!Serial);
while (!wifiSerial);

wifiSerial.println("AT+RST");
on=false;
while(!on){
if(wifiSerial.find("OK")){
Serial.println("ESP8266 Resetting");
on = true;
}
}
on = false;
delay(5000); //Give enough time for ESP8266's reset

connect_wifi();
delay(1000);
pub_msg();
}

void loop() {
if(wifiSerial.available())
Serial.write(wifiSerial.read());
if(Serial.available()){
wifiSerial.write(Serial.read());
}
}

void connect_wifi(){
wifiSerial.println("AT+RST");
while(!on){
if(wifiSerial.find("ready")){
delay(1000);
on = true;
}
}
clear_serial();
on=false;

wifiSerial.println("AT+CWMODE=1");
wifiSerial.println("\"hausahan\",\"[password]\"");
delay(3000);
if(wifiSerial.find("OK")){
Serial.println("WIFI Connected!");
delay(1000);
clear_serial();
tcp_connect();
}
}

void tcp_connect(){
wifiSerial.println("AT+CIPSTART=\"TCP\",\"114.116.239.164\",1883");
delay(1000);
while(!on){
on = true;
if(wifiSerial.find("OK")){
Serial.println("TCP Connected!");
}
}
mqtt_connect();
}

void mqtt_connect(){
u8 mqttMessage[128]={0};
u8 packetLen;
u8 baseIndex = 0;
u8 clientIdLen = strlen("arduino_pub");
u8 UserNameLen = strlen("hausa");
u8 passwordLen = strlen("[password]");
packetLen = 16 + clientIdLen + UserNameLen + passwordLen;
mqttMessage[0] = 16;
mqttMessage[1] = packetLen - 2;
mqttMessage[3] = 4; // Protocol Name Length LSB
mqttMessage[4] = 77; // ASCII Code for M
mqttMessage[5] = 81; // ASCII Code for Q
mqttMessage[6] = 84; // ASCII Code for T
mqttMessage[7] = 84; // ASCII Code for T
mqttMessage[8] = 4; // MQTT Protocol version = 4
mqttMessage[9] = 130; // conn flags
mqttMessage[10] = 0; // Keep-alive Time Length MSB
mqttMessage[11] = 60; // Keep-alive Time Length LSB
mqttMessage[12] = (0xff00 & clientIdLen)>>8;// Client ID length MSB
mqttMessage[13] = 0xff & clientIdLen;
for(u8 i = 0; i < clientIdLen; i++){
mqttMessage[14 + i] = *((u8*)"arduino_pub" + i);
}

baseIndex = 14 + clientIdLen;
mqttMessage[baseIndex++] = (0xff00 & UserNameLen)>>8; //username length MSB
mqttMessage[baseIndex++] = 0xff & UserNameLen; //username length LSB
for(u8 i = 0; i < UserNameLen ; i++){
mqttMessage[baseIndex + i] = *((u8*)"hausa" + i);
}

baseIndex = 14 + clientIdLen + UserNameLen;
mqttMessage[baseIndex++] = (0xff00 & UserNameLen)>>8; //password length MSB
mqttMessage[baseIndex++] = 0xff & UserNameLen; //password length LSB
for(u8 i = 0; i < UserNameLen ; i++){
mqttMessage[baseIndex + i] = *((u8*)"[password]" + i);
}

send_tcp_package(mqttMessage, packetLen);
}

void pub_msg(){
u8 mqttMessage[100]={0};
u16 i,index=0;
u16 topicLen = strlen("test_topic");
u16 messageLen = strlen("arduino_say_hi");

mqttMessage[index++] = 48;
mqttMessage[index++] = 2 + topicLen + messageLen;
mqttMessage[index++] = (0xff00 & topicLen)>>8;
mqttMessage[index++] = 0xff & topicLen;
for(i = 0; i < topicLen; i++){
mqttMessage[index + i] = *((u8 *)"test_topic" + i);
}
index += topicLen;
for(i = 0; i < messageLen; i++){
mqttMessage[index + i] = *((u8*)"arduino_say_hi" + i);
}

send_tcp_package(mqttMessage, 4 + topicLen + messageLen);

}

void send_tcp_package(u8 *data,u16 len){
clear_serial();
wifiSerial.print("AT+CIPSEND=");
wifiSerial.println(len);
delay(500);
if(wifiSerial.find(">")){
for(u16 i=0; i<len; i++)
wifiSerial.write(data[i]);
wifiSerial.println();
delay(500);
}
}

void clear_serial(){
while(wifiSerial.read()>= 0);
while(Serial.read()>= 0);
}

总结

还是有很多让人疑惑的问题的,比如:
ESP8266很多指令执行后必须等几秒才能进行下一步操作。
也有收获:
每次编程后使用AT+RST进行重置并在烧写完成后复位Arduino能解决很多奇怪的问题。
更理解通信协议、协议栈、wireshark的使用、、、等知识了
下一步:
考虑多买几块8266,学习直接使用8266的方法,因为看起来好像很方便,并实现一个远程控制LED的Deeeeeemo。再然后怎么不做一套智能家居?:-P
:-)