electron+node+node-serialport nodeJs向串口发送数据,自定义收银客显设备

壤驷华美
2023-12-01

一、前言

electron+node+node-serialport 实现串口通信+electron-builder打包完整流程

上篇文章讲述了electron和serialport的环境搭建,以及打包流程,实现了串口通信,本篇,继续上篇,讲述另一个操作,向串口发送数据,因为最近有个需求,需要收银系统,打通设备上的客显设备,于是有了本篇。

二、了解客显设备

笔者开始也是一抹黑,不知道到底发送了什么给客显,LED上才会显示 找零 总价,数字等,我查了一个客显指令集

上面的指令可以参考,但是我本身这台,我还是不知道,所以我下载了2个软件,一个是客显打印测试,用于向设备发送数据,这个是别人写好的,第二个我下载了一个拦截,监听串口的数据,我就能抓包到,到底发送了什么,一个是打印客显测试 ,一个是串口精灵 当然资源笔者已经上传了,

资源地址

总结:

我最终得到的是,发送的是hex,就是16进制,然后是按一定指令的,所以,如果要开发,需要按以上步骤,抓取指令,

参考:

清屏 :0C,

单价:ESCs1 , 总计 :ESCs2, 收款:ESCs3 找零:ESCs4

数字前缀: 1B5141

发送显示的价格,就是发送16进制的字符,但是有一个默认的前缀,(自己看抓包的数据)

ASCLL码对照表

三、开始操作

操作很简单,主要用的是serialport.write()方法

不过注意一点,就是写的时候,需要转换,为hex 的buffer对象,不然,你传输的数据会被当成ASCLL码,而不是16进制,可以通过对比你在串口中监听的数据来看

index.html

主要步骤,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>

main.js

接受到前端的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)

  })

})

注意事项,如果连接没成功,请先切换端口,到正确的端口,然后刷新页面即可,具体操作,查看上一篇文章

electron+node+node-serialport 实现串口通信+electron-builder打包完整流程

 类似资料: