使用Electron开发串口通信应用详解

在当今的物联网和嵌入式系统开发中,串口通信作为一种经典的通信方式,依然扮演着重要的角色。Electron作为一种跨平台桌面应用开发框架,结合Node.js的能力,使得开发串口通信应用变得异常便捷。本文将深入探讨如何使用Electron开发串口通信的核心交互代码,并提供一个完整的示例。

初始化全局变量和依赖

首先,我们需要定义一些全局变量和引入必要的模块:

globalThis.mainWindow = null

const { SerialPort } = require('serialport')
const config = require('./config.js')
const hexMapTool = require('./tools/hex-map.js')

在这里,mainWindow​用于存储Electron的主窗口对象,SerialPort​是serialport​模块提供的串口操作类,config​包含了串口配置信息,hexMapTool​则用于十六进制与文本之间的映射转换。

创建串口连接

接下来,我们定义一些变量用于管理串口连接:

let port = null
let autoOpenPortInterval = null
const autoOpenPortIntervalTime = config.autoScanPortIntervalTime

port​用于存储串口连接对象,autoOpenPortInterval​用于自动重连的定时器,autoOpenPortIntervalTime​则是重连间隔时间。

打开串口

onOpenPort​函数负责打开串口连接:

function onOpenPort() {
  if(port) {
    try {
      if(port.isOpen) {
        port.close()
      } else {
        port.open()
      }
    } catch(err) {
      console.log('关闭现有端口时出错,忽略并继续')
    }
  } else {
    port = new SerialPort({
      path: config.serialPort,
      baudRate: config.baudRate,
      // 其他配置参数...
    })
  }
}

如果已经存在一个串口连接,尝试关闭它;否则,创建一个新的串口连接。

监听串口事件

我们需要监听串口的打开、数据接收、关闭和错误事件:

port.on('open', () => {
  console.log('串口已打开')
  onSendData({ data: 'A' })
  autoOpenPortInterval && clearInterval(autoOpenPortInterval)
})

port.on('data', (data) => {
  onReceiveData({ data })
})

port.on('close', () => {
  console.log('串口异常,已关闭')
  if(!autoOpenPortInterval) {
    autoOpenPortInterval = setInterval(onOpenPort, autoOpenPortIntervalTime)
    console.log(`将在 ${autoOpenPortIntervalTime / 1000} 秒后尝试重新连接...`)
  }
})

port.on('error', (err) => {
  console.error('串口错误:', err)
  if(err.message && err.message.includes('Access denied')) {
    console.log('串口访问被拒绝,可能被其他程序占用或需要管理员权限')
  }

  try {
    if(port && port.isOpen) {
      port.close()
    }
  } catch(closeErr) {
    console.log('尝试关闭错误的端口失败,继续...')
  }

  if(!autoOpenPortInterval) {
    autoOpenPortInterval = setInterval(onOpenPort, autoOpenPortIntervalTime)
    console.log(`将在 ${autoOpenPortIntervalTime / 1000} 秒后尝试重新连接...`)
  }
})

应用关闭时的处理

在应用关闭时,我们需要正确关闭串口和清除定时器:

process.on('exit', () => {
  if(autoOpenPortInterval) {
    clearInterval(autoOpenPortInterval)
    autoOpenPortInterval = null
  }
  if(port.isOpen) {
    port.close()
    console.log('关闭串口')
  }
})

数据发送与接收

onSendData​函数负责发送数据到串口:

function onSendData({ data: text }) {
  if(!port || !port.isOpen) {
    console.error('串口未打开,无法发送数据')
    return
  }
  const hexString = hexMapTool.onMapTextToHex(text)

  if(!hexString) {
    console.error('无法发送数据:未找到字符对应的十六进制映射')
    return
  }

  try {
    const buffer = Buffer.from(hexString, 'hex')
    port.write(buffer, (err) => {
      if(err) {
        console.error('写入错误:', err)
      } else {
        console.log('数据发送到串口:', hexString)
      }
    })
  } catch(error) {
    console.error('转换十六进制字符串到Buffer失败:', error.message)
  }
}

onReceiveData​函数处理从串口接收到的数据:

function onReceiveData({ data }) {
  const hexString = Buffer.from(data).toString('hex').toUpperCase()

  console.log('数据从串口接收:', data)
  console.log('转换为十六进制:', hexString)

  const text = hexMapTool.onMapHexToText(hexString)

  mainWindow.webContents.send('receive-data', {
    data: text,
    hexString: hexString
  })
}

注入窗口和更新映射

最后,我们提供两个函数用于注入主窗口对象和更新十六进制映射:

function onInjectWindow(win) {
  mainWindow = win
}

function onUpdateHexMapping(newMapping) {
  hexMapTool.onUpdateHexMapping(newMapping)
}

module.exports = {
  onSendData,
  onInjectWindow,
  onUpdateHexMapping
}

总结

通过上述代码,我们实现了一个基于Electron的串口通信核心交互模块。该模块能够自动重连串口,发送和接收数据,并通过十六进制映射工具进行数据转换。在实际应用中,可以根据具体需求进一步扩展和优化该模块,以适应不同的串口通信场景。

希望本文能为你使用Electron开发串口通信应用提供有价值的参考。如果你有任何问题或建议,欢迎在评论区留言交流。

posted @ 2025-04-20 13:02  污斑兔  阅读(158)  评论(0)    收藏  举报