# 前言
之前我们开发的 esp8266
程序都要在代码中将 wifi 的名称和密码全部定义好,然后烧写进芯片中,而且连接 wifi 成功后,自身得到的 ip 需要通过串口打印或者我们自己去无线路由器中查看才能知道.
有没有一种方式可以解决这 2 个问题,最好是可以选择一个 wifi 连接,并且可以有个界面输入 wifi 密码,连接成功后也可以自己返回 IP 号码被我们看到.
思考了一下,我们可以使用 esp8266 的 AP 模式,提供一个界面,同时扫描并显示 wifi 列表,选择相应的 wifi 并输入密码,连接成功后将 wifi 名称和密码再存储到闪存中,下次断电再上电后也可以自动连接.
想到就做到,花费了一早晨时间终于是完成了,代码如下:(实现过程略有点复杂)
# 代码
#include <ESP8266WiFi.h> | |
#include <ESP8266WebServer.h> | |
#include <ESP8266mDNS.h> | |
#include <Arduino_JSON.h> | |
#include <EEPROM.h> | |
#include <ESP8266WiFiMulti.h> | |
//a 写入字符串长度,b 是起始位,str 为要保存的字符串 | |
void set_String(int a,int b,String str){ | |
EEPROM.write(a, str.length());//EEPROM 第 a 位,写入 str 字符串的长度 | |
// 把 str 所有数据逐个保存在 EEPROM | |
for (int i = 0; i < str.length(); i++){ | |
EEPROM.write(b + i, str[i]); | |
} | |
EEPROM.commit(); | |
} | |
//a 位是字符串长度,b 是起始位 | |
String get_String(int a, int b){ | |
String data = ""; | |
// 从 EEPROM 中逐个取出每一位的值,并链接 | |
for (int i = 0; i < a; i++){ | |
data += char(EEPROM.read(b + i)); | |
} | |
return data; | |
} | |
const char* ssid = "ESP-Access-Point"; | |
const char* password = "00000000"; | |
const int led = 2; | |
const int RSSI_MAX =-50; | |
const int RSSI_MIN =-100; | |
JSONVar wifiList; | |
ESP8266WebServer server(80); | |
ESP8266WiFiMulti WiFiMulti; | |
WiFiClient espClient; | |
String myIP = ""; | |
void getList(){ | |
digitalWrite(led, 1); | |
String jsonStr = JSON.stringify(wifiList); | |
server.send(200, "application/json", jsonStr); | |
digitalWrite(led, 0); | |
} | |
void handleRoot(){ | |
const String Html = "<!DOCTYPE html>\ | |
<html>\ | |
<head>\ | |
<meta charset=\"utf-8\">\ | |
<title>Hello ESP8266!</title>\ | |
</head>\ | |
<body>\ | |
<h1>看到的这个网页为ESP8266提供<h1>\ | |
</body>\ | |
</html>"; | |
digitalWrite(led,1); | |
server.send(200,"text/html",Html); | |
digitalWrite(led,0); | |
} | |
void handleNotFound() { | |
digitalWrite(led, 0); | |
String message = "File Not Found\n\n"; | |
message += "URI: "; | |
message += server.uri(); | |
message += "\nMethod: "; | |
message += (server.method() == HTTP_GET) ? "GET" : "POST"; | |
message += "\nArguments: "; | |
message += server.args(); | |
message += "\n"; | |
for (uint8_t i = 0; i < server.args(); i++) { | |
message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; | |
} | |
server.send(404, "text/plain", message); | |
digitalWrite(led, 1); | |
} | |
int dBmtoPercentage(int dBm) | |
{ | |
int quality; | |
if(dBm <= RSSI_MIN) | |
{ | |
quality = 0; | |
} | |
else if(dBm >= RSSI_MAX) | |
{ | |
quality = 100; | |
} | |
else | |
{ | |
quality = 2 * (dBm + 100); | |
} | |
return quality; | |
} | |
// 选择 wifi 页面 | |
void handleInitGet(){ | |
const String Html1 = "<!DOCTYPE html>\ | |
<html>\ | |
<head>\ | |
<meta charset=\"utf-8\">\ | |
<title>连接WIFI!</title>\ | |
<style>\ | |
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\ | |
</style>\ | |
</head>\ | |
<body>\ | |
<h1>连接WIFI!<h1>\ | |
<form method=\"post\" enctype=\"application/x-www-form-urlencoded\" action=\"/set_wifi\">\ | |
<div>\ | |
<select name=\"ssid\">"; | |
String Html2 = ""; | |
// 循环 wifi 列表 | |
for(int i = 0;i<wifiList.length();i++){ | |
String ssid = wifiList[i]; | |
Html2 += "<option value=\""; | |
Html2 += ssid; | |
Html2 += "\">"; | |
Html2 += ssid; | |
Html2 += "</option>"; | |
} | |
const String Html3 = "</select>\ | |
</div>\ | |
<div>\ | |
<input type=\"password\" name=\"password\" value=\"\" placeholder=\"请输入wifi密码\"><br>\ | |
</div>\ | |
<div>\ | |
<input type=\"submit\" value=\"连接\">\ | |
</div>\ | |
</form>\ | |
</body>\ | |
</html>"; | |
digitalWrite(led,1); | |
server.send(200,"text/html",Html1+Html2+Html3); | |
digitalWrite(led,0); | |
} | |
void handleInitPost(){ | |
if(server.method() != HTTP_POST){ | |
digitalWrite(led, 1); | |
server.send(405, "text/plain", "Method Not Allowed"); | |
digitalWrite(led, 0); | |
}else{ | |
digitalWrite(led, 1); | |
String message = "POST form was:\n"; | |
String ssid = ""; | |
String password = ""; | |
for (uint8_t i = 0; i < server.args(); i++) { | |
message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; | |
// 判断是否是 ssid 和 password | |
if(server.argName(i) == "ssid"){ | |
ssid = server.arg(i); | |
}else if(server.argName(i) == "password"){ | |
password = server.arg(i); | |
} | |
} | |
String html = "<!DOCTYPE html>\ | |
<html>\ | |
<head>\ | |
<meta charset=\"utf-8\">\ | |
<title>连接WIFI中...</title>\ | |
<style>\ | |
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\ | |
</style>\ | |
</head>\ | |
<body>\ | |
<h1 style=\"color:#f69;\">连接WIFI:("; | |
html += ssid; | |
html += ")中...</h1>\ | |
<a href=\"/\" style=\"font-size:20px;\">返回上一页</a>\ | |
</body>\ | |
<script>\ | |
setInterval(function(){\ | |
var xhr = new XMLHttpRequest();\ | |
xhr.withCredentials = true;\ | |
xhr.addEventListener(\"readystatechange\", function() {\ | |
if(this.readyState === 4) {\ | |
alert(\"IP:\"+this.responseText);\ | |
}\ | |
});\ | |
xhr.open(\"GET\", \"http://192.168.4.1/ip\");\ | |
xhr.send();\ | |
},2000);\ | |
</script>\ | |
</html>"; | |
server.send(200, "text/html", html); | |
// 连接 wifi | |
int tryNum = 0; | |
WiFiMulti.addAP(ssid.c_str(),password.c_str()); | |
while(tryNum < 5 && WiFiMulti.run() != WL_CONNECTED){ | |
digitalWrite(led, 0); | |
Serial.print("."); | |
delay(500); | |
tryNum += 1; | |
digitalWrite(led, 1); | |
} | |
if(tryNum >= 5){ | |
Serial.println("wifi连接失败!"); | |
// 清空存储 | |
for (int i = 0; i < 512; i++) { EEPROM.write(i, 0); } | |
EEPROM.end(); | |
setAp(); | |
}else{ | |
Serial.println("wifi连接成功!"); | |
Serial.print("IP address: "); | |
Serial.print(WiFi.localIP()); | |
myIP = WiFi.localIP().toString(); | |
// 连接成功之后将 wifi 信息存储到闪存中 | |
set_String(0,2,ssid); | |
set_String(1,256,password); | |
setSta(); | |
} | |
digitalWrite(led, 0); | |
} | |
} | |
// 返回已连接成功的 ip | |
void handleGetIp(){ | |
digitalWrite(led, 0); | |
String jsonStr = "{\"ip\":\""+myIP+"\"}"; | |
server.send(200, "application/json", jsonStr); | |
digitalWrite(led, 1); | |
} | |
// 作为 sta 模式运行 | |
void setSta(){ | |
server.on("/ip",handleGetIp); | |
server.on("/",handleRoot); | |
server.onNotFound(handleNotFound); | |
server.begin(); | |
Serial.println("wifi已连接!"); | |
} | |
// 作为 ap 模式运行 | |
void setAp(){ | |
server.on("/ip",handleGetIp); | |
server.on("/",handleInitGet); | |
server.on("/set_wifi",handleInitPost); | |
server.onNotFound(handleNotFound); | |
server.begin(); | |
Serial.println("AP服务已启动!"); | |
} | |
void setup() { | |
pinMode(led,OUTPUT); | |
digitalWrite(led,1); | |
Serial.begin(115200); | |
EEPROM.begin(512); | |
String wifi_ssid = get_String(EEPROM.read(0),2); | |
String wifi_password = get_String(EEPROM.read(1),256); | |
// 判断有存储的 wifi 名称和密码,尝试连接该 wifi | |
int tryNum = 0; | |
if(wifi_ssid.length() && wifi_password.length()){ | |
Serial.print("链接wifi中:"); | |
Serial.print(wifi_ssid+":"+wifi_password); | |
WiFi.mode(WIFI_AP_STA); | |
WiFiMulti.addAP(wifi_ssid.c_str(),wifi_password.c_str()); | |
// 此处尝试链接 5 次 | |
while(tryNum < 5 && WiFiMulti.run() != WL_CONNECTED){ | |
Serial.print("."); | |
delay(500); | |
tryNum += 1; | |
} | |
//5 次以后如果还没有连接上则代表连接失败 | |
if(tryNum >= 5){ | |
Serial.println("wifi连接失败!"); | |
// 清空存储 | |
for (int i = 0; i < 512; i++) { EEPROM.write(i, 0); } | |
EEPROM.end(); | |
}else{ | |
Serial.println("wifi连接成功!"); | |
Serial.print("IP address: "); | |
Serial.print(WiFi.localIP()); | |
// 作为 sta 运行 | |
setSta(); | |
} | |
}else{ | |
// 没有存储的 wifi 和密码 | |
Serial.println(); | |
Serial.println("没有闪存,设置AP中..."); | |
// WiFi.mode(WIFI_AP); | |
WiFi.softAP(ssid,password); | |
IPAddress IP = WiFi.softAPIP(); | |
// 作为 AP 默认的 IP 为 192.168.4.1 | |
Serial.print("AP IP address: "); | |
Serial.print(IP); | |
if (MDNS.begin("esp8266")) { | |
Serial.println("MDNS responder started"); | |
} | |
// 作为 AP 运行 | |
setAp(); | |
// 扫描周围 wifi | |
int n = WiFi.scanNetworks(); | |
Serial.println("Wifi scan ended"); | |
if (n == 0) { | |
// 没有找到 wifi | |
Serial.println("no networks found"); | |
}else{ | |
// 找到 wifi | |
Serial.print(n); | |
Serial.println(" networks found!"); | |
for(int i = 0;i<n;++i){ | |
String wifiName = ""; | |
wifiName += WiFi.SSID(i); | |
Serial.print(WiFi.SSID(i));//wifi 名称 | |
Serial.print(WiFi.RSSI(i));//wifi 强度 | |
Serial.print("dBm ("); | |
Serial.print(dBmtoPercentage(WiFi.RSSI(i))); | |
Serial.print("% )"); | |
if(WiFi.encryptionType(i) == ENC_TYPE_NONE) | |
{ | |
Serial.println(" <<***OPEN***>>"); | |
}else{ | |
Serial.println(); | |
} | |
wifiList[i] = wifiName; | |
delay(10); | |
} | |
String jsonStr = JSON.stringify(wifiList); | |
Serial.println(jsonStr); | |
} | |
} | |
digitalWrite(led,0); | |
} | |
void loop() { | |
server.handleClient(); | |
MDNS.update(); | |
} |
# 测试
上电之后,用手机搜索名为 ESP-Access-Point
的 Wifi, 密码为 8 个 0, 链接上之后使用浏览器访问 192.168.4.1
, 成功显示如下页面
然后选择我们已知的 wifi, 输入密码,点击连接按钮,成功进入下一个页面
这里使用了 Ajax
去获取 ip 并且 alert 出来,我们就成功知道了连接 wifi 之后的 ip 是什么.