授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力。希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石。。。

QQ技术互动交流群:ESP8266&32 物联网开发 群号622368884,不喜勿喷

一、你如果想学基于Arduino的ESP8266开发技术

一、基础篇

  1. ESP8266开发之旅 基础篇① 走进ESP8266的世界
  2. ESP8266开发之旅 基础篇② 如何安装ESP8266的Arduino开发环境
  3. ESP8266开发之旅 基础篇③ ESP8266与Arduino的开发说明
  4. ESP8266开发之旅 基础篇④ ESP8266与EEPROM
  5. ESP8266开发之旅 基础篇⑤ ESP8266 SPI通信和I2C通信
  6. ESP8266开发之旅 基础篇⑥ Ticker——ESP8266定时库

二、网络篇

  1. ESP8266开发之旅 网络篇① 认识一下Arduino Core For ESP8266
  2. ESP8266开发之旅 网络篇② ESP8266 工作模式与ESP8266WiFi库
  3. ESP8266开发之旅 网络篇③ Soft-AP——ESP8266WiFiAP库的使用
  4. ESP8266开发之旅 网络篇④ Station——ESP8266WiFiSTA库的使用
  5. ESP8266开发之旅 网络篇⑤ Scan WiFi——ESP8266WiFiScan库的使用
  6. ESP8266开发之旅 网络篇⑥ ESP8266WiFiGeneric——基础库
  7. ESP8266开发之旅 网络篇⑦ TCP Server & TCP Client
  8. ESP8266开发之旅 网络篇⑧ SmartConfig——一键配网
  9. ESP8266开发之旅 网络篇⑨ HttpClient——ESP8266HTTPClient库的使用
  10. ESP8266开发之旅 网络篇⑩ UDP服务
  11. ESP8266开发之旅 网络篇⑪ WebServer——ESP8266WebServer库的使用
  12. ESP8266开发之旅 网络篇⑫ 域名服务——ESP8266mDNS库
  13. ESP8266开发之旅 网络篇⑬ SPIFFS——ESP8266 Flash文件系统
  14. ESP8266开发之旅 网络篇⑭ web配网
  15. ESP8266开发之旅 网络篇⑮ 真正的域名服务——DNSServer
  16. ESP8266开发之旅 网络篇⑯ 无线更新——OTA固件更新

三、应用篇

  1. ESP8266开发之旅 应用篇① 局域网应用 ——炫酷RGB彩灯
  2. ESP8266开发之旅 应用篇② OLED显示天气屏
  3. ESP8266开发之旅 应用篇③ 简易版WiFi小车

四、高级篇

  1. ESP8266开发之旅 进阶篇① 代码优化 —— ESP8266内存管理
  2. ESP8266开发之旅 进阶篇② 闲聊Arduino IDE For ESP8266配置
  3. ESP8266开发之旅 进阶篇③ 闲聊 ESP8266 Flash
  4. ESP8266开发之旅 进阶篇④ 常见问题 —— 解决困扰
  5. ESP8266开发之旅 进阶篇⑤ 代码规范 —— 像写文章一样优美
  6. ESP8266开发之旅 进阶篇⑥ ESP-specific APIs说明

1. 前言

    在前面博文关于ESP8266WiFiWebServer的例程中,大家可以发现,博主基本上都是手动拼装html内容返回,html的内容被固定写在我们的Arduino ESP代码中。
    那么这样就有两点弊端:

  1. ESP8266代码相当臃肿
  • 为了开发方便,web server网页除了自身的html内容之外,还包括一些css文件,甚至引入了JQuery库以及一些图片相关资源。如果把这些内容也直接写入到ESP8266代码中,会导致8266整体代码变大,甚至可能超过flash规定的大小;
  1. 业务职责分离不明确
  • 一般来说,在一个开发团队中,有人负责开发ESP8266业务需求,有人负责开发WebServer网页内容,有人负责硬件部分。直接把html的内容直接写入到ESP8266代码中,就会导致业务职责混乱,并且如果要修改html内容的时候还得一个个改掉arduino的文件,也有可能改错标识符之类的。理想情况应该是,只需要更新web server的html文件就好,原来的esp8266 arduino逻辑不用更新;

    基于以上两点弊端,正式引入本篇章需要研究的ESP8266 文件系统(SPI Flash FileSystem,简称为SPIFFS)。

    先来看一个概念图:

    这个文件系统可以帮助我们存储一些变更频率不频繁的文件例如网页、配置或者是某些固化的数据等。
    其实,我们用得更多的是存储网页,将网页和相关资源(如:图片、html、css、javaScript)存入到flash的SPIFFS区域。
    原理如下图:

2. FLASH存储分配

    在讲解SPIFFS之前,我们来看看在Arduino环境下ESP8266的flash存储分配,请看下图:

    具体可以分为几部分:

  1. 代码区
  • 又叫做程序存储区,其中又区分为当前代码区(current Sketch),更新代码区(OTA update);
  1. 文件系统
  • 这个就是我们这节重点讲解的SPI Flash File System,简称SPIFFS闪存文件系统。
  • 即使文件系统与程序存储在同一个闪存芯片上,烧入新的代码也不会修改文件系统内容。这允许使用文件系统来存储Web服务器的代码数据、配置文件或内容。而这个SPIFFS文件系统的大小可以通过烧写环境来配置,目前一般有1M,2M,3M等等。博主建议如果是NodeMcu板子,可以配置成3M

  • 为了使用文件系统,需要把下面的头文件包含在代码中:

#include <FS.h>
  1. EEPROM
  • 具体讲解请回顾 ESP8266开发之旅 基础篇④ ESP8266与EEPROM
  1. WiFi Config
  • 这个区域就是我们设置WiFi模块配置的时候存储的数据。

3. SPIFFS文件系统

3.1 文件系统限制

    ESP8266的文件系统实现必须满足芯片的限制,其中最重要是有限的RAM。SPIFFS之所以被ESP8266选择作为文件系统,是因为它是为小型系统专门设计的,同时是以一些简化和限制为代价的。
    首先,SPIFFS不支持目录,它只存储一个“扁平化”的文件列表。但是与传统的文件系统相反,斜杠字符“/”在文件名中是允许的,因此处理目录列表的函数(例如,openDir("/website"))基本上只是过滤文件名,并保留以前缀(/website/)开始的那些文件。
    然后,对于文件名,总共有32个字符限制。一个“\0”字符被保留用于c字符串终止符,因此留给我们31个可用字符长度。
    综合起来,这意味着建议保持短文件名,不要使用深嵌套的目录,因为每个文件的完整路径(包括目录、“/”字符、基本名称、点和扩展名)最多只能是31个字符长度。例如,/website/images/bird_thumbnail.jpg 达到了34个字符长度,如果使用它,将导致一些问题。
    警告:这个限制很容易达到,如果忽略,问题可能会被忽略,因为在编译和运行时不会出现错误信息。

3.2 文件系统文件添加方式

    使用文件系统目的就是为了存储文件,那么存储文件的方式其实可以分为3种:

  • 直接代码中调用FS提供的API在SPIFFS上创建文件;
  • 通过 ESP8266FS 工具把文件上传到SPIFFS;
  • 通过OTA Update的方式上传到SPIFFS;

    本质上,无论是通过ESP8266FS或者OTA Update的方式把文件上传到SPIFFS,其底层都是通过调用FS提供的API去完成,所以我们只需要了解FS常用API即可

4. SPIFFS库

    了解一下SPIFFS文件系统常用的操作方法,以下是博主总结的百度脑图:

    方法分为3大类:

  1. SPIFFS专用方法
  2. Dir对象专用方法
  3. File对象专用方法

4.1 SPIFFS专用方法

4.1.1 begin —— 挂载SPIFFS文件系统

函数说明:

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

注意点:

  • 它必须在其他任何FS API被调用之前先调用;
  • Arduino IDE配置时需要启用SPIFFS;

4.1.2 format —— 格式化文件系统

函数说明:

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

注意点:

  • 可以在执行begin()之前或者之后调用

4.1.3 open —— 打开文件

函数说明:

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

注意点:

  1. 路径必须是以斜线开头的绝对路径(如:/dir/filename.txt);
  2. 模式参数是个用字符串指定的存取模式,其值为“r”、“w”、“a”、“r+”、“w+”和“a+”之中的一个。
  • r 以只读方式操作文件,读位置在文件的开始位置,文件不存在返回空对象;
  • r+ 以可读可写方式打开文件,读写位置在文件的开始位置,文件不存在返回空对象;
  • w 截取文件长度到0或者创建新文件,只能写操作,写位置在文件的开始位置;
  • w+ 截取文件长度到0或者创建新文件,可读可写操作,写位置在文件的开始位置;
  • a 在文件末尾追加内容或者文件不存在就创建新文件,追加位置在当前文件的末尾,只能写操作
  • a+ 在文件末尾追加内容或者文件不存在就创建新文件,追加位置在当前文件的末尾,可读写操作

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

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

4.1.4 exists —— 路径是否存在

函数说明:

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

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

函数说明:

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

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

函数说明:

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

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);

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;//文件名最大长度(包括一个字节的字符串结束符)
};

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对象的作用主要是遍历文件夹里的所有文件;
  • 文件夹并不是真正意义上的文件夹,文件都是平铺的;

4.2.1 openFile —— 打开文件

函数说明:

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

4.2.2 fileName —— 获取文件名字

函数说明:

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

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

函数说明:

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

注意点:

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

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对象特有的一些方法:

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,则从文件结尾处移动指定的偏移量。

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

函数说明:

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

4.3.3 size —— 返回文件大小

函数说明:

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

4.3.4 name —— 返回文件名字

函数说明:

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

4.3.5 close —— 关闭文件

函数说明:

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

注意点:

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

5. 实例

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(){
}

实验结果

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(){
}

实验结果

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(){
}

实验结果

5.4 烧写文件

实验说明

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

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

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

  1. 下载 ESP8266FS工具
  2. 将下载到的文件解压到Arduino IDE安装路径下的tools文件夹(如果不存在这个文件夹,请自行增加)。参考下图:

  1. 重启Arduino IDE
  2. 打开一个Sketch工程(新建或者打开最近的工程),去到Sketch工程目录下创建一个data目录(不存在该目录),然后把你需要放到文件系统的文件copy到这里。
  3. 确保你选择了正确的板子、com口,关闭掉串口监视器。
  4. 选择 工具 ESP8266 Sketch Data Upload

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

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

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

  • 往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(){
}

实验结果

6. 总结

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

ESP8266开发之旅 网络篇⑬ SPIFFS——ESP8266 SPIFFS文件系统的更多相关文章

  1. ESP8266开发之旅 网络篇⑯ 无线更新——OTA固件更新

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  2. ESP8266开发之旅 网络篇⑦ TCP Server & TCP Client

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  3. ESP8266开发之旅 网络篇⑧ SmartConfig——一键配网

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  4. ESP8266开发之旅 网络篇⑨ HttpClient——ESP8266HTTPClient库的使用

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  5. ESP8266开发之旅 网络篇⑩ UDP服务

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  6. ESP8266开发之旅 网络篇⑪ WebServer——ESP8266WebServer库的使用

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  7. ESP8266开发之旅 网络篇⑭ web配网

    1. 前言     目前,市面上流行多种配网方式: WIFI模块的智能配网(SmartConfig以及微信AirKiss配网) SmartConfig 配网方式 请参考博主之前的博文 ESP8266开 ...

  8. ESP8266开发之旅 网络篇③ Soft-AP——ESP8266WiFiAP库的使用

    1. 前言     在前面的篇章中,博主给大家讲解了ESP8266的软硬件配置以及基本功能使用,目的就是想让大家有个初步认识.并且,博主一直重点强调 ESP8266 WiFi模块有三种工作模式: St ...

  9. ESP8266开发之旅 网络篇④ Station——ESP8266WiFiSTA库的使用

    1. 前言     在前面的篇章中,博主给大家讲解了ESP8266的软硬件配置以及基本功能使用,目的就是想让大家有个初步认识.并且,博主一直重点强调 ESP8266 WiFi模块有三种工作模式: St ...

随机推荐

  1. 从CAP到zookeeper和eureka对比

    今天看了一篇eureka对比zookeeper的文章,对zookeeper满足CAP中的CP,eureka满足AP产生了一点疑问,故写此篇文章进行一些探讨. 首先我们来看看CAP的定义 Consist ...

  2. 软件测试的分类&软件测试生命周期

    软件测试的分类: 按测试执行阶段:单元测试.集成测试.系统测试.验收测试.(正式验收测试,Alpha 测试-内侧,Beta 测试-公测) 按测试技术分类:黑盒测试.白盒测试.灰盒测试 按测试对象是否运 ...

  3. 网站开发过程中的URL写法

    在开发网页和服务器时发现,在很多地方需要写超链接 那么可以将超链接的使用者分为服务器和浏览器,以区分不同的写法 地址可能使用的情况: 1.跳转 2.转发 3.服务器资源地址 4.浏览器超链接 impo ...

  4. 判断java中最多的词组

    其中的难点,是空格,以及如果第一个是空格怎么办,虽然事后看着很简单,但是做的时候却十分的困难! static void Daunyu()throws IOException {     Word wo ...

  5. 代码审计之CSRF原理及修复

    在平时的测试中,csrf也是比较容易发现的,主要就是看它对一个操作,有没有检测其时效性(这样表述可能不太准确),一般的防护方法就是添加token来进行校验,并及时对其进行失效处理. 以下所有代码都是我 ...

  6. 遇到不支持的 Oracle 数据类型 USERDEFINED

    以前都是sql查询mdb空间数据没有什么问题,今天在用sql方式查询Oracle中的空间数据时候,出现错误.它不支持geometry.空间数据都带有shape属性.只要不查询shape字段就没问题.但 ...

  7. latex转word公式 java (latextoword,latex_word,latex2word,latex_omml)

    latex_word 主要目的:     给大家分享一个我的原创作品:latex转为word公式(omml)工具 [java] 此工具主要用于将含有latex公式的文本下载成word时,将latex转 ...

  8. hadoop之yarn详解(框架进阶篇)

    前面在hadoop之yarn详解(基础架构篇)这篇文章提到了yarn的重要组件有ResourceManager,NodeManager,ApplicationMaster等,以及yarn调度作业的运行 ...

  9. javascript基础修炼(13)——记一道有趣的JS脑洞练习题

    目录 一. 题目 二. 解法风暴 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文目录 华为云社区地址 ...

  10. 【TencentOS tiny】深度源码分析(1)——task

    任务的基本概念 从系统的角度看,任务是竞争系统资源的最小运行单元.TencentOS tiny是一个支持多任务的操作系统,任务可以使用或等待CPU.使用内存空间等系统资源,并独立于其它任务运行,理论上 ...