一、接入点模式
NodeMCU可以建立WiFi网络供其它设备连接。当NodeMCU以此模式运行时,我们可以使用手机搜索NodeMCU所发出的WiFi网络并进行连接。
通过以下示例程序,NodeMCU将会建立一个名为我将点燃大海的WiFI。您可以使用手机或电脑连接该WiFi从而实现与NodeMCU的网络通讯。
#include <ESP8266WiFi.h> // 本程序使用ESP8266WiFi库const char *ssid = "我将点燃大海"; // 这里定义将要建立的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() {
}
NodeMCU在每次启动以后,都会自动启动接入点模式。接入点WiFi的详细信息会通过串口监视器输出给用户查看。
接下来就可以连接了,密码是12345678,当然他只是个wifi ,不能实现联网功能,但可以实现接受与发送消息,也可以cmd 输入ipconfig 查看ip,然后ping通。
二、NodeMCU开发板的无线终端模式
1、连接指定WiFI
如图所示,ESP8266可通过WiFi连接无线路由器。这与用您的手机通过WiFi连接无线路由器的模式相同,说白了就是可以连接自己的wifi 或者手机热点,是个人pc与8266在同一个局域网里面。
#include <ESP8266WiFi.h> // 本程序使用ESP8266WiFi库const char* ssid = "快去摸鱼"; // 连接WiFi名(此处使用taichi-maker为示例)// 请将您需要连接的WiFi名填入引号中
const char* password = "12345678aqz"; // 连接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() {
}
打开串口助手,就可以看到打印的信息了。
2、自动连接最强信号WiFi网络
假如我们的NodeMCU只在一个地方使用,它也就只需要知道一个WiFi网络的连接信息。但是如果NodeMCU需要在多个地方使用,这时候就需要它能存储多个地点的WiFi信息。通过以下示例程序,NodeMCU可以在它所处的网络环境里搜索预先存储好的WiFi。一旦找到预存的WiFi名称,NodeMCU将会使用预存的密码信息尝试连接该WiFi。如果同时找到多个预存WiFi,NodeMCU将会尝试连接信号最强的WiFi。
#include <ESP8266WiFi.h> // 本程序使用ESP8266WiFi库
#include <ESP8266WiFiMulti.h> // 本程序使用ESP8266WiFiMulti库ESP8266WiFiMulti wifiMulti; // 建立ESP8266WiFiMulti对象,对象名称是'wifiMulti'void setup() {Serial.begin(9600); // 启动串口通讯//通过addAp函数存储 WiFi名称 WiFi密码wifiMulti.addAP("xxx1", "12345678"); // 这三条语句通过调用函数addAP来记录3个不同的WiFi网络信息。wifiMulti.addAP("xxx2", "87654321"); // 这3个WiFi网络名称分别是taichi-maker, taichi-maker2, taichi-maker3。wifiMulti.addAP("xxx3", "13572468"); // 这3个网络的密码分别是123456789,87654321,13572468。// 此处WiFi信息只是示例,请在使用时将需要连接的WiFi信息填入相应位置。// 另外这里只存储了3个WiFi信息,您可以存储更多的WiFi信息在此处。Serial.println("Connecting ..."); // 通过串口监视器输出信息告知用户NodeMCU正在尝试连接WiFiint i = 0; while (wifiMulti.run() != WL_CONNECTED) { // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前delay(1000); // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCUSerial.print('.'); // 将会连接信号最强的那一个WiFi信号。} // 一旦连接WiFI成功,wifiMulti.run()将会返回“WL_CONNECTED”。这也是// 此处while循环判断是否跳出循环的条件。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() {
}
三、NodeMCU 网络服务器
1、NodeMCU 建立网络服务器
网络服务器有很多种类型,它们的功能也十分丰富。通常承担网络服务器工作的设备都是运算能力比较强大的电脑。我们的ESP866-NodeMCU虽然也能实现网络服务器的一些功能,但是毕竟它的运算能力是无法与那些昂贵的服务器电脑相媲美的,因此ESP8266-NodeMCU只能实现一些基本的网络服务功能。不过这些基本的网络服务功能已经足够我们开发物联网项目了,准确来说把板子当作服务器,让他们处在同一个局域网里面,然后ip + 端口的方式连接。
#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("快去摸鱼", "12345678aqz"); // 这三条语句通过调用函数addAP来记录3个不同的WiFi网络信息。int i = 0; while (wifiMulti.run() != WL_CONNECTED) { // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前delay(1000); // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCUSerial.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); // 路径为 / ,调用 handleRoot 函数esp8266_server.onNotFound(handleNotFound); // 未找到路径,调用handleNotFound
//--------"启动网络服务功能"程序部分结束--------Serial.println("HTTP esp8266_server started");// 告知用户ESP8266网络服务功能已经启动
}void loop(void){esp8266_server.handleClient(); // 处理http服务器访问
}// 进入首页,返回200
void handleRoot() { //处理网站根目录“/”的访问请求 esp8266_server.send(200, "text/plain", "Hello from ESP8266"); // NodeMCU将调用此函数。
}// 设置处理404情况的函数'handleNotFound'
void handleNotFound(){ // 当浏览器请求的网络资源无法在服务器找到时,esp8266_server.send(404, "text/plain", "404: Not found"); // NodeMCU将调用此函数。
}
2、通过网络服务实现NodeMCU开发板基本控制
#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引脚为输出模式以便控制LEDwifiMulti.addAP("快去摸鱼", "12345678aqz"); // 将需要连接的一系列WiFi ID和密码输入这里Serial.println("Connecting ..."); // 则尝试使用此处存储的密码进行连接。int i = 0; while (wifiMulti.run() != WL_CONNECTED) { // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前delay(1000); // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCUSerial.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的IPesp8266_server.begin(); // 启动网站服务esp8266_server.on("/", HTTP_GET, handleRoot); // 设置服务器根目录即'/'的函数'handleRoot'esp8266_server.on("/LED", HTTP_POST, handleLED); // 设置处理LED控制请求的函数'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>");
}//处理LED控制请求的函数'handleLED'
void handleLED() { digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));// 改变LED的点亮或者熄灭状态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"
}
点击Toggle LED 开发板的LED亮或灭。
3、通过网络服务将开发板引脚状态显示在网页中
D3引脚是与开发板上的FLASH按键开关连接好了,所以当我们按下falsh 按钮,d3的状态刷新。
#include <ESP8266WiFi.h> // 本程序使用 ESP8266WiFi库
#include <ESP8266WiFiMulti.h> // ESP8266WiFiMulti库
#include <ESP8266WebServer.h> // ESP8266WebServer库#define buttonPin D3 // 按钮引脚D3ESP8266WiFiMulti wifiMulti; // 建立ESP8266WiFiMulti对象,对象名称是'wifiMulti'ESP8266WebServer esp8266_server(80);// 建立网络服务器对象,该对象用于响应HTTP请求。监听端口(80)bool pinState; // 存储引脚状态用变量void setup(){Serial.begin(9600); // 启动串口通讯pinMode(buttonPin, INPUT_PULLUP); // 将按键引脚设置为输入上拉模式wifiMulti.addAP("快去摸鱼", "12345678aqz"); // 将需要连接的一系列WiFi ID和密码输入这里Serial.println("Connecting ..."); // 则尝试使用此处存储的密码进行连接。int i = 0; while (wifiMulti.run() != WL_CONNECTED) { // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前delay(1000); // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCUSerial.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); // 设置服务器根目录即'/'的函数'handleRoot'esp8266_server.onNotFound(handleNotFound);// 设置处理404情况的函数'handleNotFound' Serial.println("HTTP esp8266_server started");// 告知用户ESP8266网络服务功能已经启动
}void loop(){esp8266_server.handleClient(); // 处理http服务器访问pinState = digitalRead(buttonPin); // 获取引脚状态
}/* 以下函数处理网站首页的访问请求。此函数为本示例程序重点1
详细讲解请参见太极创客网站《零基础入门学用物联网》
第3章-第2节“通过网络服务将开发板引脚状态显示在网页中”的说明讲解。*/
void handleRoot() { String displayPinState; // 存储按键状态的字符串变量if(pinState == HIGH){ // 当按键引脚D3为高电平displayPinState = "Button State: HIGH"; // 字符串赋值高电平信息} else { // 当按键引脚D3为低电平displayPinState = "Button State: LOW"; // 字符串赋值低电平信息}esp8266_server.send(200, "text/plain", displayPinState); // 向浏览器发送按键状态信息
}// 设置处理404情况的函数'handleNotFound'
void handleNotFound(){ // 当浏览器请求的网络资源无法在服务器找到时,esp8266_server.send(404, "text/plain", "404: Not found"); // NodeMCU将调用此函数。
}
在以上程序的loop函数中,pinState = digitalRead(buttonPin);
语句将不断检查NodeMCU开发板D3引脚状态,也就是检查该引脚所连接的按键是否被按下。该状态将会存储与布尔变量pinState中。
没有按下:Button State: HIGH
按下了:Button State: LOW
但是这个有个缺陷,就是页面要自己手动刷新,才能看到,我们设置个页面每隔几秒刷新一次。
/*
建立用于发送给客户端浏览器的HTML代码。此代码将会每隔5秒刷新页面。
通过页面刷新,引脚的最新状态也会显示于页面中
*/
String sendHTML(bool buttonState){String htmlCode = "<!DOCTYPE html> <html>\n";htmlCode +="<head><meta http-equiv='refresh' content='5'/>\n";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;
}
四、NodeMCU 闪存文件系统
问:当我们上传程序给ESP8266时,我们的程序具体存放在什么地方呢?
每一个ESP8266都配有一个闪存,这个闪存很像是一个小硬盘,我们上传的文件就被存放在这个闪存里。这个闪存的全称是Serial Peripheral Interface Flash File System(SPIFFS)。
除了可以存放上传的程序以外,我们还可以将网页文件或者系统配置文件存放在ESP8266的闪存中。在这节课里,我们将学习如何利用程序对闪存文件系统(SPIFFS)进行文件读取和修改。
1、写入信息
函数说明:
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(); // 格式化SPIFFSSerial.println("SPIFFS format finish");if(SPIFFS.begin()){ // 启动SPIFFSSerial.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() {
}
2、读取信息
函数说明:
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() {
}
3、添加信息
函数说明:
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)写入信息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() {
}
4、读取目录内容
函数说明:
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"));// 显示目录中文件内容以及文件大小Dir dir = SPIFFS.openDir(folder_name); // 建立“目录”对象while (dir.next()) { // dir.next()用于检查目录中是否还有“下一个文件”Serial.println(dir.fileName()); // 输出文件名}
}void loop() {
}
5、删除信息
#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() {
}
通过使用SPIFFS.remove(file_name)
将file_name
所指代的文件进行了删除操作。另外,SPIFFS.remove(file_name)
的返回值为布尔型。如果文件删除执行成功则返回真,否则返回假。
6、显示闪存文件系统信息
#include <FS.h>FSInfo fs_info;void setup() {Serial.begin(9600);SPIFFS.begin(); //启动SPIFFSSerial.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() {
}
五、ESP8266网络客户端基本操作
在我们上网过程中,经常使用网页浏览器来浏览网站信息。在这一场景中,我们的网站浏览器就是一个网络客户端。网络客户端的主要功能就是向服务器发送网络请求。服务器在接收到客户端请求后会将请求的信息回复给客户端。
1、ESP8266HTTPClient库实现网络通讯
重点1. 声明HTTPClient对象,对象名称httpClient。后续程序中,我们将使用对象httpClient控制ESP8266的网络通讯。
重点2. 通过ESP8266HTTPClient库的begin函数来设置ESP8266发送HTTP请求的目标URL。
重点3. 通过ESP8266HTTPClient库的GET函数向服务器发送HTTP请求。
重点4. 以上重点3中GET函数的返回值是网络服务器响应状态码。根据该状态码,我们可以判断服务器是否成功接收到了ESP8266客户端的请求。如果服务器成功接收到请求,我们就可以在接下来使用getString函数来获取服务器响应报文(服务器响应体)信息,并且将该信息传递给responsePayload变量以便我们在后面通过串口监视器显示服务器响应报文。(这一报文信息正是www.example.com网站的首页HTML源代码)。
重点5.执行完以上操作后,我们将关闭ESP8266与服务器连接。这里是通过ESP8266HTTPClient库的end函数来实现这一操作的。
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>// 测试HTTP请求用的URL。注意网址前面必须添加"http://"
String URL = "http://www.example.com";// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "快去摸鱼";
const char* password = "12345678aqz";void setup() {//初始化串口设置Serial.begin(9600);//设置ESP8266工作模式为无线终端模式WiFi.mode(WIFI_STA);//开始连接wifiWiFi.begin(ssid, password);//等待WiFi连接,连接成功打印IPwhile (WiFi.status() != WL_CONNECTED) {delay(1000);Serial.print(".");}Serial.println("");Serial.print("WiFi Connected!");httpClientRequest();
}void loop() {}// 发送HTTP请求并且将服务器响应通过串口输出
void httpClientRequest(){//重点1 创建 HTTPClient 对象HTTPClient httpClient;WiFiClient wiFiClient;//重点2 通过begin函数配置请求地址。此处也可以不使用端口号和PATH而单纯的httpClient.begin(wiFiClient, URL); Serial.print("URL: "); Serial.println(URL);//重点3 通过GET函数启动连接并发送HTTP请求int httpCode = httpClient.GET();Serial.print("Send GET request to URL: ");Serial.println(URL);//重点4. 如果服务器响应HTTP_CODE_OK(200)则从服务器获取响应体信息并通过串口输出//如果服务器不响应HTTP_CODE_OK(200)则将服务器响应状态码通过串口输出if (httpCode == HTTP_CODE_OK) {// 使用getString函数获取服务器响应体内容String responsePayload = httpClient.getString();Serial.println("Server Response Payload: ");Serial.println(responsePayload);} else {Serial.println("Server Respose Code:");Serial.println(httpCode);}//重点5. 关闭ESP8266与服务器连接httpClient.end();
}
2、使用WiFiClient库实现网络通讯
#include <ESP8266WiFi.h>const char* host = "www.example.com"; // 网络服务器地址
const int httpPort = 80; // http端口80// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "快去摸鱼";
const char* password = "12345678aqz";void setup() {//初始化串口设置Serial.begin(9600);Serial.println("");//设置ESP8266工作模式为无线终端模式WiFi.mode(WIFI_STA);//开始连接wifiWiFi.begin(ssid, password);//等待WiFi连接,连接成功打印IPwhile (WiFi.status() != WL_CONNECTED) {delay(1000);Serial.print(".");}Serial.println("");Serial.println("WiFi Connected!");wifiClientRequest();
}void loop(){}// 向服务器发送HTTP请求
void wifiClientRequest(){// 建立WiFi客户端对象,对象名称clientWiFiClient client; // 建立字符串,用于HTTP请求String httpRequest = String("GET /") + " HTTP/1.1\r\n" +"Host: " + host + "\r\n" +"Connection: close\r\n" +"\r\n";// 通过串口输出连接服务器名称以便查阅连接服务器的网址 Serial.print("Connecting to "); Serial.print(host); // 连接网络服务器,以下段落中的示例程序为本程序重点1// 请参考太极创客网站中关于本程序的讲解页面获取详细说明信息。网址:// http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/esp8266-nodemcu-web-client/http-request/if (client.connect(host, httpPort)){ Serial.println(" Success!"); // 连接成功后串口输出“Success”信息client.print(httpRequest); // 向服务器发送HTTP请求Serial.println("Sending request: ");// 通过串口输出HTTP请求信息内容以便查阅Serial.println(httpRequest); // 通过串口输出网络服务器响应信息, 以下段落中的示例程序为本程序重点2// 请参考太极创客网站中关于本程序的讲解页面获取详细说明信息。网址:// http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/esp8266-nodemcu-web-client/http-request/Serial.println("Web Server Response:"); while (client.connected() || client.available()){ if (client.available()){String line = client.readStringUntil('\n');Serial.println(line);}}client.stop(); // 断开与服务器的连接Serial.print("Disconnected from "); // 并且通过串口输出断开连接信息Serial.print(host);} else{ // 如果连接不成功则通过串口输出“连接失败”信息Serial.println(" connection failed!");client.stop();}
}
六、ESP8266通过JSON实现物联网数据通讯
1、请求JSON数据信息
示例分为两部分,一部分为服务器程序,另一部分为客户端程序。
服务器端程序主要功能:
1. 实时读取A0、 D1、D2以及D3引脚的读数。
2. 当有客户端请求时,通过响应信息将引脚读数和测试数据信息发送给客户端。
信息发送格式为json格式。以下为该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"}
}
#include <ESP8266WiFi.h> // 本程序使用 ESP8266WiFi库
#include <ESP8266WiFiMulti.h> // ESP8266WiFiMulti库
#include <ESP8266WebServer.h> // ESP8266WebServer库#define buttonPin D3 // 按钮引脚D3ESP8266WiFiMulti wifiMulti; // 建立ESP8266WiFiMulti对象,对象名称是'wifiMulti'ESP8266WebServer esp8266_server(80);// 建立网络服务器对象,该对象用于响应HTTP请求。监听端口(80)IPAddress local_IP(192, 168, 0, 123); // 设置ESP8266-NodeMCU联网后的IP
IPAddress gateway(192, 168, 0, 1); // 设置网关IP(通常网关IP是WiFI路由IP)
IPAddress subnet(255, 255, 255, 0); // 设置子网掩码
IPAddress dns(192,168,0,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("xxx", "12345678"); // 这三条语句通过调用函数addAP来记录3个不同的WiFi网络信息。int i = 0; while (wifiMulti.run() != WL_CONNECTED) { // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前delay(1000); // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCUSerial.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.on("/", handleRoot); esp8266_server.begin(); Serial.println("HTTP esp8266_server started");// 告知用户ESP8266网络服务功能已经启动
}void loop(){// 处理http服务器访问esp8266_server.handleClient();
} void handleRoot() { //处理网站目录“/”的访问请求 esp8266_server.send(200, "application/json", rootJson());
}// 实时获取ESP8266开发板引脚信息并且建立JSON信息
// 以便ESP8266服务器通过响应信息发送给客户端
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;
}
rootJson:该信息将会通过服务器响应信息发送给请求这一信息的客户端。
客户端程序的主要功能:
1. 向服务器端请求json数据信息
2. 解析服务器端响应的json信息内容。
3. 将解析后的数据信息显示于串口监视器
4. 利用服务器端D3引脚(按键引脚)读数来控制客户端开发板上LED的点亮和熄灭
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>ESP8266WiFiMulti wifiMulti; // 建立ESP8266WiFiMulti对象const char* host = "192.168.0.123"; // 将要连接的服务器地址
const int httpPort = 80; // 将要连接的服务器端口 void setup(){Serial.begin(9600); Serial.println("");// 设置开发板LED引脚pinMode(LED_BUILTIN, OUTPUT);digitalWrite(LED_BUILTIN, HIGH);wifiMulti.addAP("xxx", "12345678"); // 将需要连接的一系列WiFi ID和密码输入这里Serial.println("Connecting ..."); int i = 0; while (wifiMulti.run() != WL_CONNECTED) { // 尝试进行wifi连接。delay(1000);Serial.print(i++); Serial.print(' ');}// WiFi连接成功后将通过串口监视器输出连接成功信息 Serial.println("");Serial.print("Connected to ");Serial.println(WiFi.SSID()); // WiFi名称Serial.print("IP address:\t");Serial.println(WiFi.localIP()); // IP
}void loop(){httpRequest();delay(3000);
}// 向服务器请求信息并对信息进行解析
void httpRequest(){WiFiClient client;String httpRequest = String("GET /") + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n";Serial.print("Connecting to "); Serial.print(host);if (client.connect(host, 80)){Serial.println(" Success!");// 向服务器发送http请求信息client.print(httpRequest);Serial.println("Sending request: ");Serial.println(httpRequest); // 获取并显示服务器响应状态行 String status_response = client.readStringUntil('\n');Serial.print("status_response: ");Serial.println(status_response);// 使用find跳过HTTP响应头if (client.find("\r\n\r\n")) {Serial.println("Found Header End. Start Parsing.");}parseInfo(client); }else {Serial.println(" connection failed!");} //断开客户端与服务器连接工作client.stop();
}void parseInfo(WiFiClient client){const size_t capacity = JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(3) + 140;DynamicJsonDocument doc(capacity);deserializeJson(doc, client);JsonObject info = doc["info"];const char* info_name = info["name"]; // "taichimaker"const char* info_url = info["url"]; // "www.taichi-maker.com"const char* info_email = info["email"]; // "taichimaker@163.com"JsonObject digital_pin = doc["digital_pin"];const char* digital_pin_d1 = digital_pin["d1"]; // "1"const char* digital_pin_d2 = digital_pin["d2"]; // "0"const char* digital_pin_d3 = digital_pin["d3"]; // "1"const char* analog_pin_a0 = doc["analog_pin"]["a0"]; // "500"String info_name_str = info["name"].as<String>();bool d3_bool = digital_pin["d3"].as<int>();Serial.print("info_name_str = ");Serial.println(info_name_str);Serial.print("d3_bool = ");Serial.println(d3_bool);d3_bool == 0 ? digitalWrite (LED_BUILTIN, LOW) : digitalWrite(LED_BUILTIN, HIGH);
}
以上程序中最重点的部分是函数httpRequest。该函数向服务器发送HTTP请求,并且对服务器相应的JSON信息进行了解析。解析后的数据信息将通过串口监视器显示,其中服务器按键引脚的状态信息还被用于控制客户端板上的LED点亮和熄灭。
2、心知天气使用
如今互联网上有很多天气信息平台,心知天气是我们使用过的信息最准确,服务最稳定的平台。难能可贵的是,心知天气所提供的基础服务是完全免费的。这些免费天气信息包括天气预报信息(3日),实时天气以及生活指数。
心知天气首页地址:心知天气 - 高精度气象数据 - 天气数据API接口 - 行业气象解决方案
注册登录之后,去控制台获取私钥,注:私钥不要随便分享给别人,否则后果自负。
2.1、获取实时天气信息(温度,天气)
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>const char* ssid = "快去摸鱼"; const char* password = "12345678aqz"; const char* host = "api.seniverse.com"; // 将要连接的服务器地址
const int httpPort = 80; // 将要连接的服务器端口 // 心知天气HTTP请求所需信息
String reqUserKey = "S-xxxxx"; // 私钥
String reqLocation = "Nanchang"; // 城市
String reqUnit = "c"; // 摄氏/华氏void setup(){Serial.begin(9600); Serial.println("");// 连接WiFiconnectWiFi();
}void loop(){// 建立心知天气API当前天气请求资源地址String reqRes = "/v3/weather/now.json?key=" + reqUserKey ++ "&location=" + reqLocation + "&language=en&unit=" +reqUnit;// 向心知天气服务器服务器请求信息并对信息进行解析httpRequest(reqRes);delay(3000);
}// 向心知天气服务器服务器请求信息并对信息进行解析
void httpRequest(String reqRes){WiFiClient client;// 建立http请求信息String httpRequest = String("GET ") + reqRes + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n";Serial.println(""); Serial.print("Connecting to "); Serial.print(host);// 尝试连接服务器if (client.connect(host, 80)){Serial.println(" Success!");// 向服务器发送http请求信息client.print(httpRequest);Serial.println("Sending request: ");Serial.println(httpRequest); // 获取并显示服务器响应状态行 String status_response = client.readStringUntil('\n');Serial.print("status_response: ");Serial.println(status_response);// 使用find跳过HTTP响应头if (client.find("\r\n\r\n")) {Serial.println("Found Header End. Start Parsing.");}// 利用ArduinoJson库解析心知天气响应信息parseInfo(client); } else {Serial.println(" connection failed!");} //断开客户端与服务器连接工作client.stop();
}// 连接WiFi
void connectWiFi(){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地址。
}// 利用ArduinoJson库解析心知天气响应信息
void parseInfo(WiFiClient client){const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(6) + 230;DynamicJsonDocument doc(capacity);deserializeJson(doc, client);JsonObject results_0 = doc["results"][0];JsonObject results_0_now = results_0["now"];const char* results_0_now_text = results_0_now["text"]; // "Sunny"const char* results_0_now_code = results_0_now["code"]; // "0"const char* results_0_now_temperature = results_0_now["temperature"]; // "32"const char* results_0_last_update = results_0["last_update"]; // "2020-06-02T14:40:00+08:00" // 通过串口监视器显示以上信息String results_0_now_text_str = results_0_now["text"].as<String>(); int results_0_now_code_int = results_0_now["code"].as<int>(); int results_0_now_temperature_int = results_0_now["temperature"].as<int>(); String results_0_last_update_str = results_0["last_update"].as<String>(); Serial.println(F("======Weahter Now======="));Serial.print(F("Weather Now: "));Serial.print(results_0_now_text_str);Serial.print(F(" "));Serial.println(results_0_now_code_int);Serial.print(F("Temperature: "));Serial.println(results_0_now_temperature_int);Serial.print(F("Last Update: "));Serial.println(results_0_last_update_str);Serial.println(F("========================"));
}
可以看出,温度22度,多云,以及数据最后更新的时间。
2.2、获取天气预报信息
获取天气预报信息包括:温度,天气,降水概率,风力,风向,湿度
此实例获取三天后的预报信息。
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>const char* ssid = "快去摸鱼"; const char* password = "12345678aqz"; const char* host = "api.seniverse.com"; // 将要连接的服务器地址
const int httpPort = 80; // 将要连接的服务器端口 // 心知天气HTTP请求所需信息
String reqUserKey = "S-xxxxxx"; // 私钥
String reqLocation = "Nanchang"; // 城市
String reqUnit = "c"; // 摄氏/华氏void setup(){Serial.begin(9600); Serial.println("");// 连接WiFiconnectWiFi();
}void loop(){// 建立心知天气API当前天气请求资源地址String reqRes = "/v3/weather/daily.json?key=" + reqUserKey ++ "&location=" + reqLocation + "&language=en&unit=" +reqUnit + "&start=0&days=3";// 向心知天气服务器服务器请求信息并对信息进行解析httpRequest(reqRes);delay(3000);
}// 向心知天气服务器服务器请求信息并对信息进行解析
void httpRequest(String reqRes){WiFiClient client;// 建立http请求信息String httpRequest = String("GET ") + reqRes + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n";Serial.println(""); Serial.print("Connecting to "); Serial.print(host);// 尝试连接服务器if (client.connect(host, 80)){Serial.println(" Success!");// 向服务器发送http请求信息client.print(httpRequest);Serial.println("Sending request: ");Serial.println(httpRequest); // 获取并显示服务器响应状态行 String status_response = client.readStringUntil('\n');Serial.print("status_response: ");Serial.println(status_response);// 使用find跳过HTTP响应头if (client.find("\r\n\r\n")) {Serial.println("Found Header End. Start Parsing.");}// 利用ArduinoJson库解析心知天气响应信息parseInfo(client); }else {Serial.println(" connection failed!");} //断开客户端与服务器连接工作client.stop();
}// 连接WiFi
void connectWiFi(){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地址。
}// 利用ArduinoJson库解析心知天气响应信息
void parseInfo(WiFiClient client){const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(3) + JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(6) + 3*JSON_OBJECT_SIZE(14) + 860;DynamicJsonDocument doc(capacity);deserializeJson(doc, client);JsonObject results_0 = doc["results"][0];JsonArray results_0_daily = results_0["daily"];JsonObject results_0_daily_0 = results_0_daily[0];const char* results_0_daily_0_date = results_0_daily_0["date"]; const char* results_0_daily_0_text_day = results_0_daily_0["text_day"]; const char* results_0_daily_0_code_day = results_0_daily_0["code_day"];const char* results_0_daily_0_text_night = results_0_daily_0["text_night"]; const char* results_0_daily_0_code_night = results_0_daily_0["code_night"]; const char* results_0_daily_0_high = results_0_daily_0["high"];const char* results_0_daily_0_low = results_0_daily_0["low"]; const char* results_0_daily_0_rainfall = results_0_daily_0["rainfall"];const char* results_0_daily_0_precip = results_0_daily_0["precip"]; const char* results_0_daily_0_wind_direction = results_0_daily_0["wind_direction"]; const char* results_0_daily_0_wind_direction_degree = results_0_daily_0["wind_direction_degree"];const char* results_0_daily_0_wind_speed = results_0_daily_0["wind_speed"];const char* results_0_daily_0_wind_scale = results_0_daily_0["wind_scale"];const char* results_0_daily_0_humidity = results_0_daily_0["humidity"];JsonObject results_0_daily_1 = results_0_daily[1];const char* results_0_daily_1_date = results_0_daily_1["date"];const char* results_0_daily_1_text_day = results_0_daily_1["text_day"];const char* results_0_daily_1_code_day = results_0_daily_1["code_day"];const char* results_0_daily_1_text_night = results_0_daily_1["text_night"]; const char* results_0_daily_1_code_night = results_0_daily_1["code_night"]; const char* results_0_daily_1_high = results_0_daily_1["high"];const char* results_0_daily_1_low = results_0_daily_1["low"]; const char* results_0_daily_1_rainfall = results_0_daily_1["rainfall"]; const char* results_0_daily_1_precip = results_0_daily_1["precip"]; const char* results_0_daily_1_wind_direction = results_0_daily_1["wind_direction"];const char* results_0_daily_1_wind_direction_degree = results_0_daily_1["wind_direction_degree"]; const char* results_0_daily_1_wind_speed = results_0_daily_1["wind_speed"];const char* results_0_daily_1_wind_scale = results_0_daily_1["wind_scale"];const char* results_0_daily_1_humidity = results_0_daily_1["humidity"]; JsonObject results_0_daily_2 = results_0_daily[2];const char* results_0_daily_2_date = results_0_daily_2["date"];const char* results_0_daily_2_text_day = results_0_daily_2["text_day"];const char* results_0_daily_2_code_day = results_0_daily_2["code_day"];const char* results_0_daily_2_text_night = results_0_daily_2["text_night"];const char* results_0_daily_2_code_night = results_0_daily_2["code_night"];const char* results_0_daily_2_high = results_0_daily_2["high"]; const char* results_0_daily_2_low = results_0_daily_2["low"]; const char* results_0_daily_2_rainfall = results_0_daily_2["rainfall"];const char* results_0_daily_2_precip = results_0_daily_2["precip"]; const char* results_0_daily_2_wind_direction = results_0_daily_2["wind_direction"]; const char* results_0_daily_2_wind_direction_degree = results_0_daily_2["wind_direction_degree"]; const char* results_0_daily_2_wind_speed = results_0_daily_2["wind_speed"];const char* results_0_daily_2_wind_scale = results_0_daily_2["wind_scale"]; const char* results_0_daily_2_humidity = results_0_daily_2["humidity"]; const char* results_0_last_update = results_0["last_update"]; // 从以上信息中摘选几个通过串口监视器显示String results_0_daily_0_date_str = results_0_daily_0["date"].as<String>();String results_0_daily_0_text_day_str = results_0_daily_0["text_day"].as<String>(); int results_0_daily_0_code_day_int = results_0_daily_0["code_day"].as<int>(); String results_0_daily_0_text_night_str = results_0_daily_0["text_night"].as<String>(); int results_0_daily_0_code_night_int = results_0_daily_0["code_night"].as<int>(); int results_0_daily_0_high_int = results_0_daily_0["high"].as<int>();int results_0_daily_0_low_int = results_0_daily_0["low"].as<int>();String results_0_last_update_str = results_0["last_update"].as<String>();Serial.println(F("======Today Weahter ======="));Serial.print(F("DATE: "));Serial.println(results_0_daily_0_date_str);Serial.print(F("Day Weather: "));Serial.print(results_0_daily_0_text_day_str);Serial.print(F(" "));Serial.println(results_0_daily_0_code_day_int);Serial.print(F("Night Weather: "));Serial.print(results_0_daily_0_text_night_str);Serial.print(F(" "));Serial.println(results_0_daily_0_code_night_int);Serial.print(F("High: "));Serial.println(results_0_daily_0_high_int);Serial.print(F("LOW: "));Serial.println(results_0_daily_0_low_int);Serial.print(F("Last Update: "));Serial.println(results_0_last_update_str);Serial.println(F("=============================="));
}
七、WiFiManager库使用说明
在我们开发物联网项目时,经常需要为ESP8266设置WiFi。在之前中,我们的设置WiFi的方法是通过修改程序中的内容来实现的。
但是假如我们做好了物联网制作后送给朋友,而朋友不知道如何写ESP8266程序.这种情况下该如何来让不懂编程的朋友也能设置ESP8266的WiFi连接呢?
我们可以利用一款非常好用的ESP8266第三方库:WiFiManager库。
1、WiFi配置流程
2、WiFi配置示例程序
ESP8266的WiFi设置是储存在它的闪存系统中的。因此在启动ESP8266并连接WiFi时,它都会尝试使用闪存系统中储存的信息来进行WiFi连接。
在开始讲解如何使用WiFiManager库来配置ESP8266的WiFi设置前,我们需要首先清除ESP8266的WiFi连接信息,这样才能看到WiFiManager库的工作效果。(如果ESP8266刚一启动就自动成功连接WiFi了,那么WiFiManager库是不会发挥作用的。)
使用wifiManager.resetSettings()
来实现清除ESP8266的闪存中所存储的WiFi连接信息这一操作。
wifiManager.autoConnect("AutoConnectAP"):使用WiFiManager来实现WiFi网络配置。
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h> // 测试HTTP请求用的URL。注意网址前面必须添加"http://"
#define URL "http://www.example.com"void setup() {//初始化串口设置Serial.begin(9600);// 建立WiFiManager对象WiFiManager wifiManager;// 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称wifiManager.autoConnect("快去摸鱼", "12345678aqz");Serial.print("WiFi Connected!");httpClientRequest();
}void loop() {}// 发送HTTP请求并且将服务器响应通过串口输出
void httpClientRequest(){//重点1 创建 HTTPClient 对象HTTPClient httpClient;WiFiClient wifiClient;//重点2 通过begin函数配置请求地址。此处也可以不使用端口号和PATH而单纯的httpClient.begin(wifiClient, URL); Serial.print("URL: "); Serial.println(URL);//重点3 通过GET函数启动连接并发送HTTP请求int httpCode = httpClient.GET();Serial.print("Send GET request to URL: ");Serial.println(URL);//重点4. 如果服务器响应HTTP_CODE_OK(200)则从服务器获取响应体信息并通过串口输出//如果服务器不响应HTTP_CODE_OK(200)则将服务器响应状态码通过串口输出if (httpCode == HTTP_CODE_OK) {// 使用getString函数获取服务器响应体内容String responsePayload = httpClient.getString();Serial.println("Server Response Payload: ");Serial.println(responsePayload);} else {Serial.println("Server Respose Code:");Serial.println(httpCode);}//重点5. 关闭ESP8266与服务器连接httpClient.end();
}
八、Ticker库使用说明
ESP8266在运行过程中,只能一条线式的依次执行任务。但是我们在开发物联网项目时,可能需要ESP8266在执行某一任务的过程中,还能处理其它任务。比如,我们使用ESP8266来控制电机运行的同时,还需要定时检查某一个引脚上连接按钮有没有被用户按下。
利用Ticker库,我们可以让ESP8266定时调用某一个函数。通过以下示例程序我们可以看到,ESP8266将会每隔一秒钟通过串口监视器输出一次信息。我们是通过语句ticker.attach(1, sayHi)
来实现这一操作的
#include <Ticker.h>Ticker ticker;// 建立Ticker用于实现定时功能
int count; // 计数用变量void setup() {Serial.begin(9600);pinMode(LED_BUILTIN, OUTPUT);// 每隔一秒钟调用sayHi函数一次,attach函数的第一个参数// 是控制定时间隔的变量。该参数的单位为秒。第二个参数是// 定时执行的函数名称。ticker.attach(1, sayHi);
}void loop() {// 用LED呼吸灯效果来演示在Tinker对象控制下,ESP8266可以定时// 执行其它任务for (int fadeValue = 0 ; fadeValue <= 1023; fadeValue += 5) {analogWrite(LED_BUILTIN, fadeValue);delay(10);}for (int fadeValue = 1023 ; fadeValue >= 0; fadeValue -= 5) {analogWrite(LED_BUILTIN, fadeValue);delay(10);}delay(3000);
}// 在Tinker对象控制下,此函数将会定时执行。
void sayHi(){count++;Serial.print("Hi ");Serial.println(count);
}
停止定时执行函数:ticker.detach()
将会让ticker对象停止调用函数
向定时调用函数传递参数:ticker.attach(1, sayHi, 8)有3个参数。其中第三个参数就是向定时调用的sayHi函数所传递的参数。
注:attach函数所能传递的参数最多只有一个。另外该参数仅能是以下类型中的一种:char, short, int, float, void*, char*。
九、OTA 操作说明
所谓OTA,就是Over-The-Air的缩写。有人将其翻译为“空中下载”,也有翻译为“隔空传输”。无论如何翻译,对于ESP2866来说,通过OTA我们无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。
#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
#include <Ticker.h>// 闪烁时间间隔(秒)
const int blinkInterval = 2; // 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "快去摸鱼";
const char* password = "12345678aqz";Ticker ticker;void setup() {Serial.begin(9600); Serial.println("");pinMode(LED_BUILTIN, OUTPUT);ticker.attach(blinkInterval, tickerCount); // 设置Ticker对象connectWifi();// OTA设置并启动ArduinoOTA.setHostname("ESP8266");ArduinoOTA.setPassword("12345678");ArduinoOTA.begin();Serial.println("OTA ready");
}
void loop() {ArduinoOTA.handle();
}// 在Tinker对象控制下,此函数将会定时执行。
void tickerCount(){digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}void connectWifi(){//开始连接wifiWiFi.begin(ssid, password);//等待WiFi连接,连接成功打印IPwhile (WiFi.status() != WL_CONNECTED) {delay(1000);Serial.print(".");}Serial.println("");Serial.println("WiFi Connected!"); Serial.print("IP address:\t"); Serial.println(WiFi.localIP());
}
OTA的局限性:
1. 程序占用空间变大
在OTA上传新程序过程中, ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如您的程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了。
2. Arduino IDE无法通过OTA端口与开发板进行串口通讯
当Arduino IDE的上传端口选为“网络端口”,Arduino IDE将无法获取ESP8266的串口通讯数据。不过ESP8266的串口通讯并不会因为OTA功能而受到影响。换句话说,您可以使用其它电脑串口通讯软件,如Putty等,来实现ESP8266与电脑之间的串口通讯。
3.使用OTA上传程序的电脑与ESP8266必须连接同一WiFi
若要使用OTA上传功能,那么电脑和ESP8266必须要在同一WiFi中,否则是无法实现OTA上传的。