参考教程:https://www.bilibili.com/video/BV1L7411c7jw/?spm_id_from=333.1387.favlist.content.click
六、ESP8266闪存文件系统应用
1、在网页中加载闪存文件系统中的文件
(1)程序流程示意图:

(2)下载至开发板的代码:
cpp
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>
#include <FS.h>
ESP8266WiFiMulti wifiMulti; // 建立ESP8266WiFiMulti对象
ESP8266WebServer esp8266_server(80); // 建立网络服务器对象,该对象用于响应HTTP请求。监听端口(80)
void setup() {
Serial.begin(9600); // 启动串口通讯
Serial.println("");
wifiMulti.addAP("Zevalin_Computer", "00114514");
wifiMulti.addAP("Zevalin_esp8266", "00114514");
Serial.println("Connecting ...");
int i = 0;
while (wifiMulti.run() != WL_CONNECTED) {
delay(1000);
Serial.print(i++); Serial.print(' ');
}
Serial.println('\n');
Serial.print("Connected to "); Serial.println(WiFi.SSID());
Serial.print("IP address:\t"); Serial.println(WiFi.localIP());
if(SPIFFS.begin()){ // 启动闪存文件系统
Serial.println("SPIFFS Started.");
}
else {
Serial.println("SPIFFS Failed to Start.");
}
esp8266_server.onNotFound(handleUserRequet); // 告知系统如何处理用户请求
esp8266_server.begin(); // 启动网站服务
Serial.println("HTTP server started");
}
void loop(void) {
esp8266_server.handleClient(); // 处理用户请求
}
// 处理用户浏览器的HTTP访问
void handleUserRequet() {
// 获取用户请求网址信息
String webAddress = esp8266_server.uri();
// 通过handleFileRead函数处理用户访问
bool fileReadOK = handleFileRead(webAddress);
// 如果在SPIFFS无法找到用户访问的资源,则回复404 (Not Found)
if (!fileReadOK){
esp8266_server.send(404, "text/plain", "404 Not Found");
}
}
bool handleFileRead(String path) { //处理浏览器HTTP访问
if (path.endsWith("/")) { // 如果访问地址以"/"为结尾
path = "/index.html"; // 将访问地址修改为/index.html便于SPIFFS访问
}
String contentType = getContentType(path); // 获取文件类型
if (SPIFFS.exists(path)) { // 如果访问的文件可以在SPIFFS中找到
File file = SPIFFS.open(path, "r"); // 尝试打开该文件
esp8266_server.streamFile(file, contentType);// 将该文件返回给浏览器
file.close(); // 关闭文件
return true; // 返回true
}
return false; // 如果文件未找到,则返回false
}
// 获取文件类型
String getContentType(String filename){
if(filename.endsWith(".htm")) return "text/html";
else if(filename.endsWith(".html")) return "text/html";
else if(filename.endsWith(".css")) return "text/css";
else if(filename.endsWith(".js")) return "application/javascript";
else if(filename.endsWith(".png")) return "image/png";
else if(filename.endsWith(".gif")) return "image/gif";
else if(filename.endsWith(".jpg")) return "image/jpeg";
else if(filename.endsWith(".ico")) return "image/x-icon";
else if(filename.endsWith(".xml")) return "text/xml";
else if(filename.endsWith(".pdf")) return "application/x-pdf";
else if(filename.endsWith(".zip")) return "application/x-zip";
else if(filename.endsWith(".gz")) return "application/x-gzip";
return "text/plain";
}
streamFile函数是ESP8266WebServer库中一个非常重要且高效的方法,它的核心作用是直接将一个文件(例如闪存中的文件)以流式(streaming)的方式作为HTTP响应的正文发送给客户端(比如浏览器),它有两个参数:第一个是已打开的File对象引用,指向想要发送的文件;第二个是一个字符串,指定了文件的MIME类型,告诉浏览器如何解析这个文件(例如,"text/html" 表示网页,"image/jpeg" 表示JPEG图片)
(3)闪存系统中的文件(文件的编写非本教程重点,具体可在前端相关教程中了解):

①taichi-maker.jpg文件(在根目录下的img文件夹中):

②clock.js文件(在根目录中):
javascript
/**
* 动态显示当前时间
*/
function showDateTime(){
var sWeek=new Array("日","一","二","三","四","五","六"); //声明数组存储一周七天
var myDate=new Date(); //获取当天日期
var sYear=myDate.getFullYear(); //获取年
var sMonth=myDate.getMonth()+1; //获取月
var sDate=myDate.getDate(); //获取日
var sDay=sWeek[myDate.getDay()]; //根据得到的数字星期,利用数组转化为星期
var h=myDate.getHours(); //获取小时
var m=myDate.getMinutes(); //获取分钟
var s=myDate.getSeconds(); //获取秒
//输入日期和星期
document.getElementById("date").innerHTML=(sYear+"年"+sMonth+"月"+sDate+"日"+"星期"+sDay+"<br/>");
h = formatTwoDigits(h); //格式化小时,如果不足两位在前面补0
m = formatTwoDigits(m); //格式化分钟,如果不足两位在前面补0
s = formatTwoDigits(s); //格式化秒钟后,如果不足两位在前面补0
//显示时间
document.getElementById("msg").innerHTML=(h+":"+m+":"+s+"<br/>");
setTimeout("showDateTime()",1000);//每秒执行一次showDateTime函数
}
window.onload=showDateTime;//在整个页面加载完成后执行此函数
//如果输入数是一位数,则在十位上补0
function formatTwoDigits(s) {
if (s<10)
return "0"+s;
else
return s;
}
③JavaScript.js文件(在根目录中):
javascript
setTimeout(function() {
let jsParagraph = document.createElement("p");
jsParagraph.textContent = "Hello from JavaScript.js!";
document.body.appendChild(jsParagraph);
}, 1000);
④main.css文件(在根目录中):
css
body {
font-family: sans-serif;
color: #444;
}
p.red {
color: red;
}
⑤index.html文件(在根目录中):
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="main.css">
<title>太极创客-零基础入门学用物联网教程</title>
<script type="text/javascript" src="clock.js"></script>
</head>
<body>
<center>
<a href="http://www.taichi-maker.com" target="_black"><img src="/img/taichi-maker.jpg" alt="太极创客"></a>
<h1>ESP8266 SPIFFS web server示例</h1>
<p>此页面用于演示如何通过ESP8266开发板的SPIFFS中读取文件并显示于浏览器中。</p>
<p>本教程可在太极创客网站免费获取。太极创客网址: <a href="http://www.taichi-maker.com" target="_black">www.taichi-maker.com</a> .
<p class="red">创建页面时可以使用css。这里的字体是红色,原因是使用了css。</p>
<!--显示时钟-->
<h3>页面支持JavaScript,以下时钟/日期使用JavaScript实现。</h3>
<h1 id="date"></h1>
<span id="msg" style="font-size: 30px;background-color: greenyellow;"></span>
</center>
</body>
</html>
2、通过网页控制ESP8266开发板引脚的输出电平
(1)程序流程示意图:

(2)下载至开发板的代码:
cpp
#include <ESP8266WiFi.h> // 本程序使用ESP8266WiFi库
#include <ESP8266WiFiMulti.h> // 本程序使用ESP8266WiFiMulti库
#include <ESP8266WebServer.h> // 本程序使用ESP8266WebServer库
#include <FS.h> // 本程序使用SPIFFS库
ESP8266WiFiMulti wifiMulti;
ESP8266WebServer esp8266_server(80);
void setup(){
Serial.begin(9600);
Serial.println("");
pinMode(LED_BUILTIN, OUTPUT); // 初始化NodeMCU控制板载LED引脚为OUTPUT
wifiMulti.addAP("Zevalin_Computer", "00114514");
wifiMulti.addAP("Zevalin_esp8266", "00114514");
Serial.println("Connecting ...");
int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(1000);
Serial.print(i++); Serial.print('.');
}
Serial.println('\n');
Serial.print("Connected to "); Serial.println(WiFi.SSID());
Serial.print("IP address:\t"); Serial.println(WiFi.localIP());
if(SPIFFS.begin()){ // 启动闪存文件系统
Serial.println("SPIFFS Started.");
}
else{
Serial.println("SPIFFS Failed to Start.");
}
esp8266_server.on("/LED-Control", handleLEDControl); // 告知系统如何处理/LED-Control请求
esp8266_server.onNotFound(handleUserRequest); // 告知系统如何处理其它用户请求
esp8266_server.begin(); // 启动网站服务
Serial.println("HTTP server started");
}
void loop(){
esp8266_server.handleClient(); //处理用户请求
}
// 处理/LED-Control请求
void handleLEDControl(){
bool ledStatus = digitalRead(LED_BUILTIN); // 此变量用于储存LED状态
ledStatus == HIGH ? digitalWrite(LED_BUILTIN, LOW) : digitalWrite(LED_BUILTIN, HIGH); // 点亮或者熄灭LED
esp8266_server.sendHeader("Location", "/LED.html");
esp8266_server.send(303);
}
// 处理用户浏览器的HTTP访问
void handleUserRequest(){
// 获取用户请求资源(Request Resource)
String reqResource = esp8266_server.uri();
Serial.print("reqResource: ");
Serial.println(reqResource);
// 通过handleFileRead函数处处理用户请求资源
bool fileReadOK = handleFileRead(reqResource);
// 如果在SPIFFS无法找到用户访问的资源,则回复404 (Not Found)
if (!fileReadOK){
esp8266_server.send(404, "text/plain", "404 Not Found");
}
}
bool handleFileRead(String resource) { //处理浏览器HTTP访问
if (resource.endsWith("/")){ // 如果访问地址以"/"为结尾
resource = "/index.html"; // 访问地址修改为/index.html便于SPIFFS访问
}
String contentType = getContentType(resource); // 获取文件类型
if (SPIFFS.exists(resource)){ // 如果访问的文件可以在SPIFFS中找到
File file = SPIFFS.open(resource, "r"); // 尝试打开该文件
esp8266_server.streamFile(file, contentType); // 将该文件返回给浏览器
file.close(); // 关闭文件
return true; // 返回true
}
return false; // 如果文件未找到,则返回false
}
// 获取文件类型
String getContentType(String filename){
if(filename.endsWith(".htm")) return "text/html";
else if(filename.endsWith(".html")) return "text/html";
else if(filename.endsWith(".css")) return "text/css";
else if(filename.endsWith(".js")) return "application/javascript";
else if(filename.endsWith(".png")) return "image/png";
else if(filename.endsWith(".gif")) return "image/gif";
else if(filename.endsWith(".jpg")) return "image/jpeg";
else if(filename.endsWith(".ico")) return "image/x-icon";
else if(filename.endsWith(".xml")) return "text/xml";
else if(filename.endsWith(".pdf")) return "application/x-pdf";
else if(filename.endsWith(".zip")) return "application/x-zip";
else if(filename.endsWith(".gz")) return "application/x-gzip";
return "text/plain";
}
(3)闪存系统中的文件(文件的编写非本教程重点,具体可在前端相关教程中了解):

①taichi-maker.jpg文件(在根目录下的img文件夹中):

②index.html文件(在根目录中):
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>太极创客-零基础入门学用物联网教程</title>
</head>
<body>
<center>
<a href="http://www.taichi-maker.com" target="_blank"><img src="/img/taichi-maker.jpg" alt="太极创客"></a>
<h1>ESP8266 LED 引脚控制</h1>
<p><a href="LED.html">前往LED控制页面</a></p>
<p>此页面用于演示如何通过网页按钮来控制ESP8266开发板引脚。</p>
<p>本教程可在太极创客网站免费获取。太极创客网址: <a href="http://www.taichi-maker.com" target="_blank">www.taichi-maker.com</a>
</center>
</body>
</html>
③LED.html文件(在根目录中):
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>太极创客-零基础入门学用物联网教程</title>
</head>
<body>
<center>
<a href="http://www.taichi-maker.com" target="_blank"><img src="/img/taichi-maker.jpg" alt="太极创客"></a>
<h1>LED引脚控制</h1>
<p>通过以下按键,您可以控制ESP8266开发板上的内置LED引脚</p>
<form action="LED-Control"><input type="submit" value="LED控制">
</form>
<br>
<form action="index.html"><input type="submit" value="返回首页">
</form>
<p>此页面用于演示如何通过网页按钮来控制ESP8266开发板引脚。</p>
<p>本教程可在太极创客网站免费获取。太极创客网址: <a href="http://www.taichi-maker.com" target="_black">www.taichi-maker.com</a>
</center>
</body>
</html>
3、通过网页文本框控制ESP8266开发板的PWM引脚
(1)程序流程示意图:

(2)下载至开发板的代码:
cpp
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>
#include <FS.h>
ESP8266WiFiMulti wifiMulti;
ESP8266WebServer esp8266_server(80);
void setup(void){
Serial.begin(9600);
Serial.println("");
pinMode(LED_BUILTIN, OUTPUT); // 初始化NodeMCU控制板载LED引脚为OUTPUT
wifiMulti.addAP("Zevalin_Computer", "00114514");
wifiMulti.addAP("Zevalin_esp8266", "00114514");
Serial.println("Connecting ...");
int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(1000);
Serial.print(i++); Serial.print('.');
}
Serial.println('\n');
Serial.print("Connected to "); Serial.println(WiFi.SSID());
Serial.print("IP address:\t"); Serial.println(WiFi.localIP());
if(SPIFFS.begin()){ // 启动闪存文件系统
Serial.println("SPIFFS Started.");
}
else{
Serial.println("SPIFFS Failed to Start.");
}
// 初始化网络服务器
esp8266_server.on("/LED-Control", handleLEDControl); // 告知系统如何处理/LED-Control请求
esp8266_server.onNotFound(handleUserRequest); // 告知系统如何处理其它用户请求
// 启动网站服务
esp8266_server.begin();
Serial.println("HTTP server started");
}
void loop(void){
esp8266_server.handleClient(); //处理网络请求
}
void handleLEDControl(){
String ledPwm = esp8266_server.arg("ledPwm"); // 从浏览器发送的信息中获取PWM控制数值(字符串格式)
int ledPwmVal = ledPwm.toInt(); // 将字符串格式的PWM控制数值转换为整数
analogWrite(LED_BUILTIN, ledPwmVal); // 设置引脚PWM输出值
// 建立基本网页信息显示当前数值以及返回链接
String httpBody = "Led PWM: " + ledPwm + "<p><a href=\"/LED.html\"><-LED Page</a></p>";
esp8266_server.send(200, "text/html", httpBody);
}
// 处理用户浏览器的HTTP访问
void handleUserRequest() {
// 获取用户请求资源(Request Resource)
String reqResource = esp8266_server.uri();
Serial.print("reqResource: ");
Serial.println(reqResource);
// 通过handleFileRead函数处处理用户请求资源
bool fileReadOK = handleFileRead(reqResource);
// 如果在SPIFFS无法找到用户访问的资源,则回复404 (Not Found)
if (!fileReadOK){
esp8266_server.send(404, "text/plain", "404 Not Found");
}
}
//处理浏览器HTTP访问
bool handleFileRead(String resource){
if (resource.endsWith("/")){ // 如果访问地址以"/"为结尾
resource = "/index.html"; // 将访问地址修改为/index.html便于SPIFFS访问
}
String contentType = getContentType(resource); // 获取文件类型
if (SPIFFS.exists(resource)){ // 如果访问的文件可以在SPIFFS中找到
File file = SPIFFS.open(resource, "r"); // 尝试打开该文件
esp8266_server.streamFile(file, contentType); // 将该文件返回给浏览器
file.close(); // 关闭文件
return true; // 返回true
}
return false; // 如果文件未找到,则返回false
}
// 获取文件类型
String getContentType(String filename){
if(filename.endsWith(".htm")) return "text/html";
else if(filename.endsWith(".html")) return "text/html";
else if(filename.endsWith(".css")) return "text/css";
else if(filename.endsWith(".js")) return "application/javascript";
else if(filename.endsWith(".png")) return "image/png";
else if(filename.endsWith(".gif")) return "image/gif";
else if(filename.endsWith(".jpg")) return "image/jpeg";
else if(filename.endsWith(".ico")) return "image/x-icon";
else if(filename.endsWith(".xml")) return "text/xml";
else if(filename.endsWith(".pdf")) return "application/x-pdf";
else if(filename.endsWith(".zip")) return "application/x-zip";
else if(filename.endsWith(".gz")) return "application/x-gzip";
return "text/plain";
}
①arg函数是ESP8266WebServer库中用来获取客户端通过HTTP请求(GET/POST)传递过来的参数值的核心方法,函数参数为需要从请求中获取参数的参数名。
②用户在浏览器访问某个地址时,"?"后面就是参数列表,格式为"<参数名1>=参数值1&<参数名2>=参数值2......"。比如当用户在浏览器访问"http://192.168.1.100/?name=John\&age=30"这个地址时,调用"server.arg("name")"就会返回"John",调用"server.arg("age")"就会返回"30"。
③arg函数通常会和另外两个函数配合使用:
|---------------------|-------------------|
| 函数 | 作用 |
| server.args() | 获取本次请求中参数的总数量 |
| server.hasArg(name) | 判断请求中是否包含某个特定的参数名 |
(3)闪存系统中的文件(文件的编写非本教程重点,具体可在前端相关教程中了解):

①taichi-maker.jpg文件(在根目录下的img文件夹中):

②index.html文件(在根目录中):
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>太极创客-零基础入门学用物联网教程</title>
</head>
<body>
<center>
<a href="http://www.taichi-maker.com" target="_blank"><img src="/img/taichi-maker.jpg" alt="太极创客"></a>
<p><a href="/LED.html">前往PWM控制页面</a></p>
<p>此示例用于演示如何通过ESP8266开发板的文本输入控制PWM引脚。</p>
<p>本教程可在太极创客网站免费获取。太极创客网址: <a href="http://www.taichi-maker.com" target="_blank">www.taichi-maker.com</a>
</center>
</body>
</html>
③LED.html文件(在根目录中):
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>太极创客-零基础入门学用物联网教程</title>
</head>
<body>
<center>
<a href="http://www.taichi-maker.com" target="_blank"><img src="/img/taichi-maker.jpg" alt="太极创客"></a>
<form action="/LED-Control">
<input type="text" name="ledPwm">
</br>
<input type="submit" value="OK">
</form>
<p>通过本页的文本框输入0 - 1023 数值来控制ESP8266开发板的LED亮度。</p>
<p>本教程可在太极创客网站免费获取。太极创客网址: <a href="http://www.taichi-maker.com" target="_blank">www.taichi-maker.com</a>
</center>
</body>
</html>