0x00 项目指北
要实现网络Server要利用好8266的两个功能SPIFFS合webServer,实现效果如下
一.SPIFFS闪存系统
1.什么叫SPIFFS? SPIFFS可以拆开成两部分来理解,一是SPI 二是FFS(文件系统)。 SPI 是 Serial Peripheral Interface 鉴于你是个爱学英语的小可爱,我告诉你第二个单词这么读 [p??r?f(?)r?l] “福瑞福若”,是外部设备的意思。所以SPI就是串行外设接口。
百度百科:
“SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,越来越多的芯片集成了这种通信协议,比如AT91RM9200。”
挺好奇的,查了一下啊,啥叫全双工,就是像柏油马路一样,双向同行就是全双工,单行道就是单工。 ?
Github跟进
不够深入,我在github上查了一下,它是github上一位叫pellepl的工程师在2013年编写的一个文件系统
Introduction是这么写的:
Spiffs is a file system intended for SPI NOR flash devices on embedded targets. Spiffs is designed with following characteristics in mind:
-
Small (embedded) targets, sparse RAM without heap -
Only big areas of data (blocks) can be erased -
An erase will reset all bits in block to ones -
Writing pulls one to zeroes -
Zeroes can only be pulled to ones by erase -
Wear leveling
意思是:
Spiffs是一个专门为基于SPI Nor flash储存架构的嵌入式设备开发的文件系统。
SPIFFS被设计用于以下几种特点:
1.用于小型嵌入式设备,不需要堆(heap)
2.只有大范围的数据块才能被擦除
3.擦除数据将把block位全部置1
4.写的操作会把1变成0
5.0只能被擦除成
6.损耗均衡
同时他也介绍了Features特性:
FEATURES
What spiffs does:
-
Specifically designed for low ram usage -
Uses statically sized ram buffers, independent of number of files -
Posix-like api: open, close, read, write, seek, stat, etc -
It can run on any NOR flash, not only SPI flash - theoretically also on embedded flash of a microprocessor -
Multiple spiffs configurations can run on same target - and even on same SPI flash device -
Implements static wear leveling -
Built in file system consistency checks -
Highly configurable
What spiffs does not:
-
Presently, spiffs does not support directories. It produces a flat structure. Creating a file with path tmp/myfile.txt will create a file called tmp/myfile.txt instead of a myfile.txt under directory tmp. -
It is not a realtime stack. One write operation might last much longer than another. -
Poor scalability. Spiffs is intended for small memory devices - the normal sizes for SPI flashes. Going beyond ~128Mbyte is probably a bad idea. This is a side effect of the design goal to use as little ram as possible. -
Presently, it does not detect or handle bad blocks. -
One configuration, one binary. There's no generic spiffs binary that handles all types of configurations.
意思是:
spiffs能干什么?
1.专门为小ram(小运存)使用而设计
2.使用静态大小的ram缓冲区,与文件大小无关
3.POSIX(Portable Operating System Interface,统一UNIX-like 的OS对外的接口)提供操作系统接口,如打开,关闭,读,写,查找,统计,等。
4.可以在任何NOR闪存运行,不仅仅是SPI flash理论上也可以在微处理器上运行
5.多个SPIFFS配置可以在相同目标上运行,甚至可以在相同SPI flash闪存设备上运行
6.flash寿命维护
7.内置文件系统一致性检查
8.高度可配置
SPIFFS不能做什么:
1、目前,spiffs不支持目录。它产生一个平面结构。使用路径tmp/myfile.txt创建文件将创建一个名为tmp/myfile.txt的文件,而不是在tmp目录下创建一个名为myfile.txt的文件。
2、它不是一个实时堆栈。一个写操作的持续时间可能比另一个长得多。
3、可怜的可伸缩性。Spiffs适用于小型内存设备——SPI flash的正常大小。超过~128Mbyte就不推荐了。由于设计目标是用尽可能少的ram,所以这是设计目标的一个副作用。
4、目前,它不能检测或处理坏块。
5、一个配置,一个二进制。没有通用的spiffs二进制可以处理所有类型的配置。
关于SPI Nor flash架构:由存储单元,行列控制单元,接口转换单元组成
总结:
所以,简单来说,这是一个简易的文件系统,可以面向任何闪存。最初考虑到大部分处理器的RAM资源有限,于是使用少量RAM设计了这样一个文件系统。同时,由于用的RAM比较少,导致它不能处理大于128MB的flash。 ?
扁平化结构,不支持目录。举例,在电脑中,假设我在C盘某路径有一个文件:C:\flexIm\hello.txt,而使用SPIFFS的话,只能创建一个名字为C:\flexIm\hello的txt格式的文件。它还有一个优点,就是考虑到了flash的寿命问题,因此做了算法均匀使用flash中的每一个block。
二.WebServer
webServer就是ESP8266WebServer库及相关功能组成的
0x01 SPIFFS API
<FS.h> 文件操作很重要,对于web来说,访问资源调用SPIFFS是必须的!
基本操作(含文件)
| SPIFFS.format() | 格式化SPIFFS | Bool | SPIFFS.begin() | 启动SPIFFS | File | SPIFFS.open(file_name,"w") | 打开文件并执行第二个参数(这里是写入) | | dataFile.println("") | 向被打开的dataFile写入信息 | | dataFile.close() | 文件操作结束后必须执行 | | SPIFFS.info((FSInfo)fs_info) | 向fs_info写入info,仍需print出具体要看哪类info,api不展示了就 |
目录操作
Bool | SPIFFS.exists(file_name) | 检查文件系统中有无此文件,有则返回True |
---|
num | dataFile.size() | 返回当前打开的文件大小 | Dir | SPIFFS.openDIR(folder_name) | 创建目录 | Bool | dir.next() | 检查是否有下一个文件 dir指向下一个文件,函数返回True | Bool | SPIFFS.remove(file_name) | 删除文件 |
0x02 通过Arduino IDE向SPIFFS系统上传文件
请确保已经完成了以下准备工作qwq:
-
将NodeMCU开发板与电脑通过USB数据线连接好 -
NodeMCU开发板驱动已经安装完毕 -
设置Arduino IDE以使其支持NodeMCU开发板
一.下载Arduino-ESP8266闪存文件插件程序
通过这个链接进行下载 https://github.com/esp8266/arduino-esp8266fs-plugin/releases
二.确定项目文件夹位置并粘贴文件
通过 ArduinoIDE ---> 文件 ---> 首选项 --->项目文件夹位置 在项目位置建立新文件夹"tools"然后把下载好的文件解压,直接丢过去 然后重新启动ArduinoIDE 如何确定安装成功了呢? 在工具 ---> 若有ESP8266 Sketch Data upload 则安装完毕了 上传 然后在项目文件夹下创建data文件夹,将文件直接拖进去就好
0x03 Connection API
<ESP8266WiFiMulti.h>
AP Mode:
AP麻,就是 Access Point 由自身成为WiFi,使别的设备可以连接
WiFi.softAP(ssid,password) | 启动ap模式,ssid是wifi名字 |
---|
WiFi.softAPIP() | 获得NodeMCU的内网地址 |
Station Mode:
即无线终端模式,纯粹的wifi收发
WiFi.status() | 获取当前wifi连接状态 |
---|
WL_CONNECTED | |
Auto Multi Connection:
机智的你会发现,一次只能链接一个wifi有点不太符合我们的日常使用逻辑,所以我们希望能够在一个库中保存许多wifi,然后自动选取信号最强的或者可以建立有效链接的进行connect,于是我们用到了ESP8266WiFiMutil.h这个库。 所以,这个库只适用于AP mode哦!
对象 | ESP8266WiFiMulti wifiMulti | 建立ESP8266WiFiMulti对象 然后可以使用对象的众多方法来实现功能 | | wifiMulti.addAP("ssid"."passwd") | 给链接库里面添加wifi | bool | wifiMulti.run() | 搜索当前链接库信号最强的wifi 连接成功则返回WL_CONNECTED |
while (wifiMulti.run() != WL_CONNECTED) { ?// 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前注意哦!!!为了实现在连接前持续搜索的效果,我们需要持续循环判断wifi是否链接,比如可以这样子写
? ?delay(1000); ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCU
? ?Serial.print('.'); ? ? ? ? ? ? ? ? ? ? ? // 将会连接信号最强的那一个WiFi信号。
} ?
others:
<ESP8266WiFi.h>
WiFi.mode(mode) | 调整ESP8266工作模式为无线终端模式(如WIFI_STA) |
---|
WiFi.begin(ssid,password) | 开始链接wifi | WiFi.SSID() | AP mode下查看当前连接的wifi名称 | WiFi.localIP() | 查看NodeMCU当前的IP地址 |
0x04 Server API
<ESP8266WebServer>
对象 | ESP8266WebServer esp8266_server(port) | 建立webServer对象 参数为打开的端口 根据协议标准,一般打开80端口 | | esp8266_server.begin(); | 让ESP8266-NodeMCU来启动网络服务功能 | | esp8266_server.on("pos",func); | 指挥NodeMCU来如何处理浏览器的http请求,pos是访问位置('/'代表首页),func是访问对应页面执行函数 | | esp8266_server.onNotFound(func) | 若无此页面则调用func处理404访问 | | esp8266_server.handleClient(); | 检查有没有设备通过网络向NodeMCU发送请求 (因此我们需要把它放在loop函数中,从而确保它能经常被调用。假如我们的loop函数里有类似delay一类的函数延迟程序运行,那么这时候就一定要注意了,如果handleClient函数长时间得不到调用,NodeMCU的网络服务会变得很不稳定。因此在使用NodeMCU执行网络服务功能的时候,一定要确保handleClient函数经常得以调用。我在这里反复强调这一点是因为这一点非常关键,请务必注意!) | | esp8266_server.send(index,"type","text") | 生成并且发送http响应信息,param1:状态码,param2:说明http响应体信息类型,parma3:具体内容(最好放在具体esp8266_Server.on对应执行函数里面,来实现“响应头”的效果) | | esp8266_server.sendHeader(headerName, headerValue, first); | 用于向响应头信息中添加自定义键值对。 parma1:自定义响应头的名称,可以使用字符串类型 parma2:自定义响应头的值,可使用字符串类型 parma3:设置该响应头是否需要放在第一行,缺省则为false(参数为bool) | | esp8266_server.send(index) | 向浏览器发送状态码 |
content-type要注意哦!!! 1.实现持续处理Client的访问响应,需要将handleClient放在loop函数中运行 而begin和on等配置型函数,在setup中使用。 2.!!!!.send函数很重要,尤其是TYPE!!!因为不同type代表了告诉client怎么处理网络字节流
常见格式如下:
-
text/html : HTML格式 -
text/plain :纯文本格式 -
text/xml : XML格式 -
image/gif :gif图片格式 -
image/jpeg :jpg图片格式 -
image/png:png图片格式
application开头的媒体类型:
-
application/xhtml+xml :XHTML格式 -
application/xml: XML数据格式 -
application/atom+xml :Atom XML聚合格式 -
application/json: JSON数据格式 -
application/pdf:pdf格式 -
application/msword : Word文档格式 -
application/octet-stream : 二进制流数据(如常见的文件下载) -
application/x-www-form-urlencoded : <form encType="">中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)
更多API如下:
0x05 Client API
其实这个项目是不需要用到Client API哒!刚好学到这里了,就大概见一下吧! 如果不想了解可以直接跳过 你可能会疑惑,我8266是Server为啥要Client API呢,因为我8266不光可以作为Server,还可以作为Client,类似浏览器干的事情,主动去访问一个别的Server,并解析他的响应。
ESP8266HTTPClient库
特征:简单易用,但是不灵活,封装好了底层内容,不够自由
| HTTPClient httpClient | 要声明HTTPClient类型对象才能操作哦!!! | | httpClient.begin(URL,[port]); | begin函数来配置请求地址,param2为默认80 | int | httpClient.GET() | 通过get函数启动链接并发送http请求 返回状态码 | int | httpClient.POST(payload,[size]) | 此函数用于ESP8266使用HTTP协议通过Server向服务器发送请求 parma1:通过post请求发送的数据信息,可使用字符串 parma2:通过post请求发送字节数(size_t) | Sting | httpClient.getString() | getString函数获得服务器响应体内容 | | httpClient.end() | 关闭esp8266与服务器的链接 此函数来清除ESP8266的接收缓存以便设备再次接收服务器发来的响应信息。 |
void httpClientRequest(){}注意!!!! 发送HTTP请求并将服务器响应通过串口输出最好用一个函数来写,且函数必须写在loop里面,然后再setup函数启用时调用!!!
WiFiClient库
bool | client.connect(host, httpport) | 与Server连接,host是网址httpport是端口号 Success则True | |
---|
bool | client.connected() | 检查当前服务器的连接情况 Connected则True | | bool | client.available() | 检查Server是否发来信息 有则True | 注意,最好写两层abaliable函数,服务器响应有延迟 | String | client.readStringUntil('\n') | 将服务器响应信息逐行分解为字符串 | | | client.print(request) | 向Server发送封装好的请求,request是String串 | |
同上一个库发送HTTP请求并将服务器响应通过串口输出最好用一个函数来写,且函数必须写在loop里面,然后再setup函数启用时调用!!!void httpClientRequest(){} ?
readStringUntil函数的实现利用了Stream 数据流概念,下面我们一起探索一下Stream
Stream & Stream API
直白来讲,就是一列有先后顺序的数据
bool | Serial.avilable() | 判断开发板是否收到数据 | 一般在loop函数下写相关功能的实现!! |
---|
| Serial.println() | 将Stream串口数据输出在串口监视器中 | |
还有很多api,暂不赘述啦!
0x06 最后几步
相信到这里,你已经利用插件在SPIFFS上传好了html文件,最后我们要做的就是将SPIFFS文件读取与webServer.on函数练习起来,我们都知道,每一个url都是一个资源,当我们在web标签调用图片之类的资源,也相当于发了一次request和response,所以我们需要给每一个url设置.on函数......确实有点麻烦。那么上代码吧。
#include <ESP8266WiFi.h> ? ? ? ?// 本程序使用ESP8266WiFi库
#include <ESP8266WebServer.h> ? // ESP8266WebServer库
#include <FS.h>
ESP8266WebServer Server(80);
?
const char *ssid = "LiuJiahuan Test Web Server"; // 这里定义将要建立的WiFi名称。此处以""为示例
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 您可以将自己想要建立的WiFi名称填写入此处的双引号中
const char *password = "12345678"; ?// 这里定义将要建立的WiFi密码。此处以12345678为示例
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 您可以将自己想要使用的WiFi密码放入引号内
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 如果建立的WiFi不要密码,则在双引号内不要填入任何信息
void setup() {
?Serial.begin(9600); ? ? ? ? ? ? ?// 启动串口通讯
?if(SPIFFS.begin()){ ? ? ? ? ? ? ? ? ? ? ? // 启动闪存文件系统
? ?Serial.println("SPIFFS Started.");
} else {
? ?Serial.println("SPIFFS Failed to Start.");
}
?WiFi.softAP(ssid, password); ? ? // 此语句是重点。WiFi.softAP用于启动NodeMCU的AP模式。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 括号中有两个参数,ssid是WiFi名。password是WiFi密码。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 这两个参数具体内容在setup函数之前的位置进行定义。
?
?Serial.print("Access Point: "); ? ?// 通过串口监视器输出信息
?Serial.println(ssid); ? ? ? ? ? ? ?// 告知用户NodeMCU所建立的WiFi名
?Serial.print("IP address: "); ? ? ?// 以及NodeMCU的IP地址
?Serial.println(WiFi.softAPIP()); ? // 通过调用WiFi.softAPIP()可以得到NodeMCU的IP地址
?Server.begin();
?Server.on("/",handleRoot);
?Server.on("/test.jpeg", handlePhoto);
}
void loop() {
? Server.handleClient();
}
?
void handleRoot() { ? //处理网站根目录“/”的访问请求
?File dataFile = SPIFFS.open("/index.html", "r");
?String rp;
?for(int i = 0; i < dataFile.size(); i++){
? ?rp += (char)dataFile.read(); ? ? ?
}
?Server.send(200, "text/html", rp);
?dataFile.close();
}
?
void handlePhoto(){
?File dataFile = SPIFFS.open("/test.jpeg", "r");
?String pt;
?for(int i = 0; i < dataFile.size(); i++){
? ?pt += (char)dataFile.read(); ? ? ?
}
?Server.send(200, "image/jpeg", pt);
?dataFile.close();
}
我给SPIFFS放了个index.html和test.jpeg 非常easy的小demo!!!给你们宿舍也做个小网站吧hhhhhh!!!做内网穿透以后更好玩了
|