Author:teacherXue
一、通过wifi控制家电
上一章节我们实现了ESP芯片的手动自主配网。可以以非常容易的将MCU接入网络,在没有公网(全球访问)资源的情况下,内网直连操作也是比较常见的做法。缺点就是操控端和被控制设备需要在一个局域网范围内。如果我到了单位忘记家里灯关了没,那也只有回去亲自看一下了。
控制LED灯的开关
现在我们来实现,通过在局域网内访问mcu芯片,通过网页对LED灯的开关进行控制,如下图。顺带也实现了温湿度的显示,看起来还是蛮实用的。
实现思路
配网成功后,当有客户端http请求后,向客户端写HTML代码,客户通过浏览器端显示页面。
服务端需要要根据当前的GPIO状态,改变HTML页面中的按钮状态。
需要注意的是:当灯光当前为亮起时,页面的灯光状态为开,但按钮显示的应该是关,使得用户知道按下去时的作用。
按钮会触发新的http请求,开和关将会访问不同的链接。
Mcu根据客户端请求的http头信息,得到请求路径,从而判断是该打开还是关闭灯光。
创建项目
1)新建项目,Lot_web_ctrl_v1.0,修改串口波特率,导入WiFiManager扩展库。
2)导入DHT传感器库
3)代码如下:
#include <Arduino.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#define LED_1 D5
#define LED_2 D6
#define DHTPIN D3 // DHT温湿度传感器引脚
#define DHTTYPE DHT22 // 声明DHT传感器类型
DHT dht(DHTPIN, DHTTYPE); // 创建DHT对象
float t = 0.0; // 温度全局保存
float h = 0.0; // 湿度全局保存
String output5State = "off"; // led灯光状态
String output4State = "off"; // led灯光状态
WiFiServer server(80); // 站点服务端口号
String header; // http请求的头部信息
// 当前时间
unsigned long currentTime;
// 任务上次执行的时间
unsigned long previousTime = millis();
// 超时时间俩秒
const long timeoutTime = 2000;
// DHT传感器计时器
unsigned long currentTime1;
unsigned long previousTime1 = millis();
// 前置函数声明
void getTH();
void setup()
{
Serial.begin(115200);
pinMode(LED_1, OUTPUT);
pinMode(LED_2, OUTPUT);
// Set outputs to LOW
digitalWrite(LED_1, LOW);
digitalWrite(LED_2, LOW);
dht.begin(); // DHT传感器对象工作
WiFiManager wm;
bool res;
res = wm.autoConnect("xm_" + ESP.getChipId(), "12345678"); // password protected ap
if (!res)
{
Serial.println("Failed to connect");
// ESP.restart();
}
else
{
// if you get here you have connected to the WiFi
Serial.println("connected...yeey :)");
}
// web服务器开始工作
server.begin();
}
void loop()
{
WiFiClient client = server.available(); // 监听连接请求
if (client)
{ // 如果有新客户端连接
Serial.println("New Client.");
String currentLine = ""; // 存储客户端传递来的数据
currentTime = millis();
previousTime = currentTime;
while (client.connected() && currentTime - previousTime <= timeoutTime)
{ // 客户端连接时循环
currentTime = millis();
if (client.available())
{ // 如果有字节要从客户端读取,
char c = client.read(); // 读取一个字节
Serial.write("c=");
Serial.write(c);
header += c;
if (c == '\n')
{ // 如果字节是换行符
// 如果当前行为空,则在一行中有两个换行符。
// 这是客户端HTTP请求的结束,所以发送一个响应:
if (currentLine.length() == 0)
{
// HTTP报头总是以响应代码开头(例如HTTP/1.1 200 OK)
// 和一个content-type以便客户端知道将要发生什么,然后是一个空行:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
// 打开和关闭gpio
if (header.indexOf("GET /1/on") >= 0)
{
Serial.println("GPIO 1 on");
output5State = "on";
digitalWrite(LED_1, HIGH);
}
else if (header.indexOf("GET /1/off") >= 0)
{
Serial.println("LED_1 off");
output5State = "off";
digitalWrite(LED_1, LOW);
}
else if (header.indexOf("GET /2/on") >= 0)
{
Serial.println("LED_2 on");
output4State = "on";
digitalWrite(LED_2, HIGH);
}
else if (header.indexOf("GET /2/off") >= 0)
{
Serial.println("LED_2 off");
output4State = "off";
digitalWrite(LED_2, LOW);
}
getTH();
// 显示页面的HTML代码
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS 样式修饰 on/off 按钮
// 请随意更改背景颜色和字体大小属性以适应您的偏好
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button { background-color: #195B6A; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
client.println(".button2 {background-color: #77878A;}</style></head>");
// 页面的头标签
client.println("<body><h1>ESP8266 Web Server</h1>");
// 显示gpio的当前状态
client.println("<p>LED_1 - State " + output5State + "</p>");
// 如果gpio状态为开,则页面按钮显示为off,以使得用户可以知道再次出发是关闭灯光
if (output5State == "off")
{
client.println("<p><a href=\"/1/on\"><button class=\"button\">ON</button></a></p>");
}
else
{
client.println("<p><a href=\"/1/off\"><button class=\"button button2\">OFF</button></a></p>");
}
// 显示led2的灯光状态
client.println("<p>LED_2- State " + output4State + "</p>");
// 如果gpio状态为开,则页面按钮显示为off,以使得用户可以知道再次出发是关闭灯光
if (output4State == "off")
{
client.println("<p><a href=\"/2/on\"><button class=\"button\">ON</button></a></p>");
}
else
{
client.println("<p><a href=\"/2/off\"><button class=\"button button2\">OFF</button></a></p>");
}
client.print("<p><h3>Temperature:");
client.print(t);
client.print("℃</h3></p>");
client.print("<p><h3>Humidity:");
client.print(h);
client.print("% </h3></p>");
client.println("</body></html>");
// HTTP响应以另一个空行结束
client.println();
// 跳出循环
break;
}
else
{ // 如果你有一个换行符,那么需要清除currentLine
currentLine = "";
}
}
else if (c != '\r')
{ // 如果你得到的不是回车字符,
currentLine += c; // 将它添加到currentLine的末尾
}
}
}
// 清除header变量
header = "";
// 关闭连接
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
}
// 获得温湿度
void getTH()
{
h = dht.readHumidity();
// Read temperature as Celsius (the default)
t = dht.readTemperature();
Serial.print(F("Humidity: "));
Serial.print(h);
Serial.print(F("% Temperature: "));
Serial.print(t);
Serial.println(F("°C "));
}
4)烧录代码并运行,结果如下: