项目上需要使用MQTT协议进行数据上报,为了方便配置热点和MQTT服务器信息,写了个简易的页面用于实现通过ESP32的WiFi热点进行配网,实现 相关参数的本地设置。
1、配置文件相关代码段
1、用到的库
#include <WiFi.h>
//用于作为AP模式使用网页进行配网,设置MQTT信息
#include <WebServer.h>
#include <ESPmDNS.h>
#include <esp_wifi.h> //用于调用esp_wifi_restore(); 清除WiFi配置信息
#include <PubSubClient.h>
#include <Preferences.h>
2、热点及MQTT服务器信息
const char* AP_SSID = "LT0155_Config"; //热点名称
String wifi_ssid = "*****";
String wifi_pass = "*************";
/* 测试MQTT服务器 */
String broker_addr = "192.168.1.206"; //内网
//String broker_addr = "118.89.**.***"; //腾讯云服
String broker_Port = "1883";
String client_id = "tck-test-001";
String user_name = "";
String pass_word = "";
// MQTT订阅主题
const char* topic_up = "lt15500100010002/up";
const char* topic_down = "lt15500100010002/down";
String scanNetworksID = ""; //用于储存扫描到的WiFi
//配置网页页面,可以在notepads上格式化查看网页文件,并转换,方便阅读和操作
#define ROOT_HTML " <!DOCTYPE html> <html> <head> <title>LT0155 WiFi & MQTT Config</title> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> </head> <style type=\"text/css\"> .input { display: block; margin-top: 10px; } .input span { width: 100px; float: left; float: left; height: 36px; line-height: 36px; } .input input { height: 30px; width: 200px; } .btn { width: 120px; height: 35px; background-color: #000000; border: 0px; color: #ffffff; margin-top: 15px; margin-left: 100px; } </style> <body> <form method=\"GET\" action=\"connect\"> <label class=\"input\"><span>WiFi SSID</span><input type=\"text\" name=\"ssid\"></label> <label class=\"input\"><span>WiFi PASS</span><input type=\"text\" name=\"pass\"></label> <label class=\"input\"><span>Broker Add</span><input type=\"text\" name=\"add\"></label> <label class=\"input\"><span>Broker Port</span><input type=\"text\" name=\"port\"></label> <label class=\"input\"><span>Client id</span><input type=\"text\" name=\"clientid\"></label> <label class=\"input\"><span>User Name</span><input type=\"text\" name=\"username\"></label> <label class=\"input\"><span>Password</span><input type=\"text\" name=\"password\"></label> <input class=\"btn\" type=\"submit\" name=\"submit\" value=\"Submie\"> <p> <span>Nearby wifi: </P> </form> "
WebServer server(80);
WiFiClient espClient;
PubSubClient client(espClient);
2.1、页面实现效果如下:
3、业务流,整合在一个处理函数中,程序中已做必要注释,直接贴代码。本代码只是学习MQTT测试代码,记录下自己的学习过程,方便梳理MQTT的业务流程,实际使用中需根据业务需要进行处理。
void WiFi_SUB() {
static uint8_t tick_mqtt_pub;
if (tick_mqtt_pub < 5) tick_mqtt_pub++;
else tick_mqtt_pub = 0;
if (flag_wifi_onoff == 0) {
//未开启WiFi
WiFi.begin(); //开启WiFi
flag_wifi_onoff = 1; //置位标志位
flag_wifi_status = 0; //写零,准备重新连接wifi
//Serial.printf("wifi start ! = %d \r\n", wifi_count);
} else {
//wifi已经开启
if (flag_wifi_status == 1) {
//已连接WiFi
if (WiFi.status() == WL_CONNECTED) {
//Serial.printf("wifi connected ! = %d \r\n", wifi_count);
if (flag_mqtt_create == 0) {
flag_mqtt_create = 1;
client.setServer(broker_addr.c_str(), broker_Port.toInt()); // 设置MQTT服务器和端口
client.setCallback(callback); // 设置回调函数
} else {
//MQTT已创建
//如果未建立连接,重新连接
if (!client.connected()) {
flag_mqtt_status = 0;
//client.connect(client_id.c_str(), user_name.c_str(), pass_word.c_str())
//client.connect(mqtt_client_id)
if (client.connect(client_id.c_str(), user_name.c_str(), pass_word.c_str())) {
//Serial.println("connected");
client.subscribe(topic_down); // 订阅主题
//client.publish(topic_up, load_mes.c_str());
} else {
// Serial.print("failed, rc=");
// Serial.print(client.state());
//Serial.println(" retrying in 5 seconds");
}
} else {
flag_mqtt_status = 1;
}
client.loop(); // 处理MQTT消息
// 发布消息********************************************************
// if (tick_mqtt_pub == 0) {
// // char message[50];
// // snprintf(message, 50, "Hello from ESP32");
// client.publish(topic_up, status_mes.c_str());
// }
switch (flag_pub_list) {
case 1:
//发布登录信息:0x01
flag_pub_list = 0;
//先进行数据计算和校验位核对
mes_serial++;
if (mes_serial == 0) mes_serial++;
mes_check = 0;
result = "";
// for (size_t i = 0; i < arraySize; i++) {
// result += (char)myArray[i];
// }
for (int i = 0; i < sizeof(lt155_status); i++) {
mes_check += lt155_status[i]; //先计算信息内容的数据和;
//result += (char)lt155_status[i];
sprintf(mqtt_data_buf + i * 2, "%02X", lt155_status[i]);
}
mes_check += 15 + 1 + mes_serial;
//pub_mes = "78780F01" + result + (char)mes_serial + (char)mes_check + "0D0A";
sprintf(mqtt_pub_buf, "78780F01%s%02X%02X0D0A", mqtt_data_buf, mes_serial, mes_check);
client.publish(topic_up, mqtt_pub_buf);
//保存下数据
preferences.begin("myApp", false); //如果未创建 myApp 数据,则创建
//preferences.putString("lt155_status", lt155_status);
preferences.putBytes("lt155_status", lt155_status, sizeof(lt155_status));
preferences.end();
break;
case 2:
//发布状态信息 : 0X20
flag_pub_list = 0;
//先进行数据计算和校验位核对
mes_serial++;
if (mes_serial == 0) mes_serial++;
mes_check = 0;
result = "";
//String result;
// for (size_t i = 0; i < arraySize; i++) {
// result += (char)myArray[i];
// }
pure_life = (uint8_t) 100*(1-gear_all_pure/5000);
lt155_status[2] = pure_life;
lt155_status[3] = 0;
lt155_status[4] = random(20, 46);
//uint16_t buf_stall_pure = 100 * stall_pure;
lt155_status[5] = (100 * stall_pure) / 256; //高位
lt155_status[6] = (100 * stall_pure) % 256; //低位
for (int i = 0; i < sizeof(lt155_status); i++) {
mes_check += lt155_status[i]; //先计算信息内容的数据和;
//result += (char)lt155_status[i];
sprintf(mqtt_data_buf + i * 2, "%02X", lt155_status[i]);
}
mes_check += 15 + 0x20 + mes_serial;
sprintf(mqtt_pub_buf, "78780F02%s%02X%02X0D0A", mqtt_data_buf, mes_serial, mes_check); //临时改0x02作为状态包
client.publish(topic_up, mqtt_pub_buf);
// pub_mes = "78780F20" + result + (char)mes_serial + (char)mes_check + "0D0A";
// client.publish(topic_up, pub_mes.c_str());
//保存下数据
preferences.begin("myApp", false); //如果未创建 myApp 数据,则创建
//preferences.putString("lt155_status", lt155_status);
preferences.putBytes("lt155_status", lt155_status, sizeof(lt155_status));
preferences.end();
break;
case 3:
//发布混温水信息:0x30
flag_pub_list = 0;
usetimes_normal++;
//先进行数据计算和校验位核对
//开水次数:
lt155_normal_w[5] = usetimes_normal / 256;
lt155_normal_w[6] = usetimes_normal % 256;
//累计总用水量
gear_all_normal += gear_cur_normal;
lt155_normal_w[9] = (int)gear_all_normal / 256;
lt155_normal_w[10] = (int)gear_all_normal % 256;
mes_serial++;
if (mes_serial == 0) mes_serial++;
mes_check = 0;
result = "";
//String result;
// for (size_t i = 0; i < arraySize; i++) {
// result += (char)myArray[i];
// }
for (int i = 0; i < sizeof(lt155_normal_w); i++) {
mes_check += lt155_normal_w[i]; //先计算信息内容的数据和;
//result += (char)lt155_normal_w[i];
sprintf(mqtt_data_buf + i * 2, "%02X", lt155_normal_w[i]);
}
mes_check += 15 + 0x30 + mes_serial;
sprintf(mqtt_pub_buf, "78780F30%s%02X%02X0D0A", mqtt_data_buf, mes_serial, mes_check);
client.publish(topic_up, mqtt_pub_buf);
// pub_mes = "78780F30" + result + (char)mes_serial + (char)mes_check + "0D0A";
// client.publish(topic_up, pub_mes.c_str());
//保存下数据
preferences.begin("myApp", false); //如果未创建 myApp 数据,则创建
//preferences.putString("lt155_normal_w", lt155_normal_w);
preferences.putBytes("lt155_normal_w", lt155_normal_w, sizeof(lt155_normal_w));
preferences.end();
break;
case 4:
//发布净水信息:0x31
flag_pub_list = 0;
usetimes_pure++;
//开水次数:
lt155_pure_w[5] = usetimes_pure / 256;
lt155_pure_w[6] = usetimes_pure % 256;
//累计总用水量
gear_all_pure += gear_cur_pure;
lt155_pure_w[9] = (int)gear_all_pure / 256;
lt155_pure_w[10] = (int)gear_all_pure % 256;
lt155_pure_w[9] = 256 * lt155_pure_w[3];
if (lt155_pure_w[10] + lt155_pure_w[4] < 256) {
//直接累加
lt155_pure_w[10] += lt155_pure_w[4];
} else {
lt155_pure_w[9]++;
lt155_pure_w[10] += lt155_pure_w[4];
}
//先进行数据计算和校验位核对
mes_serial++;
if (mes_serial == 0) mes_serial++;
mes_check = 0;
result = "";
//String result;
// for (size_t i = 0; i < arraySize; i++) {
// result += (char)myArray[i];
// }
for (int i = 0; i < sizeof(lt155_pure_w); i++) {
mes_check += lt155_pure_w[i]; //先计算信息内容的数据和;
sprintf(mqtt_data_buf + i * 2, "%02X", lt155_pure_w[i]);
// result += (char)lt155_pure_w[i];
}
mes_check += 15 + 0x31 + mes_serial;
sprintf(mqtt_pub_buf, "78780F31%s%02X%02X0D0A", mqtt_data_buf, mes_serial, mes_check);
client.publish(topic_up, mqtt_pub_buf);
// pub_mes = "78780F31" + result + (char)mes_serial + (char)mes_check + "0D0A";
// client.publish(topic_up, pub_mes.c_str());
preferences.begin("myApp", false); //如果未创建 myApp 数据,则创建
//preferences.putString("lt155_pure_w", lt155_pure_w);
preferences.putBytes("lt155_pure_w", lt155_pure_w, sizeof(lt155_pure_w));
preferences.end();
break;
case 5:
//发布心跳信息
flag_pub_list = 0;
break;
default:
break;
}
}
} else {
flag_wifi_status == 0; //切换状态为未连接
//wifi_count = 0; //准备重新连接
//Serial.printf("wifi discinnected ! = %d \r\n", wifi_count);
}
client.loop(); // 处理MQTT消息
} else {
//还未连接WiFi
if (wifi_count <= 20 && flag_wifi_status == 0) {
//先尝试等待WiFi连接,最多10次
wifi_count++;
if (WiFi.status() == WL_CONNECTED) {
//联网了
flag_wifi_status = 1; //状态切换为已连接
wifi_count = 10000;
//Serial.printf("wifi connected ! = %d \r\n", wifi_count);
} else {
//Serial.printf("try wifi ! = %d \r\n", wifi_count);
}
} else if (wifi_count < 1000) {
wifi_count++;
if (flag_wifi_status == 0) {
//未在配网模式下,先扫描周边的WiFi信息,并保存到scanNetworksID中,然后切换到配网模式
flag_wifi_status = 2; //切换到配网模式
//Serial.printf("scan WiFi start! = %d \r\n", wifi_count);
int n = WiFi.scanNetworks();
//Serial.printf("scan WiFi done ! = %d \r\n", wifi_count);
if (n == 0) {
//Serial.println("no networks found");
scanNetworksID = "no networks found";
} else {
// Serial.print(n);
// Serial.println(" networks found");
for (int i = 0; i < n; ++i) {
// Print SSID and RSSI for each network found
// Serial.print(i + 1);
// Serial.print(": ");
// Serial.print(WiFi.SSID(i));
// Serial.print(" (");
// Serial.print(WiFi.RSSI(i));
// Serial.print(")");
// Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*");
scanNetworksID += "<P>" + WiFi.SSID(i) + "</P>";
delay(10);
}
}
//Serial.println("");
WiFi.mode(WIFI_AP); //WiFi配置为AP热点模式
if (WiFi.softAP(AP_SSID, "")) {
//热点开启成功*****************
IPAddress myIP = WiFi.softAPIP();
//打印相关信息
// Serial.println("");
// Serial.print("Soft-AP IP address = ");
// Serial.println(myIP);
// Serial.println(String("MAC address = ") + WiFi.softAPmacAddress().c_str());
// Serial.println("waiting ...");
} else {
//开启热点失败************************
//Serial.println("WiFiAP Failed");
delay(3000);
ESP.restart(); //复位esp32
}
if (MDNS.begin("esp32")) {
//Serial.println("MDNS responder started"); //开启MDNS
}
//首页访问时发送
server.on("/", []() {
server.send(200, "text/html", ROOT_HTML + scanNetworksID + "</body></html>");
});
//连接触发后
server.on("/connect", []() {
server.send(200, "text/html", "<html><body><font size=\"10\">successd,wifi connecting...<br />Please close this page manually.</font></body></html>");
WiFi.softAPdisconnect(true); //关闭AP配网
//获取输入的WIFI账户和密码
wifi_ssid = server.arg("ssid");
wifi_pass = server.arg("pass");
broker_addr = server.arg("add");
broker_Port = server.arg("port");
client_id = server.arg("clientid");
user_name = server.arg("username");
pass_word = server.arg("password");
preferences.begin("myApp", false); //读写模式 true:只读
preferences.putString("broker_addr", broker_addr);
preferences.putString("broker_Port", broker_Port);
preferences.putString("client_id", client_id);
preferences.putString("user_name", user_name);
preferences.putString("pass_word", pass_word);
preferences.end();
server.close(); //关闭服务
WiFi.softAPdisconnect();
// Serial.println("WiFi Connect SSID:" + wifi_ssid + " PASS:" + wifi_pass);
//Serial.println("broker_addr:" + broker_addr + " broker_Port:" + broker_Port + " client_id:" + client_id + " user_name:" + user_name + " pass_word:" + pass_word);
WiFi.mode(WIFI_STA); // 切换为无线终端模式
WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str());
flag_wifi_status = 3; //配网成功,并启动重新联网
wifi_count = 10000;
});
server.begin(); //开启服务
} else if (flag_wifi_status == 2) {
//已经在配网模式下,无需处理
//Serial.printf("smartconfig... ! = %d \r\n", wifi_count);
} else if (flag_wifi_status == 3) {
//重新配网成功
if (WiFi.status() == WL_CONNECTED) {
//联网了
flag_wifi_status = 1; //状态切换为已连接
wifi_count = 10000;
}
}
} else {
//超时了
if (WiFi.status() == WL_CONNECTED) {
//联网了
flag_wifi_status = 1; //状态切换为已连接
wifi_count = 10000;
} else {
flag_wifi_status = 0; //状态切换为已连接
wifi_count = 10000;
}
}
}
}
}