ESP8266 network development trip to articles ⑬ SPIFFS - ESP8266 SPIFFS File System

1.1 Introduction

    Bowen routines in front of about ESP8266WiFiWebServer, we can see that bloggers are basically assembled manually return html content, html content is fixed written in our Arduino ESP code.
    Then this will have two drawbacks:

  1. ESP8266 code is very bloated
  • In order to facilitate the development, web server pages in addition to their own html content, including some css files, or even the introduction of the JQuery library and some pictures related resources. If the content is written directly to the ESP8266 code, the code will result in 8266 as a whole becomes large, and may even exceed the size limit of the flash;
  1. Business is not clear separation of duties
  • In general, in a development team, it was responsible for the development ESP8266 business needs, it was responsible for the development WebServer Web content, was responsible for the hardware part. Html content directly to write directly to the ESP8266 code, it will lead to confusion operational responsibilities, and time if you want to modify the html content had to get rid of one arduino files, error correction is also possible identifiers like. Ideally it should be, only you need to update the web server html file like the original esp8266 arduino logic without updating;

    Based on the above drawbacks, formally introduced ESP8266 file system to be studied in this chapter (SPI Flash FileSystem, referred to as SPIFFS).

    First look at a concept map:

80120-796079083f5e302a.jpg
image

    The file system can help us store some change frequency infrequent documents such as web pages, or some configuration data such as cured.
    In fact, we have more of a memory page, the pages and related resources (such as: images, html, css, javaScript) stored in the flash area to SPIFFS.
    Principle as shown below:

2340221-bd07684869bfef5a.png
image

1.2 FLASH memory allocation

    Before explaining SPIFFS, we take a look at flash storage allocation ESP8266 in the Arduino environment, see below:


2340221-5c9b879f82aa36d9.png
image

    Concrete can be divided into several parts:

  1. Code area
  • Also called a program storage area, and wherein the current code area is divided into regions (current Sketch), update the code area (OTA update);
  1. File system
  • This is what we focus on in this section to explain the SPI Flash File System, referred to SPIFFS flash file system.
  • Even with a program stored in the file system on the same flash memory chips, burned into the new code does not modify file system content. This allows the file system to store code data of the Web server, the configuration file or content. The size of this SPIFFS file system can be configured through the programming environment, there are generally 1M, 2M, 3M and so on. Bloggers suggested that if the board is NodeMcu, can be configured to 3M ;
  • In order to use the file system, the file header contains the following code:
#include <FS.h>
  1. EEPROM
  1. WiFi Config
  • This area is how we set when WiFi module configuration data stored.

1.3 SPIFFS file system

1.3.1 file system limits

    ESP8266 file system implementation must meet the restrictions of the chip, the most important is the limited RAM. SPIFFS reason was ESP8266 selected as the file system, because it is specially designed for small systems, while based on some simplifications and limitations for the price.
    First, SPIFFS does not support the directory that stores only a list of files "flat". However, contrary to the traditional file system, a character slash "/" in the file name is permitted, the processing function of the directory listing (e.g., openDir ( "/ website") ) substantially only filtering file, and retain the prefix (/ website /) those files to start.
    Then, for the file name, a total of 32 character limit . A "\ 0" is reserved for the character string c terminator, so we left available 31 characters in length.
    Taken together, this means recommended to keep the short file name , do not use deeply nested directories, because the full path to each file (including directory "/" character base name, points, and extension) is at most 31 characters in length. For example, / website / images / bird_thumbnail.jpg of 34 characters in length, if used, will cause problems.
    Warning : this restriction is easy to reach, if ignored, the problem may be ignored, because the error message does not appear in the compilation and runtime.

1.3.2 way to add file system

    The purpose is to use the file system to store files, stored files can actually be divided into three kinds:

  • FS code directly call API provided to create a file on SPIFFS;
  • By ESP8266FS tool to upload files to SPIFFS;
  • Upload SPIFFS by OTA Update manner;

    In essence, whether through ESP8266FS or OTA Update ways to upload files to SPIFFS, its underlying are to complete by calling the API provided by FS, so we only need to understand the FS API can be used .

1.4 SPIFFS library

    Look SPIFFS file system commonly used method of operation, the following is a summary of Baidu blogger mind map:

2340221-86f70683bfd454a5.png
image

    The method is divided into three categories:

  1. SPIFFS special method
  2. Dir subject-specific methods
  3. File object-specific methods

1.4.1 SPIFFS special method

1.4.1.1 begin - mount the file system SPIFFS

Function Description:

/**
 * 挂载SPIFFS文件系统
 * @return  bool  如果文件系统挂载成功,返回true,否则返回false
 */
bool begin();

important point:

  • It must be called before any other FS API is called;
  • When you need to enable SPIFFS Arduino IDE configuration;

1.4.1.2 format - Format File System

Function Description:

/**
 * 格式化文件系统
 * @return  bool 如果格式化成功则返回true
 */
bool format();

important point:

  • Can be called before the begin () or after

1.4.1.3 open - open file

Function Description:

/**
 * 打开文件,某种模式下会创建文件
 * @param path 文件路径
 * @param mode 存取模式
 * @return File 返回一个File对象
 */
File open(const char* path, const char* mode);
File open(const String& path, const char* mode);

important point:

  1. The path must be in absolute path beginning with a slash (such as: /dir/filename.txt);
  2. Mode parameter string is designated access mode, a value of "r", "w", "a", a "r +", "w +" and "a +" among.
  • r in a read-only operation file, read the file location of the starting position , return null object file does not exist;
  • r + in a readable and writable manner to open the file, read the file at the beginning position, return null object file does not exist;
  • w 0 to intercept length of the file or create a new file, only the write operation , the write position at the beginning of the file;
  • w + intercept length of the file or create a new file to 0, read and write operation , the write position at the beginning of the file;
  • a 在文件末尾追加内容或者文件不存在就创建新文件,追加位置在当前文件的末尾,只能写操作
  • a+ 在文件末尾追加内容或者文件不存在就创建新文件,追加位置在当前文件的末尾,可读写操作

如果要检查文件是否打开成功,请使用以下代码:

File f = SPIFFS.open("/f.txt", "w");
if (!f) {
    Serial.println("file open failed");
}

1.4.1.4 exists —— 路径是否存在

函数说明:

/**
 * 路径是否存在
 * @param path 文件路径
 * @return bool 如果指定的路径存在,则返回true,否则返回false
 */
bool exists(const char* path);
bool exists(const String& path);

1.4.1.5 openDir —— 打开绝对路径文件夹

函数说明:

/**
 * 打开绝对路径文件夹
 * @param path 文件路径
 * @return Dir 打开绝对路径文件夹,返回一个Dir对象
 */
Dir openDir(const char* path);
Dir openDir(const String& path);

1.4.1.6 remove —— 删除绝对路径的文件

函数说明:

/**
 * 删除绝对路径的文件
 * @param path 文件路径
 * @return bool 如果删除成功则返回true,否则返回false
 */
bool remove(const char* path);
bool remove(const String& path);

1.4.1.7 rename —— 重新命名文件

函数说明:

/**
 * 重新命名文件
 * @param pathFrom 原始路径文件名
 * @param pathTo   新路径文件名
 * @return bool 如果重新命名成功则返回true,否则返回fals
 */
bool rename(const char* pathFrom, const char* pathTo);
bool rename(const String& pathFrom, const String& pathTo);

1.4.1.8 info —— 获取文件系统的信息

函数说明:

/**
 * 获取文件系统的信息,存储在FSInfo对象
 * @param info FSInfo对象
 * @return bool 是否获取成功
 */
bool info(FSInfo& info);

FSInfo定义如下:

struct FSInfo {
    size_t totalBytes;//整个文件系统的大小
    size_t usedBytes;//文件系统所有文件占用的大小
    size_t blockSize;//SPIFFS块大小
    size_t pageSize;//SPIFFS逻辑页数大小
    size_t maxOpenFiles;//能够同时打开的文件最大个数
    size_t maxPathLength;//文件名最大长度(包括一个字节的字符串结束符)
};

1.4.2 Dir对象专用方法

在上面的方法中,我们可以获取到Dir对象,那么看看Dir对象定义是什么?

class Dir {
public:
    Dir(DirImplPtr impl = DirImplPtr()): _impl(impl) { }

    File openFile(const char* mode);//打开文件
    String fileName();//获取文件名字
    size_t fileSize();//文件大小
    bool next();//下一个文件

protected:
    DirImplPtr _impl;
};

注意点:

  • Dir对象的作用主要是遍历文件夹里的所有文件;
  • 文件夹并不是真正意义上的文件夹,文件都是平铺的;

1.4.2.1 openFile —— 打开文件

函数说明:

/**
 * 打开文件
 * @param mode 打开模式,请参考open方法
 * @return File 返回一个File对象
 */
File openFile(const char* mode);

1.4.2.2 fileName —— 获取文件名字

函数说明:

/**
 * 获取文件大小
 * @return size_t 文件大小
 */
size_t fileSize();

1.4.2.3 next —— 是否还有下一个文件

函数说明:

/**
 * 是否还有下一个文件
 * @return bool true 表示还有文件
 */
bool next();

注意点:

  • 其实这里用到了遍历;
  • 只要还有文件,dir.next()就会返回true,这个方法必须在fileName()和openFile()方法之前调用。

1.4.3 File对象专用方法

那么,我们来看看File对象结构:

class File : public Stream
{
public:
    File(FileImplPtr p = FileImplPtr()) : _p(p) {}

    // Print methods:
    size_t write(uint8_t) override;
    size_t write(const uint8_t *buf, size_t size) override;

    // Stream methods:
    int available() override;
    int read() override;
    int peek() override;
    void flush() override;
    size_t readBytes(char *buffer, size_t length)  override {
        return read((uint8_t*)buffer, length);
    }
    size_t read(uint8_t* buf, size_t size);
    bool seek(uint32_t pos, SeekMode mode);
    bool seek(uint32_t pos) {
        return seek(pos, SeekSet);
    }
    size_t position() const;
    size_t size() const;
    void close();
    operator bool() const;
    const char* name() const;

protected:
    FileImplPtr _p;
};

File对象支持Stream的所有方法,因此可以使用readBytes、findUntil、parseInt、printIn以及其他stream方法。以下是File对象特有的一些方法:

1.4.3.1 seek —— 文件偏移位置

函数说明:

/**
 * 设置文件位置偏移
 * @param pos 偏移量
 * @param mode 偏移模式
 * @return bool 如果移动成功,则返回true,否则返回false
 */
bool seek(uint32_t pos, SeekMode mode);
bool seek(uint32_t pos) {
     return seek(pos, SeekSet);
}

注意点:

  • 如果模式值是 SeekSet,则从文件开头移动指定的偏移量。
  • 如果模式值是 SeekCur,则从目前的文件位置移动指定的偏移量。
  • 如果模式值是 SeekEnd,则从文件结尾处移动指定的偏移量。

1.4.3.2 position —— 返回目前在文件中的位置

函数说明:

/**
 * 返回目前在文件中的位置
 * @return size_t 当前位置
 */
size_t position();

1.4.3.3 size —— 返回文件大小

函数说明:

/**
 * 返回文件大小
 * @return size_t 文件大小
 */
size_t size();

1.4.3.4 name —— 返回文件名字

函数说明:

/**
 * 返回文件名字
 * @return const char* 文件名字
 */
const char* name();

1.4.3.5 close —— 关闭文件

函数说明:

/**
 * 关闭文件
 */
void close();

注意点:

  • 执行这个方法之后,就不能在该文件上执行其他操作。

1.5 实例

1.5.1 文件操作

实例说明

spiffs文件操作常见方法使用,包括文件查找、创建、打开、关闭、删除

实例源码

/**
 * 功能描述:spiffs文件操作常见方法使用,包括文件查找、创建、打开、关闭、删除
 */
#include <FS.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate)    Serial.begin(baud_rate)
#define DebugPrintln(message)    Serial.println(message)
#define DebugPrint(message)    Serial.print(message)

#define myFileName  "mydemo.txt"

void setup(){
  DebugBegin(9600);
  DebugPrintln("Check Start SPIFFS...");
  //启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
  if(!SPIFFS.begin()){
     DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
     return;
  }
  DebugPrintln("Start SPIFFS Done.");
  //判断文件是否存在
  if(SPIFFS.exists(myFileName)){
    DebugPrintln("mydemo.txt exists.");
  }else{
    DebugPrintln("mydemo.txt not exists.");
  }
  
  File myFile;
  //打开文件 不存在就创建一个 可读可写
  myFile = SPIFFS.open(myFileName,"w+");
  //关闭文件
  myFile.close();
  //再次判断文件是否存在
  if(SPIFFS.exists(myFileName)){
    DebugPrintln("mydemo.txt exists.");
  }else{
    DebugPrintln("mydemo.txt not exists.");
  }
  //删除文件
  DebugPrintln("mydemo.txt removing...");
  SPIFFS.remove(myFileName);
  //再次判断文件是否存在
  if(SPIFFS.exists(myFileName)){
    DebugPrintln("mydemo.txt exists.");
  }else{
    DebugPrintln("mydemo.txt not exists.");
  }
}

void loop(){
}

实验结果

2340221-07cae8a8eea32337.png
image

1.5.2 文件列表

实例说明

查看spiffs文件系统列表

实例准备

  • NodeMcu开发板
  • 烧录配置需要开启SPIFFS

实例源码

/**
 * 功能描述:查看spiffs文件系统列表
 */
#include <FS.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate)    Serial.begin(baud_rate)
#define DebugPrintln(message)    Serial.println(message)
#define DebugPrint(message)    Serial.print(message)

void setup(){
  DebugBegin(9600);
  DebugPrintln("Check Start SPIFFS...");
  //启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
  if(!SPIFFS.begin()){
     DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
   return;
  }
  DebugPrintln("Start SPIFFS Done.");
  
  File myFile;
  //打开文件 不存在就创建一个 可读可写
  myFile = SPIFFS.open("/myDemo.txt","w+");
  //关闭文件
  myFile.close();
  
  //打开文件 不存在就创建一个 可读可写
  myFile = SPIFFS.open("/myDemo.jpg","w+");
  //关闭文件
  myFile.close();
  
    //打开文件 不存在就创建一个 可读可写
  myFile = SPIFFS.open("/myDemo.html","w+");
  //关闭文件
  myFile.close();
  
  Dir dir = SPIFFS.openDir("/");
  while(dir.next()){
    String fileName = dir.fileName();
  size_t fileSize = dir.fileSize();
  Serial.printf("FS File:%s,size:%d\n",fileName.c_str(),fileSize);
  }
  DebugPrintln("Setup Done!");
}

void loop(){
}

实验结果

2340221-21642e3daca3f151.png
image

1.5.3 文件读写

实例说明

往文件myDemo.txt中写入“单片机菜鸟博哥666”并读取出来显示。

实例源码

/**
 * 功能描述:演示文件读写功能
 */
#include <FS.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate)    Serial.begin(baud_rate)
#define DebugPrintln(message)    Serial.println(message)
#define DebugPrint(message)    Serial.print(message)

void setup(){
  DebugBegin(9600);
  DebugPrintln("Check Start SPIFFS...");
  //启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
  if(!SPIFFS.begin()){
     DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
   return;
  }
  DebugPrintln("Start SPIFFS Done.");
  
  File myFile;
  //打开文件 不存在就创建一个 可读可写
  myFile = SPIFFS.open("myDemo.txt","w+");
  if(myFile){
    DebugPrintln("Writing something to myDemo.txt...");
  myFile.println("单片机菜鸟博哥666");
  myFile.close();
  DebugPrintln("Writing Done.");
  }else{
    DebugPrintln("Open File Failed.");
  }
  
  //打开文件 可读
  myFile = SPIFFS.open("myDemo.txt","r");
  if(myFile){
    DebugPrintln("Reading myDemo.txt...");
  while(myFile.available()){
    //读取文件输出
    Serial.write(myFile.read());
  }
  myFile.close();
  }else{
    DebugPrintln("Open File Failed.");
  }
  
  DebugPrintln("Setup Done!");
}

void loop(){
}

实验结果

2340221-a4fbe0dc47a79956.png
image

1.5.4 烧写文件

实验说明

    在上面的例子中,我们都是自己手动在SPIFFS文件系统中创建或者写入文件,但是对于习惯web开发的人员来说,肯定是直接把写好的web程序(html、css、js、资源文件等)直接烧入文件系统更加令人容易接受。所以本例子主要是讲解如何往SPIFFS里面烧写文件。
    这个例子是重点,因为绝大部分的web开发(web配网、web页面等)都是常用烧写文件的方式,请读者仔细阅读。

    要存入SPIFFS区域的文件,都得事先放在代码目录里的“data”目录(请自行新增“data”目录)。
    例如,存在一个项目工程叫做espStaticWeb,其文件结构如下:

2340221-4f2388023200d5ad.png
image

    负责将文件上传到SPIFFS的工具叫做 ESP8266FS。ESP8266FS是一个集成到Arduino IDE中的工具,它将一个菜单项添加到工具菜单,用于将skench data目录的内容上传到ESP8266 Flash文件系统中。
    这个工具需要另外安装,整个上传文件步骤如下:

  1. 下载 ESP8266FS工具
  2. 将下载到的文件解压到Arduino IDE安装路径下的tools文件夹(如果不存在这个文件夹,请自行增加)。参考下图:
2340221-447b36d7ed0d578d.png
image
  1. 重启Arduino IDE
  2. 打开一个Sketch工程(新建或者打开最近的工程),去到Sketch工程目录下创建一个data目录(不存在该目录),然后把你需要放到文件系统的文件copy到这里。
  3. 确保你选择了正确的板子、com口,关闭掉串口监视器。
  4. 选择 工具 ESP8266 Sketch Data Upload
2340221-eb79de907d83c5d3.png
image

然后就会开始上传文件到ESP8266 flash文件系统。

2340221-db6f180c3eb1cf11.png
image

当IDE显示“SPIFFS Image Uploaded”,代表上传完毕。


2340221-a66998eee3d893b1.png
image

    那么接下来说明一下本例子内容:

  • 往8266 SPIFFS文件系统中上传一个config.txt文件(请读者自行创建,然后放在data目录,上传到ESP8266),然后读取出来。文件内容包括:
{"name":"esp8266","flash":"QIO","board":"NodeMcu"}

实验准备

  1. 往8266 SPIFFS文件系统中上传一个config.txt文件(请读者自行创建,然后放在data目录,上传到ESP8266)
  2. NodeMcu开发板

实验源码

/**
 * 功能描述:演示上传文件并读取文件内容
 * 前提:需要先往SPIFFS里面上传config.txt文件 
 */
#include <FS.h>

//以下三个定义为调试定义
#define DebugBegin(baud_rate)    Serial.begin(baud_rate)
#define DebugPrintln(message)    Serial.println(message)
#define DebugPrint(message)    Serial.print(message)

void setup(){
  DebugBegin(9600);
  DebugPrintln("Check Start SPIFFS...");
  //启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
  if(!SPIFFS.begin()){
     DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
   return;
  }
  DebugPrintln("Start SPIFFS Done.");
  
  File myFile;
  //打开文件 不存在就创建一个 可读可写
  myFile = SPIFFS.open("/config.txt","r");
  if(myFile){
    //打印文件大小
    int size = myFile.size();
  Serial.printf("Size=%d\r\n", size);
  //读取文件内容
  DebugPrintln(myFile.readString());
  myFile.close();
  DebugPrintln("Reading Done.");
  }else{
    DebugPrintln("Open File Failed.");
  }
}

void loop(){
}

实验结果

2340221-37bb2bb12959d6db.png
image

1.6 总结

SPIFFS文件系统属于非常重要的一篇,希望读者可以认真理解使用。

学习群 绝对不辜负你的期待 491507716 博主私人号 2421818708

Reproduced in: https: //www.jianshu.com/p/7a52c46cc8d9

Guess you like

Origin blog.csdn.net/weixin_34198881/article/details/91073379