fix(otto): WebSocket direct clients not receiving MCP responses (#1992)
* Enhance Otto Robot camera support by adding configuration for OV3660. Updated config.h to define camera types and GPIO settings, modified config.json to include new camera options, and refactored otto_robot.cc for improved camera detection and initialization logic. * fix: 移除 OttoEmojiDisplay 构造函数中的 SetTheme 调用以修复 LoadProhibited 崩溃 Made-with: Cursor * refactor: improve audio service error handling and codec timeout management - Updated AudioService to prevent input task termination on read timeout, introducing a delay instead. - Enhanced NoAudioCodec to implement a read timeout for I2S channel reads. - Adjusted WebSocketControlServer to set a control port for improved socket management. - Added manufacturer information to the config.json for waveshare ESP32-Touch-LCD-3.5. * fix(otto): WebSocket direct clients not receiving MCP responses When a browser connects directly to the WebSocket control server (port 8080) and sends a JSON-RPC request, the MCP response was routed through Application::SendMcpMessage -> protocol_->SendMcpMessage, which sends it to the cloud protocol channel. As a result, the direct WebSocket client never received the response, while the WeChat mini-program could because it communicates via the cloud. Fix: - Add BroadcastMessage() to WebSocketControlServer, using httpd_queue_work + httpd_ws_send_frame_async to asynchronously send responses back to all connected clients on port 8080 - Add RegisterMcpBroadcastCallback() to Application, allowing an additional MCP send callback to be registered; SendMcpMessage() now invokes it alongside the cloud protocol - Register the broadcast callback in OttoRobot after the WebSocket server starts successfully Also add WebSocket direct-connect API documentation to README.md with complete JSON-RPC 2.0 command examples.
This commit is contained in:
@ -190,3 +190,61 @@ void WebSocketControlServer::RemoveClient(httpd_req_t *req) {
|
||||
size_t WebSocketControlServer::GetClientCount() const {
|
||||
return clients_.size();
|
||||
}
|
||||
|
||||
struct WsBroadcastJob {
|
||||
httpd_handle_t server;
|
||||
int fd;
|
||||
char* payload;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
static void ws_broadcast_send_job(void* arg) {
|
||||
WsBroadcastJob* job = static_cast<WsBroadcastJob*>(arg);
|
||||
|
||||
httpd_ws_frame_t ws_pkt = {};
|
||||
ws_pkt.type = HTTPD_WS_TYPE_TEXT;
|
||||
ws_pkt.payload = reinterpret_cast<uint8_t*>(job->payload);
|
||||
ws_pkt.len = job->len;
|
||||
ws_pkt.final = true;
|
||||
|
||||
esp_err_t ret = httpd_ws_send_frame_async(job->server, job->fd, &ws_pkt);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE("WSControl", "BroadcastMessage: send failed fd=%d err=%d", job->fd, ret);
|
||||
}
|
||||
|
||||
free(job->payload);
|
||||
free(job);
|
||||
}
|
||||
|
||||
void WebSocketControlServer::BroadcastMessage(const std::string& message) {
|
||||
if (!server_handle_ || clients_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& [fd, req] : clients_) {
|
||||
WsBroadcastJob* job = static_cast<WsBroadcastJob*>(malloc(sizeof(WsBroadcastJob)));
|
||||
if (!job) {
|
||||
ESP_LOGE(TAG, "BroadcastMessage: failed to allocate job");
|
||||
continue;
|
||||
}
|
||||
|
||||
job->server = server_handle_;
|
||||
job->fd = fd;
|
||||
job->len = message.length();
|
||||
job->payload = static_cast<char*>(malloc(message.length() + 1));
|
||||
if (!job->payload) {
|
||||
ESP_LOGE(TAG, "BroadcastMessage: failed to allocate payload");
|
||||
free(job);
|
||||
continue;
|
||||
}
|
||||
memcpy(job->payload, message.c_str(), message.length());
|
||||
job->payload[message.length()] = '\0';
|
||||
|
||||
esp_err_t ret = httpd_queue_work(server_handle_, ws_broadcast_send_job, job);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "BroadcastMessage: httpd_queue_work failed fd=%d err=%d", fd, ret);
|
||||
free(job->payload);
|
||||
free(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user