2. WebSocket 服务器基础:创建 ESP32 WebSocket 服务器

好,咱们正式开始动手了。这一节,我们要在 ESP32 上搭建一个 WebSocket 服务器。说白了,就是让 ESP32 变成一个能实时跟浏览器“聊天”的小服务端。

我刚开始做物联网项目时,用的还是传统的 HTTP 轮询——每隔几秒发一次请求,又慢又费电。后来换成 WebSocket,那感觉,就像从拨号上网换成了光纤。你想想看,浏览器和 ESP32 之间能随时“推”数据,这才是实时控制该有的样子。

2.1 什么是 WebSocket 服务器

WebSocket 服务器,本质上就是一个常驻在 ESP32 上的网络服务。它监听某个端口(通常是 80 或 8080),等待客户端连接。

跟普通 HTTP 服务器最大的区别在于:

  • HTTP 服务器:客户端请求一次,服务器响应一次,完事就断开。像你去柜台办业务,办完就走。
  • WebSocket 服务器:客户端连上来后,连接一直保持。双方随时可以发消息。像你俩拉了个微信群,随时说话。

核心概念:WebSocket 服务器维护的是“长连接”。连接不断,通信就是实时的。

在 ESP32 上,我们通常用 ArduinoWebsockets 这个库来实现。它封装了底层细节,我们只需要关心三个事情:

  1. 服务器怎么启动
  2. 客户端连上来/断开时怎么办
  3. 收到消息后怎么处理

2.2 创建 WebSocket 服务器的步骤

嗯,这里要注意。创建服务器不是一上来就写代码,得先搭好“地基”。

我个人习惯把流程分成四步:

步骤 操作 说明
1 引入库 包含 ArduinoWebsockets.h 和 WiFi 相关头文件
2 连接 WiFi ESP32 必须先联网,才能提供 WebSocket 服务
3 创建服务器对象 实例化 WebsocketsServer,指定端口
4 启动并监听 调用 begin(),然后在 loop() 里轮询客户端事件

你看,其实不复杂。但我在项目中遇到过一个问题:很多人把 WiFi 连接和 WebSocket 服务器启动写在一起,结果 WiFi 没连上,服务器也启动失败。我的建议是——先确保 WiFi 连上了,再启动 WebSocket 服务器,分开处理,出错了也好排查。

2.3 处理客户端连接与断开

服务器启动后,最重要的就是处理客户端的“来”和“走”。

在 ArduinoWebsockets 库里,我们可以注册回调函数:

  • onClientConnect():当有浏览器或 App 连上来时触发
  • onClientDisconnect():当客户端断开时触发

我曾经在一个智能家居项目里,没处理好断开事件,结果客户端掉线后服务器还在拼命发消息,导致内存泄漏。嗯,从那以后,我每次都会在断开回调里做清理工作。

小技巧:在 onClientConnect 里,可以打印客户端的 IP 地址,方便调试时知道是谁连上来了。

2.4 实现消息收发回调函数

消息收发是 WebSocket 的核心。服务器收到客户端发来的消息后,需要解析并做出响应。

回调函数的结构一般是这样的:

void onMessageCallback(WebsocketsClient client, WebsocketsMessage message) {
    // 获取消息内容
    String msg = message.data();
    
    // 根据消息内容做不同处理
    if (msg == "LED_ON") {
        digitalWrite(LED_PIN, HIGH);
        client.send("LED is now ON");
    } else if (msg == "LED_OFF") {
        digitalWrite(LED_PIN, LOW);
        client.send("LED is now OFF");
    } else {
        client.send("Unknown command");
    }
}

这里有几个关键点:

  • message.data() 拿到的是字符串,你可以直接比较
  • client.send() 是向那个特定的客户端回复消息
  • 如果想广播给所有客户端,需要遍历客户端列表

注意:不要在回调函数里做耗时操作,比如 delay() 或者复杂的计算。WebSocket 回调是在主循环里触发的,阻塞太久会导致其他客户端连不上。

2.5 完整代码框架

下面是一个完整的 WebSocket 服务器框架。你可以直接复制到 Arduino IDE 里用:

#include <WiFi.h>
#include <ArduinoWebsockets.h>

using namespace websockets;

// WiFi 配置
const char* ssid = "你的WiFi名称";
const char* password = "你的WiFi密码";

// LED 引脚
const int ledPin = 2;

// 创建 WebSocket 服务器
WebsocketsServer server;
WebsocketsClient client;

void setup() {
    Serial.begin(115200);
    pinMode(ledPin, OUTPUT);
    
    // 连接 WiFi
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("\nWiFi 连接成功");
    Serial.print("IP 地址: ");
    Serial.println(WiFi.localIP());
    
    // 启动 WebSocket 服务器
    server.listen(80);
    Serial.println("WebSocket 服务器已启动");
    
    // 注册回调函数
    server.onClientConnect([](WebsocketsClient client) {
        Serial.println("新客户端连接");
        client.send("欢迎连接到 ESP32 WebSocket 服务器");
    });
    
    server.onClientDisconnect([](WebsocketsClient client) {
        Serial.println("客户端断开连接");
    });
    
    server.onMessage([](WebsocketsClient client, WebsocketsMessage message) {
        String msg = message.data();
        Serial.print("收到消息: ");
        Serial.println(msg);
        
        if (msg == "LED_ON") {
            digitalWrite(ledPin, HIGH);
            client.send("LED 已打开");
        } else if (msg == "LED_OFF") {
            digitalWrite(ledPin, LOW);
            client.send("LED 已关闭");
        } else {
            client.send("未知指令");
        }
    });
}

void loop() {
    // 轮询客户端事件
    server.poll();
}

这段代码跑起来后,你用浏览器打开一个 WebSocket 测试工具(比如 websocket.org 的测试页),连接到 ws://你的ESP32_IP:80,就能收发消息了。

2.6 知识结构图

下面这张图,帮你理清 WebSocket 服务器的整体架构:

ESP32 WebSocket 服务器架构 ESP32 WebSocket 服务器 WiFi 连接管理 客户端连接/断开 消息收发回调 LED 控制逻辑 浏览器 / App 客户端 说明: • 服务器启动后,通过 WiFi 监听 80 端口 • 客户端连接时触发 onClientConnect,断开时触发 onClientDisconnect • 收到消息后,在 onMessage 回调里解析指令,控制 LED 并回复客户端

2.7 常见问题与避坑

最后,分享几个我踩过的坑:

  • WiFi 连接超时:如果 ESP32 连不上 WiFi,服务器就启动不了。建议加个超时重试机制,别死等。
  • 端口被占用:默认 80 端口可能被其他服务占了。我习惯用 8080 或者 81,避免冲突。
  • 消息格式:客户端发来的消息可能是 JSON,也可能是纯文本。建议统一用 JSON,扩展性好。
  • 内存泄漏:每次收到消息都 new 一个对象,用完不 delete,跑几天就崩了。用库的时候注意看文档,确认它有没有自动管理内存。

调试建议:先用 PC 上的 WebSocket 客户端工具测试,确认服务器正常,再写前端代码。这样能快速定位问题是出在服务器还是客户端。