参考教程:https://www.bilibili.com/video/BV1L7411c7jw/?spm_id_from=333.1387.favlist.content.click
十、ESP8266应用技巧
1、ESP8266自动配网------WiFiManager库使用说明
(1)在开发物联网项目时,经常需要为ESP8266设置Wi-Fi,在以往的章节例程里,设置Wi-Fi的方法是通过修改程序中的内容来实现的,但是假如这是一个面向用户的产品,用户拿到产品后不可能通过修改程序设置Wi-Fi,因此需要有一个Wi-Fi配置流程,即使是不懂编程的用户,也能为产品设置Wi-Fi。
(2)以下是理想的Wi-Fi配置流程,为了实现该流程,可借助ESP8266的一个第三方库------WiFiManager库。
(3)Wi-Fi配置示例程序:
①预备程序:清理ESP8266储存的Wi-Fi连接信息。
ESP8266的Wi-Fi设置是储存在它的闪存系统中的,因此在启动ESP8266并连接Wi-Fi时,它都会尝试使用闪存系统中储存的信息来进行Wi-Fi连接
在开始介绍如何使用WiFiManager库来配置ESP8266的Wi-Fi设置前,首先需要清除ESP8266的Wi-Fi连接信息,这样才能看到WiFiManager库的工作效果,而WiFiManager类的resetSettings函数即可实现清除功能
运行以下示例程序,可以清除ESP8266的闪存中存储的Wi-Fi连接信息
cpp
复制代码
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
void setup() {
Serial.begin(9600);
WiFiManager wifiManager; // 建立WiFiManager对象
wifiManager.resetSettings(); // 清除ESP8266所存储的WiFi连接信息
Serial.println("ESP8266 WiFi Settings Cleared");
}
void loop() {}
②示例程序:
创建WiFiManager类后,直接调用其成员函数autoConnect,就能实现ESP8266理想的Wi-Fi配置流程
autoConnect函数有两种重载形式:如果希望启动AP模式构建的Wi-Fi网络无密码,则函数参数只有一个,为Wi-Fi网络名字符串;如果希望启动AP模式构建的Wi-Fi网络有密码,则函数参数有两个,依次为Wi-Fi网络名字符串、Wi-Fi网络密码字符串
cpp
复制代码
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
void setup() {
Serial.begin(9600);
WiFiManager wifiManager; // 建立WiFiManager对象
// 自动连接Wi-Fi,如果连接不上,启动AP模式,Wi-Fi网络名为"AutoConnectAP"
wifiManager.autoConnect("AutoConnectAP");
// 如果希望启动AP模式构建的Wi-Fi网络带有密码,则可使用以下语句
// wifiManager.autoConnect("AutoConnectAP", "00114514");
// WiFi连接成功后将通过串口监视器输出连接成功信息
Serial.println("");
Serial.print("ESP8266 Connected to "); Serial.println(WiFi.SSID());
Serial.print("IP address:\t"); Serial.println(WiFi.localIP());
}
void loop() {}
2、ESP8266多任务处理------Ticker库使用说明
(1)ESP8266在运行过程中,只能一条线式的依次执行任务,但是在开发物联网项目时,可能需要ESP8266在执行某一任务的过程中,还能处理其它任务,比如使用ESP8266来控制电机运行的同时,还需要定时检查某一个引脚上连接按钮有没有被用户按下。为了解决以上问题,可以使用Ticker库(如果有《操作系统》的前置知识储备,以下介绍会更容易理解)。
(2)Ticker类提供了以下几个成员函数:
①attach函数:该函数用于创建任务,并设置其运行周期,需要注意一个Ticker对象只能对应创建一个任务,要想创建多个任务,需要先创建多个Ticker对象。
第一种重载形式有两个函数参数,第一个参数指定调用函数的时间间隔,单位为秒,第二个参数指定调用函数(无参函数)的函数名
第二种重载形式有三个函数参数,第一个参数指定调用函数的时间间隔,单位为秒,第二个参数指定调用函数的函数名,第三个参数是传递给调用函数的参数值(最多有一个,且数据类型局限于char、short、int、float、void*、char*)
②attach_ms函数:该函数用于创建任务,并设置其运行周期,需要注意一个Ticker对象只能对应创建一个任务,要想创建多个任务,需要先创建多个Ticker对象。
第一种重载形式有两个函数参数,第一个参数指定调用函数的时间间隔,单位为毫秒,第二个参数指定调用函数(无参函数)的函数名
第二种重载形式有三个函数参数,第一个参数指定调用函数的时间间隔,单位为毫秒,第二个参数指定调用函数的函数名,第三个参数是传递给调用函数的参数值(最多有一个,且数据类型局限于char、short、int、float、void*、char*)
③detach函数:该函数用于停止其对应的任务被调度运行,该函数无参数。
(3)基本操作示例:
①示例1:
1\]任务调度示意图:

\[2\]程序代码:
```cpp
#include
Ticker ticker;// 建立Ticker用于实现定时功能
int count; // 计数用变量
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
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);
}
```
②示例2:
\[1\]任务调度示意图:

\[2\]程序代码:
```cpp
#include
Ticker ticker;// 建立Ticker用于实现定时功能
int count; // 计数用变量
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
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);
// 当定时调用了6次后,停止定时调用函数
if (count >= 6) {
ticker.detach(); // 使用detach来停止ticker对象定时调用函数
Serial.print("ticker.detach()");
}
}
```
③示例3:
\[1\]任务调度示意图:

\[2\]程序代码:
```cpp
#include
Ticker ticker;
int count;
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
ticker.attach(1, sayHi, 8);
}
void loop() {
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);
}
void sayHi(int hiTimes){
count++;
Serial.print("Hi ");
Serial.println(count);
if (count >= hiTimes) {
ticker.detach();
Serial.print("ticker.detach();");
}
}
```
④示例4:
\[1\]任务调度示意图:

\[2\]程序代码:
```cpp
#include
Ticker ticker;
Ticker buttonTicker;
int count;
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT); pinMode(D3, INPUT_PULLUP);
ticker.attach(1, sayHi, 60); buttonTicker.attach_ms(100, buttonCheck);
}
void loop() {
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);
}
void sayHi(int hiTimes){
count++;
Serial.print("Hi "); Serial.println(count);
if (count >= hiTimes) {
ticker.detach();
Serial.print("ticker.detach();");
}
}
void buttonCheck(){
if (digitalRead(D3) == LOW){
Serial.println("D3 Button Pushed...");
}
}
```
(3)Ticker定时调用的函数必须要"短小精悍",简单来说就是不能消耗过多CPU资源,执行时间不能太长,否则可能引发难以预料的问题,那么假如需要ESP8266定时执行的操作较为复杂,就不能直接将该操作封装为Ticker定时调用的函数了,而是应该"另辟蹊径",可以在Ticker类定时调用的函数中增加计数逻辑,当计数值达到一定值时,意味着间隔时间到达一定值,主程序可以根据这个计数值调用需要定时执行的复杂操作,如下所示的示例程序正是运用了这种思想。
```cpp
#include
#include
#include
#define URL "http://www.example.com"
const char* ssid = "Zevalin_Computer";
const char* password = "00114514";
Ticker ticker;
int count;
void setup() {
Serial.begin(9600);
WiFi.mode(WIFI_STA);
connectWifi();
ticker.attach(1, tickerCount); //每隔1s执行一次tickerCount函数
}
void loop() {
if (count >= 5){ //计数值达到5,说明已经间隔5s
httpRequest(); //执行复杂的操作
count = 0; //计数值清零,准备下一次计时
}
}
void tickerCount(){
count++; //执行一次tickerCount函数,计数值加1
Serial.print("count = ");
Serial.println(count);
}
void httpRequest(){
HTTPClient httpClient;
httpClient.begin(URL);
Serial.print("URL: "); Serial.println(URL);
int httpCode = httpClient.GET();
Serial.print("Send GET request to URL: ");
Serial.println(URL);
if (httpCode == HTTP_CODE_OK) {
String responsePayload = httpClient.getString();
Serial.println("Server Response Payload: ");
Serial.println(responsePayload);
} else {
Serial.println("Server Respose Code:");
Serial.println(httpCode);
}
httpClient.end();
}
void connectWifi(){
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.print("WiFi Connected!");
}
```