使用ESP32-WROOM-32完成websocket功能
本文介绍如何在ESP32-WROOM-32开发板上使用ESP-IDF v5.4.0实现WebSocket客户端功能。主要内容包括:开发环境准备(ESP-IDF v5.4.0、ESP32硬件、WebSocket测试服务)、项目目录结构说明,以及关键代码实现。代码部分展示了WebSocket客户端建立连接的流程,包括WiFi连接初始化、WebSocket帧构建和解析功能。文章提供了一个简单的WebSo
一、简介
ESP32-WROOM-32 是一款高性能、低功耗的 Wi-Fi + 蓝牙双模模组,常用于物联网项目中。WebSocket 是一种全双工通信协议,适合低延迟、高实时性的通信场景(如聊天、远程控制、状态推送等)。
本文将介绍如何使用 ESP-IDF v5.4.0 在 ESP32 上建立 WebSocket 客户端,实现与 WebSocket 服务器的实时通信。
二、开发环境准备
ESP-IDF 版本:v5.4.0
硬件:ESP32-WROOM-32 开发板
WebSocket 测试服务:wss://echo.websocket.events(或搭建自己的)
三、项目结构
## 目录结构
├── CMakeLists.txt # 项目主 CMake 配置文件
├── pytest_hello_world.py # 自动化测试脚本
├── README.md # 项目说明文档
├── sdkconfig* # 配置文件
├── websocket.zip # 相关压缩包
├── .devcontainer/ # VS Code 远程开发容器配置
├── .vscode/ # VS Code 配置
├── build/ # 构建输出目录
├── components/
│ └── wss-client/ # 自定义组件
└── main/
├── CMakeLists.txt # main 目录 CMake 配置
└── hello_world_main.c # 主程序入口
四、完整代码
//
#include <stdio.h>
#include <string.h>
#include "esp_system.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_spi_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_log.h"
#include "wss_client.h"
#define WIFI_SSID "Xiaomi_F00C"
#define WIFI_PASS "12345678"
#define WEBSOCKET_SERVER_URI "ws://echo.websocket.events"
static const char *TAG = "ws_example";
static void on_ws_message(const char *msg, size_t len) {
ESP_LOGI(TAG, "Received WebSocket message: %.*s", (int)len, msg);
}
void app_main(void) {
printf("Hello world!\n");
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
printf("This is %s chip with %d CPU cores, WiFi%s%s, ",
CONFIG_IDF_TARGET,
chip_info.cores,
(chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
printf("silicon revision %d, ", chip_info.revision);
uint32_t size = 0;
esp_flash_get_size(NULL, &size);
printf("%luMB %s flash\n", (unsigned long)(size / (1024 * 1024)),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
wifi_config_t wifi_config = {0};
strncpy((char *)wifi_config.sta.ssid, WIFI_SSID, sizeof(wifi_config.sta.ssid));
strncpy((char *)wifi_config.sta.password, WIFI_PASS, sizeof(wifi_config.sta.password));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_ERROR_CHECK(esp_wifi_connect());
printf("Connecting to WiFi SSID: %s ...\n", WIFI_SSID);
vTaskDelay(5000 / portTICK_PERIOD_MS);
wss_client_config_t ws_cfg = {
.uri = WEBSOCKET_SERVER_URI,
.on_message = on_ws_message,
};
wss_client_start(&ws_cfg);
// 发送消息等可在 wss_client.c 内部实现或扩展
while (1) {
vTaskDelay(10000 / portTICK_PERIOD_MS);
}
}
// wss_client.c
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "wss_client.h"
#define TAG "wss-client"
// 组包 WebSocket 文本帧,返回帧长度
size_t wss_client_build_frame(const char *msg, uint8_t *frame_buf, size_t buf_size) {
size_t msg_len = strlen(msg);
if (buf_size < msg_len + 6) return 0; // 缓冲区不足
frame_buf[0] = 0x81; // FIN + text frame
frame_buf[1] = 0x80 | (msg_len & 0x7F); // MASK bit + payload len
uint8_t mask[4];
for (int i = 0; i < 4; ++i) mask[i] = esp_random() & 0xFF;
memcpy(&frame_buf[2], mask, 4);
for (size_t i = 0; i < msg_len; ++i) {
frame_buf[6 + i] = msg[i] ^ mask[i % 4];
}
return msg_len + 6;
}
static void wss_client_task(void *param) {
const wss_client_config_t *config = (const wss_client_config_t *)param;
// 这里只做 ws:// 的简单演示,不支持 wss://
// 实际项目建议用更完整的库或完善此实现
struct addrinfo hints = {0}, *res;
// 解析 ws://host[:port][/path]
char host[128] = {0};
char path[128] = "/";
int port = 80;
const char *p = config->uri + strlen("ws://");
const char *slash = strchr(p, '/');
const char *colon = strchr(p, ':');
if (colon && (!slash || colon < slash)) {
strncpy(host, p, colon - p);
port = atoi(colon + 1);
if (slash) strncpy(path, slash, sizeof(path) - 1);
} else {
if (slash) {
strncpy(host, p, slash - p);
strncpy(path, slash, sizeof(path) - 1);
} else {
strncpy(host, p, sizeof(host) - 1);
}
}
ESP_LOGI(TAG, "Connecting to %s:%d%s", host, port, path);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(host, NULL, &hints, &res) != 0) {
ESP_LOGE(TAG, "DNS lookup failed");
vTaskDelete(NULL);
return;
}
struct sockaddr_in *addr = (struct sockaddr_in *)res->ai_addr;
addr->sin_port = htons(port);
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
ESP_LOGE(TAG, "Socket create failed");
freeaddrinfo(res);
vTaskDelete(NULL);
return;
}
if (connect(sock, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) != 0) {
ESP_LOGE(TAG, "Socket connect failed");
close(sock);
freeaddrinfo(res);
vTaskDelete(NULL);
return;
}
freeaddrinfo(res);
// 发送 WebSocket 握手
char req[512];
snprintf(req, sizeof(req),
"GET %s HTTP/1.1\r\n"
"Host: %s:%d\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n",
path, host, port);
send(sock, req, strlen(req), 0);
char resp[512];
int len = recv(sock, resp, sizeof(resp)-1, 0);
if (len <= 0) {
ESP_LOGE(TAG, "Handshake failed");
close(sock);
vTaskDelete(NULL);
return;
}
resp[len] = 0;
ESP_LOGI(TAG, "Handshake response: %s", resp);
// 简单循环收消息和发消息
while (1) {
const char *msg = "Hello from ESP32";
uint8_t frame[128] = {0};
size_t frame_len = wss_client_build_frame(msg, frame, sizeof(frame));
if (frame_len > 0) {
send(sock, frame, frame_len, 0);
ESP_LOGI(TAG, "Sent: %s", msg);
}
// 接收服务器返回
uint8_t hdr[2];
int r = recv(sock, hdr, 2, 0);
if (r <= 0) break;
int payload_len = hdr[1] & 0x7F;
char recv_msg[128] = {0};
if (payload_len > 0 && payload_len < sizeof(recv_msg)) {
r = recv(sock, recv_msg, payload_len, 0);
if (r > 0 && config->on_message) {
config->on_message(recv_msg, r);
}
}
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
close(sock);
vTaskDelete(NULL);
}
void wss_client_start(const wss_client_config_t *config) {
xTaskCreate(wss_client_task, "wss_client", 4096, (void *)config, 5, NULL);
}
#pragma once
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*wss_on_message_cb)(const char *msg, size_t len);
typedef struct {
const char *uri;
wss_on_message_cb on_message;
} wss_client_config_t;
void wss_client_start(const wss_client_config_t *config);
#ifdef __cplusplus
}
#endif
五、运行结果
代码运行,自动连接指定的wifi,向目标服务地址发送Hello from ESP32,会实时收到相同的数据
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐



所有评论(0)