ESP8266-Arduino网络编程实例-WebSocket服务器

WebSocket服务器

本文将演示如何创建一个页面通过Webosocket通信协议来远程控制ESP8266输出,并将输出状态显示在页面上,并在所有客户端中自动更新。Web服务器将使用ESPAsyncWebserver来实现。

在前面的文章中,对ESPAsyncWebserver作了详细的介绍,请参考:

1、WebSocket简单介绍

WebSocket 是客户端和服务器之间的持久连接,允许双方使用 TCP 连接进行双向通信。 这意味着可以在任何给定时间将数据从客户端发送到服务器以及从服务器发送到客户端。

在这里插入图片描述

客户端通过称为 WebSocket 握手的过程与服务器建立 WebSocket 连接。 握手以 HTTP 请求/响应开始,允许服务器处理同一端口上的 HTTP 连接和 WebSocket 连接。 一旦建立连接,客户端和服务器就可以以全双工模式发送 WebSocket 数据。

使用 WebSockets 协议,服务器(ESP8266 板)可以无需请求即可向客户端或所有客户端发送信息。 这也允许我们在发生更改时向网络浏览器发送信息。

2、硬件准备

  • ESP8266 NodeMCU开发板一块
  • 数据线一条

3、软件准备

  • Arduino IDE或VSCode + PlatformIO

在前面的文章中,对如何搭建ESP8266开发环境做了详细的介绍,请参考:

ESP8266 NodeMCU的引脚介绍在前面的文章中做了详细的介绍,请参考:

4、代码实现

本次演示的功能如下:

  • ESP8266 Web 服务器显示一个带有按钮的网页,用于切换 GPIO 2 的状态;
  • 界面显示当前 GPIO 状态。 每当GPIO状态发生变化时,接口都会瞬间更新;GPIO 状态会在所有客户端中自动更新。 这意味着,如果在同一设备或不同设备上打开了多个 Web 浏览器选项卡,它们都会同时更新。

在这里插入图片描述

1)ESP8266服务端代码

驱动使用到如下开源库:

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "LittleFS.h"
// Replace with your network credentials
const char\* ssid = "DESKTOP-4LOBMNU0960";
const char\* ssid_pwd = "iot123456";

bool ledState = 0;
const int ledPin = 2;

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");

void notifyClients() {
ws.textAll(String(ledState));
}

void handleWebSocketMessage(void \*arg, uint8\_t \*data, size\_t len) {
AwsFrameInfo \*info = (AwsFrameInfo\*)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
data[len] = 0;
if (strcmp((char\*)data, "toggle") == 0) {
ledState = !ledState;
notifyClients();
}
}
}

void onEvent(AsyncWebSocket \*server, AsyncWebSocketClient \*client, AwsEventType type,
void \*arg, uint8\_t \*data, size\_t len) {
switch (type) {
case WS_EVT_CONNECT:
Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c\_str());
break;
case WS_EVT_DISCONNECT:
Serial.printf("WebSocket client #%u disconnected\n", client->id());
break;
case WS_EVT_DATA:
handleWebSocketMessage(arg, data, len);
break;
case WS_EVT_PONG:
case WS_EVT_ERROR:
break;
}
}

// Initialize LittleFS
void initFS() {
if (!LittleFS.begin()) {
Serial.println("An error has occurred while mounting LittleFS");
}
else{
Serial.println("LittleFS mounted successfully");
}
}

void initWebSocket() {
ws.onEvent(onEvent);
server.addHandler(&ws);
}

String processor(const String& var){
Serial.println(var);
if(var == "STATE"){
if (!ledState){
return "ON";
}
else{
return "OFF";
}
}
return String();
}

void setup(){
// Serial port for debugging purposes
Serial.begin(115200);

pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
initFS();
// Connect to Wi-Fi
WiFi.begin(ssid, ssid_pwd);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}

// Print ESP Local IP Address
Serial.println(WiFi.localIP());

initWebSocket();

// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest \*request){
//request->send\_P(200, "text/html", index\_html, processor);
request->send(LittleFS, "/index.html", "text/html", false, processor);
});

// Start server
server.begin();
}

void loop() {
ws.cleanupClients();
digitalWrite(ledPin, ledState);
}

2)Web页面代码(index.html)

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<!DOCTYPE HTML><html>
<head>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html {
font-family: Arial, Helvetica, sans-serif;
text-align: center;
}
h1 {
font-size: 1.8rem;
color: white;
}
h2{
font-size: 1.5rem;
font-weight: bold;
color: #143642;
}
.topnav {
overflow: hidden;
background-color: #143642;
}
body {
margin: 0;
}
.content {
padding: 30px;
max-width: 600px;
margin: 0 auto;
}
.card {
background-color: #F8F7F9;;
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
padding-top:10px;
padding-bottom:20px;
}
.button {
padding: 15px 50px;
font-size: 24px;
text-align: center;
outline: none;
color: #fff;
background-color: #0f8b8d;
border: none;
border-radius: 5px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
/\*.button:hover {background-color: #0f8b8d}\*/
.button:active {
background-color: #0f8b8d;
box-shadow: 2 2px #CDCDCD;
transform: translateY(2px);
}
.state {
font-size: 1.5rem;
color:#8c8c8c;
font-weight: bold;
}
</style>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
</head>
<body>
<div class="topnav">
<h1>ESP WebSocket Server</h1>
</div>
<div class="content">
<div class="card">
<h2>Output - GPIO 2</h2>
<p class="state">state: <span id="state">%STATE%</span></p>
<p><button id="button" class="button">Toggle</button></p>
</div>
</div>
<script>
var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
window.addEventListener('load', onLoad);
function initWebSocket() {
console.log('Trying to open a WebSocket connection...');
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage; // <-- add this line
}
function onOpen(event) {
console.log('Connection opened');
}
function onClose(event) {
console.log('Connection closed');
setTimeout(initWebSocket, 2000);
}
function onMessage(event) {
var state;
if (event.data == "1"){
state = "ON";
}
else{
state = "OFF";
}
document.getElementById('state').innerHTML = state;
}
function onLoad(event) {
initWebSocket();
initButton();
}
function initButton() {
document.getElementById('button').addEventListener('click', toggle);
}
function toggle(){
websocket.send('toggle');
}
</script>
</body>
</html>

将HTML文件上传到ESP8266的Flash中,储存文件系统为LittleFS。操作过程请参考:

最后运行结果:

在这里插入图片描述

文章来源: https://iotsmart.blog.csdn.net/article/details/127388409