基础知识

WIFI模式

接入点模式

esp8266作为接入点,其他设备通过wifi与esp8266连接。 示例:

/*  
此程序用于演示如何将NodeMCU以接入点模式工作。通过此程序,您可以使用  
电脑或者手机连接NodeMCU所建立WiFi网络。  
*/  
#include <ESP8266WiFi.h>        // 本程序使用ESP8266WiFi库  
  
const char* ssid = "wifi_name";   
const char* password = "12345678"; // 如果建立的WiFi不要密码,则在双引号内不要填入任何信息  
  
void setup() {  
  Serial.begin(9600);              // 启动串口通讯  
  
  WiFi.softAP(ssid, password);     // 此语句是重点。WiFi.softAP用于启动NodeMCU的AP模式。  
  
  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() {   
}

无线终端模式

esp8266与路由器连接。

/*  
此程序用于演示如何将NodeMcu以接入点模式工作  
*/  
#include <ESP8266WiFi.h>  
  
const char* ssid = "wifi_name";  
const char* password = "pswd";  
  
void setup(){  
    Serial.begin(9600)  
  
    WiFi.begin(ssid, password);                  // 启动网络连接  
    Serial.print("Connecting to ");                
    Serial.print(ssid);   
    Serial.println(" ...");    
  
    int i = 0;                                   // 这一段程序语句用于检查WiFi是否连接成功  
    while (WiFi.status() != WL_CONNECTED) {        
        delay(1000);                                                       
        Serial.print(i++); Serial.print(' ');       
    }                                              
}  
  
void loop(){  
}

网络服务器

简单的网络服务器

/*  
 使用NodeMCU建立基本服务器。用户可通过浏览器使用8266的IP地址访问8266所建立的基本网页  
*/  
#include <ESP8266WiFi.h>         
#include <ESP8266WiFiMulti.h>     
#include <ESP8266WebServer.h>     
  
ESP8266WebServer esp8266_server(80); // 括号中的数字是网路服务器响应http请求的端口号  
                                     // 网络服务器标准http端口号为80,因此这里使用80为端口号  
const char *ssid = "";  
const char *password = "";  
  
void setup(void){  
  Serial.begin(9600);  
  
  pinMode(LED_BUILTIN,OUTPUT); //把内置led引脚设置为输出模式  
  
  WiFi.begin(ssid, password);  
  
  int i = 0;                                   
  while (WiFi.status() != WL_CONNECTED) {    
    delay(1000);                               
    Serial.print(i++); Serial.print(' ');      
  }                                            
  
  // 启动网络服务功能    
  esp8266_server.begin();                     
  esp8266_server.on("/", handleRoot);       // 将页面"/"交给handleRoot()处理  
  esp8266_server.on("/LED",handleLED);  
  esp8266_server.onNotFound(handleNotFound);  // 将404页面交给handleNotFound()处理        
  
}  
  
void loop(void){  
  // handleClient每次被调用时,NodeMCU都会检查一下是否接收到http请求。  
  esp8266_server.handleClient();     // 处理http服务器访问  
}  
  
void handleRoot() {   //处理网站根目录“/”的访问请求   
  esp8266_server.send(200, "text/html", "<form action=\"/LED\" method=\"POST\"><input type=\"submit\" value=\"Toggle LED\"></form>");   // 状态行、响应头、响应体  
}  
  
void handleLED(){  
  digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));  
  esp8266_server.sendHeader("Location","/"); // 返回"/"  
  esp8266_server.send(303);  
}  
  
// 设置处理404情况的函数'handleNotFound'  
void handleNotFound(){                                          
  esp8266_server.send(404, "text/plain", "404: Not found");   // 当浏览器请求的网络资源无法在服务器找到  
}

如果handleClient函数长时间得不到调用,NodeMCU的网络服务会变得很不稳定。因此在使用NodeMCU执行网络服务功能的时候,一定要确保handleClient函数经常得以调用。

通信

MQTT通信

MQTT通信与HTTP通信都基于TCP/IP协议,物联网应用中多使用MQTT协议,它有如下优点:

  • MQTT是一种轻量级协议,因此可以在网络带宽受限的情况下使用。
  • MQTT使用发布/订阅模式,这意味着客户端只需要订阅感兴趣的主题即可接收消息。这种模式可以减少网络流量和服务器负载。
  • MQTT支持QoS(Quality of Service)等级,可以确保消息传递的可靠性和一致性。
  • MQTT支持离线消息缓存和消息保留功能,这意味着客户端可以在重新连接到服务器时接收到之前未接收到的消息。

HTTP通信

HTTP协议

请求

请求行:  
GET / HTTP/1.1 # 'GET':读取 '/':网站首页 'HTTP/1.1':协议版本  
  
请求头:  
Host: www.baidu.com  
User-Agent: Mozilla/5.0  
Accept: text/html  
Accept-Launguage: zh-CN,zh;q=0.8  
Accept-Ecoding: gzip,deflate,sdch  
Connection: Keep-Alive

响应

状态行:  
HTTP/1.1 200 OK  
  
响应头:  
Date: Fri,22 May 2009 06:07:21 GMT  
Content-Type: text/html;charset=UTF-8  
  
响应体:  
<html>...<html>

使用WiFiClient库实现HTTP通信

void wifiClientRequest(){  
  // 建立WiFi客户端对象,对象名称client  
  WiFiClient 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);   
  
  // http服务器的默认端口号为80  
  if (client.connect(host, httpPort)){   
    Serial.println(" Success!");          
  
    client.print(httpRequest);          // 向服务器发送HTTP请求     
  
    Serial.println("Web Server Response:");          
    while (client.connected() || client.available()){   
      if (client.available()){  
        String line = client.readStringUntil('\n'); // 读取服务器发送的信息  
        Serial.println(line);  
      }  
    }  
  
    client.stop();                      // 断开与服务器的连接  
  
  } else{  
    Serial.println(" connection failed!");  
    client.stop();  
  }    
}

闪存文件系统

ESP8266具有4Mb的闪存(SPIFFS),除了能存放程序,还能存放各种资源文件,如html文件、系统配置文件等。

基本操作

写入/删除

#include <FS.h>    
  
String file_name = "/taichi-maker/notes.txt"; //被读取的文件位置和名称  
  
void setup() {  
  Serial.begin(9600);  
  
  SPIFFS.format();    // 格式化SPIFFS,清除闪存中的所有内容  
  
  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)写入信息 "w"即write  
  dataFile.println("Hello IOT World.");       // 向dataFile写入字符串信息         
  dataFile.close();                           // 完成文件写入后关闭文件  
  // 删除信息  
  if (SPIFFS.remove(file_name)){  
    Serial.print(file_name);  
  } else {  
    Serial.print(file_name);  
  }  
}  
  
void loop() {  
}

读取

// 检查闪存中是否有file_name  
if (SPIFFS.exists(file_name)){  
  Serial.print(file_name);  
  Serial.println(" FOUND.");  
} else {  
  Serial.print(file_name);  
  Serial.print(" NOT FOUND.");  
}  
  
File dataFile = SPIFFS.open(File_name,"r");  
// 读取文件内容并输出到串口  
for(int i=0; i<dataFile.size(); i++){  
  Serial.print((char)dataFile.read());         
}  
  
dataFile.close();  
Dir dir = SPIFFS.openDir(folder_name); // 读取目录对象  
while (dir.next()){  
  Serial.print(dir.fileName());  
}

闪存信息

// 闪存文件系统信息  
  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);

应用技巧

多任务之Ticker库

ESP8266在运行过程中,只能一条线式的依次执行任务。但是我们在开发物联网项目时,可能需要ESP8266在执行某一任务的过程中,还能处理其它任务。比如,我们使用ESP8266来控制电机运行的同时,还需要定时检查某一个引脚上连接按钮有没有被用户按下。 为了解决以上问题,我们可以使用多线程,我使用的库是Ticker。

#include <Tikcer.h>  
  
Ticker ticker;  
  
void setup(){  
    ticker.attach(interval, func, parameter);  
}  
  
void func(int parameter){}

Ticker.attach()会以间隔时间interval执行定时任务func(parameter),注意,Tikcer.attach()绑定的函数只能有最多一个参数。一个Tikcer对象仅能绑定一个函数。参数类型只能是char/short/int/float/void*/char* Ticker.detach()会停止定时任务。 另外,在使用Ticker.attach()绑定的函数执行的时间必须很短,否则会产生不可预料的问题。对于执行时间较长的操作,如http请求,可以采用如下方法。

#include <Tikcer.h>  
  
Ticker ticker;  
int count;  
  
void setup(){  
    count = 0;  
    ticker.attach(interval, count_func);  
}  
  
void count_func(){  
    cout++;  
}  
  
void func(int parameter){  
    if(cont>=interval){  
        httprequest()  
    }  
}