树莓派WebGL版Unity项目与串口双向通信的实现方案咨询
方案:Unity WebGL ↔ Node.js Express ↔ Python串口脚本 ↔ Arduino 双向通信
核心架构逻辑
Unity WebGL跑在浏览器沙箱里,没法直接访问本地串口或原生套接字,所以必须搭三层中转链路:
- Unity WebGL 用 WebSocket 和本地Express服务器双向通信(浏览器原生支持WebSocket,Unity也有对应API)
- Express服务器 通过 子进程管道 和Python串口脚本做数据交互(子进程模式轻量,适配树莓派性能)
- Python脚本 直接操作树莓派串口,和Arduino做双向收发
第一步:Unity WebGL 端实现WebSocket通信
用Unity 2020+原生的ClientWebSocket(避免第三方库兼容问题),实现和Express服务器的消息收发:
Unity核心代码示例
using UnityEngine; using System.Net.WebSockets; using System.Threading; using System.Text; using System.Threading.Tasks; public class WebGLSerialLink : MonoBehaviour { private ClientWebSocket _webSocket; private CancellationTokenSource _cts; async void Start() { _webSocket = new ClientWebSocket(); _cts = new CancellationTokenSource(); // 连接本地Express的WebSocket端点 await _webSocket.ConnectAsync(new System.Uri("ws://localhost:3000/serial"), _cts.Token); // 后台启动接收线程,不阻塞渲染 _ = ReceiveSerialMessages(); } // 给Arduino发数据(调用这个方法即可从Unity传数据) public async void SendToArduino(string data) { if (_webSocket.State != WebSocketState.Open) return; byte[] buffer = Encoding.UTF8.GetBytes(data); await _webSocket.SendAsync( new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, _cts.Token ); } // 接收从Arduino传来的数据 private async Task ReceiveSerialMessages() { byte[] buffer = new byte[1024]; while (_webSocket.State == WebSocketState.Open) { var result = await _webSocket.ReceiveAsync( new ArraySegment<byte>(buffer), _cts.Token ); string message = Encoding.UTF8.GetString(buffer, 0, result.Count).Trim(); // 这里把数据传给你的游戏逻辑 Debug.Log($"Received from Arduino: {message}"); } } void OnDestroy() { _cts.Cancel(); _webSocket?.CloseAsync( WebSocketCloseStatus.NormalClosure, "App closed", CancellationToken.None ); } }
第二步:Node.js Express 服务器搭建
用Express托管Unity WebGL文件,同时结合ws库实现WebSocket服务,中转WebGL和Python的数据:
1. 安装依赖
npm install express ws
2. Express核心代码示例(server.js)
const express = require('express'); const WebSocket = require('ws'); const { spawn } = require('child_process'); const path = require('path'); const app = express(); const port = 3000; // 托管Unity WebGL构建文件(把WebGL导出的Build、TemplateData、index.html放到public目录) app.use(express.static(path.join(__dirname, 'public'))); // 创建WebSocket服务器 const wss = new WebSocket.Server({ server: app.listen(port) }); // 启动Python串口处理脚本 const pythonSerial = spawn('python3', [path.join(__dirname, 'serial_handler.py')]); // 处理WebGL客户端连接 wss.on('connection', (ws) => { console.log('WebGL client connected'); // 把WebGL传来的数据转发给Python ws.on('message', (data) => { pythonSerial.stdin.write(data.toString() + '\n'); // 加换行让Python识别行输入 }); // 把Python传来的串口数据转发给WebGL pythonSerial.stdout.on('data', (data) => { ws.send(data.toString().trim()); }); // 打印Python的错误日志 pythonSerial.stderr.on('data', (data) => { console.error('Python Error:', data.toString()); ws.send(`ERROR: ${data.toString().trim()}`); }); ws.on('close', () => { console.log('WebGL client disconnected'); }); }); console.log(`Server running at http://localhost:${port}`);
第三步:Python 串口处理脚本
用pyserial库操作树莓派串口,实现和Arduino的双向通信:
1. 安装依赖
pip install pyserial
2. Python核心代码示例(serial_handler.py)
import serial import sys # 配置串口(树莓派上Arduino通常是/dev/ttyUSB0或/dev/ttyACM0,用ls /dev/tty*确认) ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1) def main(): while True: # 读取Node.js传来的数据,发给Arduino input_msg = sys.stdin.readline().strip() if input_msg: ser.write(f"{input_msg}\n".encode('utf-8')) # 读取Arduino发来的数据,传给Node.js arduino_msg = ser.readline().strip().decode('utf-8') if arduino_msg: print(arduino_msg) sys.stdout.flush() # 强制立即输出,避免缓存 if __name__ == '__main__': try: main() except KeyboardInterrupt: ser.close() sys.exit(0)
部署与测试步骤
- 将Unity WebGL构建的所有文件(index.html、Build、TemplateData)放到Express项目的
public目录下。 - 给树莓派用户串口权限:
sudo usermod -a -G dialout pi,重启生效。 - 连接Arduino到树莓派USB口,确认串口路径正确。
- 启动Express服务器:
node server.js。 - 在树莓派浏览器打开
http://localhost:3000,运行Unity应用测试数据收发。
优化建议
- 数据格式统一用JSON(比如
{"cmd":"move","value":50}),方便两端解析。 - Unity端加WebSocket断连重连逻辑,Python脚本加串口断开自动重连逻辑,提升稳定性。
- 树莓派上可以用
pm2托管Express服务器,实现开机自启和进程守护:pm2 start server.js
内容的提问来源于stack exchange,提问作者Emotibots
相关产品推荐
相关产品推荐

