electron+node-serialport串口通信
公司项目需求给客户开发一个能与电子秤硬件通信的Win7客户端,提供数据读取、处理和显示功能。项目为了兼容win7使用了
electron 22.0.0
版本,串口通信使用了serialport 12.0.0版本
js
//serialport的基本使用方法
//安装 npm i serialport
import { SerialPort } from 'serialport'
SerialPort.list()//获取串口列表
/** 创建一个串口连接
path(必需):串口设备的路径。例如,'/dev/robot' 或 'COM1'。
baudRate(必需):波特率,即每秒传输的比特数。常见值有 9600、19200、38400、57600、115200 等。
autoOpen(可选):是否在创建对象时自动打开串口。默认为 true。如果设置为 false,你需要手动调用 port.open() 来打开串口。
dataBits(可选):每字节的数据位数。可以是 5、6、7、8。默认值是 8。
stopBits(可选):停止位的位数。可以是 1 或 2。默认值是 1。
parity(可选):校验位类型。可以是 'none'、'even'、'odd'、'mark'、'space'。默认值是 'none'。
rtscts(可选):是否启用硬件流控制(RTS/CTS)。布尔值,默认值是 false。
xon(可选):是否启用软件流控制(XON)。布尔值,默认值是 false。
xoff(可选):是否启用软件流控制(XOFF)。布尔值,默认值是 false。
xany(可选):是否启用软件流控制(XANY)。布尔值,默认值是 false。
highWaterMark(可选):用于流控制的高水位标记。默认值是 16384(16KB)。
lock(可选):是否锁定设备文件,防止其他进程访问。布尔值,默认值是 true。
**/
const serialport = new SerialPort({ path: '/dev/example', baudRate: 9600 })
serialport.open()//打开串口
serialport.write('ROBOT POWER ON')//向串口发送数据
serialport.on('data', (data) => {//接收数据
//data为串口接收到的数据
})
获取串口列表
js
//主进程main.ts
import { SerialPort, SerialPortOpenOptions } from 'serialport'
//初始化先获取串口列表,提供给页面选择
ipcMain.on('initData', async () => {
const portList = await SerialPort.list()
mainWindow.webContents.send('initData', {portList})
})
js
//渲染进程
window.electron.ipcRenderer.once('initData', (_,{portList}) => {
//获取串口列表后存入本地,登录页直接做弹窗给客户选择串口,配置波特率
window.localStorage.setItem('portList', JSON.stringify(portList))
})
串口选择
波特率配置
读取数据
公司秤和客户的秤串口配置不一样,所以做了model1和model2区分
js
//主进程main.ts
let P: SerialPort | undefined
ipcMain.on('beginSerialPort', (_, { path, baudRate }) => {
//区分配置
const portConfig: SerialPortOpenOptions<AutoDetectTypes> =
import.meta.env.VITE_MODE == 'model1'
? {
path: path || 'COM1',
baudRate: +baudRate || 9600, //波特率
autoOpen: true,
dataBits: 8
}
: {
path: path || 'COM1',
baudRate: +baudRate || 115200, //波特率
autoOpen: true,
dataBits: 8,
stopBits: 1,
parity: undefined
}
if (P) {
P.close((error) => {
if (error) {
console.log('关闭失败:', error)
} else {
P = new SerialPort(portConfig)
P?.write('SIR\r\n', 'ascii')//告诉秤端开始发送信息,具体看每个秤的配置,有的不需要
P.on('data', (data) => {
//接收到的data为Buffer类型,直接转为字符串就可以使用了
mainWindow.webContents.send('readingData', data.toString())
})
}
})
} else {
P = new SerialPort(portConfig)
P?.write('SIR\r\n', 'ascii')
P.on('data', (data) => {
mainWindow.webContents.send('readingData', data.toString())
})
}
})
解析数据
html
<!--渲染进程解析数据-->
<template>
<div class="weight-con">
<div class="weight-con-main">
<div>
<el-text class="wei-title" type="primary">毛<br />重</el-text>
</div>
<div class="weight-panel">
<el-text id="wei-num">{{ weightNum!.toFixed(2) }}</el-text>
<div class="weight-con-footer">当前最大称重:600公斤</div>
</div>
<div>
<el-text class="wei-title" type="primary">公<br />斤</el-text>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { weightModel } from '@/utils/WeightReader'
const emits = defineEmits(['zeroChange'])
const weightNum = defineModel<number>()
window.electron.ipcRenderer.on('readingData', (_, data: string) => {
//渲染进程接收到主进程数据,根据环境变量解析数据
weightNum.value = weightModel[import.meta.env.VITE_MODE](data)
if (weightNum.value == 0) {
emits('zeroChange')
}
})
</script>
js
//weightReader.ts 解析配置
export type Mode = 'model1' | 'model2'
let str = ''
export const weightModel = {
model1: (weightStr: string) => {
const rev = weightStr.split('').reverse().join('')
return +rev.replace('=', '')
},
module2: (weightStr: string) => {
str += weightStr
if (str.match(/S\s+[A-Za-z]\s*(-?\d*.?\d+)\s*kg/g)) {
const num = str.match(/S\s+[A-Za-z]\s*(-?\d*.?\d+)\s*kg/m)![1]
str = ''
return Number(num)
} else {
return 0
}
}
}
完活~下班!