diff --git a/README.md b/README.md index 862e989a..b3d6ff1f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ 本项目基于人机共生智能理论和技术研发智能终端软硬件体系
为开源智能硬件项目 xiaozhi-esp32提供后端服务
根据小智通信协议使用Python、Java、Vue实现
-支持MCP接入点和声纹识别 +支持MQTT+UDP协议、Websocket协议、MCP接入点、声纹识别

@@ -241,7 +241,7 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ ![请参考-全模块安装架构图](docs/images/deploy2.png) | 功能模块 | 描述 | |:---:|:---| -| 核心架构 | 基于WebSocket和HTTP服务器,提供完整的控制台管理和认证系统 | +| 核心架构 | 基于MQTT+UDP网关、WebSocket、HTTP服务器,提供完整的控制台管理和认证系统 | | 语音交互 | 支持流式ASR(语音识别)、流式TTS(语音合成)、VAD(语音活动检测),支持多语言识别和语音处理 | | 声纹识别 | 支持多用户声纹注册、管理和识别,与ASR并行处理,实时识别说话人身份并传递给LLM进行个性化回应 | | 智能对话 | 支持多种LLM(大语言模型),实现智能对话 | diff --git a/docs/FAQ.md b/docs/FAQ.md index 356ff959..50202ac1 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -62,6 +62,7 @@ VAD: ### 7、部署相关教程 1、[如何进行最简化部署](./Deployment.md)
2、[如何进行全模块部署](./Deployment_all.md)
+2、[如何部署MQTT网关开启MQTT+UDP协议](./mqtt-gateway-integration)
3、[如何自动拉取本项目最新代码自动编译和启动](./dev-ops-integration.md)
4、[如何与Nginx集成](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)
diff --git a/docs/mqtt-gateway-integration.md b/docs/mqtt-gateway-integration.md new file mode 100644 index 00000000..68003803 --- /dev/null +++ b/docs/mqtt-gateway-integration.md @@ -0,0 +1,127 @@ +# MQTT 网关部署教程 + +`xiaozhi-esp32-server`项目,可结合虾哥开源的[xiaozhi-mqtt-gateway](https://github.com/78/xiaozhi-mqtt-gateway) 项目进行简单改造,即可实现小智硬件MQTT+UDP连接。 + +## 准备工作 + +查看你智控台首页底部的版本号,确认你的智控台版本是否是`0.7.7`及以上版本。如果不是,需要升级智控台。 + +准备好你的`xiaozhi-server`的`mqtt-websocket`连接地址。在你原来的`websocket地址`基础上,添加`?from=mqtt_gateway`字符,就可以得到`mqtt-websocket`连接地址 + +1、如果你是源码部署,你的`mqtt-websocket`地址是: +``` +ws://127.0.0.1:8000/xiaozhi/v1?from=mqtt_gateway +``` + +2、如果你是docker部署,你的`mqtt-websocket`地址是 +``` +ws://你宿主机局域网IP:8000/xiaozhi/v1?from=mqtt_gateway +``` + +## 第一步 部署MQTT网关 + +1. 克隆[改造后的xiaozhi-mqtt-gateway项目](https://github.com/xinnan-tech/xiaozhi-mqtt-gateway.git): +```bash +git clone https://ghfast.top/https://github.com/xinnan-tech/xiaozhi-mqtt-gateway.git +cd xiaozhi-mqtt-gateway +``` + +2. 安装依赖: +```bash +npm install +npm install -g pm2 +``` + +3. 配置 `config.json`: +```bash +cp config/mqtt.json.example config/mqtt.json +``` + +4. 编辑配置文件 config/mqtt.json,把你在`本文准备阶段`的`mqtt-websocket`地址替换到`chat_servers`里。例如源码部署的`xiaozhi-server`就是如下配置: + +``` +{ + "production": { + "chat_servers": [ + "ws://127.0.0.1:8000/xiaozhi/v1?from=mqtt_gateway" + ] + }, + "debug": false, + "max_mqtt_payload_size": 8192, + "mcp_client": { + "capabilities": { + }, + "client_info": { + "name": "xiaozhi-mqtt-client", + "version": "1.0.0" + }, + "max_tools_count": 128 + } +} +``` +5. 在项目根目录创建下`.env`文件,并设置以下环境变量: +``` +PUBLIC_IP=your-ip # 服务器公网IP +MQTT_PORT=1883 # MQTT服务器端口 +UDP_PORT=8884 # UDP服务器端口 +MQTT_SIGNATURE_KEY=test # MQTT签名密钥 +``` +请注意`PUBLIC_IP`配置,确保其与实际公网IP一致,如果有域名就填域名。 + +`MQTT_SIGNATURE_KEY` 是用于MQTT连接认证的密钥,最好设置成复杂一点的,这个密钥稍后还会用到。 + +6. 启动MQTT网关 +``` +# 启动服务 +pm2 start ecosystem.config.js + +# 查看日志 +pm2 logs xz-mqtt +``` + +当你看到如下日志,说明MQTT网关启动成功: +``` +0|xz-mqtt | 2025-09-11T12:14:48: MQTT 服务器正在监听端口 1883 +0|xz-mqtt | 2025-09-11T12:14:48: UDP 服务器正在监听 x.x.x.x:8884 +``` + +如果需要重启MQTT网关,执行如下命令: +``` +pm2 restart xz-mqtt +``` + +## 第二步 配置智控台 + +1. 在智控台顶部,点击`参数管理`,搜索`server.mqtt_gateway`,点击编辑,填入你在`.env`文件中设置的`PUBLIC_IP`+`:`+`MQTT_PORT`。类似这样 +``` +192.168.0.7:1883 +``` +2. 在智控台顶部,点击`参数管理`,搜索`server.mqtt_signature_key`,点击编辑,填入你在`.env`文件中设置的`MQTT_SIGNATURE_KEY`。 + +3. 在智控台顶部,点击`参数管理`,搜索`server.udp_gateway`,点击编辑,填入你在`.env`文件中设置的`PUBLIC_IP`+`:`+`UDP_PORT`。类似这样 +``` +192.168.0.7:8884 +``` + +上面的配置完成后,你可以使用curl命令,验证你的ota地址是否会下发mqtt配置,把下面的`http://localhost:8002/xiaozhi/ota/`改成你的ota地址 +``` +curl 'http://localhost:8002/xiaozhi/ota/' \ + -H 'Content-Type: application/json' \ + -H 'Client-Id: 7b94d69a-9808-4c59-9c9b-704333b38aff' \ + -H 'Device-Id: 11:22:33:44:55:66' \ + --data-raw $'{\n "application": {\n "version": "1.0.1",\n "elf_sha256": "1"\n },\n "board": {\n "mac": "11:22:33:44:55:66"\n }\n}' +``` + +如果返回的内容包含`mqtt`相关的配置,说明配置成功。类似这样 + +``` +{"server_time":{"timestamp":1757567894012,"timeZone":"Asia/Shanghai","timezone_offset":480},"activation":{"code":"460609","message":"http://xiaozhi.server.com\n460609","challenge":"11:22:33:44:55:66"},"firmware":{"version":"1.0.1","url":"http://xiaozhi.server.com:8002/xiaozhi/otaMag/download/NOT_ACTIVATED_FIRMWARE_THIS_IS_A_INVALID_URL"},"websocket":{"url":"ws://192.168.4.23:8000/xiaozhi/v1/"},"mqtt":{"endpoint":"192.168.0.7:1883","client_id":"GID_default@@@11_22_33_44_55_66@@@7b94d69a-9808-4c59-9c9b-704333b38aff","username":"eyJpcCI6IjA6MDowOjA6MDowOjA6MSJ9","password":"Y8XP9xcUhVIN9OmbCHT9ETBiYNE3l3Z07Wk46wV9PE8=","publish_topic":"device-server","subscribe_topic":"devices/p2p/11_22_33_44_55_66"}} +``` + +## 第三步 重启小智设备 +由于MQTT信息是需要靠OTA地址下发的,因此只有你保证能正常连接服务器的OTA地址,重启唤醒即可。 + +唤醒后留意mqtt-gateway的日志,确认是否有连接成功的日志。 +``` +pm2 logs xz-mqtt +``` \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/device/controller/OTAController.java b/main/manager-api/src/main/java/xiaozhi/modules/device/controller/OTAController.java index 26e5dbe6..4a4bfee8 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/device/controller/OTAController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/device/controller/OTAController.java @@ -78,7 +78,7 @@ public class OTAController { @Hidden public ResponseEntity getOTA() { String mqttUdpConfig = sysParamsService.getValue(Constant.SERVER_MQTT_GATEWAY, false); - if(StringUtils.isBlank(mqttUdpConfig)) { + if (StringUtils.isBlank(mqttUdpConfig)) { return ResponseEntity.ok("OTA接口不正常,缺少mqtt_gateway地址,请登录智控台,在参数管理找到【server.mqtt_gateway】配置"); } String wsUrl = sysParamsService.getValue(Constant.SERVER_WEBSOCKET, true); diff --git a/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java index 66380041..9e452888 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java @@ -184,9 +184,11 @@ public class DeviceServiceImpl extends BaseServiceImpl // 添加MQTT UDP配置 // 从系统参数获取MQTT Gateway地址,仅在配置有效时使用 String mqttUdpConfig = sysParamsService.getValue(Constant.SERVER_MQTT_GATEWAY, false); - if (mqttUdpConfig != null && !mqttUdpConfig.equals("null") && !mqttUdpConfig.isEmpty() && deviceById != null) { + if (mqttUdpConfig != null && !mqttUdpConfig.equals("null") && !mqttUdpConfig.isEmpty()) { try { - DeviceReportRespDTO.MQTT mqtt = buildMqttConfig(macAddress, clientId, deviceById); + String groupId = deviceById != null && deviceById.getBoard() != null ? deviceById.getBoard() + : "GID_default"; + DeviceReportRespDTO.MQTT mqtt = buildMqttConfig(macAddress, clientId, groupId); if (mqtt != null) { mqtt.setEndpoint(mqttUdpConfig); response.setMqtt(mqtt); @@ -478,10 +480,10 @@ public class DeviceServiceImpl extends BaseServiceImpl * * @param macAddress MAC地址 * @param clientId 客户端ID (UUID) - * @param device 设备信息 + * @param groupId 分组ID * @return MQTT配置对象 */ - private DeviceReportRespDTO.MQTT buildMqttConfig(String macAddress, String clientId, DeviceEntity device) + private DeviceReportRespDTO.MQTT buildMqttConfig(String macAddress, String clientId, String groupId) throws Exception { // 从环境变量或系统参数获取签名密钥 String signatureKey = sysParamsService.getValue("server.mqtt_signature_key", false); @@ -490,10 +492,11 @@ public class DeviceServiceImpl extends BaseServiceImpl return null; } - // 构建客户端ID格式:groupId@@@macAddress_without_colon@@@uuid - String groupId = device.getBoard() != null ? device.getBoard() : "GID_default"; - String deviceIdNoColon = macAddress.replace(":", "_"); - String mqttClientId = String.format("%s@@@%s@@@%s", groupId, deviceIdNoColon, clientId); + // 构建客户端ID格式:groupId@@@macAddress@@@uuid + String groupIdSafeStr = groupId.replace(":", "_"); + String deviceIdSafeStr = macAddress.replace(":", "_"); + String clientIdSafeStr = clientId.replace(":", "_"); + String mqttClientId = String.format("%s@@@%s@@@%s", groupIdSafeStr, deviceIdSafeStr, clientIdSafeStr); // 构建用户数据(包含IP等信息) Map userData = new HashMap<>(); @@ -523,7 +526,7 @@ public class DeviceServiceImpl extends BaseServiceImpl mqtt.setUsername(username); mqtt.setPassword(password); mqtt.setPublish_topic("device-server"); - mqtt.setSubscribe_topic("devices/p2p/" + deviceIdNoColon); + mqtt.setSubscribe_topic("devices/p2p/" + deviceIdSafeStr); return mqtt; }