electron+node+node-serialport 实现串口通信+electron-builder打包完整流程
上篇文章讲述了electron和serialport的环境搭建,以及打包流程,实现了串口通信,本篇,继续上篇,讲述另一个操作,向串口发送数据,因为最近有个需求,需要收银系统,打通设备上的客显设备,于是有了本篇。
笔者开始也是一抹黑,不知道到底发送了什么给客显,LED上才会显示 找零 总价,数字等,我查了一个客显指令集
上面的指令可以参考,但是我本身这台,我还是不知道,所以我下载了2个软件,一个是客显打印测试,用于向设备发送数据,这个是别人写好的,第二个我下载了一个拦截,监听串口的数据,我就能抓包到,到底发送了什么,一个是打印客显测试 ,一个是串口精灵 当然资源笔者已经上传了,
总结:
我最终得到的是,发送的是hex,就是16进制,然后是按一定指令的,所以,如果要开发,需要按以上步骤,抓取指令,
参考:
清屏 :0C,
单价:ESCs1 , 总计 :ESCs2, 收款:ESCs3 找零:ESCs4
数字前缀: 1B5141
发送显示的价格,就是发送16进制的字符,但是有一个默认的前缀,(自己看抓包的数据)
操作很简单,主要用的是serialport.write()方法
不过注意一点,就是写的时候,需要转换,为hex 的buffer对象,不然,你传输的数据会被当成ASCLL码,而不是16进制,可以通过对比你在串口中监听的数据来看
主要步骤,sendMessge方法,发送给node前,先把发送的字符串,转成16进制的字符串
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#mess{text-align: center}
</style>
</head>
<body>
<div id="mess">正在连接...</div>
<div id="socket">
</div>
<input type="text" id="inputMessage">
<button onclick="sendMessge()">发送</button>
<div>
<button onclick="buttonChange(1)">总价</button>
<button onclick="buttonChange(2)">找零</button>
<button onclick="buttonChange(3)">清除</button>
</div>
<script>
var arr = [
{type:1,name:"总价",code:"1B7332"},
{type:2,name:'找零',code:"1B7334"},
{type:3,name:'清除',code:"0C"},
{type:4,name:'输入',code:"1B5141"},
]
var mess = document.getElementById("mess");
var socket = document.getElementById("socket");
var inputElement = document.getElementById("inputMessage");
function buttonChange(type){
let code = ''
arr.map(item=>{
if(item.type==type){
code = item.code
}
})
ws.send(code)
}
function sendMessge(){
//发送前先将字符,转成16进制的字符串,并拼接上前缀
var str=''
let c = inputElement.value
for(var i=0;i<c.length;i++){
str += c.charCodeAt(i).toString(16)
}
str = "1B5141" + str
console.log(str);
ws.send(str);
}
if(window.WebSocket){
var ws = new WebSocket('ws://127.0.0.1:27611');
ws.onopen = function(e){
console.log("连接服务器成功");
ws.send("HeartBeat");
}
ws.onclose = function(e){
console.log("服务器关闭");
}
ws.onerror = function(){
console.log("连接出错");
}
ws.onmessage = function(e){
console.log(e.data);
socket.innerHTML ="获取到的数据"+ e.data
mess.innerHTML = "连接成功"
}
}
</script>
</body>
</html>
接受到前端的16进制的字符串,再通过 Buffer.from(),转换成hex的buffer对象,发送给串口
const {
app,
BrowserWindow,
Menu,
MenuItem,
globalShortcut
} = require('electron')
const SerialPort = require('serialport');
const WebSocket = require('ws');
// 引用Server类:
const WebSocketServer = WebSocket.Server;
var portName = 'COM2'; //定义串口名
var serialPort;
var strs = ''
var wss
var COMarr = []
function getPortArr() {
return new Promise((res, rej) => {
// 检索带有元数据的可用串行端口列表
SerialPort.list().then((ports) => {
ports.forEach(function (port) {
COMarr.push(port.comName)
console.log(port.comName);
console.log(port.pnpId);
console.log(port.manufacturer);
res()
});
});
})
}
function startPort() {
if (serialPort) {
try {
serialPort.close();
} catch (err) {
}
}
serialPort = new SerialPort( //设置串口属性
portName, {
baudRate: 2400, //波特率
dataBits: 8, //数据位
parity: 'none', //奇偶校验
stopBits: 1, //停止位
flowControl: false,
autoOpen: false //不自动打开
}, false);
serialPort.on('error', (error) => {
console.log('Error: ', error.message);
})
serialPort.open(function (error) {
if (error) {
console.log("打开端口" + portName + "错误:" + error);
} else {
if (wss) {
wss.close()
}
// 实例化:
wss = new WebSocketServer({
port: 27611
});
wss.on('connection', function (ws) {
console.log("开启连结")
ws.on("message", function (message) {
if (message === "HeartBeat") {
ws.send('连接已打开')
return
}
if(message){
console.log('接受数据' + message);
//将已经转成16进制的字符串,转换成hex的buffer对象
const c = Buffer.from(message,'hex')
serialPort.write(c)
}
})
ws.on("close", function () {
console.log("关闭服务");
})
})
}
});
}
startPort()
//获取对应的elctron版本和对应的node版本,
// console.log("node:",process.versions.node)
// console.log("electron:",process.versions.electron)
// console.log("modules:",process.versions.modules)
/**
* 事件
* 属性
* 方法
* **/
// 创建一个窗口
// 在主进程中.
// 或者从渲染进程中使用 `remote`.
// const { BrowserWindow } = require('electron').remote
app.on('ready', () => {
let win = new BrowserWindow({
width: 1000,
height: 800,
title: "五不像收银系统",
webPreferences: {
nodeIntegration: true
}
})
// 加载页面
win.loadFile('./index.html')
// 加载远程URL
//win.loadURL('http://localhost:9528/#/login')
// 类似浏览器的window 与窗口有关的浏览器内容都是通过下面的属性
// win.webContents
console.log(win.webContents, 1111);
// 配置esc退出全屏
globalShortcut.register('ESC', () => {
console.log('ESC');
win.setFullScreen(false);
})
console.log(globalShortcut.isRegistered('ESC'));
clearTimeout(this.timer)
this.timer = setTimeout(()=>{
win.setFullScreen(true);
},1000);
//打开开发者工具
win.webContents.openDevTools();
getPortArr().then(() => {
// 创建菜单对象
let menu = new Menu();
// 创建菜单项
let submenu = []
COMarr.map(item => {
submenu.push({
type: "normal",
label: item,
click() {
portName = item
strs = ''
startPort()
}
})
submenu.push({
type: "separator", //菜单分割符
}, )
})
let mil = new MenuItem({
type: "submenu",
label: '切换端口',
submenu: submenu
})
let mil2 = new MenuItem({
type: "submenu",
label: '功能',
submenu: [{
role: "forcereload",
label: "刷新",
},
{
type: "separator", //菜单分割符
},
{
role: "togglefullscreen",
label: "全屏",
},
{
type: "separator", //菜单分割符
},
{
role: "minimize",
label: "最小化",
},
{
type: "separator", //菜单分割符
},
{
label: '开发者工具',
accelerator: (function() {
if (process.platform == 'darwin')
return 'Alt+Command+I';
else
return 'Ctrl+Shift+I';
})(),
click: function(item, focusedWindow) {
if (focusedWindow)
focusedWindow.toggleDevTools();
}
},
{
type: "separator", //菜单分割符
},
{
role: "quit",
label: "退出",
},
]
})
//把菜单添加到指定的菜单对象
menu.append(mil)
menu.append(mil2)
Menu.setApplicationMenu(menu)
})
})
注意事项,如果连接没成功,请先切换端口,到正确的端口,然后刷新页面即可,具体操作,查看上一篇文章