ESP8266 NodeMCU
ESP8266-NodeMCU是一个开源硬件开发板,由于它支持WIFI功能,所以在IOT领域,Arduino开发板最大的对手之一就是ESP8266-NodeMCU开发板。ESP8266-NodeMCU尺寸与Nano类似,他并不是Arduino团队开发的,但是我们也可以使用Arduino IDE 对其进行开发,NodeMCU是以ESP8266芯片为核心的开发板,无论是芯片价格还是开发板的价格都是比较实惠的,总体来说ESP8266作为IOT的接入设备是非常划算的。
ESP8266 引脚对应图
NodeMCU开发板上的两排插针与ESP8266芯片的引脚相连。有了开发板上的两排插针,我们就可以很轻松的使用杜邦线将芯片的引脚接到实验电路中:
所以,当我们编写 digitalWrite(4, HIGH)
通过digitalWrite函数将引脚4设置为高电平,则意味着我们将GPIO4置为高电平,是开发板的D2引脚,而不是开发板的D4引脚。 这与digitalWrite(D2, HIGH)
是等效的。
ESP8266 引脚说明
可用引脚
ESP8266芯片有17个GPIO引脚(GPIO0 - GPIO16
)。这些引脚中的GPIO6 - GPIO 11
被用于连接开发板的闪存(Flash Memory)。如果在实验电路中使用GPIO6 - GPIO11
,NodeMCU开发板将无法正常工作。因此建议不要使用 GPIO6 - GPIO11
。
电压限制
NodeMCU开发板引脚的输入输出电压限制是3.3 V。如果向引脚施加3.6V以上的电压就有可能对芯片电路造成损坏。同时请注意,这些引脚的最大输出电流是12mA。
引脚特殊情况
GPIO2引脚在NodeMCU开发板启动时是不能连接低电平的。
GPIO15引脚在开发板运行中一直保持低电平状态。因此不要使用GPIO15引脚来读取开关状态或进行I²C通讯。
GPIO0引脚在开发板运行中需要一直保持高电平状态。否则ESP8266将进入程序上传工作模式也就无法正常工作了。其实无需对GPIO0引脚进行额外操作,因为NodeMCU的内置电路可以确保GPIO0引脚在工作时连接高电平而在上传程序时连接低电平。
上/下拉电阻
GPIO 0 -15
引脚都配有内置上拉电阻。这一点与Arduino十分类似。GPIO16引脚配有内置下拉电阻。
模拟输入引脚
ESP8266 只有一个模拟输入引脚(该引脚通过模拟- 数字转换将引脚上的模拟电压数值转化为数字量)。此引脚可以读取的模拟电压值为 0 – 1.0V。所以ESP8266 芯片模拟输入引脚连接在1.0V以上电压可能损坏ESP8266芯片。以上所描述的是针对ESP8266芯片的引脚。
而对于NodeMCU开发板引脚,情况就不同了,NodeMCU开发板配有降压电路。可以用NodeMCU开发板的模拟输入引脚读取0-3.3V的模拟电压信号。
通信引脚
串口
ESP8266有2个硬件串行端口(UART)。
串行端口0(UART0)使用GPIO1和GPIO3引脚。其中GPIO1引脚是TX0,GPIO3是RX0。
串行端口1(UART1)使用GPIO2和GPIO8引脚。其中GPIO2引脚是TX1,GPIO8是RX1。请注意,由于GPIO8被用于连接闪存芯片,串行端口1只能使用GPIO2来向外发送串行数据。
I2C
ESP8266只有软件模拟的I²C端口,没有硬件I²C端口。也就是说我们可以使用任意的两个GPIO引脚通过软件模拟来实现I²C通讯。ESP8266的数据表(datasheet)中,GPIO2标注为SDA,GPIO14标注为SCL。
SPI
GPIO14 — CLK、GPIO12 — MISO、GPIO13 — MOSI、GPIO 15 — CS(SS)
开发环境搭建
安装开发板驱动程序,安装 Arduino IDE , 通过开发板管理器安装ESP8266插件,插件也可以离线安装。
如果下面这段LED闪烁程序可以正常运行,那么证明环境是没问题的:
1void setup() {
2 // put your setup code here, to run once:
3 pinMode(LED_BUILTIN, OUTPUT);
4}
5
6void loop() {
7 // put your main code here, to run repeatedly:
8 digitalWrite(LED_BUILTIN, HIGH);
9 delay(1000);
10 digitalWrite(LED_BUILTIN, LOW);
11 delay(1000);
12}
接入点模式(Access Point)
NodeMCU可以建立WiFi网络供其它设备连接。当NodeMCU以此模式运行时,我们可以使用手机搜索NodeMCU所发出的WiFi网络并进行连接 。
1#include <ESP8266WiFi.h> // ESP8266WiFi库
2
3// WIFI名称
4const char *ssid = "ESP8266-WIFI";
5
6// WIFI密码(空字符串则表示无需密码)
7const char *password = "12345678";
8
9void setup() {
10 // 启动串口通讯(波特率9600)
11 Serial.begin(9600);
12
13 // 用于启动NodeMCU的AP模式
14 WiFi.softAP(ssid, password);
15
16 // 串口输出名称和密码
17 Serial.println(ssid);
18 Serial.println(WiFi.softAPIP());
19}
20
21void loop() {
22
23}
无线终端模式(Station)
ESP8266可通过WiFi连接无线路由器,这与手机通过WiFi连接无线路由器的模式相同,这就是无线终端模式。
1// NodeMCU无线终端模式连接WiFi
2#include <ESP8266WiFi.h>
3
4// 将要连接的WiFi和密码
5const char* ssid = "TP-LINK";
6const char* password = "xpassword";
7
8void setup() {
9 Serial.begin(9600);
10 // 启动网络连接
11 WiFi.begin(ssid, password);
12 // 告知用户NodeMCU正在尝试WiFi连接
13 Serial.print("Connecting to ");
14 Serial.print(ssid); Serial.println(" ...");
15
16 // 如果WiFi连接成功则返回值为WL_CONNECTED
17 int i = 0;
18 while (WiFi.status() != WL_CONNECTED) {
19 delay(1000);
20 Serial.print(i++); Serial.print(' ');
21 }
22
23 Serial.println("");
24 Serial.println("Connection success!");
25 Serial.print("IP address: ");
26 Serial.println(WiFi.localIP());
27}
28
29void loop() {
30}
如果NodeMCU需要在多个地方使用,这时候就需要它能存储多个地点的WiFi信息。通过以下示例程序,NodeMCU可以在它所处的网络环境里搜索预先存储好的WiFi。一旦找到预存的WiFi名称,NodeMCU将会使用预存的密码信息尝试连接该WiFi。如果同时找到多个预存WiFi,NodeMCU将会尝试连接信号最强的WiFi。
1// NodeMCU无线终端模式连接WiFi
2#include <ESP8266WiFi.h>
3#include <ESP8266WiFiMulti.h>
4
5ESP8266WiFiMulti wifiMulti;
6
7void setup() {
8 Serial.begin(9600);
9
10 // 将要连接的WiFi和密码
11 wifiMulti.addAP("TP-LINK0", "xpassword");
12 wifiMulti.addAP("TP-LINK1", "xpassword");
13 wifiMulti.addAP("TP-LINK2", "xpassword");
14
15 Serial.println("Connecting ...");
16 int i = 0;
17 // 如果WiFi连接成功则返回值为WL_CONNECTED
18 // wifiMulti.run()会在当前环境中搜索并连接信号最强的WIFI
19 while (wifiMulti.run() != WL_CONNECTED) {
20 delay(1000);
21 Serial.print('.');
22 }
23
24 Serial.println("");
25 Serial.println("Connection success!");
26 Serial.print("IP address: ");
27 Serial.println(WiFi.localIP());
28}
29
30void loop() {
31}
ESP8266 HTTP服务器
1// NodeMCU无线终端模式连接WiFi
2#include <ESP8266WiFi.h>
3#include <ESP8266WebServer.h>
4
5// 将要连接的WiFi和密码
6const char* ssid = "TP-LINK";
7const char* password = "xpassword";
8
9// 设置Web服务器端口
10ESP8266WebServer esp8266_server(8080);
11
12void setup() {
13 Serial.begin(9600);
14 // 启动网络连接
15 WiFi.begin(ssid, password);
16 // 告知用户NodeMCU正在尝试WiFi连接
17 Serial.print("Connecting to ");
18 Serial.print(ssid); Serial.println(" ...");
19
20 // 如果WiFi连接成功则返回值为WL_CONNECTED
21 int i = 0;
22 while (WiFi.status() != WL_CONNECTED) {
23 delay(1000);
24 Serial.print(i++); Serial.print(' ');
25 }
26
27 Serial.println("");
28 Serial.println("Connection success!");
29 Serial.print("IP address: ");
30 Serial.println(WiFi.localIP());
31
32
33 // ---- WebServer ---------
34 esp8266_server.begin();
35 esp8266_server.on("/", handleRoot);
36 esp8266_server.onNotFound(handleNotFound);
37 // ---- WebServer ---------
38}
39
40void handleRoot() { //处理网站根目录“/”的访问请求
41 esp8266_server.send(200, "text/plain", "Hello from ESP8266");
42}
43
44// 设置处理404情况的函数'handleNotFound'
45void handleNotFound(){
46 esp8266_server.send(404, "text/plain", "404: Not found");
47}
48
49void loop() {
50 esp8266_server.handleClient();
51}
ESP8266WebServer就是Web服务器对象,begin方法启动服务器,on方法处理访问路径绑定到处理方法上面,onNotFound用于处理404的情况, handleClient 用于检查有没有设备通过网络向NodeMCU发送请求,如果有的画就会调用对应的处理函数。
那么现在就可以用网页表单来控制开发板的LED了,代码如下:
1// NodeMCU无线终端模式连接WiFi
2#include <ESP8266WiFi.h>
3#include <ESP8266WebServer.h>
4
5// 将要连接的WiFi和密码
6const char* ssid = "TP-LINK";
7const char* password = "xpassword";
8
9// 设置Web服务器端口
10ESP8266WebServer esp8266_server(8080);
11
12void setup() {
13 Serial.begin(9600);
14 // 启动网络连接
15 WiFi.begin(ssid, password);
16 // 告知用户NodeMCU正在尝试WiFi连接
17 Serial.print("Connecting to ");
18 Serial.print(ssid); Serial.println(" ...");
19
20 // 如果WiFi连接成功则返回值为WL_CONNECTED
21 int i = 0;
22 while (WiFi.status() != WL_CONNECTED) {
23 delay(1000);
24 Serial.print(i++); Serial.print(' ');
25 }
26
27 Serial.println("");
28 Serial.println("Connection success!");
29 Serial.print("IP address: ");
30 Serial.println(WiFi.localIP());
31
32
33 // ---- WebServer ---------
34 esp8266_server.begin();
35 esp8266_server.on("/", handleRoot);
36 esp8266_server.onNotFound(handleNotFound);
37 esp8266_server.on("/LED", handleLED);
38 // ---- WebServer ---------
39
40 pinMode(LED_BUILTIN, OUTPUT);
41}
42
43void handleRoot() { //处理网站根目录“/”的访问请求
44 esp8266_server.send(200, "text/html", "<form action=\"/LED\" method=\"POST\"><input type=\"submit\" value=\"Toggle LED\"></form>");
45}
46
47// 设置处理404情况的函数'handleNotFound'
48void handleNotFound(){
49 esp8266_server.send(404, "text/plain", "404: Not found");
50}
51
52void handleLED() {
53 digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));
54 esp8266_server.sendHeader("Location","/");
55 esp8266_server.send(303);
56}
57
58void loop() {
59 esp8266_server.handleClient();
60}
同时也可以读取开发板的引脚状态展示到Web页面上,实现的复杂点就是用WebSocket,简单点就是自动刷新,这里选择自动刷新网页,读取D3引脚的状态来展示到Web页面:
1// NodeMCU无线终端模式连接WiFi
2#include <ESP8266WiFi.h>
3#include <ESP8266WebServer.h>
4
5// 将要连接的WiFi和密码
6const char* ssid = "TP-LINK";
7const char* password = "192.168.1.1";
8
9// 设置Web服务器端口
10ESP8266WebServer esp8266_server(8080);
11
12// 存储引脚状态用变量,检测按键按下
13bool buttonState;
14
15void setup() {
16 Serial.begin(9600);
17 // 启动网络连接
18 WiFi.begin(ssid, password);
19 // 告知用户NodeMCU正在尝试WiFi连接
20 Serial.print("Connecting to ");
21 Serial.print(ssid); Serial.println(" ...");
22
23 // 如果WiFi连接成功则返回值为WL_CONNECTED
24 int i = 0;
25 while (WiFi.status() != WL_CONNECTED) {
26 delay(1000);
27 Serial.print(i++); Serial.print(' ');
28 }
29
30 Serial.println("");
31 Serial.println("Connection success!");
32 Serial.print("IP address: ");
33 Serial.println(WiFi.localIP());
34
35
36 // ---- WebServer ---------
37 esp8266_server.begin();
38 esp8266_server.on("/", handleRoot);
39 esp8266_server.onNotFound(handleNotFound);
40 // ---- WebServer ---------
41
42 // 设置为输入上拉模式
43 pinMode(LED_BUILTIN, INPUT_PULLUP);
44}
45
46void handleRoot() {
47 String htmlCode = "<html><head><meta http-equiv='refresh' content='1'/></head><body>";
48 if(buttonState){
49 htmlCode +="<p>Button Status: HIGH</p>\n";
50 }else{
51 htmlCode +="<p>Button Status: LOW</p>\n";
52 }
53 htmlCode +="</body></html>\n";
54 esp8266_server.send(200, "text/html", htmlCode);
55}
56
57void handleNotFound(){
58 esp8266_server.send(404, "text/plain", "404: Not found");
59}
60
61void loop() {
62 esp8266_server.handleClient();
63 buttonState = digitalRead(D3); // 获取引脚状态
64}
ESP8266 闪存文件系统
每一个ESP8266都配有一个闪存,这个闪存类似于硬盘,我们上传的文件就被存放在这个闪存里。这个闪存的全称是Serial Peripheral Interface Flash File System(SPIFFS)。
除了可以存放上传的程序以外,还可以将网页文件或者系统配置文件存放在ESP8266的闪存中。如何利用程序对闪存文件系统(SPIFFS)进行文件读取和修改呢?
写入 & 读取文件
1#include <FS.h>
2
3String file_name = "/myfile.txt";
4
5void setup() {
6 Serial.begin(9600);
7
8 // ----------- 写入操作 ---------
9 SPIFFS.format(); // 格式化SPIFFS(删除全部用户文件)
10 Serial.println("format fs success.");
11
12 if(SPIFFS.begin()){ // 启动SPIFFS
13 Serial.println("SPIFFS Started.");
14 } else {
15 Serial.println("SPIFFS Failed to Start.");
16 }
17
18 // 写入字符串
19 File dataFile = SPIFFS.open(file_name, "w");
20 dataFile.println("Hello IOT World.");
21 dataFile.close();
22
23 Serial.println("Finished Writing data to SPIFFS");
24
25 // 追加写入字符串(a->append代表向该文件添加信息)
26 File dataFile = SPIFFS.open(file_name, "a");
27 dataFile.println("This is Appended Info.");
28 dataFile.close();
29 Serial.println("Finished append writing data to SPIFFS");
30
31 // ---------- 读取操作 ---------
32 // 确认闪存中是否有file_name文件
33 if (SPIFFS.exists(file_name)){
34 Serial.print(file_name);
35 Serial.println(" FOUND.");
36 } else {
37 Serial.print(file_name);
38 Serial.print(" NOT FOUND.");
39 }
40
41 File readDataFile = SPIFFS.open(file_name, "r");
42 // 读取文件内容并且通过串口监视器输出文件信息
43 for(int i=0; i<readDataFile.size(); i++){
44 Serial.print((char)readDataFile.read());
45 }
46 //完成文件读取后关闭文件
47 readDataFile.close();
48}
49
50void loop() {
51}
读取目录下全部文件
1#include <FS.h>
2String folder_name = "/myDir";
3
4void setup() {
5 Serial.begin(9600);
6
7 SPIFFS.format(); // 格式化SPIFFS(删除全部用户文件)
8 Serial.println("format fs success.");
9
10 if(SPIFFS.begin()){ // 启动SPIFFS
11 Serial.println("SPIFFS Started.");
12 } else {
13 Serial.println("SPIFFS Failed to Start.");
14 }
15
16 // 显示目录中文件内容以及文件大小
17 Dir dir = SPIFFS.openDir(folder_name);
18 // 串口输出目录的全部文件
19 while (dir.next()) {
20 Serial.println(dir.fileName());
21 }
22}
23
24void loop() {
25}
删除指定的文件
1#include <FS.h>
2
3String folder_name = "/myfile.txt";
4
5void setup() {
6 Serial.begin(9600);
7 Serial.println("");
8
9 if(SPIFFS.begin()){ // 启动闪存文件系统
10 Serial.println("SPIFFS Started.");
11 } else {
12 Serial.println("SPIFFS Failed to Start.");
13 }
14
15 //从闪存中删除file_name文件
16 if (SPIFFS.remove(file_name)){
17
18 Serial.print(file_name);
19 Serial.println(" remove sucess");
20
21 } else {
22 Serial.print(file_name);
23 Serial.println(" remove fail");
24 }
25}
26
27void loop() {
28}
显示闪存文件系统信息
1#include <FS.h>
2
3FSInfo fs_info;
4
5void setup() {
6 Serial.begin(9600);
7
8 SPIFFS.begin();//启动SPIFFS
9 Serial.println("");
10 Serial.println("SPIFFS Started.");
11
12 // 闪存文件系统信息
13 SPIFFS.info(fs_info);
14
15 // 可用空间总和(单位:字节)
16 Serial.print("totalBytes: ");
17 Serial.print(fs_info.totalBytes);
18 Serial.println(" Bytes");
19
20 // 已用空间(单位:字节)
21 Serial.print("usedBytes: ");
22 Serial.print(fs_info.usedBytes);
23 Serial.println(" Bytes");
24
25 // 最大文件名字符限制(含路径和'\0')
26 Serial.print("maxPathLength: ");
27 Serial.println(fs_info.maxPathLength);
28
29 // 最多允许打开文件数量
30 Serial.print("maxOpenFiles: ");
31 Serial.println(fs_info.maxOpenFiles);
32
33 // 存储块大小
34 Serial.print("blockSize: ");
35 Serial.println(fs_info.blockSize);
36
37 // 存储页大小
38 Serial.print("pageSize: ");
39 Serial.println(fs_info.pageSize);
40}
41
42void loop() {
43}
对于我手上这块ESP8266 NodeMCU来说还剩余1.86M的空间:
ArduinoIDE插件上传文件
这个是插件的下载链接:
https://download.fastgit.org/esp8266/arduino-esp8266fs-plugin/releases/download/0.5.0/ESP8266FS-0.5.0.zip
,解压出来的Jar包放在C:\Users\XXX\Documents\Arduino\tools\ESP8266FS\tool
即可 ,这个根据项目的不同会有所不同。
先确定插件是否安装成功,再根据上传的文件总大小来设置闪存大小,在把文件放在工程的data目录下即可上传
下面就上传成功了:
ESP8266 客户端操作
ESP8266-Arduino库中有两个库用于控制ESP8266与网络服务器进行通讯。分别是WiFiClient库和ESP8266HTTPClient库。其实就是一个TCPClient,一个HttpClient而已。 在使用ESP8266开发项目时,更多的时候是使用WiFiClient库来实现物联网通讯功能,WiFiClient库的重要性和实用性要高于ESP8266HTTPClient库,如果是HTTP协议通信,那么选ESP8266HTTPClient咯。
ESP8266HTTPClient
1#include <ESP8266WiFi.h>
2#include <ESP8266HTTPClient.h>
3
4#define URL "http://www.example.com"
5
6const char* ssid = "TP-LINK";
7const char* password = "192.168.1.1";
8
9void setup() {
10 //初始化串口
11 Serial.begin(9600);
12
13 //设置ESP8266工作模式为无线终端模式
14 WiFi.mode(WIFI_STA);
15
16 //开始连接wifi
17 WiFi.begin(ssid, password);
18
19 //等待WiFi连接,连接成功打印IP
20 while (WiFi.status() != WL_CONNECTED) {
21 delay(1000);
22 Serial.print(".");
23 }
24 Serial.println("");
25 Serial.print("WiFi Connected!");
26
27 httpClientRequest();
28}
29
30void loop() {
31}
32
33void httpClientRequest(){
34
35 // 创建 HTTPClient 对象
36 HTTPClient httpClient;
37 WiFiClient client;
38
39 // 通过begin函数配置请求地址
40 httpClient.begin(client, URL);
41 Serial.print("URL: "); Serial.println(URL);
42
43 // 通过GET函数启动连接并发送HTTP请求
44 int httpCode = httpClient.GET();
45 Serial.print("Send GET request to URL: ");
46 Serial.println(URL);
47
48 // 判断HTTP_CODE_OK(200)
49 if (httpCode == HTTP_CODE_OK) {
50 String responsePayload = httpClient.getString();
51 Serial.println("Server Response Payload: ");
52 Serial.println(responsePayload);
53 } else {
54 Serial.println("Server Respose Code:");
55 Serial.println(httpCode);
56 }
57
58 // 断开链接
59 httpClient.end();
60}
WiFiClient
通过TCP Server发送来的信息就可以控制引脚状态了,比如控制LED的亮灭:
1#include <ESP8266WiFi.h>
2
3const char* ssid = "TP-LINK";
4const char* password = "192.168.1.1";
5
6void setup() {
7 pinMode(LED_BUILTIN, OUTPUT);
8
9 //初始化串口
10 Serial.begin(9600);
11
12 //设置ESP8266工作模式为无线终端模式
13 WiFi.mode(WIFI_STA);
14
15 //开始连接wifi
16 WiFi.begin(ssid, password);
17
18 //等待WiFi连接,连接成功打印IP
19 while (WiFi.status() != WL_CONNECTED) {
20 delay(1000);
21 Serial.print(".");
22 }
23 Serial.println("");
24 Serial.print("WiFi Connected!");
25
26 wifiClientRequest();
27}
28
29void loop() {
30}
31
32void wifiClientRequest(){
33
34 // 创建 WiFiClient 对象
35 WiFiClient client;
36
37 String low = "LOW";
38 String high = "HIGH";
39
40 if (client.connect("192.168.199.201", 8080)){
41 while (client.connected() || client.available()){
42 if (client.available()){
43 // 读取服务器返回的字符串直到读取到换行符
44 String line = client.readStringUntil('\n');
45 if(line.equals(low)) {
46 digitalWrite(LED_BUILTIN, LOW);
47 }else if(line.equals(high)) {
48 digitalWrite(LED_BUILTIN, HIGH);
49 }
50 Serial.println(line);
51 }
52 }
53
54 client.stop();
55 Serial.print("Disconnected!");
56 }else {
57 Serial.println(" connection failed!");
58 client.stop();
59 }
60}
Stream
Stream对于ESP8266-Arduino语言来说指的是数据序列。在C++编程中Stream常被称作流,其实将Stream称为数据序列更加直观。因为数据序列这一概念有两个很关键特点。 第一个特点是“序”,即数据序列不能是杂乱无章的数据罗列,第二个特点是“列”,即数据序列是排成一列的。
在ESP8266-Arduino中,第一个接触到的处理Stream的库就是Serial,用于处理串口通信中的各种数据序列。下面的程序就是使用了Serial.available
来判断ESP8266开发板是否接收到串口数据。这里的开发板通过串口所接收到的数据就是Stream数据。另外,程序通过Serial.println
语句将接收到的Stream数据通过串口输出并显示在串口监视器中,这里ESP8266通过串口所输出的数据也是Stream数据。换句话说,ESP8266开发板通过串口收发的数据都是Stream数据。
1void setup() {
2 // 启动串口通讯
3 Serial.begin(9600);
4 Serial.println();
5}
6
7void loop() {
8 // 当串口接收到信息
9 if (Serial.available()){
10 // 将接收到的信息使用readString()存储于serialData变量
11 String serialData = Serial.readString();
12 Serial.print(serialData);
13 }
14}
另外,HTTPClient、WIfiClient等都是Stream数据处理器,还有File的IO操作,操作的都是Stream数据,如果有过C++/Java等编程经验的话Stream其实很容易理解,在此不再赘述了。
Serial库,WiFiClient库,FS库所建立的对象都可以处理Stream数据。另外除了以上这些库以外,以下列表中的库也可以处理Stream数据:
库 | 类 |
---|---|
Serial | Serial |
SoftwareSerial | SoftwareSerial |
Ehternet | EthernetClient |
ESP8266FS | File |
SD | File |
Wire | Wire |
GSM | GSMClient |
WifiClient | WiFiClient |
WiFiServer | WiFiServer |
WiFiUDP | WiFiUDP |
WiFiClientSecure | WiFiClientSecure |
下面我们来看一个Stream操作的实例,这个实例演示了如何利用find函数配合parseInt函数从ESP8266接收到的Serial数据中寻找整数数值:
1void setup() {
2 Serial.begin(9600);
3 Serial.println("");
4 Serial.println("Please enter input...");
5}
6
7void loop() {
8 while(Serial.available()){
9 if(Serial.find("ok")){
10 Serial.println("Found ok in user input.");
11
12 int serialParseInt = Serial.parseInt();
13 Serial.print("serialParseInt = ");
14 Serial.println(serialParseInt);
15
16 String serialInput = Serial.readString();
17 Serial.print("serialInput = ");
18 Serial.println(serialInput);
19 }
20 }
21}
当ESP8266找到了find函数所指定的参数“ok”后,随即在后续接收到的数据中查找数字信息。一旦找到数字,则通过串口监视器输出。接下来串口监视器还将输出找到数字后剩余的串口输入信息是什么。
JSON处理
解析JSON格式信息是一个较为繁琐的工作,但是可以借助解析Arduino – ESP8266平台中解析JSON格式信息的第三方库——ArduionJson库,该库是目前最受好评的解析JSON信息第三方库。GitHub: https://github.com/bblanchon/ArduinoJson
下载安装这个依赖库:
1#include <ArduinoJson.h>
2
3void setup() {
4 Serial.begin(9600);
5 Serial.println("");
6
7 // DynamicJsonDocument对象
8 const size_t capacity = JSON_OBJECT_SIZE(2) + 30;
9 DynamicJsonDocument doc(capacity);
10
11 // 即将解析的JSON
12 String json = "{\"name\":\"Tim\",\"number\":252}";
13
14 // 反序列化数据
15 deserializeJson(doc, json);
16
17 // 获取解析后的数据信息
18 String nameStr = doc["name"].as<String>();
19 int numberInt = doc["number"].as<int>();
20
21 // 通过串口监视器输出解析后的数据信息
22 Serial.print("nameStr = ");Serial.println(nameStr);
23 Serial.print("numberInt = ");Serial.println(numberInt);
24}
25
26void loop() {}
先建立DynamicJsonDocument对象,该对象名称为doc。在建立该对象时需要提供一个参数,也就是括号中的参数capacity。这个capacity参数的作用是告诉ESP8266我们所建立的DynamicJsonDocument对象将要占用多大的内存空间。这个空间大小是由语句const size_t capacity = JSON_OBJECT_SIZE(2) + 30;
计算出来的。因为要解析的字符串包含两个数据对,所以JSON_OBJECT_SIZE为2。+30这些额外增加的数值是由于ArduinoJson库在解析信息时,需要额外的空间来复制JSON信息。
下面是解析JSONArray的示例:
1void setup() {
2 Serial.begin(9600);
3
4 const size_t capacity = JSON_ARRAY_SIZE(2) + 2*JSON_OBJECT_SIZE(1) + 60;
5 DynamicJsonDocument doc(capacity);
6
7 String json = "[{\"name\":\"Tim\"},{\"website\":\"zouchanglin.cn\"}]";
8
9 // 反序列化数据
10 deserializeJson(doc, json);
11
12
13 String nameStr = doc[0]["name"].as<String>();
14 String websiteStr = doc[1]["website"].as<String>();
15
16 // 通过串口监视器输出解析后的数据信息
17 Serial.print("nameStr = ");Serial.println(nameStr);
18 Serial.print("websiteStr = ");Serial.println(websiteStr);
19}
20
21void loop() {}
以上JSON信息是一个数组,该数组含有两个元素。所以在计算capacity时首先使用了语句JSON_ARRAY_SIZE(2)
来获得含有两个元素的数组所占用内存的大小。
其实,计算capacity可以使用ArduinoJson官网的在线工具 ->
Use arduinojson.org/v6/assistant to compute the capacity.
ArduinoJson官网提供了在线工具可帮助我们自动生成JSON解析代码。该工具网址如下: https://arduinojson.org/v6/assistant/
ESP8266闪存存储的JSON解析
有时可能需要ESP8266解析比较大型的存放在闪存中的JSON信息,以下示例程序演示如何使用ESP8266读取并且解析存储在闪存中的JSON。 以解析WIFI的JSON配置为例,链接信号最佳的WIFI,闪存中的WIFI配置如下:
1{
2 "wifi": [
3 {
4 "ssid": "HUAWEI-LINK",
5 "password": "12345678"
6 },
7 {
8 "ssid": "TP-LINK",
9 "password": "xpassword"
10 },
11 {
12 "ssid": "XiaoMi-WiFi",
13 "password": "19274390133"
14 }
15 ]
16}
代码如下:
1#include <ArduinoJson.h>
2#include <ESP8266WiFi.h>
3#include <ESP8266WiFiMulti.h>
4#include <FS.h>
5
6// 建立ESP8266WiFiMulti对象
7ESP8266WiFiMulti wifiMulti;
8
9void setup() {
10 Serial.begin(9600);
11
12 // 启动闪存文件系统
13 if(SPIFFS.begin()){
14 Serial.println("SPIFFS Started.");
15 } else {
16 Serial.println("SPIFFS Failed to Start.");
17 }
18
19 const size_t capacity = JSON_ARRAY_SIZE(1) + 3*JSON_OBJECT_SIZE(2) + 120;
20 DynamicJsonDocument doc(capacity);
21
22 // 从闪存文件系统中读取即将解析的json文件
23 File file = SPIFFS.open("/config.json", "r");
24
25 // 反序列化数据
26 deserializeJson(doc, file);
27
28 JsonObject results_0 = doc["wifi"][0];
29 JsonObject results_1 = doc["wifi"][1];
30 JsonObject results_2 = doc["wifi"][2];
31
32 // 通过串口监视器输出解析后的数据信息
33 wifiMulti.addAP(results_0["ssid"], results_0["password"]);
34 wifiMulti.addAP(results_1["ssid"], results_1["password"]);
35 wifiMulti.addAP(results_2["ssid"], results_2["password"]);
36
37 Serial.println("Connecting ...");
38
39 int i = 0;
40 while (wifiMulti.run() != WL_CONNECTED) { // 尝试进行wifi连接。
41 delay(1000);
42 Serial.print(i++); Serial.print(' ');
43 }
44
45 // WiFi连接成功后将通过串口监视器输出连接成功信息
46 Serial.println("");
47 Serial.print("Connected to ");
48 Serial.println(WiFi.SSID()); // WiFi名称
49 Serial.print("IP address:\t");
50 Serial.println(WiFi.localIP()); // IP
51}
52
53void loop() {}
ESP8266 自动配网
通过WiFiManager库,则无需修改ESP8266的程序,就可以完成ESP8266的WiFi连接设置。这为我们在开发物联网项目提供了很多便利。Github:https://github.com/tzapu/WiFiManager,下面是其工作流程:
ESP8266的WiFi设置是储存在它的闪存系统中的。因此在启动ESP8266并连接WiFi时,它都会尝试使用闪存系统中储存的信息来进行WiFi连接。
在使用WiFiManager库来配置ESP8266的WiFi设置前,需要先清除ESP8266的WiFi连接信息,这样才能看到WiFiManager库的工作效果。(如果ESP8266刚一启动就自动成功连接WiFi了,那么WiFiManager库是不会发挥作用的。可以使用 wifiManager.resetSettings()
来实现清除ESP8266的闪存中所存储的WiFi连接信息。
1#include <ESP8266WiFi.h>
2#include <DNSServer.h>
3#include <ESP8266WebServer.h>
4#include <WiFiManager.h>
5
6void setup() {
7 Serial.begin(9600);
8 // 建立WiFiManager对象
9 WiFiManager wifiManager;
10
11 // 清除ESP8266所存储的WiFi连接信息以便测试WiFiManager工作效果
12 wifiManager.resetSettings();
13 Serial.println("ESP8266 WiFi Settings Cleared");
14}
15
16void loop() {}
以下示例程序使用了WiFiManager来实现WiFi网络配置:
1#include <ESP8266WiFi.h>
2#include <DNSServer.h>
3#include <ESP8266WebServer.h>
4#include <WiFiManager.h>
5
6void setup() {
7 Serial.begin(9600);
8 // 建立WiFiManager对象
9 WiFiManager wifiManager;
10
11 // 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称
12 wifiManager.autoConnect("AutoConnectAP");
13
14 // 如果您希望该WiFi添加密码,可以使用以下语句:
15 // wifiManager.autoConnect("AutoConnectAP", "12345678");
16
17 // WiFi连接成功后将通过串口监视器输出连接成功信息
18 Serial.println("");
19 Serial.print("ESP8266 Connected to ");
20 Serial.println(WiFi.SSID()); // WiFi名称
21 Serial.print("IP address:\t");
22 Serial.println(WiFi.localIP()); // IP
23}
24
25void loop() {}
ESP8266 多任务处理
利用Ticker库,我们可以让ESP8266定时处理任务。主要是学会Ticker的使用方法以及如何利用Ticker库来实现ESP8266的多任务处理。
利用Ticker库,可以让ESP8266定时调用某一个函数。通过以下示例程序我们可以看到,ESP8266将会每隔一秒钟通过串口监视器输出一次信息。我们是通过语句ticker.attach(1, sayHi)
来实现这一操作的。
该语句中的attach函数有两个参数。第一个参数可控制调用函数的时间间隔,单位是秒。这里的数字1说明ESP8266将会每隔一秒钟调用一次函数。那么具体调用哪一个函数呢?这个函数名称正是是通过第二个参数来限定的。也就是名称为sayHi的函数。
启动任务与停止任务
其实,这与其他MCU的定时器是一样的原理:
1#include <Ticker.h>
2
3Ticker ticker;// 建立Ticker用于实现定时功能
4int count; // 计数用变量
5
6void setup() {
7 Serial.begin(9600);
8 pinMode(LED_BUILTIN, OUTPUT);
9
10 // 每隔二秒钟调用sayHi函数一次
11 // 参数是控制定时间隔的变量,单位为秒;以及定时执行的函数名称
12 ticker.attach(1, sayHi);
13}
14
15void loop() {
16 // 用LED呼吸灯效果来演示在Tinker对象控制下,ESP8266可以定时执行其它任务
17 for (int fadeValue = 0 ; fadeValue <= 1023; fadeValue += 5) {
18 analogWrite(LED_BUILTIN, fadeValue);
19 delay(10);
20 }
21
22 for (int fadeValue = 1023 ; fadeValue >= 0; fadeValue -= 5) {
23 analogWrite(LED_BUILTIN, fadeValue);
24 delay(10);
25 }
26 delay(3000);
27}
28
29// 在Tinker对象控制下,此函数将会定时执行。
30void sayHi(){
31 count++;
32 Serial.print("Hi ");
33 Serial.println(count);
34
35 // 当定时调用了6次后,停止定时调用函数
36 if (count >= 6) {
37 ticker.detach(); // 使用detach来停止ticker对象定时调用函数
38 Serial.print("ticker.detach()");
39 }
40}
当Ticker定时调用某一函数执行到一定次数后,可以使用detach函数来停止定时调用函数。以下示例程序中的语句ticker.detach()
将会让ticker对象停止调用函数。
向定时调用函数传递参数
attach函数所能传递的参数最多只有一个。另外该参数仅能是以下类型中的一种:char
, short
, int
, float
, void*
, char*
。
1#include <Ticker.h>
2
3Ticker ticker;
4
5int count;
6
7void setup() {
8 Serial.begin(9600);
9 pinMode(LED_BUILTIN, OUTPUT);
10
11 ticker.attach(1, sayHi, 8);
12}
13
14void loop() {
15 for (int fadeValue = 0 ; fadeValue <= 1023; fadeValue += 5) {
16 analogWrite(LED_BUILTIN, fadeValue);
17 delay(10);
18 }
19
20 for (int fadeValue = 1023 ; fadeValue >= 0; fadeValue -= 5) {
21 analogWrite(LED_BUILTIN, fadeValue);
22 delay(10);
23 }
24 delay(3000);
25}
26
27void sayHi(int hiTimes){
28 count++;
29 Serial.print("Hi ");
30 Serial.println(count);
31
32 if (count >= hiTimes) {
33 ticker.detach();
34 Serial.print("ticker.detach();");
35 }
36}
ESP8266 OTA操作(不常用)
OTA是Over-The-Air的缩写。有人将其翻译为“空中下载”,也有翻译为“隔空传输”。无论如何翻译,对于ESP2866来说,通过OTA就无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。
第一步:通过数据线上传初始示例程序
1#include <ESP8266WiFi.h>
2#include <ArduinoOTA.h>
3#include <Ticker.h>
4
5// 闪烁时间间隔(秒)
6const int blinkInterval = 2;
7
8// 设置wifi接入信息(请根据您的WiFi信息进行修改)
9const char* ssid = "TP-LINK";
10const char* password = "192.168.1.1";
11
12Ticker ticker;
13
14void setup() {
15 Serial.begin(9600);
16 Serial.println("");
17 pinMode(LED_BUILTIN, OUTPUT);
18
19 ticker.attach(blinkInterval, tickerCount); // 设置Ticker对象
20
21 connectWifi();
22
23 // OTA设置并启动
24 ArduinoOTA.setHostname("ESP8266");
25 ArduinoOTA.setPassword("12345678");
26 ArduinoOTA.begin();
27
28 Serial.println("OTA ready");
29}
30void loop() {
31 ArduinoOTA.handle();
32}
33
34// 在Tinker对象控制下,此函数将会定时执行。
35void tickerCount(){
36 digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
37}
38
39void connectWifi(){
40 //开始连接wifi
41 WiFi.begin(ssid, password);
42
43 //等待WiFi连接,连接成功打印IP
44 while (WiFi.status() != WL_CONNECTED) {
45 delay(1000);
46 Serial.print(".");
47 }
48 Serial.println("");
49 Serial.println("WiFi Connected!");
50 Serial.print("IP address:\t");
51 Serial.println(WiFi.localIP());
52}
第二步:通过Arduino IDE正确选择OTA端口
程序上传后,请重新启动Arduino IDE。并且通过Arduino IDE正确选择ESP8266的OTA端口。
点击Arduino IDE的上传按钮后, IDE将会弹出对话框让用户输入OTA上传密码。根据示例程序中的setPassword
函数所设置的信息来输入密码。完成密码输入后,点击确定程序开始上传。
程序上传结束后,ESP8266将会自动重启开发板,新的程序也将在重启后开始运行。所以OTA缺点也很明显:
1、程序占用空间变大,在OTA上传新程序过程中,ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了; 2、Arduino IDE无法通过OTA端口与开发板进行串口通讯; 3、使用OTA上传程序的电脑与ESP8266必须连接同一WiFi;
ESP32的引脚图
2022-08-14 更新 最近从ESP8266升级到了ESP32,最新的引脚图如下: