可参考的代码文件说明

1
2
3
4
5
6
7
8
9
10
11
12
13
/**********************************************************************
项目名称/Project : 零基础入门学用物联网
程序名称/Program name : 3_2_1_First_Web_Server
团队/Team : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author : CYNO朔
日期/Date(YYYYMMDD) : 20191107
程序目的/Purpose : 使用NodeMCU建立基本服务器。用户可通过浏览器使用8266的IP地址
访问8266所建立的基本网页(Hello from ESP8266)
-----------------------------------------------------------------------
修订历史/Revision History
日期/Date 作者/Author 参考号/Ref 修订说明/Revision Description

***********************************************************************/

理论知识

使用Arduino IDE为NodeMCU编写控制程序

关于开发板

引脚D1,D2(GPIO)…数字引脚,A0(ADC)模拟引脚(能够读取连续变化的电压值)
两种供电方式,一种是通过micro口,一种是vin引脚
NodeMCU的数字引脚电压为3.3V,模拟引脚可读取电压范围0~1V

1
2
digitalWrite(D2, HIGH);//D2引脚电压为3.3V
digitalRead(D2);//D2引脚所能连接的电压不能超过3.3V

关于互联网

TCP/IP协议:包含许多协议
alt text

IP地址协议有两个版本
IPv4,四个十进制数字组成
IPv6

默认网关:WIFI路由器的IP地址

子网掩码:限制哪些是属于子网地址,哪些是设备本身自带的。几台设备的IP地址相同部分对应子网掩码是255的部分,而设备IP地址不同的部分对应子网掩码0,用255这个数字标出IP地址的哪一部分是子网地址。而用0这个数字来表示IP地址的哪一部分是设备部分

MAC地址:网络中的每一个设备都有一个独立的MAC地址。不光要知道彼此的IP地址,还要知道设备的MAC地址(也叫MAC码)。会依据IP地址得到对应设备的MAC码,并记录下来。让两台完全不认识彼此的设备获取到对方IP和MAC地址的协议就是ARP

TCP,UDP:选择这两种协议中的一种来保证数据传输的准确性

HTTP协议:由请求和响应构成。HTTP请求主要分为两大部分。一部分是请求头(Request Header)一部分是请求体(Request Body)。相应也是。
alt text

DNS(Domain Name System/域名系统):可以解析出IP

接入点模式,关于开发板的联网功能

ESP8266建立wifi,别的设备可以依此和开发板进行连接

接入MCU创建的wifi,但注意这个是没有internet互联网服务的
连接后可在shell输入如下代码进行确认

1
2
ping 192.168.4.1
#MCU默认IP地址

“ping”:测试电脑与某一设备是否已成功建立网络连接。原理是电脑向IP地址所在的网络设备发送一连串的数据包,当网络设备成功接收到数据包后会回复一些数据包。以此判断是否已成功连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <ESP8266WiFi.h>        // 本程序使用ESP8266WiFi库

const char *ssid = "taichi-maker"; // 这里定义将要建立的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() {
}

无线终端模式

开发板可以连接到wifi服务器,其他网络设备就可以连接到开发板
此时IP地址是由路由器动态分配的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <ESP8266WiFi.h>        // 本程序使用ESP8266WiFi库

const char* ssid = "xxxx"; // 连接WiFi名(此处使用taichi-maker为示例)
// 请将您需要连接的WiFi名填入引号中
const char* password = "xxx"; // 连接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() {
}

会搜索预先存储好的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//会搜索预先存储好的
#include <ESP8266WiFi.h> // 本程序使用ESP8266WiFi库
#include <ESP8266WiFiMulti.h> // 本程序使用ESP8266WiFiMulti库

ESP8266WiFiMulti wifiMulti; // 建立ESP8266WiFiMulti对象,对象名称是'wifiMulti'

void setup() {
Serial.begin(9600); // 启动串口通讯

//通过addAp函数存储 WiFi名称 WiFi密码
wifiMulti.addAP("taichi-maker", "12345678"); // 这三条语句通过调用函数addAP来记录3个不同的WiFi网络信息。
wifiMulti.addAP("taichi-maker2", "87654321"); // 这3个WiFi网络名称分别是taichi-maker, taichi-maker2, taichi-maker3。
wifiMulti.addAP("taichi-maker3", "13572468"); // 这3个网络的密码分别是123456789,87654321,13572468。
// 此处WiFi信息只是示例,请在使用时将需要连接的WiFi信息填入相应位置。
// 另外这里只存储了3个WiFi信息,您可以存储更多的WiFi信息在此处。

Serial.println("Connecting ..."); // 通过串口监视器输出信息告知用户NodeMCU正在尝试连接WiFi
int i = 0;
while (wifiMulti.run() != WL_CONNECTED) { // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前
delay(1000); // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCU
Serial.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() {
}

混合模式

又能是无线终端模式又能是接入点模式

建立基本网络服务器

浏览器访问网站默认是通过80端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#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("taichi-maker", "12345678"); // 这三条语句通过调用函数addAP来记录3个不同的WiFi网络信息。
wifiMulti.addAP("taichi-maker2", "87654321"); // 这3个WiFi网络名称分别是taichi-maker, taichi-maker2, taichi-maker3。
wifiMulti.addAP("taichi-maker3", "13572468"); // 这3个网络的密码分别是123456789,87654321,13572468。
// 此处WiFi信息只是示例,请在使用时将需要连接的WiFi信息填入相应位置。
// 另外这里只存储了3个WiFi信息,您可以存储更多的WiFi信息在此处。

int i = 0;
while (wifiMulti.run() != WL_CONNECTED) { // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前
delay(1000); // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCU
Serial.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地址

//--------"启动网络服务功能"程序部分开始-------- // 此部分为程序为本示例程序重点1
esp8266_server.begin(); // 启动网络服务器
esp8266_server.on("/", handleRoot); // 说明网站服务具体是什么。访问斜杠的页面,用handleRoot服务函数,后面有设定//多个界面就放多个on函数
esp8266_server.onNotFound(handleNotFound); //没找到时调用的
//--------"启动网络服务功能"程序部分结束--------
Serial.println("HTTP esp8266_server started");// 告知用户ESP8266网络服务功能已经启动
}

void loop(void){//反复被调用
esp8266_server.handleClient(); // 处理http服务器访问.每次调用这个函数,就会检查,当有客户端请求访问网页信息,就会setup函数看请求信息是什么
}

void handleRoot() { //处理网站根目录“/”的访问请求,提供网页信息
esp8266_server.send(200, "text/plain", "Hello from ESP8266"); // NodeMCU将调用此函数//服务器发送信息,服务器的响应状态码,信息
}//text/plain为纯文本信息,text/html为网页代码

// 设置处理404情况的函数'handleNotFound'
void handleNotFound(){ // 当浏览器请求的网络资源无法在服务器找到时,
esp8266_server.send(404, "text/plain", "404: Not found"); // NodeMCU将调用此函数。
}

通过网络服务实现NodeMCU开发板基本控制,即物联网

添加点灯按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//setup()内加入
pinMode(LED_BUILTIN,OUTPUT);//设置模块内部LED引脚模式

//新增网络服务函数
esp8266_server.on("/", HTTP_GET, handleRoot);//满足前面两个条件时才会调用函数
esp8266_server.on("/LED", HTTP_POST, handleLED);//请求页面,并且请求是post的时候

/*设置服务器根目录即'/'的函数'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>");
}//后面是html格式

//处理LED控制请求的函数'handleLED'
void handleLED() {
digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));// 改变LED的点亮或者熄灭状态
esp8266_server.sendHeader("Location","/"); // 跳转回页面根目录
esp8266_server.send(303); // 发送Http相应代码303 跳转
}

对其中的html进行分析。其中的双引号与反斜杠是为了说明哪些是html的双引号

1
2
3
4
5
<!--跳转到与请求方法-->
<form action=\"/LED\" method=\"POST\">
<!--这是按钮-->
<input type=\"submit\" value=\"Toggle LED\">
</form>

通过网络服务将开发板引脚状态显示在网页中

可以通过NodeMCU开发板上的FLASH按键控制D3引脚的电平。当我们没有按下该按键时,D3引脚将会保持高电平状态。当按下该按键后,D3引脚会变为低电平

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#define buttonPin D3            // 按钮引脚D3
bool pinState; // 存储引脚状态用变量

//setup()
pinMode(buttonPin, INPUT_PULLUP); // 将按键引脚设置为输入上拉模式

//loop()
pinState = digitalRead(buttonPin); // 获取引脚状态

//handleRoot
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);
// 向浏览器发送按键状态信息
}

当要自动刷新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void handleRoot() {   //处理网站目录“/”的访问请求 
esp8266_server.send(200, "text/html", sendHTML(pinState));
}

/*
建立用于发送给客户端浏览器的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;
}

对于其中html分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--每一个htmlCode +=都是在string htmlCode后新加代码,这些代码如果换行要记得加\n-->
<!DOCTYPE html>
<html>
<head>
<!--每隔五秒钟刷新一次-->
<meta http-equiv='refresh' content='5'/>
<title>ESP8266 Butoon State</title>
<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}
body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
</style>
</head>
<body>
<h1>ESP8266 BUTTON STATE</h1>

<!--一种if-->
<p>Button Status: HIGH</p>
<!--另一种if-->
<p>Button Status: LOW</p>

</body>
</html>