源码链接:
1. https://github.com/Leisure27/ESP32-WifiManager.git
2. https://gitee.com/leisure27/WifiManager.git
强制门户:连接上wifi后,自动打开一个网页
### EEPROM和Flash的区别
1. 擦除方式:EEPROM需要逐个字节地擦除,而Flash可以在大块中擦除,这使得Flash的擦除速度比EEPROM更快。
2. 存储密度:Flash的存储密度比EEPROM更高。Flash存储器通常比EEPROM大得多,并且可以存储更多的数据。
3. 电源管理:EEPROM和Flash存储器在断电时的表现也不同。EEPROM可以在断电时保持数据不变,而Flash需要定期刷新以防止数据丢失。
4. 使用寿命:Flash的擦除次数比EEPROM多。EEPROM的擦除次数通常为10万次左右,而Flash的擦除次数通常为100万次左右。
5. 成本:由于Flash的生产成本更低,因此它通常比EEPROM更便宜。
### EEPROM
* ROM:Read only memory,只读存储器,**断电仍可保存数据**
* PROM:可编程ROM
* EPROM:可擦除可编程ROM,通过紫外光的照射擦除原先的程序
* EEPROM:电可擦编程只读存储器(Electrically Erasable Programmable Read-Only Memory),通过电子擦除数据
* RAM:Random-access memory,随机存储器,断电数据丢失
* **特点:写的速度比较慢(一次最快为3.3~5ms),读的速度较快。写有次数限制(10万次左右),读无次数限制。**
#### 查看芯片EEPROM的大小
```c
#include
void setup() {
Serial.begin(9600);
}
void loop() {
int eepromSize = EEPROM.length();
Serial.print("EEPROM大小为:");
Serial.print(eepromSize);
Serial.println("字节");
delay(5000); // 每隔5秒更新一次
}
// ESP系列运行结果为0
```
esp32实际上没有eeprom, 使用flash模拟的
| Microcontroller | EEPROM |
| --------------------------- | ---------- |
| Atmega 328(Uno、Nano、Mini) | 1024 bytes |
| Atmega 168(Nano) | 512 bytes |
| Atmega 2560(Arduino Mega) | 4096 bytes |
* **每个地址占1byte,1byte = 8bit,数据范围:0~255**
* 字符串Arduino:大小为7个byte,一个字符为1byte。
* 汉字:大小为4 bytes,一个汉字为2个英文字符,也就是2 bytes
* 1K(1024 bytes)的EEPROM,可用地址为0~1023
#### EEPROM读写操作
* `EEPROM.begin(512)`:ESP8266/32使用Flash模拟出EEPROM的空间大小,最好为4的倍数
* `EEPROM.put()`:EEPROM.write()函数每次只能写入一个字节的数据到EEPROM。而大部分数据类型占用的字节数量都是超过1个字节的。如浮点型数据,整形数据等。EEPROM.put()函数允许我们向EEPROM写入多字节的数据。语法:`EEPROM.put(address, var)`
* EEPROM.commit():每次写操作完成后,进行提交,执行保存操作。功能相同 EEPROM.end();
* `EEPROM.get()`:EEPROM.read()函数每次只能读取一个字节的数据。而大部分数据类型占用的字节数量都是超过1个字节的,如浮点型数据,整形数据等。EEPROM.get()函数允许用户一次获取多个字节的数据。这就允许我们向EEPROM存储带有小数点的浮点型数据或整数型数据以及其它数据类型。语法:`EEPROM.get(address, var)`
#### EEPROM格式化
第一次使用时,先进行格式化
```c
#include
void setup()
{
EEPROM.begin(512);
// write a 0 to all 512 bytes of the EEPROM
for (int i = 0; i < 512; i++)
{
EEPROM.write(i, 0);
}
// turn the LED on when we're done
pinMode(13, OUTPUT);
digitalWrite(13, HIGH);
EEPROM.end(); // 同EEPROM.commit();
}
void loop()
{
}
```
读写基本测试
```c++
#include
#include
String ssid = "leisure";
String password = "shadow27";
String test;
void setup()
{
Serial.begin(9600);
EEPROM.begin(512);
EEPROM.put(10, ssid);
EEPROM.commit();
EEPROM.get(10, test);
}
void loop()
{
Serial.println(test);
delay(1000);
}
```
### LittleFS闪存文件系统
ESP32 LittleFS文件系统,在未来的某个版本中将取代SPIFFS文件系统。目前SPIFFS系统已停止维护更新。LittleFS支持文件目录,并且对于大多数操作来说速度更快。SPIFFS使用方法与LittleFS兼容。注:ESP32自带的SPIFFS.h文件管理系统是没有文件层级的,比如建立一个/doc/test.txt的文件,那么这个文件的名字就是/doc/test,而不是根目录下有个test.txt
* SPIFFS:Peripheral Interface Flash File System,外围接口闪存文件系统
#### 使用Platformio开发闪存文件系统
1. 在工程文件中创建一个data文件(与src同级),在里放需要的文件。
![](/images/创建闪存文件.png)
2. 编译并上传闪存文件:
![](/images/编译闪存文件.png)
3. 编译并上传整个工程文件
#### SPIFFS相关函数(LittleFS兼容)
* LittleFS.begin()、SPIFFS.begin(); // 开启SPIFFS
* SPIFFS.open("/index.html", "r") // 创建或打开文件,"r"代表读操作,"w"代表读操作
```c++
File dataFile = SPIFFS.open(file_name, "w");// 建立File对象用于向SPIFFS中的file对象(即/notes.txt)写入信息
// 此操作将会在文件系统中建立该文件。如果文件系统有该文件,则程序将会重新建立该文件,即原有文件信息将会被覆盖。
dataFile.println("Hello IOT World."); // 向dataFile写入字符串信息
dataFile.close(); // 完成文件写入后关闭文件
```
* SPIFFS.format() // 文件系统格式化,相当于U盘格式化
* SPIFFS.exists("/test.txt") // 判断有没有某个文件
* SPIFFS.remove("/test.txt"); //删除某个文件
* SPIFFS.rename("原文件名","新文件名"); //文件重命名
* SPIFFS.end(); //结束文件系统挂载
File类相关函数
* file.name(); //读取文件的名字,返回文件的名字,char* 字符串
* file.print(""); //往文件中写入内容
```c
参数: 要写入的字符串
返回值: 写入的char的个
size_t Print::print(const char *)
```
* file.println("xiongba,haha"); //往文件中写入一行内容
* file.printf("tizhong:%dKG",90);//写入内容
* file.find('!'); //在文件中寻找某个char或字符串
### JSON信息解析和建立
所用ArduinoJson库版本:5.13.5
采用ArduinoJson库进行Json数据处理:https://arduinojson.org/v5/assistant/
![](/images/2.png)
![](/images/1.png)
#### JSON信息解析
```c++
const size_t capacity = JSON_OBJECT_SIZE(2) + 40;
DynamicJsonBuffer jsonBuffer(capacity);
const char* json = "{\"ssid\":\"xxxxx\",\"password\":\"xxxxxxxxxxx\"}";
JsonObject& root = jsonBuffer.parseObject(json);
const char* ssid = root["ssid"]; // "xxxxx"
const char* password = root["password"]; // "xxxxxxxxxxx"
```
#### 构造JSON信息
```c++
const size_t capacity = JSON_OBJECT_SIZE(2);
DynamicJsonBuffer jsonBuffer(capacity);
JsonObject& root = jsonBuffer.createObject();
root["ssid"] = "xxxxx";
root["password"] = "xxxxxxxxxxx";
root.printTo(Serial);
```
#### 读写测试(正常)
```
#include
#include
#include
const char *ssid = "leisure";
const char *password = "shadow27";
void read()
{
/*读测试*/
File ConfigJson = LittleFS.open("/config.json", "r"); // 读取配置文件中的JSON信息
const size_t capacity = JSON_OBJECT_SIZE(2) + 80;
DynamicJsonBuffer jsonBuffer(capacity);
JsonObject &root = jsonBuffer.parseObject(ConfigJson);
const char *ssid1 = root["ssid"];
const char *password1 = root["password"];
Serial.print("ssid1: ");
Serial.println(ssid1);
Serial.print("password1: ");
Serial.println(password1);
}
void write()
{
/*向闪存文件中写数据*/
const size_t capacity = JSON_OBJECT_SIZE(2) + 40;
DynamicJsonBuffer jsonBuffer(capacity);
JsonObject &root = jsonBuffer.createObject();
root["ssid"] = ssid;
root["password"] = password;
String jsonCode;
root.printTo(jsonCode);
File wifiConfig = LittleFS.open("/config.json", "w");
wifiConfig.println(jsonCode); // 将数据写入config.json文件中
wifiConfig.close();
Serial.print("jsonCode: ");
Serial.println(jsonCode); // {"ssid":"leisure","password":"shadow27"}
}
void setup()
{
Serial.begin(9600);
LittleFS.begin();
write();
read();
}
void loop()
{
}
```
### 功能测试
![](images/3.jpg)
![](images/4.jpg)
### Bug记录与解决
问题:连接成功后,在程序中已设置`server.stop();`,可是还会一直执行loop()中的程序,不理解为什么会这样。
```c++
if (WiFi.status() == WL_CONNECTED)
{
Serial.println(F("wifi连接成功"));
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
server.stop();
}
```
```c++
void loop()
{
server.handleClient(); // 处理Web服务器的客户端连接请求(get、post)
dnsServer.processNextRequest(); // 处理DNS请求
delay(200);
}
```
![](images/bug1.png)
#### Bug解决方法
定义一个全局变量`Web_Status`,使用Web配网时,令其等于1,否则为0
```c++
void loop()
{
if (Web_Status == 1)
{
server.handleClient(); // 处理Web服务器的客户端连接请求(get、post)
dnsServer.processNextRequest(); // 处理DNS请求
delay(200);
}
else
{
}
}
```