告别命令行:用Electron + SerialPort给你的串口设备做个可视化控制面板
用Electron + SerialPort打造串口设备可视化控制面板
每次调试串口设备都要盯着黑漆漆的命令行窗口,输入晦涩的指令,查看毫无格式的数据流——这种体验简直让人抓狂。想象一下,如果能给这些设备做个漂亮的图形界面,实时显示数据曲线,点点按钮就能发送控制指令,那该多酷?这正是Electron和SerialPort这对黄金组合能带给我们的超能力。
我去年接手过一个农业大棚监控项目,需要实时采集十几个温湿度传感器的数据。最初用Python脚本通过串口读取数据,每次调试都要反复修改代码重启程序。直到改用Electron开发可视化面板,不仅调试效率提升3倍,连不懂技术的农户都能轻松操作。下面我就分享这套让硬件开发变得优雅的解决方案。
1. 为什么需要图形化串口控制界面
传统串口调试工具如Putty、SecureCRT虽然功能强大,但存在几个致命缺陷:首先,纯文本界面无法直观展示数据变化趋势;其次,复杂指令需要记忆或频繁复制粘贴;最重要的是,这些工具无法与现代Web技术生态集成。
典型应用场景:
- 工业控制台需要实时可视化设备状态
- 实验室仪器希望保存历史数据并生成报告
- 智能硬件产品需要友好的用户配置界面
- 物联网网关要同时管理多个串口设备
Electron作为跨平台桌面应用框架,配合SerialPort模块,可以完美解决这些问题。我们既能享受Node.js操作硬件的强大能力,又能使用React/Vue等前端框架构建美观界面,还能打包成各平台可执行文件。
2. 开发环境搭建
2.1 基础工具链配置
先确保系统已安装:
- Node.js 16+ (推荐使用nvm管理多版本)
- Python 3.x (某些原生模块编译需要)
- 对应平台的构建工具:
# Windows npm install --global windows-build-tools # macOS xcode-select --install # Linux sudo apt-get install build-essential
2.2 创建Electron项目
使用官方推荐的项目结构:
mkdir serial-port-dashboard && cd serial-port-dashboard npm init -y npm install electron --save-dev添加基础文件main.js:
const { app, BrowserWindow } = require('electron') let mainWindow app.whenReady().then(() => { mainWindow = new BrowserWindow({ width: 1200, height: 800, webPreferences: { nodeIntegration: true, contextIsolation: false } }) mainWindow.loadFile('index.html') })2.3 集成SerialPort模块
安装核心依赖:
npm install serialport @serialport/parser-readline注意:SerialPort是原生模块,安装时可能遇到编译错误。常见解决方案:
- 检查Python和构建工具是否配置正确
- 尝试
npm install --build-from-source- 使用
node-pre-gyp预编译版本
3. 核心功能实现
3.1 串口通信层设计
创建serialService.js作为通信核心:
const { SerialPort } = require('serialport') const { ReadlineParser } = require('@serialport/parser-readline') class SerialService { constructor() { this.port = null this.parser = null } async listPorts() { return await SerialPort.list() } connect(config) { return new Promise((resolve, reject) => { this.port = new SerialPort(config, err => { if (err) return reject(err) this.parser = this.port.pipe(new ReadlineParser()) resolve({ write: data => this.port.write(data), onData: callback => this.parser.on('data', callback), close: () => this.port.close() }) }) }) } } module.exports = new SerialService()3.2 前端界面开发
使用Vue 3构建控制面板:
<!-- index.html --> <div id="app"> <div class="control-panel"> <select v-model="selectedPort"> <option v-for="port in ports" :value="port.path"> {{ port.path }} - {{ port.manufacturer }} </option> </select> <button @click="connect">连接设备</button> <button @click="sendCommand('ON')">开启设备</button> <line-chart :data="sensorData" /> </div> </div> <script> const { ipcRenderer } = require('electron') export default { data() { return { ports: [], selectedPort: null, sensorData: [] } }, async mounted() { this.ports = await ipcRenderer.invoke('list-ports') ipcRenderer.on('serial-data', (_, data) => { this.sensorData.push({ x: new Date(), y: parseFloat(data) }) }) }, methods: { async connect() { await ipcRenderer.invoke('connect', { path: this.selectedPort, baudRate: 9600 }) }, sendCommand(cmd) { ipcRenderer.send('serial-write', cmd) } } } </script>3.3 进程间通信优化
在主进程(main.js)中添加IPC处理:
const { ipcMain } = require('electron') const serialService = require('./serialService') ipcMain.handle('list-ports', async () => { return await serialService.listPorts() }) ipcMain.handle('connect', (_, config) => { return serialService.connect(config) }) ipcMain.on('serial-write', (_, data) => { serialService.write(data) })4. 高级功能扩展
4.1 数据持久化存储
集成SQLite保存历史数据:
const sqlite3 = require('sqlite3').verbose() const db = new sqlite3.Database('sensor.db', err => { if (err) return console.error(err) db.run(` CREATE TABLE IF NOT EXISTS sensor_readings ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, value REAL ) `) }) function saveReading(value) { db.run('INSERT INTO sensor_readings (value) VALUES (?)', [value]) }4.2 多设备管理
使用串口池管理多个连接:
class SerialPortPool { constructor() { this.connections = new Map() } async addConnection(config) { const id = Symbol(config.path) const connection = await serialService.connect(config) this.connections.set(id, connection) return id } removeConnection(id) { const connection = this.connections.get(id) if (connection) { connection.close() this.connections.delete(id) } } }4.3 打包与分发
使用electron-builder打包应用:
// package.json { "build": { "appId": "com.example.serialdashboard", "win": { "target": "nsis", "icon": "build/icon.ico" }, "mac": { "target": "dmg", "icon": "build/icon.icns" } } }运行打包命令:
npx electron-builder --win --mac5. 实战案例:温湿度监控面板
最近为一个红酒窖开发的监控系统完美展示了这套方案的威力。我们使用DS18B20温度传感器和DHT22湿度传感器,通过Arduino采集数据后发送到Electron面板。
关键实现细节:
- 自定义数据协议:
T:25.4,H:62.3\n - 数据解析逻辑:
parser.on('data', line => { const [temp, hum] = line.split(',') const tempValue = parseFloat(temp.split(':')[1]) const humValue = parseFloat(hum.split(':')[1]) ipcRenderer.send('sensor-update', { temp: tempValue, hum: humValue }) }) - 告警功能实现:
watch(() => state.temperature, (newVal) => { if (newVal > config.maxTemp) { playAlarmSound() sendSMSAlert() } })
这个项目最终打包成Windows和macOS双平台应用,酒庄工作人员通过直观的曲线图随时掌握各区域温湿度,当数值超标时会自动收到手机短信提醒。
