【ESP32日记005】实现通过WiFi热点进行配网,设置MQTT参数

项目上需要使用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;
        }
      }
    }
  }
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值