**
ESP8266配置MQTT的相关函数
1. 连接wifi
1.1 有账号密码的网络连接
//ssid、password为手动输入的WiFi账号、密码
#include <ESP8266WiFi.h>
const char* ssid = "........";//wifi账号
const char* password = "........";//wifi秘密
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
//也可先配置网络,station的固定IP地址staticIP、网关地址gateway、子网掩码地址subnet。
//如:IPAddress staticIP(192,168,1,22);
//IPAddress gateway(192,168,1,9);
//IPAddress subnet(255,255,255,0);
//WiFi.config(staticIP,gateway,subnet);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
//改变随机数生成器的种子,有参数则生成相同的随机数
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
1.2 Samrtconfig配置WiFi
当不知道wifi账号密码或者不方便重新烧录程序时使用Samrtconfig配置WiFi,IOS连接wifi后采用Esptouch app输入密码使esp8266连接WiFi
#include <ESP8266WiFi.h>
void smartConfig()
{
WiFi.mode(WIFI_STA);
Serial.println("\r\nWait for Smartconfig");
delay(2000);
// 等待配网
WiFi.beginSmartConfig();
while (1)
{
Serial.print(".");
delay(500);
if (WiFi.smartConfigDone())
{
Serial.println("SmartConfig Success");
Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
WiFi.setAutoConnect(true); // 设置自动连接
break;
}
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
1.3 断电重连并智能配网(Smartconfig)
#include <ESP8266WiFi.h>
bool autoConfig()
{
WiFi.begin();
for (int i = 0; i < 20; i++)
{
int wstatus = WiFi.status();
if (wstatus == WL_CONNECTED)
{
Serial.println("AutoConfig Success");
Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
WiFi.printDiag(Serial);
return true;
//break;
}
else
{
Serial.print("AutoConfig Waiting......");
Serial.println(wstatus);
delay(1000);
}
}
Serial.println("AutoConfig Faild!" );
return false;
//WiFi.printDiag(Serial);
}
void smartConfig()
{
WiFi.mode(WIFI_STA);
Serial.println("\r\nWait for Smartconfig");
WiFi.beginSmartConfig();
while (1)
{
Serial.print(".");
if (WiFi.smartConfigDone())
{
Serial.println("SmartConfig Success");
Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
WiFi.setAutoConnect(true); // 设置自动连接
break;
}
delay(1000); // 这个地方一定要加延时,否则极易崩溃重启
}
}
void setup()
{
Serial.begin(115200);
if (!autoConfig())
{
Serial.println("Start module");
smartConfig();
}
}
void loop()
{
delay(1000);
Serial.println("loop");
}
1.4 web配网
通过先配置esp8266为AP模式建立一个开放网络,其他设备连接wifi后,网页进入设置的固定IP(IPAddress local_IP(192,168,4,1))页面,输入wif账号和密码,esp8266在转换为STA模式自动连接wifi。
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#define DEBUG
#ifdef DEBUG
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#else
//以下三个定义为调试定义
#define DebugBegin(baud_rate)
#define DebugPrintln(message)
#define DebugPrint(message)
#endif
const char* ap_ssid = "esp_webconfig";
const char* ap_password = "";//开放式网络
char sta_ssid[32] = {0};
char sta_password[64] = {0};
//网页界面配置
const char* webpage_html = "\
<!DOCTYPE html>\r\n\
<html lang='en'>\r\n\
<head>\r\n\
<meta charset='UTF-8'>\r\n\
<title>Document</title>\r\n\
</head>\r\n\
<body>\r\n\
<form name='input' action='/' method='POST'>\r\n\
wifi名称: <br>\r\n\
<input type='text' name='ssid'><br>\r\n\
wifi密码:<br>\r\n\
<input type='text' name='password'><br>\r\n\
<input type='submit' value='保存'>\r\n\
</form>\r\n\
</body>\r\n\
</html>\r\n\
";
//esp_webconfig wifi的信息
IPAddress local_IP(192,168,4,1);//IP地址
IPAddress gateway(192,168,4,1);//网关地址
IPAddress subnet(255,255,255,0);//子网掩码地址
//函数声明
void initApConfig();
void initWebServer();
void connectToWifi();
void handleRootPost();
void handleRoot();
void handleNotFound();
ESP8266WebServer server(80);
//除去用于DEBUG的打印命令,setup函数里主要有 initApConfig(); initWebServer();两个初始化函数
void setup(void) {
DebugBegin(115200);
DebugPrintln("");
DebugPrint("connect ap: ");
DebugPrintln(ap_ssid);
initApConfig();
DebugPrint("IP address: ");
DebugPrintln(WiFi.softAPIP());
initWebServer();
DebugPrintln("HTTP server started");
Serial.printf("Ready! Open http://%s in your browser\n",
WiFi.softAPIP().toString().c_str());
}
/**
* 初始化AP配置
*/
void initApConfig(){
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.softAP(ap_ssid, ap_password);
}
/**
* 初始化webserver配置
*/
void initWebServer(){
server.on("/", HTTP_GET, handleRoot);
server.on("/", HTTP_POST, handleRootPost);
server.onNotFound(handleNotFound);
server.begin();
}
/**
* 连接到WiFi,在web smartconfig配网中被放在web响应回调函数中。
*/
void connectToWifi(){
DebugPrintln("connectToWifi");
WiFi.disconnect();
WiFi.mode(WIFI_STA);
WiFi.begin(sta_ssid, sta_password);
int cnt = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(500);
cnt++;
Serial.print(".");
if(cnt>=40){
cnt = 0;
//重启系统
DebugPrintln("\r\nRestart now!");
ESP.restart();
}
}
DebugPrintln("connectToWifi Success!");
}
/**
* 处理web post请求
*/
void handleRootPost() {
DebugPrintln("handleRootPost");
if (server.hasArg("ssid")) {
DebugPrint("got ssid:");
strcpy(sta_ssid, server.arg("ssid").c_str());
DebugPrintln(sta_ssid);
} else {
DebugPrintln("error, not found ssid");
server.send(200, "text/html", "<meta charset='UTF-8'>error, not found ssid");
return;
}
if (server.hasArg("password")) {
DebugPrint("got password:");
strcpy(sta_password, server.arg("password").c_str());
DebugPrintln(sta_password);
} else {
DebugPrintln("error, not found password");
server.send(200, "text/html", "<meta charset='UTF-8'>error, not found password");
return;
}
server.send(200, "text/html", "<meta charset='UTF-8'>保存成功");
delay(2000);
//连接wifi
connectToWifi();
}
/**
* 处理web get请求
*/
void handleRoot() {
DebugPrintln("handleRoot");
server.send(200, "text/html", webpage_html);
}
void handleNotFound() {
String message = "File Not Found\n\n";
server.send(404, "text/plain", message);
}
void loop(void) {
/**
必须在loop循环中使用,检查有没有客户端设备通过网络向ESP8266网络服务器发送请求。
每一次handleClient`函数被调用时,ESP8266网络服务器都会检查一下是否有客户端发送HTTP请求。
loop函数里有类似delay一类的函数延迟程序运行,那么就一定要注意了。
如果handleClient函数长时间得不到调用,ESP8266网络服务器会因为无法经常检查HTTP客户端请求而导致服务器响应*变慢,严重的情况下,会导致服务器工作不稳定。
*/
server.handleClient();。
}
2. 配置MQTT服务
2.1 配置MQTT服务器和客户端
添加 PubSubClient.h 头文件,先配置客户端
/**
* 配置客户端
* @param client client实例,比如 wificlient
*/
PubSubClient& PubSubClient::setClient(Client& client){
this->_client = &client;
return *this;
}
再配置MQTT服务器,服务器配置在setup函数中,服务参数有(数组)、(IPAddress)、(字符串),另一个参数为端口号
PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port)
PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port)
PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port)
服务器和客户端具体配置代码(部分)
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
//wifi配置信息、mqtt服务器信息
const char* ssid = "........";//wifi账号
const char* password = "........";//wifi秘密
const char* mqtt_server = "broker.mqtt-dashboard.com";//mqtt服务器
//配置客户端函数参数一般选择wificlient
WiFiClient espClient;
//定义PubSubClient为client, 之后的函数编写都用client.****()
PubSubClient client(espClient);
/*
相关的其他函数
*/
void setup() {
//MQTT服务器配置函数在setup内
client.setServer(mqtt_server, 1883);
}
2.2 MQTT连接
使用 client.connected(),可在setup内一次设置,但一般在loop中做一个连接并检测的作用,配合使用的还有 reconnect()函数(需要自己配置)
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// 建立一个随机的服务器IP
String clientId = "ESP8266Client-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str())) {
Serial.println("connected");
// 连接后操作由具体而定,也可以做个重连标志位
client.publish("outTopic", "hello world");
client.subscribe("inTopic");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void loop() {
//重连机制
if (!client.connected()) {
reconnect();
}
//同 server.handleClient() 一样需要不断监听信息
client.loop();
}
2.3 客户端订阅主题
使用 client.subscribe()函数,参数有(const char* topic)、(const char* topic, uint8_t qos)
qos为MQTT消息等级
- QoS 0:“最多一次”,消息发布完全依赖底层 TCP/IP 网络。分发的消息可能丢失或重复。例如,这个等级可用于环境传感器数据,单次的数据丢失没关系,因为不久后还会有第二次发送。
- QoS 1:“至少一次”,确保消息可以到达,但消息可能会重复。
- QoS 2:“只有一次”,确保消息只到达一次。例如,这个等级可用在一个计费系统中,这里如果消息重复或丢失会导致不正确的收费。
boolean PubSubClient::subscribe(const char* topic)
boolean PubSubClient::subscribe(const char* topic, uint8_t qos)
//例如
client.subscribe("inTopic");
2.3.1 订阅回调处理函数
先定义处理回调函数 client.setCallback() 参数为处理函数,放在setup函数内,处理函数单独定义
PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE)
//例如
client.setCallback(callback);
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
// Switch on the LED if an 1 was received as first character
if ((char)payload[0] == '1') {
digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level
// but actually the LED is on; this is because
// it is active low on the ESP-01)
} else {
digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH
}
}
2.4 发布消息
由客户端向服务器对应主题发送消息,函数client.publish,参数主要为(主题,内容)、(主题、内容、内容长度)
boolean PubSubClient::publish(const char* topic, const char* payload)
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength)
/* 发布对应主题消息
* @param topic 主题
* @param payload 有效负载(F(xxx))
* @param retained 是否保持
*/
boolean PubSubClient::publish_P(const char* topic, const char* payload, boolean retained)
//例如
client.publish("outTopic", msg);