这是一个完整的ESP32 SignalR客户端测试示例,演示如何使用 esp-signalr 库连接到ASP.NET Core SignalR服务器。
- ✅ WiFi 连接管理
- ✅ SignalR Hub 连接
- ✅ 消息发送和接收
- ✅ 多种消息处理器
- ✅ 自动重连机制
- ✅ 错误处理和日志
- ✅ 定期发送测试消息
- ✅ 模拟传感器数据上报
- ✅ 内存使用监控
- ESP32 / ESP32-S2 / ESP32-S3 / ESP32-C3 / ESP32-C6 开发板
- USB 数据线
- WiFi 网络环境
- ESP-IDF >= 5.0.0
- Python 3.6+
- ASP.NET Core SignalR 服务器 (用于测试)
cd d:\github\esp-signalr-example# 创建 managed_components 目录
mkdir managed_components
cd managed_components
# 克隆 esp-signalr 库
git clone https://github.com/maker-community/esp-signalr.git verdure__esp-signalr
cd ..使用 ESP-IDF 的 menuconfig 工具配置项目:
idf.py menuconfig在菜单中导航到:
ESP SignalR Example Configuration --->
WiFi Configuration --->
WiFi SSID: 输入你的WiFi名称
WiFi Password: 输入你的WiFi密码
SignalR Configuration --->
SignalR Hub URL: 输入你的服务器地址
示例配置:
- WiFi SSID:
MyWiFi - WiFi Password:
MyPassword123 - SignalR Hub URL:
http://192.168.1.100:5000/chatHub
配置完成后按 S 保存,然后按 Q 退出。
配置选项说明:
| 配置项 | 说明 | 默认值 |
|---|---|---|
| WiFi SSID | WiFi网络名称 | myssid |
| WiFi Password | WiFi密码 | mypassword |
| Maximum retry | WiFi最大重试次数 | 5 |
| SignalR Hub URL | SignalR服务器地址 | http://192.168.1.100:5000/chatHub |
| Enable Auto Reconnect | 启用自动重连 | 是 |
| Reconnect Interval | 重连间隔(秒) | 5 |
注意: 配置保存在 sdkconfig 文件中,如需重新配置可随时运行 idf.py menuconfig。
# 设置目标芯片 (根据你的开发板选择)
idf.py set-target esp32
# 构建项目
idf.py build
# 烧录到设备
idf.py flash
# 查看串口输出
idf.py monitor或者一次性执行:
idf.py flash monitor创建一个简单的 SignalR 测试服务器:
dotnet new web -n SignalRTestServer
cd SignalRTestServer
dotnet add package Microsoft.AspNetCore.SignalRusing Microsoft.AspNetCore.SignalR;
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
Console.WriteLine($"Received from {user}: {message}");
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
public async Task UpdateSensor(string sensorId, double value)
{
Console.WriteLine($"Sensor {sensorId}: {value}");
await Clients.All.SendAsync("UpdateSensorData", sensorId, value);
}
public override async Task OnConnectedAsync()
{
Console.WriteLine($"Client connected: {Context.ConnectionId}");
await base.OnConnectedAsync();
await Clients.Caller.SendAsync("Notification", "Welcome to SignalR!");
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
Console.WriteLine($"Client disconnected: {Context.ConnectionId}");
await base.OnDisconnectedAsync(exception);
}
}var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSignalR();
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
var app = builder.Build();
app.UseCors();
app.MapHub<ChatHub>("/chatHub");
// 监听所有网络接口
app.Urls.Add("http://0.0.0.0:5000");
Console.WriteLine("SignalR server running on http://0.0.0.0:5000");
Console.WriteLine("Hub endpoint: http://0.0.0.0:5000/chatHub");
app.Run();dotnet run服务器将在 http://0.0.0.0:5000 上运行。
I (0) SIGNALR_EXAMPLE: ==============================================
I (0) SIGNALR_EXAMPLE: ESP32 SignalR Client Test Example
I (0) SIGNALR_EXAMPLE: ==============================================
I (320) SIGNALR_EXAMPLE: ✓ NVS initialized
I (330) SIGNALR_EXAMPLE: Step 1: Initializing WiFi...
I (2450) SIGNALR_EXAMPLE: ✓ Connected to WiFi SSID: MyWiFi
I (2455) SIGNALR_EXAMPLE: Got IP address: 192.168.1.105
I (3460) SIGNALR_EXAMPLE: Step 2: Initializing SignalR...
I (3462) SIGNALR_EXAMPLE: ✓ SignalR connection object created
I (3465) SIGNALR_EXAMPLE: Step 3: Setting up message handlers...
I (3470) SIGNALR_EXAMPLE: ✓ Registered handler: ReceiveMessage
I (3475) SIGNALR_EXAMPLE: ✓ Registered handler: Notification
I (3480) SIGNALR_EXAMPLE: ✓ Registered handler: UpdateSensorData
I (3485) SIGNALR_EXAMPLE: Step 5: Starting SignalR connection...
I (4520) SIGNALR_EXAMPLE: ==============================================
I (4520) SIGNALR_EXAMPLE: ✓✓✓ Connected to SignalR Hub! ✓✓✓
I (4525) SIGNALR_EXAMPLE: ==============================================
I (4530) SIGNALR_EXAMPLE: 🔔 Notification: Welcome to SignalR!
I (14530) SIGNALR_EXAMPLE: 📤 Sending message...
I (14532) SIGNALR_EXAMPLE: User: ESP32-Device
I (14535) SIGNALR_EXAMPLE: Message: Test message #1 from ESP32
I (14640) SIGNALR_EXAMPLE: ✓ Message sent successfully!
I (14650) SIGNALR_EXAMPLE: ==============================================
I (14655) SIGNALR_EXAMPLE: 📩 Message received from server:
I (14660) SIGNALR_EXAMPLE: From: ESP32-Device
I (14665) SIGNALR_EXAMPLE: Text: Test message #1 from ESP32
I (14670) SIGNALR_EXAMPLE: ==============================================
// 服务器发送
await Clients.All.SendAsync("ReceiveMessage", "username", "message text");
// ESP32 接收
connection.on("ReceiveMessage", [](const std::vector<signalr::value>& args) {
std::string user = args[0].as_string();
std::string message = args[1].as_string();
// 处理消息
});// ESP32 发送
send_sensor_data("Temperature", 25.5);
// 服务器接收
public async Task UpdateSensor(string sensorId, double value)
{
await Clients.All.SendAsync("UpdateSensorData", sensorId, value);
}// 服务器发送
await Clients.Caller.SendAsync("Notification", "Welcome!");
// ESP32 接收
connection.on("Notification", [](const std::vector<signalr::value>& args) {
ESP_LOGI(TAG, "Notification: %s", args[0].as_string().c_str());
});# C++ 异常支持 (必需)
CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=512
# 主任务堆栈大小
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
# WiFi 缓冲区
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32
CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=32
# 网络套接字
CONFIG_LWIP_MAX_SOCKETS=16如果遇到内存不足,可以启用 PSRAM:
CONFIG_SPIRAM=y
CONFIG_SPIRAM_USE_MALLOC=y- 内存使用: ~45KB RAM (典型)
- 连接时间: 2-4 秒 (包括协商)
- 消息延迟: 50-150ms (取决于网络)
- 吞吐量: ~100 消息/秒 (小消息)
解决方案:
idf.py menuconfig
# 导航到: Component config → Compiler options → Enable C++ exceptions或确保 sdkconfig.defaults 包含:
CONFIG_COMPILER_CXX_EXCEPTIONS=y
检查:
- WiFi SSID 和密码是否正确
- WiFi 信号强度是否足够
- 路由器是否支持 2.4GHz (ESP32 不支持 5GHz)
检查:
- 服务器是否运行:
netstat -an | findstr 5000 - URL 是否正确 (注意 IP 地址和端口)
- 防火墙是否允许连接
- 服务器日志是否有错误
解决方案:
# 启用 PSRAM
CONFIG_SPIRAM=y
CONFIG_SPIRAM_USE_MALLOC=y
# 增加堆栈大小
CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384原因:
- 网络不稳定
- 服务器超时设置太短
- 消息处理耗时过长
解决方案:
- 检查网络质量
- 增加服务器超时时间
- 优化消息处理逻辑
- esp-signalr 库: https://github.com/maker-community/esp-signalr
- 快速开始指南: docs/QUICKSTART.md
- 集成指南: https://github.com/maker-community/esp-signalr/blob/main/INTEGRATION_GUIDE.md
- ESP-IDF 文档: https://docs.espressif.com/projects/esp-idf/
- SignalR 文档: https://learn.microsoft.com/aspnet/core/signalr/
┌──────────────────────────────────────────────────────────┐
│ SignalR 核心协议 (不变) │
│ - Hub 连接, 协商, 握手 │
│ - JSON 协议, 消息路由 │
└──────────────────────────────────────────────────────────┘
↓ 抽象接口
┌──────────────────────────────────────────────────────────┐
│ 平台适配器 (ESP32 实现) │
│ - esp32_websocket_client → esp_websocket_client │
│ - esp32_http_client → esp_http_client │
│ - JSON 适配器 → cJSON │
│ - 调度器适配器 → FreeRTOS │
└──────────────────────────────────────────────────────────┘
-
esp32_websocket_client: WebSocket 传输层
- 包装 ESP-IDF 的
esp_websocket_client - 处理 WebSocket 生命周期事件
- 使用 FreeRTOS EventGroups 进行同步
- 包装 ESP-IDF 的
-
esp32_http_client: HTTP 客户端
- 包装 ESP-IDF 的
esp_http_client - 用于 SignalR 协商阶段
- 支持 GET 和 POST 请求
- 包装 ESP-IDF 的
-
json_adapter: JSON 序列化
- 使用 ESP32 原生的 cJSON
- API 兼容原始 SignalR 协议
- 高效的内存管理
-
signalr_default_scheduler: FreeRTOS 调度器
- 基于 FreeRTOS 任务的线程池
- 5 个可配置的工作线程
- 适当的同步机制
- WiFi 连接: 总是在启动 SignalR 之前检查 WiFi 连接
- 自动重连: 在生产系统中实现自动重连
- 内存监控: 开发期间监控内存使用
- 消息大小: 保持消息负载小 (推荐 < 4KB)
- 错误处理: 为所有回调实现适当的错误处理
- 日志级别: 生产环境中降低日志级别以节省内存
MIT License - 详见 LICENSE 文件
- 基于实现: Microsoft SignalR-Client-Cpp
- ESP32 适配: ESP32 Community
- 平台: Espressif ESP-IDF
如有问题或建议,请:
- 提交 Issue: https://github.com/maker-community/esp-signalr/issues
- 查看文档: https://github.com/maker-community/esp-signalr
祝你使用愉快! 🚀