最近在使用 WeMos D1 mini 制作一个局域网开机模块,需要将 WiFi 信息设置为可配置,网上搜索良久都未发现满意的解决方案,最终在一个台湾上人的博客上,发现了一个相对比较友好的程序,博客原链接如下http://nhs-tw.blogspot.com/2015/11/step-by-step-esp8266-12-with-arduino_27.html,我将它整理了一下,并修改添加了部分注释。
代码
//声明
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
#include <ESP8266mDNS.h>
int EPPROM_Address = 512;
// WiFi,可以写入在这里预先输入自己最常用的 WiFi 信息,也可以设置为空字符串
String WiFi_SSID = "";
String WiFi_Password = "";
//Ap 设置热点信息
const char* AP_Host = "esp8266";
const char* AP_SSID = "Esp8266AP";
const char* AP_Password = "12345678";
// web
String webContent;
int webStatusCode;
String wifiScanContent;
ESP8266WebServer WebServer(80);
void DebugMessage(String str)
{
Serial.println(str);
}
void setup() {
Serial.begin(115200);
DebugMessage("The program startup...");
EEPROM.begin(EPPROM_Address);
readWiFiInfoFromEEPROM();
bool connectStatus = tryToConnectToWiFi();
if (connectStatus)
{
DebugMessage("WiFi connected, IP address: " + WiFi.localIP());
createConnectedWeb();
}
else
{
DebugMessage("Connect timed out, ESP8266 change to AP mode");
ScanNetwork();
SetupToAPMode();
connectStatus = tryToConnectToWiFi();
}
WebServer.begin();
delay(100);
}
void loop() {
WebServer.handleClient();
}
/********* 从存储器中读取 WiFi 信息 ***************/
void readWiFiInfoFromEEPROM()
{
//--- 从 EEPROM 中读取 WiFi 的 SSID 和密码
DebugMessage("--- Reading EEPROM SSID & PASSWORD");
//--- 读取 wifi ssid
WiFi_SSID = "";
for (int i = 0; i < 32; ++i)
{
WiFi_SSID += char(EEPROM.read(i));
}
delay(100);
//--- 读取 wifi 密码
WiFi_Password = "";
for (int i = 32; i < 96; ++i)
{
WiFi_Password += char(EEPROM.read(i));
}
delay(100);
//--- 打印 EEPROM 中存储的 WiFi 信息
DebugMessage("SSID NAME : " + WiFi_SSID);
DebugMessage("PASSWORD NAME : " + WiFi_Password);
DebugMessage("essid.length = " + WiFi_SSID.length());
}
/********* 尝试连接 WiFi ***************/
bool tryToConnectToWiFi()
{
DebugMessage("--- try to connect to ssid: " + WiFi_SSID + " with password: " + WiFi_Password);
WiFi.mode(WIFI_STA);
WiFi.begin(WiFi_SSID.c_str(), WiFi_Password.c_str());
delay(100);
// WiFi 连接需要时间,因此这里相当于持续检测 10 秒
int times = 0;
while ( times < 10 ) {
if (WiFi.status() == WL_CONNECTED) {
return true;
}
delay(1000);
times++;
}
return false;
}
/********* 设置为 AP 模式 ***************/
void SetupToAPMode() {
DebugMessage("--- Setup Ap Model");
WiFi.mode(WIFI_AP);
WiFi.softAP(AP_SSID, AP_Password, 6, 0);
createApModeWeb();
//貌似 dns 不生效
if (MDNS.begin(AP_Host))
{
MDNS.addService("http", "tcp", 80);
DebugMessage("MDNS responder started");
DebugMessage("You can now connect to http://" + String(AP_Host) + ".local");
}
}
/********* 创建 AP 模式网页 ***************/
void createApModeWeb()
{
//加载网页
WebServer.on("/", []() {
IPAddress ip = WiFi.softAPIP();
webContent = "<!DOCTYPE HTML>";
webContent += "<html>";
webContent += "<title>WiFi 信息设置</title>";
webContent += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">";
webContent += "<p>请输入 WiFi 信息</p>";
webContent += wifiScanContent;
// 代表插入一个空格, 重复5个就是插入5个空格
webContent += "<br>";
webContent += "<form method='get' action='setting'>";
webContent += "<table border=\"0\"><tr><td><label>SSID</label></td><td><input type=\"text\" placeholder=\"请输入你要连接的 SSID\" name='ssid' maxlength=32 size=64></td></tr>";
webContent += "<tr><td><label>PASSWORD</label></td><td><input type=\"text\" placeholder=\"请输入该 WiFi 密码\" name='pass' maxlength=64 size=64></td></tr></table>";
// webContent += "<input type=\"button\" value=\"重新扫描网络\" onclick=\"self.location.href='/rescannetwork'\">";
webContent += " <input type='reset' value=\"重设\"> <input type='submit' value=\"存储\"></form></html>";
WebServer.send(200, "text/html", webContent); //200代表服务器状态码为 OK, text/html代表用html网页类型, 不加这个会找不到网页
});
WebServer.on("/setting", []() {
WiFi_SSID = WebServer.arg("ssid");
WiFi_Password = WebServer.arg("pass");
if (WiFi_SSID.length() > 0 && WiFi_Password.length() > 0)
{
if (WiFi_SSID.length() <= 32 && WiFi_Password.length() <= 64)
{
DebugMessage("clearing eeprom");
for (int i = 0; i < 96; ++i) {
EEPROM.write(i, 0);
}
DebugMessage("SSID To Write: " + WiFi_SSID + " Password To Write: " + WiFi_Password);
for (int i = 0; i < WiFi_SSID.length(); ++i)
{
EEPROM.write(i, WiFi_SSID[i]);
}
for (int i = 0; i < WiFi_Password.length(); ++i)
{
EEPROM.write(32 + i, WiFi_Password[i]);
}
EEPROM.commit(); //EEPROM.write 并不会马上写入 EEPROM, 而是要执行 EEPROM.commit()才会实际的写入EEPROM
delay(50);
webContent = "存储成功, 请按 RESET 键重新开机!";
webStatusCode = 200;
}
else {
webContent = "<p>输入错误!!! SSID 允许的最大长度为 32,PASSWORD 允许的最大长度为 64, 按上一页重新输入</p>";
webContent += "<input type=\"button\" value=\"上一页\" onclick=\"self.location.href='/'\"></html>";
webStatusCode = 404;
DebugMessage("SSID length OR Password length too long");
}
}
else {
webContent = "<p>输入错误!!! SSID 和 PASSWORD 都不允许为空, 按上一页重新输入</p>";
webContent += "<input type=\"button\" value=\"上一页\" onclick=\"self.location.href='/'\"></html>";
webStatusCode = 404;
DebugMessage("SSID length Password length is empty");
}
WebServer.send(webStatusCode, "text/html", webContent);
});
}
/********* 扫描无线网络 ***************/
void ScanNetwork() {
DebugMessage("--- Scan WiFi");
WiFi.disconnect();
WiFi.mode(WIFI_STA);
delay(100);
int wifiCount = WiFi.scanNetworks();
if (wifiCount == 0)
DebugMessage("Scan Network Done...and No Any Networks Found!");
else
{
DebugMessage("Scan Network Done...and " + String(wifiCount) + " Networks Found!");
for (int i = 0; i < wifiCount; ++i)
{
// Print SSID and RSSI for each network found
DebugMessage(String(i + 1) + ": " + WiFi.SSID(i) + " (" + WiFi.RSSI(i) + ") Encryption Type:" + getEncryptionTypeString(WiFi.encryptionType(i))) ;
delay(100);
}
}
// 将 WiFi 信息构造成 html 格式
wifiScanContent = "<ol type=\"1\" start=\"1\">";
for (int i = 0; i < wifiCount; ++i)
{
// Print SSID and RSSI for each network found
wifiScanContent += "<table border=\"0\"><tr><td width=\"300px\">";
wifiScanContent += String(i + 1) + ". ";
wifiScanContent += WiFi.SSID(i);
wifiScanContent += " (";
wifiScanContent += WiFi.RSSI(i);
wifiScanContent += ")";
wifiScanContent += "</td><td width=\"200px\">";
byte encryption = WiFi.encryptionType(i);
wifiScanContent += getEncryptionTypeString(encryption);
wifiScanContent += "</td></tr>";
}
wifiScanContent += "</ol></table><br>";
}
String getEncryptionTypeString(byte type)
{
String str = "";
switch (type) {
case 2: str = "TKIP(WPA)"; break;
case 5: str = "WEP"; break;
case 4: str = "CCMP(WPA)"; break;
case 7: str = "NONE"; break;
case 8: str = "AUTO(WPA or WPA2)"; break;
default:
str = "";
}
return str;
}
void createConnectedWeb()
{
WebServer.on("/", []() {
webContent = "<!DOCTYPE HTML>";
webContent += "<html>";
webContent += "<title>WiFi 信息</title>";
webContent += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">";
webContent += "<p>当前 WiFi 信息</p>";
// webContent += wifiScanContent;
// 代表插入一个空格, 重复5个就是插入5个空格
webContent += "<br>";
webContent += "<table border=\"0\"><tr><td><label>当前 SSID: </label></td>";
webContent += "<td><label>" + WiFi_SSID + "</label></td></tr>";
webContent += "<tr><td><label>当前 IP 地址: </label></td>";
webContent += "<td><label>" + WiFi.localIP().toString() + "</label></td></tr></table>";
webContent += "</html>";
WebServer.send(200, "text/html", webContent);
});
}