之前接触了一段时间的ESP8266,在太极创客教程的指引下做了一个基本网络服务器
附上太极创客教程网址。大家感兴趣的话可以跟着教程学习,本文主要对本人学习过程中的思路变化过程,做一个阶段性总结。
根据太极创客教程已经可以通过网页控制ESP8266的单个引脚,那是否可以在网页中控制多个引脚,PWM控制,读取引脚状态,在网站上放上自己喜欢的图片等等。要实现这部分功能,首先需要对HTML有一个基础的认识,因此花了几天的时间粗步学习了一下HTML的基础,接下来就是开始实践了,网页排版之类的没太去仔细研究,一切就都居中处理啦
这个过程中有两方面是比较难的,一个是网页向ESP8266下发命令的处理函数,要注意HTML代码编写以及收发命令的一致性。另一个是对引脚状态,PWM值轮询的实现。这两个考验最多的是对HTML的了解程度,对我这种还不算入门的属于是较难的部分了,而ESP8266代码部分则相对简单,只需根据网页下发的命令进行对应的处理即可。
至此,已经实现了网页端对ESP8266的控制,通过手机接入ESP8266的WIFI,在网址中输入ESP8266的IP地址即可进入网页实现对ESP8266的控制。但是存在几个问题,ESP8266处于AP模式,若用户不知道其WIFI名称和密码,如何接入该服务器?若用户不知道ESP8266的IP地址,那如何打开网页进行控制?如何直观地看出各部分引脚的状态?带着以上的疑问进行代码的进一步优化。
经过思考决定ESP8266外接一块0.96寸显示屏,通过显示屏显示WIFI名称和密码,IP地址,引脚状态等信息,并增加开机画面,从观感上提高体验。
#include // 本程序使用ESP8266WiFi库
#include // 本程序使用ESP8266WiFiMulti库
#include // 本程序使用ESP8266WebServer库
#include // 本程序使用SPIFFS库
#include // 本程序使用U8g2lib库
#include // 本程序使用SPI库
//配置OLED SPI引脚
U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ D5, /* data=*/ D7, /* cs=*/ D8, /* dc=*/ D0, /* reset=*/ D1);
const char *ssid = "FreeWIFI"; // 这里定义将要建立的WiFi名称。
const char *password = "20202020"; // 这里定义将要建立的WiFi密码。
ESP8266WebServer esp8266_server(80); // 建立网络服务器对象,该对象用于响应HTTP请求。监听端口(80)
bool pinState; // 存储引脚状态用变量
int ledPwmVal;//用于存储pwm数值
int LED=D4;//LED引脚
static unsigned char logo[] U8X8_PROGMEM ={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,
0x00,0x00,0x00,0x00,0xC0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x0F,0x00,0x00,
0x00,0x00,0x7C,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0xFE,0x03,0x00,0x7E,0x00,0x00,
0x00,0x00,0xFF,0x3F,0x00,0xFC,0x00,0x00,0x00,0x80,0xFF,0x7F,0x00,0xF8,0x01,0x00,
0x00,0xC0,0xFF,0x1F,0x00,0xF0,0x03,0x00,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0x07,0x00,
0x00,0xF0,0xFF,0x07,0x00,0xC0,0x0F,0x00,0x00,0xF8,0xFF,0x03,0x00,0x80,0x1F,0x00,
0x00,0xFC,0xFF,0x01,0x00,0x80,0x1F,0x00,0x00,0xFE,0xFF,0x00,0x00,0x00,0x3F,0x00,
0x00,0xFF,0xFF,0x00,0x00,0x00,0x7F,0x00,0x80,0xFF,0xFF,0x01,0x00,0x00,0x7E,0x00,
0xC0,0xFF,0xFF,0x03,0x00,0x00,0xFE,0x00,0x80,0xFF,0xFF,0x07,0x00,0x00,0xFC,0x01,
0x00,0xFF,0xFF,0x0F,0x00,0x00,0xFC,0x01,0x00,0xFE,0xF3,0x1F,0x00,0x00,0xF8,0x03,
0x00,0xFC,0xE1,0x3F,0x00,0x00,0xF8,0x03,0x00,0xF8,0xC0,0x7F,0x00,0x00,0xF8,0x03,
0x00,0x70,0x80,0xFF,0x00,0x00,0xF0,0x07,0x00,0x20,0x00,0xFF,0x01,0x00,0xF0,0x07,
0x00,0x00,0x00,0xFE,0x03,0x00,0xF0,0x07,0x00,0x00,0x00,0xFC,0x07,0x00,0xF0,0x07,
0x00,0x00,0x00,0xF8,0x0F,0x00,0xF0,0x07,0x00,0x00,0x00,0xF0,0x1F,0x00,0xF0,0x07,
0x00,0x00,0x00,0xE0,0x3F,0x00,0xF0,0x07,0x00,0x00,0x00,0xC0,0x7F,0x00,0xF0,0x07,
0x00,0x00,0x00,0x80,0xFF,0x00,0xF0,0x07,0x00,0x00,0x00,0x00,0xFF,0x01,0xF0,0x07,
0x00,0x00,0x00,0x00,0xFE,0x03,0xF0,0x07,0x00,0x00,0x00,0x00,0xFC,0x07,0xF0,0x07,
0x00,0x00,0x00,0x00,0xF8,0x0F,0xF8,0x07,0x00,0x00,0x00,0x00,0xF0,0x1F,0xF8,0x03,
0x00,0x00,0x00,0x00,0xF0,0x3F,0xFC,0x03,0x00,0x00,0x00,0x00,0xE0,0x7F,0xFC,0x03,
0x00,0x00,0x18,0x00,0xC0,0xFF,0xFE,0x01,0x00,0x00,0x3D,0x00,0x80,0xFF,0xFF,0x01,
0x00,0x80,0x7F,0x00,0x00,0xFF,0xFF,0x01,0x00,0xE0,0xFF,0x00,0x00,0xFE,0xFF,0x00,
0x00,0xF0,0xFF,0x03,0x00,0xFC,0x7F,0x00,0x00,0xF8,0xFF,0x0F,0x00,0xF8,0x7F,0x00,
0x00,0xF8,0xFB,0x7F,0x00,0xFE,0x3F,0x00,0x00,0xFE,0xE3,0xFF,0xFF,0xFF,0xFF,0x00,
0x00,0xFF,0xC0,0xFF,0xFF,0xFF,0xFF,0x00,0x80,0x7F,0x80,0xFF,0xFF,0xFF,0xFF,0x03,
0xC0,0x3F,0x00,0xFF,0xFF,0xFF,0xFF,0x07,0xF0,0x1F,0x00,0xFC,0xFF,0xFF,0xFE,0x0F,
0xF8,0x0F,0x00,0xF0,0xFF,0x3F,0xFC,0x1F,0xFC,0x07,0x00,0xC0,0xFF,0x0F,0xF8,0x3F,
0xFC,0x07,0x00,0x00,0x78,0x00,0xF0,0x3F,0xFC,0x03,0x00,0x00,0x00,0x00,0xE0,0x1F,
0xFC,0x01,0x00,0x00,0x00,0x00,0xC0,0x1F,0xFC,0x00,0x00,0x00,0x00,0x00,0x80,0x0F,
0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
void setup()
{
Serial.begin(9600);
u8g2.begin(); //OLED显示功能开启
WiFi.softAP(ssid, password); // WiFi.softAP用于启动NodeMCU的AP模式
pinMode(LED, OUTPUT); // 初始化NodeMCU控制板载LED引脚为OUTPUT
//int i = 0;
if(SPIFFS.begin())
{ // 启动闪存文件系统
Serial.println("SPIFFS Started.");
}
else
{
Serial.println("SPIFFS Failed to Start.");
}
esp8266_server.on("/getLED", handleLEDControl); // HTML控制开关灯
esp8266_server.on("/LED_PWM_Control", handleLED_PWM_Control); // HTML控制PWM
esp8266_server.on("/LED_PIN_Control", handleLED_PIN_Control); // HTML控制LED引脚
esp8266_server.on("/getLEDstate", LED_STATE_Read); //获取LED开关灯状态
esp8266_server.on("/getLEDpwm", LED_PWM_Read); //获取LED当前PWM值
esp8266_server.on("/getLEDpin", LED_PIN_Read); //获取当前LED引脚
esp8266_server.onNotFound(handleUserRequest); // 告知系统如何处理其它用户请求
esp8266_server.begin(); // 启动网站服务
Serial.println("HTTP server started"); //串口显示HTML服务已开启
OLED_Start(); //开机画面
}
void loop()
{
esp8266_server.handleClient(); //处理用户请求
OLED(); //OLED界面显示
}
// 处理用户浏览器的HTTP访问
void handleUserRequest()
{
// 获取用户请求资源(Request Resource)
String reqResource = esp8266_server.uri();
Serial.print("reqResource: ");
Serial.println(reqResource);
// 通过handleFileRead函数处处理用户请求资源
bool fileReadOK = handleFileRead(reqResource);
// 如果在SPIFFS无法找到用户访问的资源,则回复404 (Not Found)
if (!fileReadOK)
{
esp8266_server.send(404, "text/plain", "404 Not Found");
}
}
bool handleFileRead(String resource)
{ //处理浏览器HTTP访问
if (resource.endsWith("/"))
{ // 如果访问地址以"/"为结尾
resource = "/index.html"; // 则将访问地址修改为/index.html便于SPIFFS访问
}
String contentType = getContentType(resource); // 获取文件类型
if (SPIFFS.exists(resource))
{ // 如果访问的文件可以在SPIFFS中找到
File file = SPIFFS.open(resource, "r"); // 则尝试打开该文件
esp8266_server.streamFile(file, contentType);// 并且将该文件返回给浏览器
file.close(); // 并且关闭文件
return true; // 返回true
}
return false; // 如果文件未找到,则返回false
}
// 获取文件类型
String getContentType(String filename)
{
if(filename.endsWith(".htm")) return "text/html";
else if(filename.endsWith(".html")) return "text/html";
else if(filename.endsWith(".css")) return "text/css";
else if(filename.endsWith(".js")) return "application/javascript";
else if(filename.endsWith(".png")) return "image/png";
else if(filename.endsWith(".gif")) return "image/gif";
else if(filename.endsWith(".jpg")) return "image/jpeg";
else if(filename.endsWith(".ico")) return "image/x-icon";
else if(filename.endsWith(".xml")) return "text/xml";
else if(filename.endsWith(".pdf")) return "application/x-pdf";
else if(filename.endsWith(".zip")) return "application/x-zip";
else if(filename.endsWith(".gz")) return "application/x-gzip";
return "text/plain";
}
// 处理/LED-Control请求
void handleLEDControl()
{
String ledState = "OFF";
String LED_State = esp8266_server.arg("LEDstate"); //参考xhttp.open("GET", "getLED?LEDstate="+led, true);
if(LED_State == "1")
{
digitalWrite(LED,HIGH); //LED 点亮
ledState = "ON"; //反馈参数
ledPwmVal=255;//PWM值至255
}
else
{
digitalWrite(LED,LOW); //LED 熄灭
ledState = "OFF"; //反馈参数
ledPwmVal=0;//PWM值至0
}
esp8266_server.send(200, "text/plain", ledState); //发送网页
}
// 处理/LED_PWM_Control请求
void handleLED_PWM_Control()
{
String ledPwm = esp8266_server.arg("LED_PWM_Control"); // 从浏览器发送的信息中获取PWM控制数值(字符串格式)
ledPwmVal = ledPwm.toInt();// 将字符串格式的PWM控制数值转换为整数
if(ledPwmVal>100)ledPwmVal=100;//大于100以100计算
ledPwmVal=map(ledPwmVal, 0, 100, 0, 255);//数值转换
analogWrite(LED, ledPwmVal);// 实施引脚PWM设置
// 建立基本网页信息显示当前数值以及返回链接
ledPwm=ledPwmVal;
esp8266_server.send(200,"text/plain" , ledPwm); //发送网页
}
//获取LED状态
void LED_STATE_Read()
{
if(ledPwmVal!=0)
{
esp8266_server.send(200, "text/plain", "ON"); //发送网页
}
else
{
esp8266_server.send(200, "text/plain", "OFF"); //发送网页
}
}
// 处理/LED_PIN_Control请求
void handleLED_PIN_Control()
{
String ledPin = esp8266_server.arg("LED_PIN"); // 从浏览器发送的信息中获取PWM控制数值(字符串格式)
LED = ledPin.toInt();// 将字符串格式的PWM控制数值转换为整数
pinMode(LED, OUTPUT); // 初始化NodeMCU控制板载LED引脚为OUTPUT·
digitalWrite(LED,HIGH); //LED 点亮
ledPwmVal=255;//PWM值至255
esp8266_server.send(200,"text/plain" , ledPin); //发送网页
}
//获取引脚PWM状态
void LED_PWM_Read()
{
String DATA;
DATA = ledPwmVal;//转化成字符串
esp8266_server.send(200, "text/plain", DATA);
}
//获取当前控制的是哪个引脚
void LED_PIN_Read()
{
String DATA;
DATA = LED;//转化成字符串
esp8266_server.send(200, "text/plain", DATA);
}
//OLED开机画面
void OLED_Start()
{/*
u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
u8g2.drawStr(24,30,"IOT SYSTEM");
u8g2.drawStr(50,60,"Loading...");
u8g2.sendBuffer(); // transfer internal memory to the display
delay(5000);//开机画面延迟5s*/
u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawXBMP(30,0, 64, 64, logo);
u8g2.sendBuffer(); // transfer internal memory to the display
delay(5000);
}
//主界面
void OLED()
{
u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
u8g2.drawStr(0,10,"WIFI:");
u8g2.setCursor(70, 10);
u8g2.print(ssid);
u8g2.drawStr(0,20,"Password:");
u8g2.setCursor(70, 20);
u8g2.print(password);
u8g2.drawStr(0,30,"ESP8266 IP:");
u8g2.setCursor(70, 30);
u8g2.print(WiFi.softAPIP());
u8g2.drawStr(0,40,"LED PIN:");
u8g2.setCursor(70, 40);
u8g2.print(LED);
u8g2.drawStr(0,50,"LED STATE:");
u8g2.drawStr(0,60,"LED PWM:");
if(ledPwmVal!=0)
{
u8g2.drawStr(70,50,"ON");
u8g2.setCursor(70, 60);
u8g2.print(ledPwmVal);
}
else
{
u8g2.drawStr(70,50,"OFF");
u8g2.drawStr(70,60,"0");
}
u8g2.sendBuffer(); // transfer internal memory to the display
}
以上为ESP8266代码,完整工程包括(ESP8266代码,HTML网页,图片等),完整工程文件链接:https://download.csdn.net/download/weixin_43278295/15294943 ,如有需要可至此处下载。
至此代码烧写完成,手机接入ESP8266的WIFI即可实现预想功能。关于Arduino-ESP8266闪存文件插件程序可根据太极创客教程安装。
再次附上完整工程文件链接:https://download.csdn.net/download/weixin_43278295/15294943
该网络服务器存在局限性,ESP8266只能在AP模式下,通过手机接入ESP8266的WIFI,在网页中输入ESP8266的IP地址,如192.168.4.1即可进入,在网页中实现对引脚电平的操作。AP模式意味着使用区域是受限于ESP8266的无线覆盖范围的。ESP8266进行大数据处理是较弱的,且引脚有限,扩展范围有限。
此时有一个想法,是否可以通过外接一个处理能力更强的STM32进行数据处理?STM32与ESP8266进行串口通信,相当于ESP8266是一个通信节点,只负责数据传输,而STM32为主控芯片,进行数据处理。ESP8266处理能力有限和引脚较少的缺点就被消除了。因为ESP8266是AP模式的,那是否可以通过STM32+按键+显示屏对ESP8266的无线名称和无线密码进行手动设置,同时云端能实现的控制是否可以也通过STM32+按键+显示屏来手动实现。怀着对这些未知的尝试之心和希望对过去的一些关于STM32设计进行总结整合,如CAN通信,温湿度采集,0.96寸OLED,DS1302实时时钟等等,产生了制作一个集成ESP8266和STM32的物联网开发板的想法。这部分内容将在下篇文章介绍。
不足之处还望各位大佬不吝赐教!