在本教程中,您將學習如何使用ESP8266使用WebSocket通信協議建置Web Server。作為範例,我們將向您展示如何建立一個網頁來遠程控制ESP8266的輸出。輸出狀態顯示在網頁上,並在所有客戶端中自動更新。
ESP8266將使用Arduino IDE和ESPAsyncWebServer進行編程。我們也為ESP32提供了類似的WebSocket指南。
如果您一直在關注我們以前的某些Web服務器項目,例如,您可能已經註意到,如果同時打開多個選項卡(在相同或不同設備上),則狀態不會全部更新標籤,除非您刷新網頁。為了解決這個問題,我們可以使用WebSocket協議-發生更改時可以通知所有客戶端,並相應地更新網頁。
本教程基於(StéphaneCalderoni)建立記錄的項目。您可以在這裡閱讀他優秀的教程。
內容目錄
ToggleWebSocket簡介
Client通過稱為WebSocket handshake的過程與Server建立WebSocket連接。handshake始於HTTP請求/回應,允許Server在同一端口上處理HTTP連接以及WebSocket連接。建立連接後,Client和Server即可以全雙工模式發送WebSocket數據。
使用WebSockets協議,Server(ESP32板)可以將信息發送到客戶端或所有客戶端,而無需請求。這也使我們可以在發生更改時將信息發送到Web瀏覽器。
此更改可能是網頁上發生的事情(點擊按鈕),也可能是ESP32端發生的事情,例如按下電路上的物理按鈕。
專案概況
- ESP8266 WebServer顯示一個帶有按鈕的網頁,用於切換GPIO 2的狀態;
- 為簡單起見,我們將控制GPIO 2 –開發板上的LED。您可以使用本範例來控制任何其他GPIO;
- 該界面顯示當前的GPIO狀態。每當GPIO狀態發生變化時,界面會立刻更新。
- GPIO狀態會在所有客戶端中自動更新。這意思是,如果在同一設備或不同設備上打開了多個Web瀏覽器選項,它們將同時更新。
它是如何運作的?
下圖描述了點擊“切換”按鈕時發生的情況。
點擊“切換”按鈕時,將會發生以下情況:
- 點擊“切換”按鈕;
- 客戶端(您的瀏覽器)通過WebSocket協議發送帶有“ toggle(切換)”消息的數據;
- ESP8266(Server)收到此消息,因此它知道應切換LED狀態。如果該LED先前已熄滅,則會開啟LED燈。
- 然後,它也通過WebSocket協議以新的LED狀態向所有客戶端發送數據;
- 客戶收到消息並相應地更新網頁上的led狀態。這樣,當發生更改時,我們幾乎可以立即更新所有客戶端。
準備Arduino IDE
安裝庫–AsyncWebServer
為了建立WebServer,我們將使用ESPAsyncWebServer庫。該庫需要ESPAsyncTCP庫才能正常工作。點擊下面的鏈接下載庫。
這些庫無法通過Arduino庫管理器安裝,因此您需要將庫文件複製到Arduino安裝庫文件夾。另外,在Arduino IDE中,您可以轉到 “草稿碼” >“ 匯入程式庫” >“ 加入.zip程式庫”, 然後選擇剛下載的程式庫。
ESP8266 NodeMCU WebSocket Server程式碼
將
以下代程式碼複製到您的Arduino IDE。
/*********
Terry Lee
完整程式碼說明請參閱 http://honeststore.com.tw/
*********/
// 載入必須程式庫
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
// 替換成您的網絡憑據
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
bool ledState = 0;
const int ledPin = 2;
// 在端口80上建立asyncwebserver物件
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<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>
)rawliteral";
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;
}
}
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";
}
}
}
void setup(){
// Serial port for debugging purposes
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
// Connect to Wi-Fi
WiFi.begin(ssid, password);
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);
});
// Start server
server.begin();
}
void loop() {
ws.cleanupClients();
digitalWrite(ledPin, ledState);
}
將下列的網路憑據替換成您的區域SSID及密碼,該程式碼就可以立即作用。
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
程式碼如何工作
繼續閱讀以了解程式碼的工作原理,或跳至“展示”部分。
導入庫
導入必要的庫以建立WebServer。
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
網路憑證
將網路憑據替換以下變數值:
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
GPIO輸出
建立一個變數名為 ledState 來保存GPIO狀態和一個變數 ledPin參照為您要控制的GPIO。在這種情況下,我們將控制開發板LED(連接到GPIO 2)。
bool ledState = 0;
const int ledPin = 2;
AsyncWebServer和AsyncWebSocket
建立一個 AsyncWebServer 端口為80的物件。
AsyncWebServer server(80);
ESPAsyncWebServer程式庫包含一個WebSocket插件,可以容易處理WebSocket連接。建立一個AsyncWebSocket 物件稱為 ws 來處理 / ws 路徑的連結。
AsyncWebSocket ws("/ws");
建立網頁
index_html 變數要建立,設置網頁樣式和使用WebSocket協議處理客戶端-服務器交互所需要HTML,CSS和JavaScript。
注意:我們將建立網頁需要的所有內容都放置在Arduino草稿碼中的index_html使用的變量上。
請注意,分別HTML,CSS和JavaScript程式,然後將它們上傳到ESP8266文件系統並在程式碼中引用它們,會更為實用。
下面是index_html的內容:
<!DOCTYPE HTML>
<html>
<head>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<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: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;
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;
}
window.addEventListener('load', onLoad);
function onLoad(event) {
initWebSocket();
initButton();
}
function initButton() {
document.getElementById('button').addEventListener('click', toggle);
}
function toggle(){
websocket.send('toggle');
}
</script>
</body>
</html>
CSS
在<style></style>標籤之間,我們使用CSS對網頁進行樣式設置的語言。隨時對其進行更改,以使網頁看起來像您希望的樣式。這裏不會解釋此網頁的CSS如何作業,因為它與本WebSocket教程無關。
<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:active {
background-color: #0f8b8d;
box-shadow: 2 2px #CDCDCD;
transform: translateY(2px);
}
.state {
font-size: 1.5rem;
color:#8c8c8c;
font-weight: bold;
}
</style>
HTML
在<body></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>
標題為<h1>ESP WebSocket Server</h1>,可隨意修改該標題。
<h1>ESP WebSocket Server</h1>
然後,標題<h2>帶有“Output – GPIO 2”文本。
<h2>Output - GPIO 2</h2>
之後,我們有一段顯示當前GPIO狀態。
<p class="state">state: <span id="state">%STATE%</span></p>
%STATE%是GPIO狀態的預留參數。發送網頁時,ESP8266會將其替換為當前值。HTML text上的預留參數應介於%之間。意思是%STATE% text就像一個變數,然後將其替換為實際值。
將網頁發送給客戶端后,只要GPIO狀態發生變化,狀態就需要動態更改。我們將通過WebSocket協議接收該信息。然後,JavaScript處理如何處理接收到的信息以相應地更新狀態。為了能夠使用JavaScript處理該text,該text必須具有我們可以引用的ID。在本案中,id代表state (<span id =“ state”>)。
最後,有一個帶有按鈕的段落可切換GPIO狀態。
<p><button id="button" class="button">Toggle</button></p>
請注意,我們為按鈕指定了ID(id =“button”)。
JavaScript –處理WebSockets
JavaScript在 <script> </ script>標籤之間。一旦Web界面完全加載到瀏覽器中,它便負責初始化與服務器WebSocket的連接,並通過WebSocket處理數據交換。
<script>
var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
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;
}
window.addEventListener('load', onLoad);
function onLoad(event) {
initWebSocket();
initButton();
}
function initButton() {
document.getElementById('button').addEventListener('click', toggle);
}
function toggle(){
websocket.send('toggle');
}
</script>
讓我們看一下它是如何工作的。
閘道器是WebSocket接口的入口點。
var gateway = `ws://${window.location.hostname}/ws`;
window.location.hostname 獲得當前頁面地址(Web Server IP地址)。
建立一個新的全域變量,名為 websocket。
var websocket;
增加事件監聽器,該事件監聽器呼叫 onload 函數,它在網頁重載時會起作用。
window.addEventListener('load', onload);
onload() 函數調用 initWebSocket() 函數初始化與服務器和服務器之間的WebSocket連接,而initButton() 用於將事件監聽器增加到按鈕的功能。
function onload(event) {
initWebSocket();
initButton();
}
initWebSocket()函數在先前定義的閘道器上初始化WebSocket連接。我們還為WebSocket連接狀態是開啟、關閉或接收消息時分配了幾個回調函數。
function initWebSocket() {
console.log('Trying to open a WebSocket connection…');
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage;
}
打開連接後,我們只需在控制台中印出一條消息,然後發送一條消息“ hi”。ESP8266在收到該消息後,代表我們知道連接已初始化。
function onOpen(event) {
console.log('Connection opened');
websocket.send('hi');
}
如果由於某種原因Web socket連接已關閉,我們會再次調用 initWebSocket()函數 在2000毫秒(2秒)後再次運行。
function onClose(event) {
console.log('Connection closed');
setTimeout(initWebSocket, 2000);
}
setTimeout() 方法在指定的毫秒數後調用函數或 運算式評估求值。
最後,我們需要處理收到新消息時發生的情況。服務器(您的ESP板)將發送“ 1”或“ 0”消息。根據收到的消息,我們希望在顯示狀態的段落上顯示“ ON”或“ OFF”消息。記住<span> 標記 id =“state”?我們將獲取該元素並將其值設置為ON或OFF。
function onMessage(event) {
var state;
if (event.data == "1"){
state = "ON";
}
else{
state = "OFF";
}
document.getElementById('state').innerHTML = state;
}
initButton() 函數通過其ID獲取按鈕(button)並增加’click’類型的事件監聽器 。
function initButton() {
document.getElementById('button').addEventListener('click', toggle);
}
這意思是當您點擊按鈕時, toggle 函數會被調用。
這 toggle 函數將與WebSocket連接並送出訊息來 “切換” text。
function toggle(){
websocket.send('toggle');
}
然後,ESP8266會在收到此消息後進行處理–切換當前的GPIO狀態。
處理WebSockets –Server
這此之前,您已經了解瞭如何在客戶端(瀏覽器)上處理WebSocket連接。現在,讓我們看一下如何在服務器端處理它。
通知所有客戶
notifyClients()函數通過一條消息通知所有客戶端,其中包含您作為參數傳遞的內容。在這種情況下,每當發生更改時,我們都希望將當前的LED狀態通知所有客戶端。
void notifyClients() {
ws.textAll(String(ledState));
}
AsyncWebSocket 類提供了一個 textAll() 向同一時間連接到服務器的所有客戶端發送相同消息的方法。
處理WebSocket消息
handleWebSocketMessage() function是一個回調函數,只要我們通過WebSocket協議從客戶端接收到新數據,該函數便會運行。
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();
}
}
}
如果我們收到“toggle”消息,則會切換 ledState變數。此外,我們通過致電notifyClients()功能。這樣,所有客戶端都會收到有關更改的通知,並相應地更新介面。
if (strcmp((char*)data, "toggle") == 0) {
ledState = !ledState;
notifyClients();
}
配置WebSocket Server
現在,我們需要配置一個事件監聽器,以處理WebSocket協議的不同異步步驟。可以通過定義以下事件來實現此事件處理程序:onEvent() 如下:
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;
}
}
這 類型參數表示發生的事件。它可以採用以下值:
- WS_EVT_CONNECT 客戶端登錄時;
- WS_EVT_DISCONNECT 客戶註銷時;
- WS_EVT_DATA 當從客戶端接收到數據包時;
- WS_EVT_PONG 響應ping請求;
- WS_EVT_ERROR 從客戶端收到錯誤時。
初始化WebSocket
最後, initWebSocket() 函數初始化WebSocket協議。
void initWebSocket() {
ws.onEvent(onEvent);
server.addHandler(&ws);
}
processor()
processor ()函數負責在HTML文本上搜索預留參數,然後將其替換為我們想要的內容,然後再將網頁發送到瀏覽器。在我們的情況下,我們將替換%STATE% 預留參數,若預留參數 ledState的值是 1則顯示ON。否則,將其切換為OFF。
String processor(const String& var){
Serial.println(var);
if(var == "STATE"){
if (ledState){
return "ON";
}
else{
return "OFF";
}
}
}
setup()
在setup()裡面,初始化串列監視器以進行調試。
Serial.begin(115200);
設置 ledPin 作為一個 OUTPUT 並將其設置為 LOW 程序在首次啟動時。
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
初始化Wi-Fi並在串列監視器上印出ESP8266 IP地址。
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
// Print ESP Local IP Address
Serial.println(WiFi.localIP());
通過調用初始化WebSocket協議 initWebSocket() 先前建立的功能。
initWebSocket();
處理請求
服務保存在 index_html當您在根目錄/ URL上收到請求時,該變量–您需要傳遞處理器 用作將預留參數替換為當前GPIO狀態的參數。
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
});
最後,啟動服務器。
server.begin();
loop()
LED將通過物理方式控制 loop()。
void loop() {
ws.cleanupClients();
digitalWrite(ledPin, ledState);
}
請注意,我們都稱 cleanupClients()方法。原因如下(來自ESPAsyncWebServer庫GitHub頁面的說明):
瀏覽器有時無法正確關閉WebSocket連接,即使 關閉()函數在JavaScript中被調用。這最終將耗盡Web服務器的資源,並導致服務器崩潰。定期致電cleanupClients() 主要功能 loop()當超過最大客戶端數量時,通過關閉最舊的客戶端來限制客戶端的數量。可以在每個週期調用一次,但是,如果您希望使用更少的功率,那麼每秒調用一次就足夠了。
範例
上載程式碼後,以115200的鮑率打開串列監視器,然後按一下板載EN / RST按鈕。應印出ESP IP地址。
打開區域網上的瀏覽器,並鍵入ESP8266 IP地址。您應該可以訪問網頁來控制輸出。
點擊按鈕切換LED。您可以同時打開多個Web瀏覽器選項卡,也可以從不同的設備訪問Web服務器,並且只要有更改,LED狀態就會在所有客戶端中自動更新。
總結
這非常有用,因為服務器可以在發生任何事情時將數據發送到客戶端。例如,您可以在此設置中增加一個物理按鈕,當按下該按鈕時,該按鈕會通知所有客戶端以更新Web界面。
在本示例中,我們向您展示了如何控制ESP8266的一個GPIO。您可以使用此方法來控制更多的GPIO。您還可以使用WebSocket協議在任何給定時間發送傳感器讀數或通知。
我們希望您發現本教程對您有所幫助。
參考原文:https://randomnerdtutorials.com/esp8266-nodemcu-websocket-server-arduino/
註明來源比較恰當。