ESP8266零基础入门学习笔记-太极创客团队教程

前言

提示:这里可以添加本文要记录的大概内容:

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、ESP8266开发板详解

1.引脚图

        NodeMCU上的D2引脚引出ESP8266芯片的GPIO4引脚

        即digitalWrite(D2,HIGH);等价于digitalWrite(4,HIGH);

        D开头的是数字引脚,A开头的是模拟引脚(右下角的ADC引脚)

        3V3可以为外界提供3.3V电压,Vin为NodeMCU供电(也可以用数据线供电)

        数字引脚为3.3V,digitalWrite(D2,HIGH);即是将D2引脚置为3.3V.而digitalRead(D2);

        引脚所连电压不能超过3.3V。

        模拟引脚可读电压范围为0-1V。

        USB下载串口占用的是RX和TX(GPIO3和GPIO1)

        蓝底黑字都是通讯端口(如GPIO3、GPIO1等)

        黑底白字都是NodeMCU操作内部存储单元的引脚(如GPIO6、GPIO15等)

        注意:GPIO6-GPIO11不要使用

二、互联网基础

1.TCP/IP协议族

        1.链路层的主要作用是实现设备之间的物理链接,如WiFi

        2.网络层为网络设备提供地址——IP协议

        

        IP协议分为IPV4和IPV6,IPV4由4个十进制数(0~255)组成

        默认网关即WiFi路由器的IP地址

        子网掩码用来区分IP地址的子网地址与机器本身地址

        参考:互联网知识基础-网络层(第2章 – 第3节) – 太极创客 (taichi-maker.com)

        而IPV6由8组16进制数组成,可以为更多网络设备提供独立的IP地址

        3.传输层——TCP协议

                TCP协议特点:稳

                ·可保证所有数据都能被接收端接收

                ·数据的传输顺序不会被打乱

                ·传输数据如有损坏则重发受损数据

                适用于电子邮件、文件传输等领域

        传输层——UDP协议

                UDP协议特点:快

                ·UDP比TCP速度快

                ·不保证所有数据都能被接收端接收

                ·数据一旦受损,UDP协议将抛弃受损数据

                ·传输数据如有损坏不会重发受损数据

                适用于在线语音/视频、网游等领域

        4.应用层

                最常用HTTP协议,HTTP协议由请求和响应组成,类似于一问一答,具体参考:互联网知识基础-应用层(第2章 – 第5节) – 太极创客 (taichi-maker.com)

        请求

        响应

2.ESP8266工作模式

1.AP(Access Point)模式(接入点模式)类似于手机热点

2.无线终端模式(Wireless Station)

3.混合模式

三、ESP8266接入点模式及无线终端模式

1.接入点模式代码

3-1-3 NodeMCU开发板的接入点模式 – 太极创客 (taichi-maker.com)

/*
NodeMCU接入点模式
By 太极创客(http://www.taichi-maker.com)
2019-03-11
 
此程序用于演示如何将NodeMCU以接入点模式工作。通过此程序,您可以使用
电脑或者手机连接NodeMCU所建立WiFi网络。
 
网络名: taichi-maker
密码:12345678
 
如需获得更多关于如何使用NodeMCU开发物联网的教程和资料信息
请参考太极创客网站(http://www.taichi-maker.com)
并在首页搜索栏中搜索关键字:物联网
*/
 
#include <ESP8266WiFi.h>        // 本程序使用ESP8266WiFi库
 
const char *ssid = "taichi-maker"; // 这里定义将要建立的WiFi名称。此处以"taichi-maker"为示例
                                   // 您可以将自己想要建立的WiFi名称填写入此处的双引号中
 
const char *password = "12345678";  // 这里定义将要建立的WiFi密码。此处以12345678为示例
                                    // 您可以将自己想要使用的WiFi密码放入引号内
                                    // 如果建立的WiFi不要密码,则在双引号内不要填入任何信息
 
void setup() {
  Serial.begin(9600);              // 启动串口通讯
 
  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地址
}
 
void loop() { 
}

2.无线终端模式代码

3-1-4 NodeMCU开发板的无线终端模式 – 太极创客 (taichi-maker.com)

/*
NodeMCU无线终端模式连接WiFi
By 太极创客(http://www.taichi-maker.com)
2019-03-11
 
本示例程序用于演示如何使用NodeMCU无线终端模式连接WiFi
 
如需获得更多关于如何使用NodeMCU开发物联网的教程和资料信息
请参考太极创客网站(http://www.taichi-maker.com)
并在首页搜索栏中搜索关键字:物联网
*/
 
#include <ESP8266WiFi.h>        // 本程序使用ESP8266WiFi库
 
const char* ssid     = "taichi-maker";      // 连接WiFi名(此处使用taichi-maker为示例)
                                            // 请将您需要连接的WiFi名填入引号中
const char* password = "12345678";          // 连接WiFi密码(此处使用12345678为示例)
                                            // 请将您需要连接的WiFi密码填入引号中
                                            
void setup() {
  Serial.begin(9600);         // 启动串口通讯
  
  WiFi.begin(ssid, password);                  // 启动网络连接
  Serial.print("Connecting to ");              // 串口监视器输出网络连接信息
  Serial.print(ssid); Serial.println(" ...");  // 告知用户NodeMCU正在尝试WiFi连接
  
  int i = 0;                                   // 这一段程序语句用于检查WiFi是否连接成功
  while (WiFi.status() != WL_CONNECTED) {      // WiFi.status()函数的返回值是由NodeMCU的WiFi连接状态所决定的。 
    delay(1000);                               // 如果WiFi连接成功则返回值为WL_CONNECTED                       
    Serial.print(i++); Serial.print(' ');      // 此处通过While循环让NodeMCU每隔一秒钟检查一次WiFi.status()函数返回值
  }                                            // 同时NodeMCU将通过串口监视器输出连接时长读秒。
                                               // 这个读秒是通过变量i每隔一秒自加1来实现的。
                                               
  Serial.println("");                          // WiFi连接成功后
  Serial.println("Connection established!");   // NodeMCU将通过串口监视器输出"连接成功"信息。
  Serial.print("IP address:    ");             // 同时还将输出NodeMCU的IP地址。这一功能是通过调用
  Serial.println(WiFi.localIP());              // WiFi.localIP()函数来实现的。该函数的返回值即NodeMCU的IP地址。
}
 
void loop() {                                   
}

四、 建立基本网络服务器

1.建立基本网络服务器

/**********************************************************************
项目名称/Project          : 零基础入门学用物联网
程序名称/Program name     : 3_2_1_First_Web_Server
团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author              : CYNO朔
日期/Date(YYYYMMDD)     : 20191107
程序目的/Purpose          : 使用NodeMCU建立基本服务器。用户可通过浏览器使用8266的IP地址
                           访问8266所建立的基本网页(Hello from ESP8266)
-----------------------------------------------------------------------
修订历史/Revision History  
日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
 
***********************************************************************/
#include <ESP8266WiFi.h>        // 本程序使用 ESP8266WiFi库
#include <ESP8266WiFiMulti.h>   //  ESP8266WiFiMulti库
#include <ESP8266WebServer.h>   //  ESP8266WebServer库
 
ESP8266WiFiMulti wifiMulti;     // 建立ESP8266WiFiMulti对象,对象名称是'wifiMulti'
 
ESP8266WebServer esp8266_server(80);// 建立ESP8266WebServer对象,对象名称为esp8266_server
                                    // 括号中的数字是网路服务器响应http请求的端口号
                                    // 网络服务器标准http端口号为80,因此这里使用80为端口号
 
void setup(void){
  Serial.begin(9600);          // 启动串口通讯
 
  //通过addAp函数存储  WiFi名称       WiFi密码
  wifiMulti.addAP("RFID", "rfid&iot0243"); 
  wifiMulti.addAP("taichi-maker", "12345678");  // 这三条语句通过调用函数addAP来记录3个不同的WiFi网络信息。
  wifiMulti.addAP("taichi-maker2", "87654321"); // 这3个WiFi网络名称分别是taichi-maker, taichi-maker2, taichi-maker3。
  wifiMulti.addAP("taichi-maker3", "13572468"); // 这3个网络的密码分别是123456789,87654321,13572468。
                                                // 此处WiFi信息只是示例,请在使用时将需要连接的WiFi信息填入相应位置。
                                                // 另外这里只存储了3个WiFi信息,您可以存储更多的WiFi信息在此处。
 
  int i = 0;                                 
  while (wifiMulti.run() != WL_CONNECTED) {  // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前
    delay(1000);                             // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCU
    Serial.print(i++); Serial.print(' ');    // 将会连接信号最强的那一个WiFi信号。
  }                                          // 一旦连接WiFI成功,wifiMulti.run()将会返回“WL_CONNECTED”。这也是
                                             // 此处while循环判断是否跳出循环的条件。
 
  // WiFi连接成功后将通过串口监视器输出连接成功信息 
  Serial.println('\n');                     // WiFi连接成功后
  Serial.print("Connected to ");            // NodeMCU将通过串口监视器输出。
  Serial.println(WiFi.SSID());              // 连接的WiFI名称
  Serial.print("IP address:\t");            // 以及
  Serial.println(WiFi.localIP());           // NodeMCU的IP地址
  
//--------"启动网络服务功能"程序部分开始-------- //  此部分为程序为本示例程序重点1
  esp8266_server.begin();                   //  详细讲解请参见太极创客网站《零基础入门学用物联网》
  esp8266_server.on("/", handleRoot);       //  第3章-第2节 ESP8266-NodeMCU网络服务器-1
  esp8266_server.onNotFound(handleNotFound);        
//--------"启动网络服务功能"程序部分结束--------
  Serial.println("HTTP esp8266_server started");//  告知用户ESP8266网络服务功能已经启动
}
 
/* 以下函数语句为本示例程序重点3
详细讲解请参见太极创客网站《零基础入门学用物联网》
第3章-第2节 3_2_1_First_Web_Server 的说明讲解*/  
void loop(void){
  esp8266_server.handleClient();     // 处理http服务器访问(检查是否有浏览器请求网页信息)
}
 
/* 以下两个函数为本示例程序重点2
详细讲解请参见太极创客网站《零基础入门学用物联网》
第3章-第2节 3_2_1_First_Web_Server 的说明讲解*/                                                                            
void handleRoot() {   //处理网站根目录“/”的访问请求 
  esp8266_server.send(200, "text/plain", "臭婆娘~ o(* ̄▽ ̄*)o这招你会不会捏");   // NodeMCU将调用此函数。
}
 
// 设置处理404情况的函数'handleNotFound'
void handleNotFound(){                                        // 当浏览器请求的网络资源无法在服务器找到时,
  esp8266_server.send(404, "text/plain", "404: Not found");   // NodeMCU将调用此函数。
}

2.通过网络服务实现NodeMCU开发板基本控制

/**********************************************************************
项目名称/Project          : 零基础入门学用物联网
程序名称/Program name     : 3_2_2_Turning_on_and_off_an_LED
团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author              : CYNO朔
日期/Date(YYYYMMDD)     : 20191108
程序目的/Purpose          : 使用NodeMCU建立基本服务器。用户可通过浏览器使用8266的IP地址
                           访问8266所建立的基本网页并通过该页面点亮/熄灭NodeMCU的内置LED
-----------------------------------------------------------------------
修订历史/Revision History  
日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
 
***********************************************************************/
#include <ESP8266WiFi.h>        // 本程序使用 ESP8266WiFi库
#include <ESP8266WiFiMulti.h>   //  ESP8266WiFiMulti库
#include <ESP8266WebServer.h>   //  ESP8266WebServer库
 
ESP8266WiFiMulti wifiMulti;     // 建立ESP8266WiFiMulti对象,对象名称是 'wifiMulti'
 
ESP8266WebServer esp8266_server(80);// 建立网络服务器对象,该对象用于响应HTTP请求。监听端口(80)
 
void setup(void){
  Serial.begin(9600);   // 启动串口通讯
 
  pinMode(LED_BUILTIN, OUTPUT); //设置内置LED引脚为输出模式以便控制LED
  wifiMulti.addAP("RFID", "rfid&iot0243"); 
  wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1"); // 将需要连接的一系列WiFi ID和密码输入这里
  wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2"); // ESP8266-NodeMCU再启动后会扫描当前网络
  wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3"); // 环境查找是否有这里列出的WiFi ID。如果有
  Serial.println("Connecting ...");                            // 则尝试使用此处存储的密码进行连接。
  
  int i = 0;                                 
  while (wifiMulti.run() != WL_CONNECTED) {  // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前
    delay(1000);                             // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCU
    Serial.print(i++); Serial.print(' ');    // 将会连接信号最强的那一个WiFi信号。
  }                                          // 一旦连接WiFI成功,wifiMulti.run()将会返回“WL_CONNECTED”。这也是
                                             // 此处while循环判断是否跳出循环的条件。
  
  // WiFi连接成功后将通过串口监视器输出连接成功信息 
  Serial.println('\n');
  Serial.print("Connected to ");
  Serial.println(WiFi.SSID());              // 通过串口监视器输出连接的WiFi名称
  Serial.print("IP address:\t");
  Serial.println(WiFi.localIP());           // 通过串口监视器输出ESP8266-NodeMCU的IP
 
  esp8266_server.begin();                           // 启动网站服务
  esp8266_server.on("/", HTTP_GET, handleRoot);     // 设置服务器根目录即'/'的函数'handleRoot'
  /*当有浏览器请求首页"/"并且方法是HTTP_GET时,才调用handleRoot*/
  esp8266_server.on("/LED", HTTP_POST, handleLED);  // 设置处理LED控制请求的函数'handleLED'
  /*当有浏览器请求页面"/LED"并且方法是HTTP_POST时,才调用handleLED*/
  esp8266_server.onNotFound(handleNotFound);        // 设置处理404情况的函数'handleNotFound'
 
  Serial.println("HTTP esp8266_server started");//  告知用户ESP8266网络服务功能已经启动
}
 
void loop(void){
  esp8266_server.handleClient();                     // 检查http服务器访问
}
 
/*设置服务器根目录即'/'的函数'handleRoot'
  该函数的作用是每当有客户端访问NodeMCU服务器根目录时,
  NodeMCU都会向访问设备发送 HTTP 状态 200 (Ok) 这是send函数的第一个参数。
  同时NodeMCU还会向浏览器发送HTML代码,以下示例中send函数中第三个参数,
  也就是双引号中的内容就是NodeMCU发送的HTML代码。该代码可在网页中产生LED控制按钮。 
  当用户按下按钮时,浏览器将会向NodeMCU的/LED页面发送HTTP请求,请求方式为POST。
  NodeMCU接收到此请求后将会执行handleLED函数内容*/
void handleRoot() {       
  esp8266_server.send(200, "text/html", "<form action=\"/LED\" method=\"POST\"><input type=\"submit\" value=\"Toggle LED\"></form>");
}
/*html——超文本标记语言

<form action=\"/LED\" method=\"POST\">
    <input type=\"submit\" value=\"Toggle LED\">
</form>

form为表单格式,其中划出一个名为Toggle LED的按键,按下后,会以POST方式跳转到/LED页面
*/

//处理LED控制请求的函数'handleLED'
void handleLED() {                          
  digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));// 改变LED的点亮或者熄灭状态,翻转IO口
  esp8266_server.sendHeader("Location","/");          // 跳转回页面根目录(首页)
  esp8266_server.send(303);                           // 发送Http相应代码303 跳转  
}
 
// 设置处理404情况的函数'handleNotFound'
void handleNotFound(){
  esp8266_server.send(404, "text/plain", "404: Not found"); // 发送 HTTP 状态 404 (未找到页面) 并向浏览器发送文字 "404: Not found"
}

3.通过网络服务将开发板引脚状态显示在网页中

/**********************************************************************
项目名称/Project          : 零基础入门学用物联网
程序名称/Program name     : 3_2_4_Pin_State_Display_Auto_Refresh
团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author              : CYNO朔
日期/Date(YYYYMMDD)     : 20200128
程序目的/Purpose          : 使用NodeMCU建立基本服务器。该网页将显示引脚D3状态。同时状态会
                           每隔5秒钟更新一次。
-----------------------------------------------------------------------
修订历史/Revision History  
日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
 
***********************************************************************/
 
#include <ESP8266WiFi.h>        // 本程序使用 ESP8266WiFi库
#include <ESP8266WiFiMulti.h>   //  ESP8266WiFiMulti库
#include <ESP8266WebServer.h>   //  ESP8266WebServer库
 
#define buttonPin 0           // 按钮引脚D3(GPIO0)因为它已经与开发板上的FLASH按键开关连接好了。
 
ESP8266WiFiMulti wifiMulti;     // 建立ESP8266WiFiMulti对象,对象名称是'wifiMulti'
 
ESP8266WebServer esp8266_server(80);// 建立网络服务器对象,该对象用于响应HTTP请求。监听端口(80)
 
bool pinState;                      // 存储引脚状态用变量
 
void setup(){
  Serial.begin(9600);          // 启动串口通讯
  delay(10);
  Serial.println("");
 
  pinMode(buttonPin, INPUT_PULLUP); // 将按键引脚设置为输入上拉模式

  wifiMulti.addAP("RFID", "rfid&iot0243"); 
  wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1"); // 将需要连接的一系列WiFi ID和密码输入这里
  wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2"); // ESP8266-NodeMCU在启动后会扫描当前网络
  wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3"); // 环境查找是否有这里列出的WiFi ID。如果有
  Serial.println("Connecting ...");                            // 则尝试使用此处存储的密码进行连接。
                                                               // 另外这里只存储了3个WiFi信息,您可以存储更多
                                                               // 的WiFi信息在此处。
  int i = 0;                                 
  while (wifiMulti.run() != WL_CONNECTED) {  // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前
    delay(1000);                             // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCU
    Serial.print(i++); Serial.print(' ');    // 将会连接信号最强的那一个WiFi信号。
  }                                          // 一旦连接WiFI成功,wifiMulti.run()将会返回“WL_CONNECTED”。这也是
                                             // 此处while循环判断是否跳出循环的条件。
  // WiFi连接成功后将通过串口监视器输出连接成功信息 
  Serial.println('\n');                     // WiFi连接成功后
  Serial.print("Connected to ");            // NodeMCU将通过串口监视器输出。
  Serial.println(WiFi.SSID());              // 连接的WiFI名称
  Serial.print("IP address:\t");            // 以及
  Serial.println(WiFi.localIP());           // NodeMCU的IP地址
  
  esp8266_server.begin();                  
  esp8266_server.on("/", handleRoot);      
  esp8266_server.onNotFound(handleNotFound);        
 
  Serial.println("HTTP esp8266_server started");//  告知用户ESP8266网络服务功能已经启动
}
 
void loop(){
  esp8266_server.handleClient();     // 处理http服务器访问
  pinState = digitalRead(buttonPin); // 获取引脚状态
}                                                                   
 
/* 以下函数处理网站首页的访问请求。此函数为本示例程序重点1
详细讲解请参见太极创客网站《零基础入门学用物联网》
第3章-第2节“通过网络服务将开发板引脚状态显示在网页中”的说明讲解。*/    
void handleRoot() {   //处理网站目录“/”的访问请求 
  esp8266_server.send(200, "text/html", sendHTML(pinState));  
}
 
/*
建立用于发送给客户端浏览器的HTML代码。此代码将会每隔5秒刷新页面。
通过页面刷新,引脚的最新状态也会显示于页面中
*/
String sendHTML(bool buttonState){
  
  String htmlCode = "<!DOCTYPE html> <html>\n";
  htmlCode +="<head><meta http-equiv='refresh' content='5'/>\n";//每隔5s自动刷新一次
  htmlCode +="<title>ESP8266 Butoon State</title>\n";
  htmlCode +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
  htmlCode +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
  htmlCode +="</style>\n";
  htmlCode +="</head>\n";
  htmlCode +="<body>\n";
  htmlCode +="<h1>ESP8266 BUTTON STATE</h1>\n";
  
  if(buttonState)
    {htmlCode +="<p>Button Status: HIGH</p>\n";}
  else
    {htmlCode +="<p>Button Status: LOW</p>\n";}
    
  htmlCode +="</body>\n";
  htmlCode +="</html>\n";
  
  return htmlCode;
}
 
// 设置处理404情况的函数'handleNotFound'
void handleNotFound(){                                        // 当浏览器请求的网络资源无法在服务器找到时,
  esp8266_server.send(404, "text/plain", "404: Not found");   // NodeMCU将调用此函数。
}

五、ESP8266闪存文件系统

ESP8266闪存文件系统基本操作

1.ESP8266闪存文件系统大小

        大小一般为4MB,具体以生产商为准,可以从Arduino IDE中读取

        若程序使用了Flash,需配置一下Flash Size

2.通过程序向闪存文件系统写入信息

/**********************************************************************
项目名称/Project           : 零基础入门学用物联网
程序名称/Program name      : esp8266-flash-write
团队/Team                 : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author               : CYNO 朔 
日期/Date(YYYYMMDD)      : 20191109
程序目的/Purpose           : 此程序用于演示如何向NodeMCU的SPIFFS中建立名为
                            notes.txt的文件,程序还将向该文件写入信息。
-----------------------------------------------------------------------
函数说明:
SPIFFS.open(file_name, "w"); 
以上函数有两个参数:
第一个参数是被操作的文件名称,本示例中该文件为/notes.txt
第二个参数"w" 代表写入文件信息。(如需了解如何读取信息,请参阅示例程序esp8266-flash-read)
***********************************************************************/


#include <FS.h>//包含闪存的一些函数
 
String file_name = "/taichi-maker/notes.txt"; //被读取的文件位置和名称
 
void setup() {
  Serial.begin(9600);
  Serial.println("");
  
  Serial.println("SPIFFS format start");
  SPIFFS.format();    // 格式化SPIFFS(Serial Peripheral Interface Flash File System)闪存文件系统
  Serial.println("SPIFFS format finish");
  
  if(SPIFFS.begin()){ // 启动SPIFFS
    Serial.println("SPIFFS Started.");
  } else {
    Serial.println("SPIFFS Failed to Start.");
  }
  
  File dataFile = SPIFFS.open(file_name, "w");// 建立File对象用于向SPIFFS中的file对象(即/notes.txt)写入信息
  dataFile.println("Hello IOT World.");       // 向dataFile写入字符串信息
  dataFile.close();                           // 完成文件写入后关闭文件
  Serial.println("Finished Writing data to SPIFFS");
}
 
void loop() {
}

3. 通过程序从闪存文件系统读取信息

/**********************************************************************
项目名称/Project           : 零基础入门学用物联网
程序名称/Program name      : esp8266-flash-read
团队/Team                 : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author               : CYNO 朔
日期/Date(YYYYMMDD)      : 20191109
程序目的/Purpose           : 此程序用于演示如何从NodeMCU的内置SPIFFS中存储的文件notes.txt读取数据。
                           notes.txt 文件内容将会通过串口监视器显示出来供用户确认。
                           注意在使用本程序以前需要先将notes.txt 文件上传到NodeMCU开发板的SPIFFS中
-----------------------------------------------------------------------
修订历史/Revision History
日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
-----------------------------------------------------------------------
函数说明:
SPIFFS.open(file_name, "r"); 
以上SPIFFS函数有两个参数:
第一个参数是被操作的文件名称,本示例中该文件为/notes.txt
第二个参数"r" 代表读取文件信息。(如需了解如何写入信息,请参阅示例程序esp8266-flash-write)
***********************************************************************/
 
#include <FS.h>
 
String file_name = "/taichi-maker/notes.txt";              //被读取的文件位置和名称
 
void setup() {
  Serial.begin(9600);
  Serial.println("");
  
  if(SPIFFS.begin()){ // 启动闪存文件系统
    Serial.println("SPIFFS Started.");
  } else {
    Serial.println("SPIFFS Failed to Start.");
  }
 
  //确认闪存中是否有file_name文件
  if (SPIFFS.exists(file_name)){
    Serial.print(file_name);
    Serial.println(" FOUND.");
  } else {
    Serial.print(file_name);
    Serial.print(" NOT FOUND.");
  }
 
  //建立File对象用于从SPIFFS中读取文件
  File dataFile = SPIFFS.open(file_name, "r");
 
  //读取文件内容并且通过串口监视器输出文件信息
  for(int i=0; i<dataFile.size(); i++){
    Serial.print((char)dataFile.read());       
  }
 
  //完成文件读取后关闭文件
  dataFile.close();                           
}
 
void loop() {
}

4. 通过程序向闪存文件系统文件添加信息

        使用"a"会从文件尾部开始写,而"w"操作将会在文件系统中建立该文件。如果文件系统有该文件,则程序将会重新建立该文件,即原有文件信息将会被覆盖。

/**********************************************************************
项目名称/Project           : 零基础入门学用物联网
程序名称/Program name      : esp8266-flash-append
团队/Team                 : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author               : CYNO 朔 
日期/Date(YYYYMMDD)      : 20191109
程序目的/Purpose           : 此程序用于演示如何向NodeMCU的内置SPIFFS中存储的文件
                            notes.txt添加数据。                      
-----------------------------------------------------------------------  
函数说明:
SPIFFS.open(file_name, "a"); 
以上SPIFFS函数有两个参数:
第一个参数是被操作的文件名称,本示例中该文件为/notes.txt
第二个参数"a" 代表添加文件信息。(如需了解如何读取信息,请参阅示例程序esp8266-flash-read)
此示例程序所演示的是向SPIFFS中的文件里添加信息。这一操作写入信息有所区别。
添加信息是不会删除文件内原有信息,而是在原有信息后面添加新的信息。
但写入操作(示例 esp8266-flash-write.ino)是将文件内容完全清除,重新写入新信息。    
***********************************************************************/
 
#include <FS.h>
 
String file_name = "/taichi-maker/notes.txt";              //被读取的文件位置和名称
 
void setup() {
  Serial.begin(9600);
  Serial.println("");
  
  if(SPIFFS.begin()){ // 启动闪存文件系统
    Serial.println("SPIFFS Started.");
  } else {
    Serial.println("SPIFFS Failed to Start.");
  }
 
  //确认闪存中是否有file_name文件
  if (SPIFFS.exists(file_name)){
    
    Serial.print(file_name);
    Serial.println(" FOUND.");
 
    File dataFile = SPIFFS.open(file_name, "a");// 建立File对象用于向SPIFFS中的file对象(即/notes.txt)写入信息 "a"即Append
    dataFile.println("This is Appended Info."); // 向dataFile添加字符串信息
    dataFile.close();                           // 完成文件操作后关闭文件   
    Serial.println("Finished Appending data to SPIFFS");
    
  } else {
    Serial.print(file_name);
    Serial.print(" NOT FOUND.");
  }
                        
}
 
void loop() {
}

5. 通过程序读取目录内容(读取文件夹中所包含文件)

/**********************************************************************
项目名称/Project           : 零基础入门学用物联网
程序名称/Program name      : esp8266-flash-folder-read
团队/Team                 : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author               : CYNO 朔
日期/Date(YYYYMMDD)      : 20191109
程序目的/Purpose           : 此程序用于演示如何从NodeMCU的内置SPIFFS中文件夹里读取文件信息
                           文件夹内容将会通过串口监视器显示出来。
                           
-----------------------------------------------------------------------
修订历史/Revision History
日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
-----------------------------------------------------------------------
函数说明:
SPIFFS.openDir(folder_name);
以上函数打开指定目录并返回一个目录对象实例。
***********************************************************************/
 
 
#include <FS.h>
 
String file_name = "/taichi-maker/myFile.txt"; //被读取的文件位置和名称
String folder_name = "/taichi-maker";         //被读取的文件夹
 
void setup() {
  Serial.begin(9600);
  Serial.println("");
  
  if(SPIFFS.begin()){ // 启动闪存文件系统
    Serial.println("SPIFFS Started.");
  } else {
    Serial.println("SPIFFS Failed to Start.");
  }
 
  File dataFile = SPIFFS.open(file_name, "w");// 建立File对象用于向SPIFFS中的file对象(即myFile.txt)写入信息
  dataFile.println("Hello Taichi-Maker.");    // 向dataFile写入字符串信息
  dataFile.close();                           // 完成文件写入后关闭文件
  Serial.println(F("Finished Writing data to SPIFFS"));
  /*添加 F() 相当于为字符串常量定义了PROGMEM属性,常量字符串仍然存储在FLASH中,
    但是程序运行时不会再将常量字符串从FLASH中copy到SRAM中,
  而是直接读取FLASH中的字符串,这样一来就节约了SRAM,但是代码运行速度就下降了。*/
 
  // 显示目录中文件内容以及文件大小
  Dir dir = SPIFFS.openDir(folder_name);  // 建立“目录”对象 dir=folder_name="/taichi-maker"
  
  while (dir.next()) {  // dir.next()用于检查目录中是否还有“下一个文件”
    Serial.println(dir.fileName()); // 输出文件名
  }
}
 
void loop() {
}

6. 从闪存文件系统中删除文件

/**********************************************************************
项目名称/Project           : 零基础入门学用物联网
程序名称/Program name      : esp8266-flash-remove
团队/Team                 : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author               : CYNO 朔 
日期/Date(YYYYMMDD)      : 20191109
程序目的/Purpose           : 此程序用于演示如何删除SPIFFS中存储的文件                        
***********************************************************************/
 
#include <FS.h>
 
String file_name = "/taichi-maker/notes.txt";              //被读取的文件位置和名称
 
void setup() {
  Serial.begin(9600);
  Serial.println("");
  
  if(SPIFFS.begin()){ // 启动闪存文件系统
    Serial.println("SPIFFS Started.");
  } else {
    Serial.println("SPIFFS Failed to Start.");
  }
  
  //从闪存中删除file_name文件
  if (SPIFFS.remove(file_name)){
    
    Serial.print(file_name);
    Serial.println(" remove sucess");
    
  } else {
    Serial.print(file_name);
    Serial.println(" remove fail");
  }                       
}
 
void loop() {
}

7. 显示闪存文件系统信息

/**********************************************************************
项目名称/Project           : 零基础入门学用物联网
程序名称/Program name      : esp8266-flash-info
团队/Team                 : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author               : CYNO 朔
日期/Date(YYYYMMDD)      : 20200204
程序目的/Purpose           : 此程序用于演示如何使用FSInfo对象来显示闪存文件系统状态
-----------------------------------------------------------------------
修订历史/Revision History
日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
***********************************************************************/
 
 
#include <FS.h>
 
FSInfo fs_info;
 
void setup() {
  Serial.begin(9600);
 
  SPIFFS.begin();       //启动SPIFFS
  Serial.println("");
  Serial.println("SPIFFS Started.");
 
  // 闪存文件系统信息
  SPIFFS.info(fs_info);
 
  // 可用空间总和(单位:字节)
  Serial.print("totalBytes: ");     
  Serial.print(fs_info.totalBytes); 
  Serial.println(" Bytes"); 
 
  // 已用空间(单位:字节)
  Serial.print("usedBytes: "); 
  Serial.print(fs_info.usedBytes);
  Serial.println(" Bytes"); 
 
  // 最大文件名字符限制(含路径和'\0')
  Serial.print("maxPathLength: "); 
  Serial.println(fs_info.maxPathLength);
 
  // 最多允许打开文件数量
  Serial.print("maxOpenFiles: "); 
  Serial.println(fs_info.maxOpenFiles);
 
  // 存储块大小
  Serial.print("blockSize: "); 
  Serial.println(fs_info.blockSize);
 
  // 存储页大小
  Serial.print("pageSize: ");
  Serial.println(fs_info.pageSize);
}
 
void loop() {
}

8.通过Arduino IDE向闪存文件系统上传文件

        将需要上传的文件保存在程序路径下的data文件夹中

        根据所要上传的文件大小调整Flash Size大小 

六、使用闪存文件系统建立功能更加丰富的网络服务器

  • 在网页中加载闪存文件系统中的图片、CSS和JavaScript
  • 通过网页控制ESP8266开发板的引脚
  • 通过网页文本框控制ESP8266开发板的PWM引脚
  • (Ajax)控制LED引脚并将A0引脚读数实时显示于网页中
  • (JavaScript)通过网页图形界面控制ESP8266的PWM引脚
  • (JavaScript)使用指针表显示模拟输入引脚数值
  • 通过网页将文件上传到ESP8266开发板闪存文件系统
  • 七、ESP8266帮助文档

    1.

    ESP8266-Arduino库 开发参考资料 – 太极创客 (taichi-maker.com)

    注意书写格式,如GET / HTTP中间的空格 

     在http请求时,我们一般会在request header 或 response header 中看到”Connection:Keep-Alive”或 “Connection:close”,这里具体的含义是有关http 请求的是否保持长连接,即链接是否复用,每次请求是复用已建立好的请求,还是重新建立一个新的请求。

    响应体即www.example.com的网页信息

    2.

    客户端向服务器发送数据信息

    GET即可以用来发送信息也可以用来请求信息 

    3.

    客户端向服务器请求数据信息

    七、JSON

    size_t  相当于  unsigned long

     

     效果如下

     

    这样也打印的是123

      String jsonCode = "{\"info\": {\"name\": \"taichimaker\",\"url\": \"www.taichi-maker.com\",\"email\": \"taichimaker@163.com\"},\"digital_pin\": {\"d1\": \"";
      jsonCode += String(digitalRead(D1));  
      jsonCode += "\",\"d2\": \""; 
      jsonCode += String(digitalRead(D2));  
      jsonCode += "\",\"d3\": \""; 
      jsonCode += String(digitalRead(D3));  
      jsonCode += "\"},\"analog_pin\": {\"a0\": \"";
      jsonCode += String(analogRead(A0));
      jsonCode += "\"}}";  
    
    
    合成后如下
    {
      "info": {
        "name": "taichimaker",
        "url": "www.taichi-maker.com",
        "email": "taichimaker@163.com"
      },
      "digital_pin": {
        "d1": "1",
        "d2": "0",
        "d3": "1"
      },
      "analog_pin": {
        "a0": "500"
      }
    }

    Json建立过程 

    {
      "info": {
        "name": "taichimaker",
        "url": "www.taichi-maker.com",
        "email": "taichimaker@163.com"
      },
      "digital_pin": {
        "d1": "1",
        "d2": "0",
        "d3": "1"
      },
      "analog_pin": {
        "a0": "500"
      }
    }
    
    
    该段Json代码有两种建立方式
    1.手动建立
    
    String rootJson(){
    
      String jsonCode = "{\"info\": {\"name\": \"taichimaker\",\"url\": \"www.taichi-maker.com\",\"email\": \"taichimaker@163.com\"},\"digital_pin\": {\"d1\": \"";
      jsonCode += String(digitalRead(D1));  
      jsonCode += "\",\"d2\": \""; 
      jsonCode += String(digitalRead(D2));  
      jsonCode += "\",\"d3\": \""; 
      jsonCode += String(digitalRead(D3));  
      jsonCode += "\"},\"analog_pin\": {\"a0\": \"";
      jsonCode += String(analogRead(A0));
      jsonCode += "\"}}";  
      
      Serial.print("jsonCode: ");Serial.println(jsonCode);
      
      return jsonCode;
    }
    
    2.使用官方工具建立(详细步骤如下图)
    https://arduinojson.org/v5/assistant/
    
    String rootJson(){
      const size_t capacity = JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(3)+140;//
      DynamicJsonDocument doc(capacity);
      
      JsonObject info = doc.createNestedObject("info");
      info["name"] = "taichimaker";
      info["url"] = "www.taichi-maker.com";
      info["email"] = "taichimaker@163.com";
      
      JsonObject digital_pin = doc.createNestedObject("digital_pin");
      digital_pin["d1"] = String(digitalRead(D1));
      digital_pin["d2"] = String(digitalRead(D2));
      digital_pin["d3"] = String(digitalRead(D3));
      
      JsonObject analog_pin = doc.createNestedObject("analog_pin");
      analog_pin["a0"] = String(analogRead(A0));
      // 结束assistant的serialize代码
     
      String jsonCode;  
      serializeJson(doc, jsonCode);
      Serial.print("Root Json Code: ");Serial.println(jsonCode); 
       
      return jsonCode;
    }

    使用官方工具建立步骤

     首先复制Serializing program中的代码,将1,0,1,500改为识别对应引脚的状态,capcity修改为Parsing program同样大小,这样运行更加稳定,然后修改串口打印方式

    单独请求一段Json

    /**********************************************************************
    项目名称/Project          : 零基础入门学用物联网
    程序名称/Program name     : cgj_server_1_serialize
    团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
    作者/Author              : CYNO朔
    日期/Date(YYYYMMDD)     : 2020517
    程序目的/Purpose          : 
    本实例用于演示esp8266的JSON数据通讯。
    操作测试本程序需要使用两台8266开发板。其中一台为服务器端,一台为客户端。
    本程序为服务器程序,功能如下:
    
    1. 实时读取A0、 D1、D2以及D3引脚的读数。
    2. 当有客户端请求信息时,将会通过http响应将引脚读数等信息发送给客户端。
       信息发送格式为JSON格式。
    3. 使用ArduinoJson库的Serialize方式建立响应JSON信息
    -----------------------------------------------------------------------
    修订历史/Revision History  
    日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
    ***********************************************************************/
    #include <ESP8266WiFi.h>        // 本程序使用 ESP8266WiFi库
    #include <ESP8266WiFiMulti.h>   //  ESP8266WiFiMulti库
    #include <ESP8266WebServer.h>   //  ESP8266WebServer库
    #include <ArduinoJson.h>        //  ArduinoJson库
    
    #define buttonPin D3            // 按钮引脚D3
    
    ESP8266WiFiMulti wifiMulti;     // 建立ESP8266WiFiMulti对象,对象名称是'wifiMulti'
     
    ESP8266WebServer esp8266_server(80);// 建立网络服务器对象,该对象用于响应HTTP请求。监听端口(80)
    
    IPAddress local_IP(192, 168, 3, 12); // 设置ESP8266-NodeMCU联网后的IP
    IPAddress gateway(192, 168, 3, 1);    // 设置网关IP(通常网关IP是WiFI路由IP)
    IPAddress subnet(255, 255, 255, 0);   // 设置子网掩码
    IPAddress dns(192,168,3,1);           // 设置局域网DNS的IP(通常局域网DNS的IP是WiFI路由IP)
             
    void setup(){
      Serial.begin(9600);          // 启动串口通讯
      Serial.println("");
    
      // 将引脚设置为输入上拉模式
      pinMode(D1, INPUT_PULLUP);
      pinMode(D2, INPUT_PULLUP);
      pinMode(buttonPin, INPUT_PULLUP);   // NodeMCU开发板按键连接在D3引脚上
       
      // 设置开发板网络环境
      if (!WiFi.config(local_IP, gateway, subnet)) {
        Serial.println("Failed to Config ESP8266 IP"); 
      } 
    
      //通过addAp函数存储  WiFi名称       WiFi密码
      wifiMulti.addAP("RFID", "rfid&iot0243"); // 这三条语句通过调用函数addAP来记录3个不同的WiFi网络信息。
      wifiMulti.addAP("taichi-maker2", "87654321"); // 这3个WiFi网络名称分别是taichi-maker, taichi-maker2, taichi-maker3。
      wifiMulti.addAP("taichi-maker3", "13572468"); // 这3个网络的密码分别是123456789,87654321,13572468。
                                                    // 此处WiFi信息只是示例,请在使用时将需要连接的WiFi信息填入相应位置。
                                                    // 另外这里只存储了3个WiFi信息,您可以存储更多的WiFi信息在此处。
    
      int i = 0;                                 
      while (wifiMulti.run() != WL_CONNECTED) {  // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前
        delay(1000);                             // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCU
        Serial.print(i++); Serial.print(' ');    // 将会连接信号最强的那一个WiFi信号。
      }                                          // 一旦连接WiFI成功,wifiMulti.run()将会返回“WL_CONNECTED”。这也是
                                                 // 此处while循环判断是否跳出循环的条件。
      // WiFi连接成功后将通过串口监视器输出连接成功信息 
      Serial.println('\n');                     // WiFi连接成功后
      Serial.print("Connected to ");            // NodeMCU将通过串口监视器输出。
      Serial.println(WiFi.SSID());              // 连接的WiFI名称
      Serial.print("IP address:\t");            // 以及
      Serial.println(WiFi.localIP());           // NodeMCU的IP地址
      
      esp8266_server.begin();                  
      esp8266_server.on("/", handleRoot);    
      esp8266_server.on("/info", handleInfo); 
      esp8266_server.on("/digital_pin", handleDigitalPin);       
    
      Serial.println("HTTP esp8266_server started");//  告知用户ESP8266网络服务功能已经启动
    }
    
    void loop(){
      // 处理http服务器访问
      esp8266_server.handleClient(); 
    }                                                                   
    
    void handleRoot() {   //处理网站目录“/”的访问请求 
      esp8266_server.send(200, "application/json", rootJson());  
    }
    
    void handleInfo(){
      esp8266_server.send(200, "application/json", infoJson());  
    }
    
    void handleDigitalPin(){
      esp8266_server.send(200, "application/json", digitalpinJson());  
    }
    
    // 实时获取ESP8266开发板引脚信息并且建立JSON信息
    // 以便ESP8266服务器通过响应信息发送给客户端
    String rootJson(){
      // 开始ArduinoJson Assistant的serialize代码 
      const size_t capacity = JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(3)+140;
      DynamicJsonDocument doc(capacity);
      
      JsonObject info = doc.createNestedObject("info");
      info["name"] = "taichimaker";
      info["url"] = "www.taichi-maker.com";
      info["email"] = "taichimaker@163.com";
      
      JsonObject digital_pin = doc.createNestedObject("digital_pin");
      digital_pin["d1"] = String(digitalRead(D1));
      digital_pin["d2"] = String(digitalRead(D2));
      digital_pin["d3"] = String(digitalRead(D3));
      
      JsonObject analog_pin = doc.createNestedObject("analog_pin");
      analog_pin["a0"] = String(analogRead(A0));
      // 结束assistant的serialize代码
     
      String jsonCode;  
      serializeJson(doc, jsonCode);
      Serial.print("Root Json Code: ");Serial.println(jsonCode); 
       
      return jsonCode;
    }
    
    //V6版本
    String infoJson(){
      StaticJsonDocument<128> doc;
    
      JsonObject info = doc.createNestedObject("info");
      info["name"] = "taichimaker";
      info["url"] = "www.taichi-maker.com";
      info["email"] = "taichimaker@163.com";
      String jsonCode;
      serializeJson(doc, jsonCode);
      return jsonCode;
    }
    
    
    String digitalpinJson(){
      StaticJsonDocument<96> doc;
    
      JsonObject digital_pin = doc.createNestedObject("digital_pin");
      digital_pin["d1"] = String(digitalRead(D1));
      digital_pin["d2"] = String(digitalRead(D2));
      digital_pin["d3"] = String(digitalRead(D3));
      String jsonCode;
      serializeJson(doc, jsonCode);
      return jsonCode;
    }
    
    //V5版本
    /*
    String infoJson(){
      const size_t capacity = JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(3)+90;
      DynamicJsonBuffer jsonBuffer(capacity);
    
      JsonObject& root = jsonBuffer.createObject();
    
      JsonObject& info = root.createNestedObject("info");
      info["name"] = "taichimaker";
      info["url"] = "www.taichi-maker.com";
      info["email"] = "taichimaker@163.com";
    
      String jsonCode;
      root.printTo(jsonCode);
    
      return jsonCode;
    }
    */
    
    /*
    String digitalpinJson(){
      const size_t capacity = JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(3)+30;
      DynamicJsonBuffer jsonBuffer(capacity);
    
      JsonObject& root = jsonBuffer.createObject();
    
      JsonObject& digital_pin = root.createNestedObject("digital_pin");
      digital_pin["d1"] = String(digitalRead(D1));
      digital_pin["d2"] = String(digitalRead(D2));
      digital_pin["d3"] = String(digitalRead(D3));
    
      String jsonCode;
      root.printTo(jsonCode);
    
      return jsonCode;
    }
    */
    

    ESP8266客户端发送多种JSON数据信ddd息

    /**********************************************************************
    项目名称/Project          : 零基础入门学用物联网
    程序名称/Program name     : client_send_multi_json
    团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
    作者/Author              : Dapenson
    日期/Date(YYYYMMDD)     : 20200425
    程序目的/Purpose          : 
    本实例用于演示esp8266的json数据通讯。
    操作测试本程序需要使用两台8266开发板。其中一台为服务器端,一台为客户端
    
    本程序为客户端程序,功能如下:
    1. 实时读取A0、 D1、D2以及D3引脚的读数。
    2. 向服务器发送多种Json信息。
    3. 在有些发送的信息中包含有D3引脚状态从而控制服务器开发板上
       的LED点亮或熄灭
    -----------------------------------------------------------------------
    修订历史/Revision History  
    日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
    20200521      CYNO朔           001        调整以使程序与csj_client一致
    -----------------------------------------------------------------------
    本示例程序为太极创客团队制作的《零基础入门学用物联网》中示例程序。
    该教程为对物联网开发感兴趣的朋友所设计和制作。如需了解更多该教程的信息,请参考以下网页:
    http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/esp8266-nodemcu-web-client/http-request/
    ***********************************************************************/
    #include <ESP8266WiFi.h>
    #include <ESP8266WiFiMulti.h>
    #include <ArduinoJson.h>
    
    ESP8266WiFiMulti wifiMulti;           // 建立ESP8266WiFiMulti对象
     
    const char* host = "192.168.3.12"; // 网络服务器地址
    const int httpPort = 80;            // http端口80
    
    void setup(){
      Serial.begin(9600);          
      Serial.println("");
      
       //通过addAp函数存储  WiFi名称       WiFi密码
      wifiMulti.addAP("RFID", "rfid&iot0243"); // 这三条语句通过调用函数addAP来记录3个不同的WiFi网络信息。
      wifiMulti.addAP("taichi-maker2", "87654321"); // 这3个WiFi网络名称分别是taichi-maker, taichi-maker2, taichi-maker3。
      wifiMulti.addAP("taichi-maker3", "13572468"); // 这3个网络的密码分别是123456789,87654321,13572468。
                                                    // 此处WiFi信息只是示例,请在使用时将需要连接的WiFi信息填入相应位置。
                                                    // 另外这里只存储了3个WiFi信息,您可以存储更多的WiFi信息在此处。
      int i = 0;                                 
      while (wifiMulti.run() != WL_CONNECTED) {  // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前
        delay(1000);                             // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCU
        Serial.print(i++); Serial.print(' ');    // 将会连接信号最强的那一个WiFi信号。
      }                                          // 一旦连接WiFI成功,wifiMulti.run()将会返回“WL_CONNECTED”。这也是
                                                 // 此处while循环判断是否跳出循环的条件。
      // WiFi连接成功后将通过串口监视器输出连接成功信息 
      Serial.println('\n');                     // WiFi连接成功后
      Serial.print("Connected to ");            // NodeMCU将通过串口监视器输出。
      Serial.println(WiFi.SSID());              // 连接的WiFI名称
      Serial.print("IP address:\t");            // 以及
      Serial.println(WiFi.localIP());           // NodeMCU的IP地址
    }
     
    void loop(){
      // 发送HTTP请求,使用参数控制函数发送不同类型JSON
      httpRequest(1);    
      delay(2000);
      httpRequest(2); 
      delay(2000); 
      httpRequest(3); 
      delay(2000);   
      Serial.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
    }
     
    // 向服务器发送HTTP请求,请求信息中包含json信息
    void httpRequest(int jsonType){
      // 建立WiFi客户端对象,对象名称client
      WiFiClient client;    
    
      // 根据jsonType参数建立不同类型JSON
      String payloadJson = buildJson(jsonType); 
     
      // 建立字符串,用于HTTP请求
      String httpRequest =  String("GET /") + " HTTP/1.1\r\n" +
                            "Host: " + host + "\r\n" +
                            "Connection: close\r\n\r\n" + payloadJson;
      
      // 通过串口输出连接服务器名称以便查阅连接服务器的网址                      
      Serial.print("Connecting to "); 
      Serial.print(host); 
    
      if (client.connect(host, httpPort)){ 
        Serial.println(" Success!");            // 连接成功后串口输出“Success”信息
        
        client.print(httpRequest);              // 向服务器发送请求
        Serial.println("Sending request: ");    // 通过串口输出HTTP请求信息内容以便查阅
        Serial.println(httpRequest);     
        
        Serial.println("Web Server Response:"); // 通过串口监视输出服务器响应信息        
        while (client.connected() || client.available()){ 
          if (client.available()){
            String line = client.readStringUntil('\n');
            Serial.println(line);
          }
        } 
      } else{    // 如果连接不成功则通过串口输出“连接失败”信息
        Serial.println(" failed!");
      } 
      
      client.stop();                      // 断开与服务器的连接
      Serial.print("Disconnected from "); // 并且通过串口输出断开连接信息
      Serial.println(host);   
      Serial.println("*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*"); 
    }
    
    //建立json信息v6
    String buildJson(int type){
    
      StaticJsonDocument<256> doc;
      
      if (type == 1){
        JsonObject info = doc.createNestedObject("info");
        info["name"] = "taichimaker";
        info["url"] = "www.taichi-maker.com";
        info["email"] = "taichimaker@163.com";
      }
    
      if (type == 2){
        JsonObject digital_pin = doc.createNestedObject("digital_pin");
        digital_pin["d1"] = String(digitalRead(D1));
        digital_pin["d2"] = String(digitalRead(D2));
        digital_pin["d3"] = String(digitalRead(D3));
      }
    
      if (type == 3){
        doc["analog_pin"]["a0"] = String(analogRead(A0));
        //JsonObject analog_pin = doc.createNestedObject("analog_pin");
        //analog_pin["a0"] = String(analogRead(A0));
      }
    
      String jsonCode;  
      serializeJson(doc, jsonCode);
      Serial.print("type="); 
      Serial.println(type); 
      Serial.print("json Code: ");Serial.println(jsonCode); 
      return jsonCode;
    }
    
    /*
    //建立json信息v5
    String buildJson(int type){
    
      Serial.println("开始建立Json信息"); 
      // 开始ArduinoJson Assistant的serialize代码 
      const size_t capacity = JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(3)+140;
      DynamicJsonDocument doc(capacity);
      
      if (type == 1){
        JsonObject info = doc.createNestedObject("info");
        info["name"] = "taichimaker";
        info["url"] = "www.taichi-maker.com";
        info["email"] = "taichimaker@163.com";
      }
    
      if (type == 2){
        JsonObject digital_pin = doc.createNestedObject("digital_pin");
        digital_pin["d1"] = String(digitalRead(D1));
        digital_pin["d2"] = String(digitalRead(D2));
        digital_pin["d3"] = String(digitalRead(D3));
      }
    
      if (type == 3){
        JsonObject analog_pin = doc.createNestedObject("analog_pin");
        analog_pin["a0"] = String(analogRead(A0));
      }
      // 结束assistant的serialize代码
    
      String jsonCode;  
      serializeJson(doc, jsonCode);
      Serial.print("type="); 
      Serial.println(type); 
      Serial.print("json Code: ");Serial.println(jsonCode); 
      return jsonCode;
    }
    */
    

    六、MQTT的connect与publish在各情景下的区别

    订阅主题-基础

    void connectMQTTServer(){
      // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
      String clientId = "esp8266-" + WiFi.macAddress();
    
      // 连接MQTT服务器
      if (mqttClient.connect(clientId.c_str())) { 
        Serial.println("MQTT Server Connected.");
        Serial.println("Server Address: ");
        Serial.println(mqttServer);
        Serial.println("ClientId:");
        Serial.println(clientId);
      } else {
        Serial.print("MQTT Server Connect Failed. Client State:");
        Serial.println(mqttClient.state());
        delay(3000);
      }   
    }

    订阅主题-Qos

    const int subQoS = 1;     // 客户端订阅主题时使用的QoS级别(截止2020-10-07,仅支持QoS = 1,不支持QoS = 2)
    
    const char* willTopic = "willTopic"; // 遗嘱主题名称
    const int willQos = 0;               // 遗嘱QoS
    const int willRetain = false;        // 遗嘱保留
    const char* willMsg = "willMsg";     // 遗嘱主题信息
    const bool cleanSession = false; // 清除会话(如QoS>0必须要设为false)
    
    // 连接MQTT服务器并订阅信息
    void connectMQTTserver(){
      // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
      String clientId = "client-" + WiFi.macAddress();
    
      /* 连接MQTT服务器
      boolean connect(const char* id, const char* user, 
                      const char* pass, const char* willTopic, 
                      uint8_t willQos, boolean willRetain, 
                      const char* willMessage, boolean cleanSession); 
      若让设备在离线时仍然能够让qos1工作,则connect时的cleanSession需要设置为false                
                      */
      if (mqttClient.connect(clientId.c_str(), NULL, NULL, willTopic, willQos, willRetain, willMsg, cleanSession)) { 
        Serial.print("MQTT Server Connected. ClientId: ");
        Serial.println(clientId);
        subscribeTopic(); // 订阅指定主题
      } else {
        Serial.print("MQTT Server Connect Failed. Client State:");
        Serial.println(mqttClient.state());
        delay(5000);
      }   
    }
    
    // 订阅指定主题
    void subscribeTopic(){
      String topicString = "Qos-test";
      char subTopic[topicString.length() + 1];  
      strcpy(subTopic, topicString.c_str());
      
      // 通过串口监视器输出是否成功订阅主题以及订阅的主题名称
      // 请注意subscribe函数第二个参数数字为QoS级别。这里为QoS = 1
      if(mqttClient.subscribe(subTopic, subQoS)){
        Serial.print("Subscribed Topic: ");
        Serial.println(subTopic);
      } else {
        Serial.print("Subscribe Fail...");
      }  
    }

    订阅主题-遗嘱

    // 遗嘱设置
    const char* willMsg = "CLIENT-OFFLINE"; // 遗嘱消息内容
    const int willQoS = 0;                   // 遗嘱QoS
    const bool willRetain = false;           // 遗嘱保留
    
    // 连接MQTT服务器并订阅信息
    void connectMQTTserver(){
      
      // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
      String clientId = "esp8266-" + WiFi.macAddress();
    
      // 建立遗嘱主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址,最后
      // 以“-Will”结尾,这是为确保不同ESP8266客户端的遗嘱主题名称各不相同。
      String willString = "Taichi-Maker-" + WiFi.macAddress() + "-Will";
      char willTopic[willString.length() + 1];  
      strcpy(willTopic, willString.c_str());
    
      // 连接MQTT服务器,在连接过程中提供以下参数:
      // 客户端ID,遗嘱主题,遗嘱QoS,遗嘱保留,遗嘱信息
      if (mqttClient.connect(clientId.c_str(), willTopic, willQoS, willRetain, willMsg)){ 
        Serial.println("MQTT Server Connected.");
        Serial.print("Server Address: ");Serial.println(mqttServer);
        Serial.print("ClientId: ");Serial.println(clientId);
        Serial.print("Will Topic: ");Serial.println(willTopic);    
      } else {
        Serial.print("MQTT Server Connect Failed. Client State:");
        Serial.println(mqttClient.state());
        delay(5000);
      }   
    }
    

    订阅主题-用户密码认证

    // MQTT服务端连接用户名密码
    const char* mqttUserName = "test-user";
    const char* mqttPassword = "ranye-iot";
    
    void connectMQTTServer(){
      // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
      String clientId = "esp8266-" + WiFi.macAddress();
     
      // 连接MQTT服务器。此处使用了程序首部定义的用户名和密码来实现MQTT服务端认证
      if (mqttClient.connect(clientId.c_str(), mqttUserName, mqttPassword)) { 
        Serial.println("MQTT Server Connected.");
        Serial.print("Server Address: ");
        Serial.println(mqttServer);
        Serial.print("ClientId: ");
        Serial.println(clientId);
      } else {
        Serial.print("MQTT Server Connect Failed. Client State:");
        Serial.println(mqttClient.state());
        delay(3000);
      }   
    }
    
    // 发布信息
    void pubMQTTmsg(){
      static int value;
     
      // 建立发布主题。使用然也物联免费端口时,主题名称必须以test-user/为前缀。
      String topicString = "test-user/" + WiFi.macAddress();
      char publishTopic[topicString.length() + 1];  
      strcpy(publishTopic, topicString.c_str());
     
      // 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。
      String messageString = "Hello World " + String(value++); 
      char publishMsg[messageString.length() + 1];   
      strcpy(publishMsg, messageString.c_str());
     
      // 实现ESP8266向主题发布信息
      if(mqttClient.publish(publishTopic, publishMsg)){
        Serial.println("Publish Topic:");Serial.println(publishTopic);
        Serial.println("Publish message:");Serial.println(publishMsg);      
      } else {
        Serial.println("Message Publish Failed."); 
      }
    }

    发布消息

    void pubMQTTmsg(){
      static int value; // 客户端发布信息用数字
    
      // 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。
      // 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,
      String topicString = "Taichi-Maker-Pub-" + WiFi.macAddress();
      char publishTopic[topicString.length() + 1];  
      strcpy(publishTopic, topicString.c_str());
    
      // 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。
      String messageString = "Hello World " + String(value++); 
      char publishMsg[messageString.length() + 1];   
      strcpy(publishMsg, messageString.c_str());
      
      // 实现ESP8266向主题发布信息
      if(mqttClient.publish(publishTopic, publishMsg)){
        Serial.println("Publish Topic:");Serial.println(publishTopic);
        Serial.println("Publish message:");Serial.println(publishMsg);    
      } else {
        Serial.println("Message Publish Failed."); 
      }
    }
    

    收到信息后的回调函数

    // 收到信息后的回调函数
    void receiveCallback(char* topic, byte* payload, unsigned int length) {
      Serial.print("Message Received [");
      Serial.print(topic);
      Serial.print("] ");
      for (int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
      }
      Serial.println("");
      Serial.print("Message Length(Bytes) ");
      Serial.println(length);
     
      if ((char)payload[0] == '1') {     // 如果收到的信息以“1”为开始
        digitalWrite(LED_BUILTIN, LOW);  // 则点亮LED。
      } else {                           
        digitalWrite(LED_BUILTIN, HIGH); // 否则熄灭LED。
      }
    }

    MQTT汇总

    #include <ESP8266WiFi.h>
    #include <PubSubClient.h>
    
    // 设置wifi接入信息(请根据您的WiFi信息进行修改)
    const char* ssid = "RFID";
    const char* password = "12345678";
    const char* mqttServer = "test.ranye-iot.net";
    
    WiFiClient wifiClient;
    PubSubClient mqttClient(wifiClient);
    
    const int subQoS = 1;     // 客户端订阅主题时使用的QoS级别(截止2020-10-07,仅支持QoS = 1,不支持QoS = 2)
    const bool cleanSession = false; // 清除会话(如QoS>0必须要设为false)
    
    const char* willTopic = "willTopic";    // 遗嘱主题名称
    const char* willMsg = "willMsg";        // 遗嘱主题信息
    const int willQos = 0;                  // 遗嘱QoS
    const int willRetain = false;           // 遗嘱保留
    const char* mqttUserName = "test-user"; // 服务端连接用户名
    const char* mqttPassword = "ranye-iot"; // 服务端连接密码
    //然也物联免费端口只提供这一个用户名密码
    void setup() {
      pinMode(LED_BUILTIN, OUTPUT);     // 设置板上LED引脚为输出模式
      digitalWrite(LED_BUILTIN, HIGH);  // 启动后关闭板上LED
      Serial.begin(9600);               // 启动串口通讯
      
      //设置ESP8266工作模式为无线终端模式
      WiFi.mode(WIFI_STA);
      
      // 连接WiFi
      connectWifi();
      
      // 设置MQTT服务器和端口号
      mqttClient.setServer(mqttServer, 1883);
      mqttClient.setCallback(receiveCallback);
    
      // 连接MQTT服务器
      connectMQTTserver();
    }
    
    void loop() {
      // 如果开发板未能成功连接服务器,则尝试连接服务器
      if (!mqttClient.connected()) {
        connectMQTTserver();
      }
       // 处理信息以及心跳
       mqttClient.loop();
    }
    
    // 连接MQTT服务器并订阅信息
    void connectMQTTserver(){
      // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
      String clientId = "client-" + WiFi.macAddress();
    
      /* 连接MQTT服务器
      boolean connect(const char* id, const char* user, 
                      const char* pass, const char* willTopic, 
                      uint8_t willQos, boolean willRetain, 
                      const char* willMessage, boolean cleanSession); 
      若让设备在离线时仍然能够让qos1工作,则connect时的cleanSession需要设置为false                
                      */
      if (mqttClient.connect(clientId.c_str(), mqttUserName, 
                             mqttPassword, willTopic, 
                             willQos, willRetain, willMsg, cleanSession)) { 
        Serial.print("MQTT Server Connected. ClientId: ");
        Serial.println(clientId);
        Serial.print("MQTT Server: ");
        Serial.println(mqttServer);    
        
        subscribeTopic(); // 订阅指定主题
      } else {
        Serial.print("MQTT Server Connect Failed. Client State:");
        Serial.println(mqttClient.state());
        delay(5000);
      }   
    }
    
    // 收到信息后的回调函数
    void receiveCallback(char* topic, byte* payload, unsigned int length) {
      Serial.print("Message Received [");
      Serial.print(topic);
      Serial.print("] ");
      for (int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
      }
      Serial.println("");
      Serial.print("Message Length(Bytes) ");
      Serial.println(length);
    
      if ((char)payload[0] == '1') {     // 如果收到的信息以“1”为开始
        digitalWrite(BUILTIN_LED, LOW);  // 则点亮LED。
      } else {                           
        digitalWrite(BUILTIN_LED, HIGH); // 否则熄灭LED。
      }
    }
    
    // 订阅指定主题
    void subscribeTopic(){
      // 建立订阅主题。使用然也物联的免费端口进行用户名密码连接时只能以"test-user/"开头
      // 这么做是为确保不同设备使用同一个MQTT服务器测试消息订阅时,所订阅的主题名称不同
      String topicString = "test-user/" + WiFi.macAddress();
      char subTopic[topicString.length() + 1];  
      strcpy(subTopic, topicString.c_str());
      
      // 通过串口监视器输出是否成功订阅主题以及订阅的主题名称
      // 请注意subscribe函数第二个参数数字为QoS级别。这里为QoS = 1
      if(mqttClient.subscribe(subTopic, subQoS)){
        Serial.print("Subscribed Topic: ");
        Serial.println(subTopic);
      } else {
        Serial.print("Subscribe Fail...");
      }  
    }
    
    // ESP8266连接wifi
    void connectWifi(){
    
      WiFi.begin(ssid, password);
     
      //等待WiFi连接,成功连接后输出成功信息
      while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.print(".");
      }
      Serial.println("");
      Serial.println("WiFi Connected!");  
      Serial.println(""); 
    }

    物联沃分享整理
    物联沃-IOTWORD物联网 » ESP8266零基础入门学习笔记-太极创客团队教程

    发表评论