参考
espidf的esp32版的webServer测试.csdn
HttpClient.hpp
c
#ifndef HTTP_CLIENT_HPP
#define HTTP_CLIENT_HPP
#include <string>
#include <cstring>
#include "esp_log.h"
#include "esp_http_client.h"
class HttpClient {
private:
static const char* TAG;
static constexpr int DEFAULT_TIMEOUT_MS = 5000;
// 回调上下文:用于在回调中传递数据
struct CallbackContext {
std::string* response;
};
// 事件回调函数:在请求过程中实时接收响应数据
static esp_err_t httpEventHandler(esp_http_client_event_t *evt) {
CallbackContext* ctx = static_cast<CallbackContext*>(evt->user_data);
if (ctx == nullptr || ctx->response == nullptr) {
return ESP_OK;
}
switch (evt->event_id) {
case HTTP_EVENT_ON_DATA:
if (evt->data_len > 0 && evt->data != nullptr) {
ctx->response->append(static_cast<char*>(evt->data), evt->data_len);
}
break;
default:
break;
}
return ESP_OK;
}
public:
// GET 请求
static std::string get(const std::string& url, int timeout_ms = DEFAULT_TIMEOUT_MS) {
std::string response = "";
CallbackContext ctx = { &response };
esp_http_client_config_t config = {};
config.url = url.c_str();
config.method = HTTP_METHOD_GET;
config.timeout_ms = timeout_ms;
config.keep_alive_enable = false;
config.event_handler = httpEventHandler;
config.user_data = &ctx;
esp_http_client_handle_t client = esp_http_client_init(&config);
if (client == NULL) {
ESP_LOGE(TAG, "Failed to initialize HTTP client");
return "";
}
ESP_LOGI(TAG, "GET %s", url.c_str());
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
int status_code = esp_http_client_get_status_code(client);
ESP_LOGI(TAG, "HTTP Status = %d", status_code);
} else {
ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
return response;
}
// POST 请求 (JSON)
static std::string post(const std::string& url, const std::string& data, int timeout_ms = DEFAULT_TIMEOUT_MS) {
std::string response = "";
CallbackContext ctx = { &response };
esp_http_client_config_t config = {};
config.url = url.c_str();
config.method = HTTP_METHOD_POST;
config.timeout_ms = timeout_ms;
config.keep_alive_enable = false;
config.event_handler = httpEventHandler;
config.user_data = &ctx;
esp_http_client_handle_t client = esp_http_client_init(&config);
if (client == NULL) {
ESP_LOGE(TAG, "Failed to initialize HTTP client");
return "";
}
esp_http_client_set_header(client, "Content-Type", "application/json");
esp_http_client_set_post_field(client, data.c_str(), data.length());
ESP_LOGI(TAG, "POST %s", url.c_str());
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
int status_code = esp_http_client_get_status_code(client);
ESP_LOGI(TAG, "HTTP Status = %d", status_code);
} else {
ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
return response;
}
};
const char* HttpClient::TAG = "HttpClient";
#endif // HTTP_CLIENT_HPP
main.cpp
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "app_wifi.h"
#include "HttpClient.hpp"
static const char *TAG = "HTTP_TEST";
static const char* SERVER_IP = "192.168.3.38";
static const int SERVER_PORT = 8888;
static void test_http_client(void)
{
while (1) {
// --- 测试 GET ---
char getUrl[128];
snprintf(getUrl, sizeof(getUrl), "http://%s:%d/apiGet", SERVER_IP, SERVER_PORT);
TickType_t startGet = xTaskGetTickCount();
std::string getResp = HttpClient::get(getUrl);
TickType_t costGet = (xTaskGetTickCount() - startGet) * portTICK_PERIOD_MS;
if (!getResp.empty()) {
ESP_LOGI(TAG, "GET Response (%lu ms):\n%s", (unsigned long)costGet, getResp.c_str());
} else {
ESP_LOGW(TAG, "GET request returned empty response (cost %lu ms)", (unsigned long)costGet);
}
vTaskDelay(pdMS_TO_TICKS(2000));
// --- 测试 POST ---
char postUrl[128];
snprintf(postUrl, sizeof(postUrl), "http://%s:%d/apiPost", SERVER_IP, SERVER_PORT);
std::string jsonData = "{\"device\":\"ESP32\", \"temperature\":25.6, \"message\":\"Hello from ESP32\"}";
TickType_t startPost = xTaskGetTickCount();
std::string postResp = HttpClient::post(postUrl, jsonData);
TickType_t costPost = (xTaskGetTickCount() - startPost) * portTICK_PERIOD_MS;
if (!postResp.empty()) {
ESP_LOGI(TAG, "POST Response (%lu ms):\n%s", (unsigned long)costPost, postResp.c_str());
} else {
ESP_LOGW(TAG, "POST request returned empty response (cost %lu ms)", (unsigned long)costPost);
}
vTaskDelay(100);
}
}
// ============================================================
// app_main
// ============================================================
extern "C" void app_main(void) {
// --------------------------------------------------------
// 连接现有路由器
// --------------------------------------------------------
app_wifi_main();
test_http_client();
}
测试
js
let k=0;
app.get("/apiGet",(req,res)=>{
res.send(M.successResult(k++));
})
app.post("/apiPost",(req,res)=>{
console.log(req.params);
res.send(M.successResult(k++));
})
bash
[16:16:33.160]收←◆I (20799) HttpClient: GET http://192.168.3.38:8888/apiGet
I (20809) HttpClient: HTTP Status = 200
I (20819) HTTP_TEST: GET Response (20 ms):
{"code":0,"msg":"success","data":352}
[16:16:35.180]收←◆I (22819) HttpClient: POST http://192.168.3.38:8888/apiPost
[16:16:35.243]收←◆I (22879) HttpClient: HTTP Status = 200
I (22879) HTTP_TEST: POST Response (60 ms):
{"code":0,"msg":"success","data":353}
TcpClient.hpp
c
#ifndef TCP_CLIENT_HPP
#define TCP_CLIENT_HPP
#include <string>
#include <cstring>
#include "esp_log.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
class TcpClient {
private:
static const char* TAG;
int sock_;
std::string ip_;
int port_;
public:
TcpClient() : sock_(-1), port_(0) {}
~TcpClient() {
close();
}
// 禁止拷贝
TcpClient(const TcpClient&) = delete;
TcpClient& operator=(const TcpClient&) = delete;
// 连接服务器
bool connect(const char* ip, uint16_t port, uint32_t timeout_ms = 5000) {
if (sock_ >= 0) {
ESP_LOGW(TAG, "Already connected, close first");
close();
}
sock_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock_ < 0) {
ESP_LOGE(TAG, "Failed to create socket: errno %d", errno);
return false;
}
// 设置收发超时
struct timeval tv;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(sock_, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
inet_pton(AF_INET, ip, &server_addr.sin_addr);
ESP_LOGI(TAG, "Connecting to %s:%d", ip, port);
int err = ::connect(sock_, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (err != 0) {
ESP_LOGE(TAG, "Connect failed: errno %d", errno);
::close(sock_);
sock_ = -1;
return false;
}
ip_ = ip;
port_ = port;
ESP_LOGI(TAG, "Connected to %s:%d", ip, port);
return true;
}
// 发送数据
int32_t write(const void* buffer, uint32_t size, uint32_t timeout_ms = 5000) {
if (sock_ < 0) {
ESP_LOGE(TAG, "Not connected");
return -1;
}
if (buffer == nullptr || size == 0) {
ESP_LOGE(TAG, "Invalid buffer or size");
return -1;
}
// 更新发送超时
struct timeval tv;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
setsockopt(sock_, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
int sent = ::send(sock_, buffer, size, 0);
if (sent < 0) {
ESP_LOGE(TAG, "Send failed: errno %d", errno);
} else {
ESP_LOGI(TAG, "Sent %d/%u bytes", sent, size);
}
return sent;
}
// 接收数据
int32_t read(void* buffer, uint32_t size, uint32_t timeout_ms = 5000) {
if (sock_ < 0) {
ESP_LOGE(TAG, "Not connected");
return -1;
}
if (buffer == nullptr || size == 0) {
ESP_LOGE(TAG, "Invalid buffer or size");
return -1;
}
// 更新接收超时
struct timeval tv;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
int len = ::recv(sock_, buffer, size, 0);
if (len > 0) {
ESP_LOGI(TAG, "Received %d bytes", len);
} else if (len == 0) {
ESP_LOGW(TAG, "Connection closed by peer");
} else {
ESP_LOGE(TAG, "Recv failed: errno %d", errno);
}
return len;
}
// 关闭连接
void close() {
if (sock_ >= 0) {
::close(sock_);
sock_ = -1;
ESP_LOGI(TAG, "Socket closed");
}
}
// 是否已连接
bool isConnected() const {
return sock_ >= 0;
}
};
const char* TcpClient::TAG = "TcpClient";
#endif // TCP_CLIENT_HPP
main.cpp
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "app_wifi.h"
#include "TcpClient.hpp"
static const char *TAG = "TCP_TEST";
static const char* SERVER_IP = "192.168.3.38";
static const int SERVER_PORT = 8888;
static void test_tcp_client(void)
{
TcpClient tcp;
while (1) {
// --- 连接服务器 ---
if (!tcp.connect(SERVER_IP, SERVER_PORT)) {
ESP_LOGW(TAG, "Connect failed, retry after 2s");
vTaskDelay(pdMS_TO_TICKS(2000));
continue;
}
// --- 测试 write ---
const char* msg = "Hello from ESP32";
TickType_t startWrite = xTaskGetTickCount();
int32_t sent = tcp.write(msg, strlen(msg), 3000);
TickType_t costWrite = (xTaskGetTickCount() - startWrite) * portTICK_PERIOD_MS;
if (sent > 0) {
ESP_LOGI(TAG, "Write %d bytes (%lu ms): %s", sent, (unsigned long)costWrite, msg);
} else {
ESP_LOGW(TAG, "Write failed (cost %lu ms)", (unsigned long)costWrite);
tcp.close();
vTaskDelay(pdMS_TO_TICKS(2000));
continue;
}
// --- 测试 read ---
char buf[256] = {0};
TickType_t startRead = xTaskGetTickCount();
int32_t len = tcp.read(buf, sizeof(buf) - 1, 3000);
TickType_t costRead = (xTaskGetTickCount() - startRead) * portTICK_PERIOD_MS;
if (len > 0) {
buf[len] = '\0';
ESP_LOGI(TAG, "Read %d bytes (%lu ms): %s", len, (unsigned long)costRead, buf);
} else if (len == 0) {
ESP_LOGW(TAG, "Connection closed by peer (cost %lu ms)", (unsigned long)costRead);
} else {
ESP_LOGW(TAG, "Read failed (cost %lu ms)", (unsigned long)costRead);
}
tcp.close();
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
// ============================================================
// app_main
// ============================================================
extern "C" void app_main(void) {
// --------------------------------------------------------
// 连接现有路由器
// --------------------------------------------------------
app_wifi_main();
test_tcp_client();
}
测试
bash
[16:32:10.115]收←◆I (22239) TcpClient: Connecting to 192.168.3.38:8888
I (22239) TcpClient: Connected to 192.168.3.38:8888
I (22239) TcpClient: Sent 16/16 bytes
I (22239) TCP_TEST: Write 16 bytes (0 ms): Hello from ESP32
I (22259) TcpClient: Received 47 bytes
I (22259) TCP_TEST: Read 47 bytes (10 ms): HTTP/1.1 400 Bad Request
Connection: close
I (22259) TcpClient: Socket closed
UdpClient.hpp
c
#ifndef UDP_CLIENT_HPP
#define UDP_CLIENT_HPP
#include <string>
#include <cstring>
#include "esp_log.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
class UdpClient {
private:
static const char* TAG;
int sock_;
std::string ip_;
uint16_t port_;
public:
UdpClient() : sock_(-1), port_(0) {}
~UdpClient() {
close();
}
// 禁止拷贝
UdpClient(const UdpClient&) = delete;
UdpClient& operator=(const UdpClient&) = delete;
// 创建 UDP socket 并绑定目标地址
bool open(const char* ip, uint16_t port, uint16_t local_port = 0, uint32_t timeout_ms = 5000) {
if (sock_ >= 0) {
ESP_LOGW(TAG, "Already opened, close first");
close();
}
sock_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock_ < 0) {
ESP_LOGE(TAG, "Failed to create socket: errno %d", errno);
return false;
}
// 绑定本地端口
if (local_port > 0) {
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
local_addr.sin_port = htons(local_port);
if (bind(sock_, (struct sockaddr*)&local_addr, sizeof(local_addr)) < 0) {
ESP_LOGE(TAG, "Bind to local port %d failed: errno %d", local_port, errno);
::close(sock_);
sock_ = -1;
return false;
}
ESP_LOGI(TAG, "Bound to local port %d", local_port);
}
// 设置收发超时
struct timeval tv;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(sock_, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
ip_ = ip;
port_ = port;
ESP_LOGI(TAG, "UDP socket opened, target %s:%d", ip, port);
return true;
}
// 发送数据到目标地址
int32_t write(const void* buffer, uint32_t size, uint32_t timeout_ms = 5000) {
if (sock_ < 0) {
ESP_LOGE(TAG, "Not opened");
return -1;
}
if (buffer == nullptr || size == 0) {
ESP_LOGE(TAG, "Invalid buffer or size");
return -1;
}
// 更新发送超时
struct timeval tv;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
setsockopt(sock_, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
struct sockaddr_in dest_addr;
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(port_);
inet_pton(AF_INET, ip_.c_str(), &dest_addr.sin_addr);
int sent = sendto(sock_, buffer, size, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
if (sent < 0) {
ESP_LOGE(TAG, "Send failed: errno %d", errno);
} else {
ESP_LOGI(TAG, "Sent %d/%u bytes to %s:%d", sent, size, ip_.c_str(), port_);
}
return sent;
}
// 接收数据
int32_t read(void* buffer, uint32_t size, uint32_t timeout_ms = 5000) {
if (sock_ < 0) {
ESP_LOGE(TAG, "Not opened");
return -1;
}
if (buffer == nullptr || size == 0) {
ESP_LOGE(TAG, "Invalid buffer or size");
return -1;
}
// 更新接收超时
struct timeval tv;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
struct sockaddr_in src_addr;
socklen_t addr_len = sizeof(src_addr);
int len = recvfrom(sock_, buffer, size, 0, (struct sockaddr*)&src_addr, &addr_len);
if (len > 0) {
ESP_LOGI(TAG, "Received %d bytes from %s:%d",
len, inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));
} else if (len == 0) {
ESP_LOGW(TAG, "Received empty datagram");
} else {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
ESP_LOGW(TAG, "Recv timeout");
} else {
ESP_LOGE(TAG, "Recv failed: errno %d", errno);
}
}
return len;
}
// 关闭 socket
void close() {
if (sock_ >= 0) {
::close(sock_);
sock_ = -1;
ESP_LOGI(TAG, "UDP socket closed");
}
}
// 是否已打开
bool isOpened() const {
return sock_ >= 0;
}
};
const char* UdpClient::TAG = "UdpClient";
#endif // UDP_CLIENT_HPP
main.cpp
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "app_wifi.h"
#include "UdpClient.hpp"
static const char *TAG = "UDP_TEST";
static const char* SERVER_IP = "192.168.3.38";
static const int SERVER_PORT = 8888;
static void test_udp_client(void)
{
UdpClient udp;
while (1) {
// --- 打开 UDP ---
if (!udp.open(SERVER_IP, SERVER_PORT,8888)) {
ESP_LOGW(TAG, "Open failed, retry after 2s");
vTaskDelay(pdMS_TO_TICKS(2000));
continue;
}
// --- 测试 write ---
const char* msg = "Hello UDP from ESP32";
TickType_t startWrite = xTaskGetTickCount();
int32_t sent = udp.write(msg, strlen(msg), 3000);
TickType_t costWrite = (xTaskGetTickCount() - startWrite) * portTICK_PERIOD_MS;
if (sent > 0) {
ESP_LOGI(TAG, "Write %d bytes (%lu ms): %s", sent, (unsigned long)costWrite, msg);
} else {
ESP_LOGW(TAG, "Write failed (cost %lu ms)", (unsigned long)costWrite);
udp.close();
vTaskDelay(pdMS_TO_TICKS(2000));
continue;
}
// --- 测试 read ---
char buf[10] = {0};
TickType_t startRead = xTaskGetTickCount();
int32_t len = udp.read(buf, sizeof(buf) - 1, 3000);
TickType_t costRead = (xTaskGetTickCount() - startRead) * portTICK_PERIOD_MS;
if (len > 0) {
buf[len] = '\0';
ESP_LOGI(TAG, "Read %d bytes (%lu ms): %s", len, (unsigned long)costRead, buf);
} else if (len == 0) {
ESP_LOGW(TAG, "Received empty datagram (cost %lu ms)", (unsigned long)costRead);
} else {
ESP_LOGW(TAG, "Read failed or timeout (cost %lu ms)", (unsigned long)costRead);
}
udp.close();
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
// ============================================================
// app_main
// ============================================================
extern "C" void app_main(void) {
// --------------------------------------------------------
// 连接现有路由器
// --------------------------------------------------------
app_wifi_main();
test_udp_client();
}
测试
c
[16:52:26.395]收←◆I (140459) UdpClient: Bound to local port 8888
I (140459) UdpClient: UDP socket opened, target 192.168.3.38:8888
I (140459) UdpClient: Sent 20/20 bytes to 192.168.3.38:8888
I (140459) UDP_TEST: Write 20 bytes (0 ms): Hello UDP from ESP32
[16:52:29.404]收←◆W (143469) UdpClient: Recv timeout
W (143469) UDP_TEST: Read failed or timeout (cost 3000 ms)
I (143469) UdpClient: UDP socket closed
WebSocketClient.hpp
c
#ifndef WEBSOCKET_CLIENT_HPP
#define WEBSOCKET_CLIENT_HPP
#include <string>
#include <cstring>
#include <functional>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_websocket_client.h"
class WebSocketClient {
public:
// 数据回调类型:(data, len, opcode)
using DataCallback = std::function<void(const uint8_t*, int, uint8_t)>;
// 事件回调类型:(event_id)
using EventCallback = std::function<void(int32_t)>;
private:
static const char* TAG;
esp_websocket_client_handle_t handle_;
SemaphoreHandle_t connSem_; // 连接/断开信号量
bool connected_;
DataCallback onData_;
EventCallback onEvent_;
// 静态事件处理函数(转发到实例方法)
static void eventHandler(void* handler_args, esp_event_base_t base,
int32_t event_id, void* event_data) {
auto* self = static_cast<WebSocketClient*>(handler_args);
if (self) {
self->onWsEvent(event_id, (esp_websocket_event_data_t*)event_data);
}
}
void onWsEvent(int32_t event_id, esp_websocket_event_data_t* data) {
switch (event_id) {
case WEBSOCKET_EVENT_CONNECTED:
ESP_LOGI(TAG, "Connected");
connected_ = true;
if (connSem_) xSemaphoreGive(connSem_);
break;
case WEBSOCKET_EVENT_DISCONNECTED:
ESP_LOGW(TAG, "Disconnected");
connected_ = false;
if (connSem_) xSemaphoreGive(connSem_);
break;
case WEBSOCKET_EVENT_CLOSED:
ESP_LOGI(TAG, "Closed cleanly");
connected_ = false;
if (connSem_) xSemaphoreGive(connSem_);
break;
case WEBSOCKET_EVENT_DATA:
if (data) {
ESP_LOGD(TAG, "Recv opcode=%d, len=%d, total=%d, offset=%d",
data->op_code, data->data_len,
data->payload_len, data->payload_offset);
// 关闭帧
if (data->op_code == 0x08 && data->data_len >= 2) {
int code = (data->data_ptr[0] << 8) | data->data_ptr[1];
ESP_LOGW(TAG, "Close frame, code=%d", code);
}
// 用户回调
if (onData_ && data->data_ptr && data->data_len > 0) {
onData_((const uint8_t*)data->data_ptr, data->data_len, data->op_code);
}
}
break;
case WEBSOCKET_EVENT_ERROR:
ESP_LOGE(TAG, "Error event");
break;
}
// 用户事件回调
if (onEvent_) {
onEvent_(event_id);
}
}
public:
WebSocketClient()
: handle_(nullptr)
, connSem_(nullptr)
, connected_(false) {}
~WebSocketClient() {
close();
if (connSem_) {
vSemaphoreDelete(connSem_);
connSem_ = nullptr;
}
}
// 禁止拷贝
WebSocketClient(const WebSocketClient&) = delete;
WebSocketClient& operator=(const WebSocketClient&) = delete;
// 连接 WebSocket 服务器
// uri 格式:ws://host:port/path 或 wss://host:port/path
bool connect(const char* uri, uint32_t timeout_ms = 5000,
const char* subprotocol = nullptr,
const char* cert_pem = nullptr) {
if (handle_) {
ESP_LOGW(TAG, "Already created, close first");
close();
}
// 创建信号量
if (!connSem_) {
connSem_ = xSemaphoreCreateBinary();
}
// 配置
esp_websocket_client_config_t cfg = {};
cfg.uri = uri;
cfg.subprotocol = subprotocol;
cfg.cert_pem = cert_pem;
cfg.buffer_size = 2048;
cfg.task_stack = 4096;
cfg.task_prio = 5;
cfg.pingpong_timeout_sec = 30;
cfg.ping_interval_sec = 10;
handle_ = esp_websocket_client_init(&cfg);
if (!handle_) {
ESP_LOGE(TAG, "Failed to init websocket client");
return false;
}
// 注册事件
esp_websocket_register_events(handle_, WEBSOCKET_EVENT_ANY,
eventHandler, this);
ESP_LOGI(TAG, "Connecting to %s", uri);
// 启动连接
esp_err_t err = esp_websocket_client_start(handle_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Start failed: %s", esp_err_to_name(err));
esp_websocket_client_destroy(handle_);
handle_ = nullptr;
return false;
}
// 等待连接建立
if (xSemaphoreTake(connSem_, pdMS_TO_TICKS(timeout_ms)) != pdTRUE) {
ESP_LOGE(TAG, "Connect timeout (%lu ms)", (unsigned long)timeout_ms);
esp_websocket_client_stop(handle_);
esp_websocket_client_destroy(handle_);
handle_ = nullptr;
return false;
}
if (!connected_) {
ESP_LOGE(TAG, "Connect failed");
esp_websocket_client_destroy(handle_);
handle_ = nullptr;
return false;
}
ESP_LOGI(TAG, "Connected to %s", uri);
return true;
}
// 发送文本
int32_t write(const void* buffer, uint32_t size, uint32_t timeout_ms = 5000) {
if (!handle_ || !connected_) {
ESP_LOGE(TAG, "Not connected");
return -1;
}
if (!buffer || size == 0) {
ESP_LOGE(TAG, "Invalid buffer or size");
return -1;
}
int sent = esp_websocket_client_send_text(
handle_, (const char*)buffer, (int)size,
pdMS_TO_TICKS(timeout_ms));
if (sent > 0) {
ESP_LOGI(TAG, "Sent %d bytes (text)", sent);
} else {
ESP_LOGE(TAG, "Send text failed");
}
return sent;
}
// 发送二进制
int32_t writeBin(const void* buffer, uint32_t size, uint32_t timeout_ms = 5000) {
if (!handle_ || !connected_) {
ESP_LOGE(TAG, "Not connected");
return -1;
}
if (!buffer || size == 0) {
ESP_LOGE(TAG, "Invalid buffer or size");
return -1;
}
int sent = esp_websocket_client_send_bin(
handle_, (const char*)buffer, (int)size,
pdMS_TO_TICKS(timeout_ms));
if (sent > 0) {
ESP_LOGI(TAG, "Sent %d bytes (binary)", sent);
} else {
ESP_LOGE(TAG, "Send binary failed");
}
return sent;
}
// 发送 ping
bool ping() {
if (!handle_ || !connected_) return false;
// 官方没有 send_ping API,通过底层 send 发送 ping 控制帧
// 实际上依赖自动 ping 即可,这里仅作兼容
ESP_LOGW(TAG, "Manual ping not supported, using auto ping instead");
return true;
}
// 关闭连接
void close() {
if (handle_) {
ESP_LOGI(TAG, "Closing...");
esp_websocket_client_close(handle_, pdMS_TO_TICKS(3000));
esp_websocket_client_destroy(handle_);
handle_ = nullptr;
connected_ = false;
ESP_LOGI(TAG, "Closed");
}
}
// 是否已连接
bool isConnected() const {
return connected_;
}
// 设置数据接收回调
void setDataCallback(DataCallback cb) {
onData_ = cb;
}
// 设置事件回调
void setEventCallback(EventCallback cb) {
onEvent_ = cb;
}
};
const char* WebSocketClient::TAG = "WebSocketClient";
#endif // WEBSOCKET_CLIENT_HPP
main/main.cpp
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "app_wifi.h"
#include "WebSocketClient.hpp"
static const char *TAG = "WEBSOCKET_TEST";
static const char* SERVER_IP = "192.168.3.11";
static const int SERVER_PORT = 8001;
static void test_websocket_client(void)
{
WebSocketClient ws;
// 设置数据接收回调
ws.setDataCallback([&](const uint8_t* data, int len, uint8_t opcode) {
// opcode: 0x01=文本, 0x02=二进制
if (opcode == 0x01) {
ESP_LOGI(TAG, "Recv text: %.*s", len, (const char*)data);
} else if (opcode == 0x02) {
ESP_LOGI(TAG, "Recv binary, len=%d", len);
}
});
// 设置事件回调(可选)
ws.setEventCallback([&](int32_t event_id) {
if (event_id == WEBSOCKET_EVENT_DISCONNECTED) {
ESP_LOGW(TAG, "Connection lost");
}
});
// 构造 URI
char uri[128];
snprintf(uri, sizeof(uri), "ws://%s:%d/ws", SERVER_IP, SERVER_PORT);
// 连接
if (!ws.connect(uri, 5000)) {
ESP_LOGE(TAG, "Connect failed");
return;
}
// 发送文本
const char* msg = "Hello WebSocket from ESP32";
ws.write(msg, strlen(msg));
// 发送二进制
uint8_t bin[] = {0x01, 0x02, 0x03, 0x04};
ws.writeBin(bin, sizeof(bin));
// 保持运行,接收数据靠回调
vTaskDelay(pdMS_TO_TICKS(30000));
// 关闭
ws.close();
ESP_LOGI(TAG, "Test done");
}
// ============================================================
// app_main
// ============================================================
extern "C" void app_main(void) {
// --------------------------------------------------------
// 连接现有路由器
// --------------------------------------------------------
app_wifi_main();
test_websocket_client();
}
main/idf_component.yml
yml
dependencies:
idf: ">=5.0"
espressif/esp_websocket_client:
version: "^1.0.0"
main/CMakeLists.txt
bash
idf_component_register(SRCS "main.cpp" "app_wifi.cpp"
INCLUDE_DIRS "."
REQUIRES esp_websocket_client esp_wifi esp_event nvs_flash esp_netif
)
测试
bash
[17:12:05.716]收←◆I (2166) WEBSOCKET_TEST: Recv text: {"id":0,"method":"connect","params":{"clientCount":2,"clientId":2988711024},"slaveInstId":null,"timestamp":22528829}
I (2186) WEBSOCKET_TEST: Recv binary, len=2048
I (2186) WEBSOCKET_TEST: Recv binary, len=2048
I (2186) WEBSOCKET_TEST: Recv binary, len=2048