一、HttpClient—–ESP8266HTTPClient库的使用
HTTP知识:
1、概述:
HTTP协议(Hyper Text Transfer Protocol)超文本传输协议,用于从WWW服务器传输文本到本地浏览器的传送协议。基于TCP/IP通信协议:浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。WEB服务器根据接收到的请求后,向客户端发送响应信息;
HTTP协议作为TCP/IP模型中应用层的协议,承载与TCP协议上,有时也承载与TLS或者SSL协议层之上,这时就是HTTPS;
HTTP由请求与响应构成,是一个标准的客户端服务器模型,默认端口号80,HTTPS是443;
浏览网页时HTTP主要应用(不只是这个功能,只是一种协议,只要通信双方都支持这个协议,HTTP就能用)
特点:HTTP0.9与1.0使用非持续连接;1.1使用持续连接。
无状态,无记忆能力
2、工作流程:(一次HTTP操作称位一个事务,可分4步)
client与server建立连接;
连接后,client发送一个请求给server,请求方法格式:统一资源标示符(URL)、HTTP协议版本号、请求头、请求内容等;
server接收到请求后,给予响应,格式:状态行(包括协议版本、成功或者失败代码、服务器信息、实体信息等)
client接收到server返回信息,通过浏览器显示在用户显示屏上,然后client与server断开连接
3、HTTP请求:
Get请求:
- 请求行(request line):说明请求类型,要访问的资源及HTTP版本
- 请求头部(header):说明服务器要使用的附加信息(第二行起位请求头部,HOST指出目的地;User-Agent是client与server脚本都能访问,是浏览器检测逻辑的重要基础)(该信息由浏览器定义,并在每个请求中自动发送)
- 空行(empty line):请求头部后空行是必须的(即使第四部分请求数据为空,也必须有空行)
- 请求数据(request body):主体,可添加任意其他数据(下面例子中请求数据为空)
请求例子(Gharles抓取的request)
GET /562f25980001b1b106000338.jpg HTTP/1.1
Host img.mukewang.com
User-Agent Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
Accept image/webp,image/*,*/*;q=0.8
Referer http://www.imooc.com/
Accept-Encoding gzip, deflate, sdch
Accept-Language zh-CN,zh;q=0.8
POST请求:
请求例子(Gharles抓取的request)
POST / HTTP1.1
Host:www.wrox.com
User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Content-Type:application/x-www-form-urlencoded
Content-Length:40
Connection: Keep-Alive
name=Professional%20Ajax&publisher=Wiley
4、HTTP Response响应信息
- 状态行
- 消息报头
- 空行
- 响应正文
HTTP状态码
库
示例:
- 获取天气请求(心知天气)
setup中配置好url,串口参数和httpclient并设置client请求头。loop中每1秒请求一次get服务,把获取回来的天气信息通过json库转成具体对应的数值
/**
* Demo:
* 演示Http请求天气接口信息
* https://api.seniverse.com/v3/weather/now.json?key=Sv6J6HVHp8V2Cc34a&location=beijing&language=zh-Hans&unit=c
*/
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <ESP8266HTTPClient.h> //不属于ESP8266WiFi库的一部分
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
const char* AP_SSID = "XU-ChinaNet"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "15358228063"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const char* HOST = "http://api.seniverse.com";
const char* APIKEY = ""; //API KEY
const char* CITY = "yancheng";
const char* LANGUAGE = "zh-Hans";//zh-Hans 简体中文 会显示乱码
const unsigned long BAUD_RATE = 115200; // serial connection speed
const unsigned long HTTP_TIMEOUT = 5000; // max respone time from server
// 我们要从此网页中提取的数据的类型
struct WeatherData {
char city[16];//城市名称
char weather[32];//天气介绍(多云...)
char temp[16];//温度
char udate[32];//更新时间
};
WiFiClient client;
HTTPClient http;
String GetUrl;
String response;
WeatherData weatherData;
void setup() {
// put your setup code here, to run once:
WiFi.mode(WIFI_STA); //设置esp8266 工作模式
DebugBegin(BAUD_RATE);
DebugPrint("Connecting to ");//写几句提示,哈哈
DebugPrintln(AP_SSID);
WiFi.begin(AP_SSID, AP_PSK); //连接wifi
WiFi.setAutoConnect(true);
while (WiFi.status() != WL_CONNECTED) {
//这个函数是wifi连接状态,返回wifi链接状态
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrintln("WiFi connected");
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
//拼接get请求url 博哥后面考虑看看是否可以封装一个方法来用用 不需要自己一个个拼装这个url
GetUrl = String(HOST) + "/v3/weather/now.json?key=";
GetUrl += APIKEY;
GetUrl += "&location=";
GetUrl += CITY;
GetUrl += "&language=";
GetUrl += LANGUAGE;
//设置超时
http.setTimeout(HTTP_TIMEOUT);
//设置请求url
//http.begin(GetUrl);
http.begin(client,GetUrl);
//以下为设置一些头 其实没什么用 最重要是后端服务器支持
http.setUserAgent("esp8266");//用户代理版本
http.setAuthorization("esp8266","boge");//用户校验信息
}
void loop() {
//心知天气 发送http get请求
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
//判断请求是否成功
if (httpCode == HTTP_CODE_OK) {
//读取响应内容
response = http.getString();
DebugPrintln("Get the data from Internet!");
DebugPrintln(response);
//解析响应内容
if (parseUserData(response, &weatherData)) {
//打印响应内容
printUserData(&weatherData);
}
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
delay(1000);//每1s调用一次
}
/**
* @Desc 解析数据 Json解析
* 数据格式如下:
* {
* "results": [
* {
* "location": {
* "id": "WX4FBXXFKE4F",
* "name": "北京",
* "country": "CN",
* "path": "北京,北京,中国",
* "timezone": "Asia/Shanghai",
* "timezone_offset": "+08:00"
* },
* "now": {
* "text": "多云",
* "code": "4",
* "temperature": "23"
* },
* "last_update": "2017-09-13T09:51:00+08:00"
* }
* ]
*}
*/
bool parseUserData(String content, struct WeatherData* weatherData) {
// -- 根据我们需要解析的数据来计算JSON缓冲区最佳大小
// 如果你使用StaticJsonBuffer时才需要
// const size_t BUFFER_SIZE = 1024;
// 在堆栈上分配一个临时内存池
// StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
// -- 如果堆栈的内存池太大,使用 DynamicJsonBuffer jsonBuffer 代替
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(content);
if (!root.success()) {
DebugPrintln("JSON parsing failed!");
return false;
}
//复制我们感兴趣的字符串
strcpy(weatherData->city, root["results"][0]["location"]["name"]);
strcpy(weatherData->weather, root["results"][0]["now"]["text"]);
strcpy(weatherData->temp, root["results"][0]["now"]["temperature"]);
strcpy(weatherData->udate, root["results"][0]["last_update"]);
// -- 这不是强制复制,你可以使用指针,因为他们是指向“内容”缓冲区内,所以你需要确保
// 当你读取字符串时它仍在内存中
return true;
}
// 打印从JSON中提取的数据
void printUserData(const struct WeatherData* weatherData) {
DebugPrintln("Print parsed data :");
DebugPrint("City : ");
DebugPrint(weatherData->city);
DebugPrint(", \t");
DebugPrint("Weather : ");
DebugPrint(weatherData->weather);
DebugPrint(",\t");
DebugPrint("Temp : ");
DebugPrint(weatherData->temp);
DebugPrint(" C");
DebugPrint(",\t");
DebugPrint("Last Updata : ");
DebugPrint(weatherData->udate);
DebugPrintln("\r\n");
}
演视响应头获取信息
设置了获取四个请求头的信息,然后通过header方法获取它们的数值(接口限制,无法发挥作用)
/**
* Demo:
* 演示Http请求天气接口信息,演示响应头操作
*/
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <ESP8266HTTPClient.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
const char* AP_SSID = "XU-ChinaNet"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "15358228063"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const char* HOST = "http://api.seniverse.com";
const char* APIKEY = ""; //API KEY
const char* CITY = "yancheng";
const char* LANGUAGE = "zh-Hans";//zh-Hans 简体中文 会显示乱码
const char *keys[] = {"Content-Length","Content-Type","Connection","Date"};//需要收集的响应头的信息
const unsigned long BAUD_RATE = 115200; // serial connection speed
const unsigned long HTTP_TIMEOUT = 5000; // max respone time from server;
WiFiClient client;
HTTPClient http;
String GetUrl;
String response;
void setup() {
// put your setup code here, to run once:
WiFi.mode(WIFI_STA); //设置esp8266 工作模式
DebugBegin(BAUD_RATE);
DebugPrint("Connecting to ");//写几句提示,哈哈
DebugPrintln(AP_SSID);
WiFi.begin(AP_SSID, AP_PSK); //连接wifi
WiFi.setAutoConnect(true);
while (WiFi.status() != WL_CONNECTED) {
//这个函数是wifi连接状态,返回wifi链接状态
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrintln("WiFi connected");
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
//拼接get请求url 博哥后面考虑看看是否可以封装一个方法来用用 不需要自己一个个拼装这个url
GetUrl = String(HOST) + "/v3/weather/now.json?key=";
GetUrl += APIKEY;
GetUrl += "&location=";
GetUrl += CITY;
GetUrl += "&language=";
GetUrl += LANGUAGE;
//设置超时
http.setTimeout(HTTP_TIMEOUT);
//设置请求url
http.begin(client,GetUrl);
//以下为设置一些头 其实没什么用 最重要是后端服务器支持
http.setUserAgent("esp8266");//用户代理版本
http.setAuthorization("esp8266","boge");//用户校验信息
http.addHeader("myname","cainiaobo");
//设置获取响应头的信息
http.collectHeaders(keys,4);
}
void loop() {
//心知天气 发送http get请求
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
//判断请求是否成功
if (httpCode == HTTP_CODE_OK) {
//读取响应内容
response = http.getString();
DebugPrintln("Get the data from Internet!");
DebugPrintln(response);
DebugPrintln(String("Content-Length:")+ http.header("Content-Length"));
DebugPrintln(String("Content-Type:")+ http.header("Content-Type"));
DebugPrintln(String("Connection:")+ http.header("Connection"));
DebugPrintln(String("Date:")+ http.header("Date"));
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
delay(1000);//每1s调用一次
}
二、UDP服务
UDP知识:
UDP(User Datagram Protocol)一种无连接、不可靠的协议(可能会丢包),
相对于TCP而言:
UDP面向无连接的,不需建立连接(TCP是面向连接的)
UDP尽力做到可靠,但不绝对可靠(TCP无差错、不丢失、不重复且按序到达)
UDP较好实时性,效率比TCP高
UDP支持一对一,一对多,多对一和多对多的交互通信(TCP点到点)
UDP对系统资源要求较少(TCP多)
库
- 通过UDP收发数据
ESP8266作为UDP服务端,把电脑UDP客户端发过来的数据打印到串口调试器,并回复应答消息(使用Packet Sender软件发送UDP数据)
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
const char* ssid = "XU-ChinaNet";//wifi账号
const char* password = "15358228063";//wifi密码
WiFiUDP Udp;
unsigned int localUdpPort = 4210; // 本地监听端口
char incomingPacket[255]; // 存储Udp客户端发过来的数据
char replyPacket[] = "Hi there! Got the message :-)"; // 应答信息
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println(" connected");
//启动Udp监听服务
Udp.begin(localUdpPort);
//打印本地ip地址,udp client端会使用到
Serial.printf("Now listening at IP %s, UDP port %d\n", WiFi.localIP().toString().c_str(), localUdpPort);
}
void loop()
{
//解析Udp数据包
int packetSize = Udp.parsePacket();
if (packetSize)
{
// 收到Udp数据包
Serial.printf("Received %d bytes from %s, port %d\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort());
// 读取Udp数据包
int len = Udp.read(incomingPacket, 255);
if (len > 0)
{
incomingPacket[len] = 0;
}
//向D串口调试器打印信息
Serial.printf("UP packet contents: %s\n", incomingPacket);
//往udp 远端发送应答信息
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(replyPacket);
Udp.endPacket();
}
}
- 通过UDP控制LED
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
const char *ssid = "XU-ChinaNet"; //wifi名称
const char *password = "15358228063"; //wifi密码
WiFiUDP Udp;
unsigned int localUdpPort = 4210; // 本地端口号
char incomingPacket[255]; // 接收缓冲区
void setup()
{
//以下为基本功能初始化,初始化串口和网络和LED
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
Serial.println();
Serial.printf("Connecting to %s ", ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println(" connected");
//以下开启UDP监听并打印输出信息
Udp.begin(localUdpPort);
Serial.printf("Now listening at IP %s, UDP port %d\n", WiFi.localIP().toString().c_str(), localUdpPort);
}
void loop()
{
int packetSize = Udp.parsePacket(); //获取当前队首数据包长度
if (packetSize) // 有数据可用
{
Serial.printf("Received %d bytes from %s, port %d\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort());
int len = Udp.read(incomingPacket, 255); // 读取数据到incomingPacket
if (len > 0) // 如果正确读取
{
incomingPacket[len] = 0; //末尾补0结束字符串
Serial.printf("UDP packet contents: %s\n", incomingPacket);
if (strcmp(incomingPacket, "LED_OFF") == 0) // 命令LED_OFF
{
digitalWrite(LED_BUILTIN, HIGH); // 熄灭LED
sendCallBack("LED has been turn off");
}
else if (strcmp(incomingPacket, "LED_ON") == 0) // 如果收到LED_ON
{
digitalWrite(LED_BUILTIN, LOW); // 点亮LED
sendCallBack("LED has been turn on");
}
else // 如果非指定消息
{
sendCallBack("Command Error!");
}
}
}
}
/**
* 发送响应信息
*/
void sendCallBack(const char *buffer){
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(buffer); //回复内容
Udp.endPacket();
}
三、WebServer—ESP8266WebServer库的使用
示例:
- 演示webserver基础功能,wifi模块连接上热点后,在PC浏览器上输入serverip+url访问
/**
* Demo:
* 演示webserver基础功能
* (当wifi模块连接上ap之后,在pc浏览器中输入ip+uri来访问)
*/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
const char* AP_SSID = "TP-LINK_5344"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "6206908you11011010"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const unsigned long BAUD_RATE = 115200; // serial connection speed
//声明一下函数
void initBasic(void);
void initWifi(void);
void initWebServer(void);
ESP8266WebServer server(80);//创建一个webserver
/**
* 处理根目录uri请求
* uri:http://server_ip/
*/
void handleRoot() {
server.send(200, "text/plain", "hello from esp8266!");
}
/**
* 处理无效uri
* uri:http://server_ip/xxxx
*/
void handleNotFound() {
//打印无效uri的信息 包括请求方式 请求参数
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void setup(void) {
initBasic();
initWifi();
initWebServer();
}
/**
* 初始化基础功能:波特率
*/
void initBasic(){
DebugBegin(BAUD_RATE);
}
/**
* 初始化wifi模块:工作模式 连接网络
*/
void initWifi(){
WiFi.mode(WIFI_STA);
WiFi.begin(AP_SSID, AP_PSK);
DebugPrintln("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrint("Connected to ");
DebugPrintln(AP_SSID);
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}
/**
* 初始化webserver
*/
void initWebServer(){
//以下配置uri对应的handler
server.on("/", handleRoot);
server.on("/inline", []() {
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
//启动webserver
server.begin();
DebugPrintln("HTTP server started");
}
void loop(void) {
server.handleClient();
}
- 演示webserver返回html功能
/**
* Demo:
* 演示webserver html功能
* (当wifi模块连接上ap之后,在pc浏览器中输入ip+uri来访问)
*/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
const char* AP_SSID = "XU-ChinaNet"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "15358228063"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const unsigned long BAUD_RATE = 115200; // serial connection speed
//声明一下函数
void initBasic(void);
void initWifi(void);
void initWebServer(void);
ESP8266WebServer server(80);
/**
* 处理根目录uri请求
* uri:http://server_ip/
*/
void handleRoot() {
char temp[400];
int sec = millis() / 1000;
int min = sec / 60;
int hr = min / 60;
snprintf(temp, 400,
"<html>\
<head>\
<meta http-equiv='refresh' content='5'/>\
<title>ESP8266 Demo</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h1>Hello from ESP8266!</h1>\
<p>Uptime: %02d:%02d:%02d</p>\
<img src=\"/test.svg\" />\
</body>\
</html>",
hr, min % 60, sec % 60
);
server.send(200, "text/html", temp);
}
/**
* 处理无效uri
* uri:http://server_ip/xxxx
*/
void handleNotFound() {
//打印无效uri的信息 包括请求方式 请求参数
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void setup(void) {
initBasic();
initWifi();
initWebServer();
}
void loop(void) {
server.handleClient();
}
/**
* 初始化基础功能:波特率
*/
void initBasic(){
DebugBegin(BAUD_RATE);
}
/**
* 初始化wifi模块:工作模式 连接网络
*/
void initWifi(){
WiFi.mode(WIFI_STA);
WiFi.begin(AP_SSID, AP_PSK);
DebugPrintln("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrint("Connected to ");
DebugPrintln(AP_SSID);
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}
/**
* 初始化webserver
*/
void initWebServer(){
//以下配置uri对应的handler
server.on("/", handleRoot);
server.on("/inline", []() {
server.send(200, "text/plain", "this works as well");
});
server.on("/test.svg", drawGraph);
server.onNotFound(handleNotFound);
//启动webserver
server.begin();
DebugPrintln("HTTP server started");
}
/**
* 画图
*/
void drawGraph() {
String out = "";
char temp[100];
out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
out += "<g stroke=\"black\">\n";
int y = rand() % 130;
for (int x = 10; x < 390; x += 10) {
int y2 = rand() % 130;
sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
out += temp;
y = y2;
}
out += "</g>\n</svg>\n";
server.send(200, "image/svg+xml", out);
}
- 演示webserver校验账号密码功能,Autherticate请求头
/**
* Demo:
* 演示webserver auth校验功能
* (当wifi模块连接上ap之后,在pc浏览器中输入ip+uri来访问,不过需要带校验请求头)
*/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
const char* AP_SSID = "XU-ChinaNet"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "15358228063"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const unsigned long BAUD_RATE = 115200; // serial connection speed
const char* www_username = "admin";
const char* www_password = "esp8266";
//声明一下函数
void initBasic(void);
void initWifi(void);
void initWebServer(void);
ESP8266WebServer server(80);//创建webserver
void setup() {
initBasic();
initWifi();
initWebServer();
}
void loop() {
server.handleClient();
}
/**
* 初始化基础功能:波特率
*/
void initBasic(){
DebugBegin(BAUD_RATE);
}
/**
* 初始化wifi模块:工作模式 连接网络
*/
void initWifi(){
WiFi.mode(WIFI_STA);
WiFi.begin(AP_SSID, AP_PSK);
DebugPrintln("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrint("Connected to ");
DebugPrintln(AP_SSID);
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}
/**
* 初始化webserver
*/
void initWebServer(){
//以下配置uri对应的handler
server.on("/", []() {
//校验帐号和密码
if (!server.authenticate(www_username, www_password)) {
return server.requestAuthentication();
}
server.send(200, "text/plain", "Login OK");
});
server.begin();
DebugPrint("Open http://");
DebugPrint(WiFi.localIP());
DebugPrintln("/ in your browser to see it working");
}
- 演示webserver登录功能,html登录页面
/**
* Demo:
* 演示webserver auth校验功能
* (当wifi模块连接上ap之后,在pc浏览器中输入ip+uri来访问,不过需要带校验请求头)
*/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
const char* AP_SSID = "TP-LINK_5344"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "6206908you11011010"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const unsigned long BAUD_RATE = 115200; // serial connection speed
//声明一下函数
void initBasic(void);
void initWifi(void);
void initWebServer(void);
ESP8266WebServer server(80);
/**
* 校验是否存在cookie头并且cookie头的值是正确的
*/
bool is_authentified() {
DebugPrintln("Enter is_authentified");
//是否存在cookie头
if (server.hasHeader("Cookie")) {
DebugPrint("Found cookie: ");
//获取cookie头的信息
String cookie = server.header("Cookie");
DebugPrintln(cookie);
if (cookie.indexOf("ESPSESSIONID=1") != -1) {
DebugPrintln("Authentification Successful");
return true;
}
}
DebugPrintln("Authentification Failed");
return false;
}
/**
* 处理登陆uri
*/
void handleLogin() {
String msg;
//判断是否存在cookie头
if (server.hasHeader("Cookie")) {
DebugPrint("Found cookie: ");
String cookie = server.header("Cookie");
DebugPrint(cookie);
}
//判断是否存在DISCONNECT参数
if (server.hasArg("DISCONNECT")) {
DebugPrintln("Disconnection");
server.sendHeader("Location", "/login");
server.sendHeader("Cache-Control", "no-cache");
server.sendHeader("Set-Cookie", "ESPSESSIONID=0");
server.send(301);
return;
}
//判断是否存在USERNAME和PASSWORD参数
if (server.hasArg("USERNAME") && server.hasArg("PASSWORD")) {
if (server.arg("USERNAME") == "admin" && server.arg("PASSWORD") == "admin") {
server.sendHeader("Location", "/");
server.sendHeader("Cache-Control", "no-cache");
server.sendHeader("Set-Cookie", "ESPSESSIONID=1");
server.send(301);
DebugPrintln("Log in Successful");
return;
}
msg = "Wrong username/password! try again.";
DebugPrintln("Log in Failed");
}
//返回html 填写账号密码页面
String content = "<html><body><form action='/login' method='POST'>To log in, please use : admin/admin<br>";
content += "User:<input type='text' name='USERNAME' placeholder='user name'><br>";
content += "Password:<input type='password' name='PASSWORD' placeholder='password'><br>";
content += "<input type='submit' name='SUBMIT' value='Submit'></form>" + msg + "<br>";
content += "You also can go <a href='/inline'>here</a></body></html>";
server.send(200, "text/html", content);
}
/**
* 根目录处理器
*/
//root page can be accessed only if authentification is ok
void handleRoot() {
DebugPrintln("Enter handleRoot");
String header;
if (!is_authentified()) {
//校验不通过
server.sendHeader("Location", "/login");
server.sendHeader("Cache-Control", "no-cache");
server.send(301);
return;
}
String content = "<html><body><H2>hello, you successfully connected to esp8266!</H2><br>";
if (server.hasHeader("User-Agent")) {
content += "the user agent used is : " + server.header("User-Agent") + "<br><br>";
}
content += "You can access this page until you <a href=\"/login?DISCONNECT=YES\">disconnect</a></body></html>";
server.send(200, "text/html", content);
}
/**
* 无效uri处理器
*/
void handleNotFound() {
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void setup(void) {
initBasic();
initWifi();
initWebServer();
}
void loop(void) {
server.handleClient();
}
/**
* 初始化基础功能:波特率
*/
void initBasic(){
DebugBegin(BAUD_RATE);
}
/**
* 初始化wifi模块:工作模式 连接网络
*/
void initWifi(){
WiFi.mode(WIFI_STA);
WiFi.begin(AP_SSID, AP_PSK);
DebugPrintln("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrint("Connected to ");
DebugPrintln(AP_SSID);
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}
/**
* 初始化webserver
*/
void initWebServer(){
//以下配置uri对应的handler
server.on("/", handleRoot);
server.on("/login", handleLogin);
server.on("/inline", []() {
server.send(200, "text/plain", "this works without need of authentification");
});
server.onNotFound(handleNotFound);
//设置需要收集的请求头
const char * headerkeys[] = {"User-Agent", "Cookie"} ;
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
//收集头信息
server.collectHeaders(headerkeys, headerkeyssize);
server.begin();
DebugPrintln("HTTP server started");
}
四、域名服务—ESP8266mDNS库
概述:
DNS(Domain Name System)域名系统(因特网上作为域名和IP地址相互映射的一个分布式数据库)
DNS协议运行在UDP协议上,端口号53
mDNS(Multicast DNS)组播dns,主要实现了在没有传统DNS服务器的情况下使用局域网内的主机实现本地发现和域名访问,端口号5353,遵从dns协议(基于UDP协议,即运用了UDP广播)
库
- 演示ESP8266 mDNS responder功能
电脑端输入http://esp8266.local/
以域名方式访问webserver
/**
* Demo:
* 演示ESP8266 mDNS responder功能
*/
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
const char* AP_SSID = "TP-LINK_5344"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "6206908you11011010"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const unsigned long BAUD_RATE = 115200;// serial connection speed
//声明一下函数
void initBasic(void);
void initWifi(void);
void initWebServer(void);
void initmDNS(void);
ESP8266WebServer server(80);
/**
* 处理根目录uri请求
* uri:http://server_ip/
*/
void handleRoot() {
DebugPrintln("handleRoot");
server.send(200, "text/html", "Hello From ESP8266 mDNS demo");
}
/**
* 处理无效uri
* uri:http://server_ip/xxxx
*/
void handleNotFound() {
DebugPrintln("handleNotFound");
//打印无效uri的信息 包括请求方式 请求参数
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void setup(void) {
initBasic();
initWifi();
initWebServer();
initmDNS();
}
void loop(void) {
MDNS.update(); //少这行
server.handleClient();
}
/**
* 初始化基础功能:波特率
*/
void initBasic(){
DebugBegin(BAUD_RATE);
}
/**
* 初始化wifi模块:工作模式 连接网络
*/
void initWifi(){
WiFi.mode(WIFI_STA);
WiFi.begin(AP_SSID, AP_PSK);
DebugPrintln("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrint("Connected to ");
DebugPrintln(AP_SSID);
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}
/**
* 初始化webserver
*/
void initWebServer(){
//以下配置uri对应的handler
server.on("/", handleRoot);
server.on("/inline", []() {
DebugPrintln("handleInline");
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
//启动webserver
server.begin();
DebugPrintln("HTTP server started");
}
/**
* 初始化mDNS
*/
void initmDNS(){
if (!MDNS.begin("esp8266")) {
DebugPrintln("Error setting up MDNS responder!");
while (1) {
delay(1000);
}
}
DebugPrintln("mDNS responder started,please input http://esp8266.local/ in your browser after install Bonjour");
}
/*
演示ESP8266 mDNS 发现服务功能
注意:
- 输入你的 WiFi SSID 和 password.
- 烧写到两块 ESP8266 板子
*/
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
const char* ssid = "...";
const char* password = "...";
char hostString[16] = {0};
void setup() {
Serial.begin(115200);
delay(100);
Serial.println("\r\nsetup()");
sprintf(hostString, "ESP_%06X", ESP.getChipId());
Serial.print("Hostname: ");
Serial.println(hostString);
WiFi.hostname(hostString);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(250);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (!MDNS.begin(hostString)) {
Serial.println("Error setting up MDNS responder!");
}
Serial.println("mDNS responder started");
//往mDNS里面注册服务
MDNS.addService("esp", "tcp", 8080);
Serial.println("Sending mDNS query");
//查找服务
int n = MDNS.queryService("esp", "tcp"); // Send out query for esp tcp services
Serial.println("mDNS query done");
if (n == 0) {
Serial.println("no services found");
} else {
Serial.print(n);
Serial.println(" service(s) found");
for (int i = 0; i < n; ++i) {
// 打印查找到的服务具体信息
Serial.print(i + 1);
Serial.print(": ");
Serial.print(MDNS.hostname(i));
Serial.print(" (");
Serial.print(MDNS.IP(i));
Serial.print(":");
Serial.print(MDNS.port(i));
Serial.println(")");
}
}
Serial.println();
Serial.println("loop() next");
}
void loop() {
// put your main code here, to run repeatedly:
五、SPIFFS—-ESP8266 SPIFFS文件系统
概述:
Arduino环境下的esp8266的flash存储分配:
- 代码区:程序存储区(包含当前代码区current Sketch、更新代码区OTA update)
- 文件系统:SPIFFS闪存文件系统(SPI Flash File System)可通过IDE配置,一般NodeMcu配置成3M```#include<FS.h>``
不支持目录 文件名32字符限制 建议保持短文件名
- EEPROM
- WIFI Config:设置WiFi模块配置时存储的数据
- 文件操作
/**
* 功能描述:spiffs文件操作常见方法使用,包括文件查找、创建、打开、关闭、删除
*/
#include <FS.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define myFileName "mydemo.txt"
void setup(){
DebugBegin(9600);
DebugPrintln("Check Start SPIFFS...");
//启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
if(!SPIFFS.begin()){
DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
return;
}
DebugPrintln("Start SPIFFS Done.");
//判断文件是否存在
if(SPIFFS.exists(myFileName)){
DebugPrintln("mydemo.txt exists.");
}else{
DebugPrintln("mydemo.txt not exists.");
}
File myFile;
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open(myFileName,"w+");
//关闭文件
myFile.close();
//再次判断文件是否存在
if(SPIFFS.exists(myFileName)){
DebugPrintln("mydemo.txt exists.");
}else{
DebugPrintln("mydemo.txt not exists.");
}
//删除文件
DebugPrintln("mydemo.txt removing...");
SPIFFS.remove(myFileName);
//再次判断文件是否存在
if(SPIFFS.exists(myFileName)){
DebugPrintln("mydemo.txt exists.");
}else{
DebugPrintln("mydemo.txt not exists.");
}
}
void loop(){
}
- 查看spiffs文件系统列表
/**
* 功能描述:查看spiffs文件系统列表
*/
#include <FS.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
void setup(){
DebugBegin(9600);
DebugPrintln("Check Start SPIFFS...");
//启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
if(!SPIFFS.begin()){
DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
return;
}
DebugPrintln("Start SPIFFS Done.");
File myFile;
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("/myDemo.txt","w+");
//关闭文件
myFile.close();
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("/myDemo.jpg","w+");
//关闭文件
myFile.close();
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("/myDemo.html","w+");
//关闭文件
myFile.close();
Dir dir = SPIFFS.openDir("/");
while(dir.next()){
String fileName = dir.fileName();
size_t fileSize = dir.fileSize();
Serial.printf("FS File:%s,size:%d\n",fileName.c_str(),fileSize);
}
DebugPrintln("Setup Done!");
}
void loop(){
}
- 往文件myDemo.txt中写入一句话并读取出来显示
/**
* 功能描述:演示文件读写功能
*/
#include <FS.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
void setup(){
DebugBegin(9600);
DebugPrintln("Check Start SPIFFS...");
//启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
if(!SPIFFS.begin()){
DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
return;
}
DebugPrintln("Start SPIFFS Done.");
File myFile;
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("myDemo.txt","w+");
if(myFile){
DebugPrintln("Writing something to myDemo.txt...");
myFile.println("单片机菜鸟博哥666");
myFile.close();
DebugPrintln("Writing Done.");
}else{
DebugPrintln("Open File Failed.");
}
//打开文件 可读
myFile = SPIFFS.open("myDemo.txt","r");
if(myFile){
DebugPrintln("Reading myDemo.txt...");
while(myFile.available()){
//读取文件输出
Serial.write(myFile.read());
}
myFile.close();
}else{
DebugPrintln("Open File Failed.");
}
DebugPrintln("Setup Done!");
}
void loop(){
}
- 烧写文件,将要存入SOIFFS区域的文件,提前放在代码目录的”data”目录中,使用ESP8266FS工具烧写(集成在ArduinoIDE中,但要安装这个工具)(提示没有找到这个工具的话,一般都是版本问题,换个版本就好了)
/**
* 功能描述:演示上传文件并读取文件内容
* 前提:需要先往SPIFFS里面上传config.txt文件
*/
#include <FS.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
void setup(){
DebugBegin(9600);
DebugPrintln("Check Start SPIFFS...");
//启动SPIFFS,如果下载配置没有配置SPIFFS,返回false
if(!SPIFFS.begin()){
DebugPrintln("Start SPIFFS Failed!please check Arduino Download Config.");
return;
}
DebugPrintln("Start SPIFFS Done.");
File myFile;
//打开文件 不存在就创建一个 可读可写
myFile = SPIFFS.open("/config.txt","r");
if(myFile){
//打印文件大小
int size = myFile.size();
Serial.printf("Size=%d\r\n", size);
//读取文件内容
DebugPrintln(myFile.readString());
myFile.close();
DebugPrintln("Reading Done.");
}else{
DebugPrintln("Open File Failed.");
}
}
void loop(){
}
六、web配网
自定义AP配网
/**
* 功能:AP配网(web配网)
* 作者:单片机菜鸟哥
* 时间:2020-02-15
* 描述:
* 1.设置固定IP 192.168.4.1
* 2.设置webserver监听web请求
* 3.处理web请求,获取ssid和pwd
* 4.连接网络
*/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#define DEBUG
#ifdef DEBUG
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#else
//以下三个定义为调试定义
#define DebugBegin(baud_rate)
#define DebugPrintln(message)
#define DebugPrint(message)
#endif
const char* ap_ssid = "esp_webconfig";
const char* ap_password = "";//开放式网络
char sta_ssid[32] = {0};
char sta_password[64] = {0};
const char* webpage_html = "\
<!DOCTYPE html>\r\n\
<html lang='en'>\r\n\
<head>\r\n\
<meta charset='UTF-8'>\r\n\
<title>Document</title>\r\n\
</head>\r\n\
<body>\r\n\
<form name='input' action='/' method='POST'>\r\n\
wifi名称: <br>\r\n\
<input type='text' name='ssid'><br>\r\n\
wifi密码:<br>\r\n\
<input type='text' name='password'><br>\r\n\
<input type='submit' value='保存'>\r\n\
</form>\r\n\
</body>\r\n\
</html>\r\n\
";
IPAddress local_IP(192,168,4,1);
IPAddress gateway(192,168,4,1);
IPAddress subnet(255,255,255,0);
void initApConfig();
void initWebServer();
void connectToWifi();
void handleRootPost();
void handleRoot();
void handleNotFound();
ESP8266WebServer server(80);
void setup(void) {
DebugBegin(115200);
DebugPrintln("");
DebugPrint("connect ap: ");
DebugPrintln(ap_ssid);
initApConfig();
DebugPrint("IP address: ");
DebugPrintln(WiFi.softAPIP());
initWebServer();
DebugPrintln("HTTP server started");
Serial.printf("Ready! Open http://%s in your browser\n",
WiFi.softAPIP().toString().c_str());
}
void loop(void) {
server.handleClient();
}
/**
* 初始化AP配置
*/
void initApConfig(){
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.softAP(ap_ssid, ap_password);
}
/**
* 初始化webserver配置
*/
void initWebServer(){
server.on("/", HTTP_GET, handleRoot);
server.on("/", HTTP_POST, handleRootPost);
server.onNotFound(handleNotFound);
server.begin();
}
/**
* 连接到WiFi
*/
void connectToWifi(){
DebugPrintln("connectToWifi");
WiFi.disconnect();
WiFi.mode(WIFI_STA);
WiFi.begin(sta_ssid, sta_password);
int cnt = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(500);
cnt++;
Serial.print(".");
if(cnt>=40){
cnt = 0;
//重启系统
DebugPrintln("\r\nRestart now!");
ESP.restart();
}
}
DebugPrintln("connectToWifi Success!");
}
/**
* 处理web post请求
*/
void handleRootPost() {
DebugPrintln("handleRootPost");
if (server.hasArg("ssid")) {
DebugPrint("got ssid:");
strcpy(sta_ssid, server.arg("ssid").c_str());
DebugPrintln(sta_ssid);
} else {
DebugPrintln("error, not found ssid");
server.send(200, "text/html", "<meta charset='UTF-8'>error, not found ssid");
return;
}
if (server.hasArg("password")) {
DebugPrint("got password:");
strcpy(sta_password, server.arg("password").c_str());
DebugPrintln(sta_password);
} else {
DebugPrintln("error, not found password");
server.send(200, "text/html", "<meta charset='UTF-8'>error, not found password");
return;
}
server.send(200, "text/html", "<meta charset='UTF-8'>保存成功");
delay(2000);
//连接wifi
connectToWifi();
}
/**
* 处理web get请求
*/
void handleRoot() {
DebugPrintln("handleRoot");
server.send(200, "text/html", webpage_html);
}
void handleNotFound() {
String message = "File Not Found\n\n";
server.send(404, "text/plain", message);
}
七、NTP—时间服务
NTP(Network Time Protocol)网络时间协议,基于UDP,用于网络时间同步的协议,使网络中的计算机时钟同步到UTC,再配合各个时区的偏移调整就能实现精准同步对时功能。
NTP报文协议
实现:
- 拼接协议
- 使用现成NTP第三方库(NTPClient)
#include <NTPClient.h>
// change next line to use with another board/shield
#include <ESP8266WiFi.h>
//#include <WiFi.h> // for WiFi shield
//#include <WiFi101.h> // for WiFi 101 shield or MKR1000
#include <WiFiUdp.h>
const char *ssid = "XU-ChinaNet";
const char *password = "15358228063";
WiFiUDP ntpUDP;
// You can specify the time server pool and the offset (in seconds, can be
// changed later with setTimeOffset() ). Additionaly you can specify the
// update interval (in milliseconds, can be changed using setUpdateInterval() ).
NTPClient timeClient(ntpUDP, "ntp1.aliyun.com", 60*60*8, 30*60*1000);
void setup(){
Serial.begin(115200);
WiFi.begin(ssid, password);
while ( WiFi.status() != WL_CONNECTED ) {
delay ( 500 );
Serial.print ( "." );
}
timeClient.begin();
}
void loop() {
timeClient.update();
Serial.println(timeClient.getFormattedTime());
int hours = timeClient.getHours();
int minu = timeClient.getMinutes();
int sece = timeClient.getSeconds();
Serial.printf("hour:%d minu:%d sece:%d\n", hours,minu,sece);
delay(1000);
}
八、DNSServer—-真正的域名服务
建立DNS服务,使用时模块必须处于AP模式下;
真正意义上的精简版DNS服务器;
DNSServer运行于UDP服务;
这里DNS服务器唯一的作用是把域名转成对应映射地址(只支持一个)
/**
* 功能描述:在手机浏览器访问 "www.danpianji.com"会显示“Hello World”
*/
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )
const byte DNS_PORT = 53;
IPAddress apIP(192, 168, 1, 1);
DNSServer dnsServer;
ESP8266WebServer webServer(80);
void setup() {
DebugBegin(115200);
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
WiFi.softAP("DNSServer example");
// modify TTL associated with the domain name (in seconds)
// default is 60 seconds
dnsServer.setTTL(300);
// set which return code will be used for all other domains (e.g. sending
// ServerFailure instead of NonExistentDomain will reduce number of queries
// sent by clients)
// default is DNSReplyCode::NonExistentDomain
dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure);
// 启动DNS server,映射主机名为 www.danpianji.com
bool status = dnsServer.start(DNS_PORT, "www.danpianji.com", apIP);
if(status){
DebugPrintln("start dnsserver success.");
}else{
DebugPrintln("start dnsserver failed.");
}
// simple HTTP server to see that DNS server is working
webServer.onNotFound([]() {
String message = "Hello World!\n\n";
message += "URI: ";
message += webServer.uri();
webServer.send(200, "text/plain", message);
});
webServer.begin();
}
void loop() {
dnsServer.processNextRequest();
webServer.handleClient();
}
Portal认证
/**
* 功能描述:portal认证
*/
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266WiFi.h>
//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )
const byte DNS_PORT = 53;
IPAddress apIP(192, 168, 1, 1);
DNSServer dnsServer;
ESP8266WebServer webServer(80);
String responseHTML = ""
"<!DOCTYPE html><html><head><title>CaptivePortal</title></head><body>"
"<h1>Hello World!</h1><p>This is a captive portal example. All requests will "
"be redirected here.</p></body></html>";
void setup() {
DebugBegin(115200);
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
WiFi.softAP("DNSServer CaptivePortal example");
// 所有请求都映射到一个具体地址
dnsServer.start(DNS_PORT, "*", apIP);
// replay to all requests with same HTML
webServer.onNotFound([]() {
DebugPrintln("webServer handle.");
webServer.send(200, "text/html", responseHTML);
});
webServer.begin();
}
void loop() {
dnsServer.processNextRequest();
webServer.handleClient();
}
九、无线更新—–OTA固件更新
1、ArduinoOTA:
- 连接WIFI;
- 配置ArduinoOTA对象的事件函数;
- 启动ArduinoOTA服务ArduinoOTA.begin();
- 在loop()函数将处理权交由ArduinoOTA.handle()。
为了区分正常工作模式以及更新模式,可以设置标志位来区分(标志位可通过其他手段修改,如按键、软件控制)
void loop() {
if (flag ==0 ) {
// 正常工作状态的代码
} else {
ArduinoOTA.handle();
}
}
python2.7环境
旧代码:
/**
* 功能描述:OTA之Arduino IDE更新 V1.0版本代码
*
*/
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )
#define CodeVersion "CodeVersion V1.0"
const char* ssid = "xxxx";//填上wifi账号
const char* password = "xxxxx";//填上wifi密码
void setup() {
DebugBegin(115200);
DebugPrintln("Booting Sketch....");
DebugPrintln(CodeVersion);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
DebugPrintln("Connection Failed! Rebooting...");
delay(5000);
//重启ESP8266模块
ESP.restart();
}
// Port defaults to 8266
// ArduinoOTA.setPort(8266);
// Hostname defaults to esp8266-[ChipID]
// ArduinoOTA.setHostname("myesp8266");
// No authentication by default
// ArduinoOTA.setPassword("admin");
// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
ArduinoOTA.onStart([]() {
String type;
//判断一下OTA内容
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_SPIFFS
type = "filesystem";
}
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
DebugPrintln("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
DebugPrintln("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
DebugPrintF("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
DebugPrintF("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
DebugPrintln("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
DebugPrintln("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
DebugPrintln("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
DebugPrintln("Receive Failed");
} else if (error == OTA_END_ERROR) {
DebugPrintln("End Failed");
}
});
ArduinoOTA.begin();
DebugPrintln("Ready");
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}
void loop() {
ArduinoOTA.handle();
}
烧写成功后,重启IDE(IDE与8266建立无线连接)端口项出现网络端口
新代码:
/**
* 功能描述:OTA之Arduino IDE更新 V1.1版本代码
*
*/
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )
#define CodeVersion "CodeVersion V1.1"
const char* ssid = "xxxx";//填上wifi账号
const char* password = "xxxx";//填上wifi密码
void setup() {
DebugBegin(115200);
DebugPrintln("Booting Sketch....");
DebugPrintln(CodeVersion);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
DebugPrintln("Connection Failed! Rebooting...");
delay(5000);
//重启ESP8266模块
ESP.restart();
}
// Port defaults to 8266
// ArduinoOTA.setPort(8266);
// Hostname defaults to esp8266-[ChipID]
// ArduinoOTA.setHostname("myesp8266");
// No authentication by default
// ArduinoOTA.setPassword("admin");
// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
ArduinoOTA.onStart([]() {
String type;
//判断一下OTA内容
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_SPIFFS
type = "filesystem";
}
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
DebugPrintln("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
DebugPrintln("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
DebugPrintF("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
DebugPrintF("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
DebugPrintln("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
DebugPrintln("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
DebugPrintln("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
DebugPrintln("Receive Failed");
} else if (error == OTA_END_ERROR) {
DebugPrintln("End Failed");
}
});
ArduinoOTA.begin();
DebugPrintln("Ready");
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}
void loop() {
ArduinoOTA.handle();
}
2、WebUpdateOTA:
- 系统自带OTA之WEB更新(通过建立 webserver来上传新固件以达到更新目的)
V1.1代码:
/*
* 功能描述:OTA之web更新 V1.0版本代码
*/
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>
//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )
#define CodeVersion "CodeVersion V1.0"
const char* host = "esp8266-webupdate";
const char* ssid = "xxx";//填上wifi账号
const char* password = "xxx";//填上wifi密码
ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;
void setup(void) {
DebugBegin(115200);
DebugPrintln("Booting Sketch...");
DebugPrintln(CodeVersion);
WiFi.mode(WIFI_AP_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
WiFi.begin(ssid, password);
DebugPrintln("WiFi failed, retrying.");
}
//启动mdns服务
MDNS.begin(host);
//配置webserver为更新server
httpUpdater.setup(&httpServer);
httpServer.begin();
MDNS.addService("http", "tcp", 80);
DebugPrintF("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host);
}
void loop(void) {
httpServer.handleClient();
MDNS.update();
}
同一WIFI下,打开调试器中提示的网站
编译V1.1代码:
/*
* 功能描述:OTA之web更新 V1.1版本代码
*/
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>
//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )
#define CodeVersion "CodeVersion V1.1"
const char* host = "esp8266-webupdate";
const char* ssid = "xxx";//填上wifi账号
const char* password = "xxx";//填上wifi密码
ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;
void setup(void) {
DebugBegin(115200);
DebugPrintln("Booting Sketch...");
DebugPrintln(CodeVersion);
WiFi.mode(WIFI_AP_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
WiFi.begin(ssid, password);
DebugPrintln("WiFi failed, retrying.");
}
//启动mdns服务
MDNS.begin(host);
//配置webserver为更新server
httpUpdater.setup(&httpServer);
httpServer.begin();
MDNS.addService("http", "tcp", 80);
DebugPrintF("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host);
}
void loop(void) {
httpServer.handleClient();
MDNS.update();
}
选择bin文件,通过WEB网页上传
- 自定义OTA之WEB更新(个性化页面)
3.SerialUpdateOTA—OTA之服务器更新
放在服务器上,无感知更新
十、WebSocket Client—-全双工通信
问题:HTTP:一个请求-响应应用层协议
客户端没有主动发请求,服务器不能主动给客户端发数据
方法:
- 轮询(浪费带宽资源)
- 应用层协议解决MQTT协议
- WebSocket协议:让客户端与服务器之间建立无限制的全双工通信,任何一方都可主动发消息给对方(HTML5定义的协议,更好的节省服务器资源和带宽,并实时进行通讯)
两阶段:
通过HTTP请求确认WebSocket握手协议阶段;
通过WebSocket交互数据阶段。