使用 JavaScript,HTML 和 CSS 构建跨平台的桌面应用程序
只要你会javascript html css 就可以构建自己想要做的PC桌面和MACos app 应用,是不是很强大。
今天的重点是通过它来实现串口通信的功能,想要实现这部分功能不得不做些准备工作
下面跟我一步一步的来操作吧
想构建electron 必须要有支持的基础环境,node 和 npm
node想必大家并不陌生,前端的小伙伴太熟悉不过了,Node.js 就是运行在服务端的 JavaScript
检测你的电脑环境中是否安装了node.js
检测是否安装node,的命令是
node -v
我这里是win10 开发环境
打开命令行工具
我这里已经安装过了,看到有版本信息v10.16.1 说明已经安装成功
接下来再检查下是否安装了npm 工具
npm -v
我这里也已经安装了npm ,显示版本6.9.0
有的同学小伙伴不知道npm是什么
PS:是nodejs内置的软件包管理器, 在项目开发中,需要用到说明包就拿这个下载就行了,下面有介绍
好了,有了基础的环境,我们就开始构建一个桌面程序吧
在工作的根目录创建一个文件夹eletest
在创建一个普通的index.html 文件,这样就有了一个基本的前端界面,electron 在node.js基础上构建的,下面是应用的基本目录结构,我们已经创建了index.html
eletest/
├── package.json
├── main.js
└── index.html
mian.js文件也是electron的入口文件
const electron = require('electron')
// Module to control application life.
const app = electron.app
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow
const path = require('path')
const url = require('url')
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow
function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 1920,
height: 1080,
frame:false,
resizable: false,
fullscreen:true,
webPreferences: {
nodeIntegration: true,
// preload: path.join(__dirname, 'preload.js')
}
})
// and load the index.html of the app.
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
}))
// Open the DevTools.
mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed', function() {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)
// Quit when all windows are closed.
app.on('window-all-closed', function() {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
app.quit()
})
app.on('activate', function() {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow()
}
})
这里的方法 函数不在过多的解释了,复制代码到你创建的main.js中去就可以了,也可以去electron文档中查看对应的API
package.json 这是一个包构建信息的文件 在eletest文件下运行命令
npm init -y
就会自动生成package.json文件 ,是不是很简单啊
要想运行你写的hml界面 打开这个文件修改一处
"scripts": {
"start": "electron ."
},
这样就完成了几个基本的配置
下面安装electron 包了 运行命令
npm i --save-dev electron
你的运行结果和上面的图片里的信息说明就成功安装了electron 默认安装的最新稳定的依赖包
前期的工作都做完了,来运行它,看看是否出现我们想要的界面
运行命令
npm start
hello world! 是不是很熟悉,很惊喜,很意外。
出现了平时我们打开windows应用窗口
以上步骤都是构建一个electron的桌面应用的,串口是如何实现的呢?
如果你不熟悉串口是说明,先去补补串口的基本概念和相关信息
串口、COM口是指的物理接口形式(硬件)
你也可以打开设备管理器看到相应的串口,我这里有COM11和COM10 ,串口是成对出现的
了解了说明是串口后,来实现我们的应用串口通信吧
运行命令
npm install serialport
出现serialport 的版本信息 说明已经安装成功
electron 通信或者一些交互都是在node上完成的
查看了文档后 我们可以在html页面上
引入serialport包
设置要监听的串口端口 比如COM11
配置写端口基本信息
serialPost.on 接收发过来的信息,如果在控制台上打印出信息,就说明串口通信成功
再次运行electron npm start
打印控台看到 信息:打印端口成功,正在监听数据中,就说明实现了串口的通信最重要一部打开通道
为了验证是否能通信,我们找个串口精灵 发送一写信息 ,再次看控制台收到了发送的信息
如图 在测试串口工具中输入aaaa, 运行的控制台收到了aaaa ,说明已经成功实现串口通信。
是不是很简单,是不是很惊喜,是不是你在今后项目当中有需要串口通信的就可以复制粘贴了。
本文将使用 nodejs 的 SerialPort 包来实现串口通讯功能。 Node SerialPort 是一个 JavaScript 库,用于连接到在 NodeJS 和 Electron 中工作的串行端口,以下是准备环境:
本文操作过程来自: https://girishjoshi.io/post/access-serialport-from-electron-application-and-creating-gui-for-micropython-repl-on-esp8266/
文档地址: https://serialport.io/docs/
主对象,使用流式传输支持跨平台的串行端口访问。
为绑定提供的流式接口。
为nodejs、electron提供跨平台的绑定支持。
为测试实现模拟绑定功能。
一个typescript 接口用来实现自己的绑定时使用。
解析器用来对原始的二进制数据转换成自己需要的消息格式。 包含以下解析器包,这里不进行详解: - @serialport/parser-byte-length - @serialport/parser-cctalk - @serialport/parser-delimiter - @serialport/parser-inter-byte-timeout - @serialport/parser-packet-length - @serialport/parser-readline - @serialport/parser-ready - @serialport/parser-regex - @serialport/parser-slip-encoder - @serialport/parser-spacepacket
比较有用的命令行工具,包括: - @serialport/list - @serialport/repl - @serialport/terminal
# Clone the Quick Start repository
$ git clone https://github.com/electron/electron-quick-start
# Go into the repository
$ cd electron-quick-start
# Install the dependencies and run
$ npm install && npm start
npm install --save serialport
因为选择npm版本不同,这里要对库进行重编译,要先安装 electron-rebuild 工具。
npm install --save-dev electron-rebuild
.\node_modules\.bin\electron-rebuild
npm rebuild
注意原文路径中用的 / , 我在windows 系统,改用 \。
npm install node-gyp electron electron-rebuild serialport --build-from-source
./node_modules/.bin/electron-rebuild
npm start
修改 WebPreferences 如下:
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: false
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Electron test serialport</title>
</head>
<body>
<h1>Serial terminal</h1>
<script>
//load serialport module
const {SerialPort} = require('serialport');
SerialPort.list().then(_=>{console.info(_);});
const serialPort = new SerialPort({path:'COM3', baudRate: 115200}, function (err) {
if(err) {
console.error(err);
}
});
console.info(serialPort);
serialPort.write("abc", (err)=>{
if (err) {
return console.log('Error on write: ', err.message)
}
console.log('message written')
});
serialPort.on('error', function(err) {
console.log('Error: ', err.message)
});
serialPort.on('data', function (data){
console.info('data', data);
});
</script>
<script>
require('./renderer.js')
</script>
</body>
</html>
npm start
项目结构:
在这里插入图片描述
在执行electron-rebuild时,可能需要安装windows-build-tools。新版本的windows-build-tools支持Python3.*版本,但使用Python2.7比较保险。
如果本机没有Python2.7,则可以使用MiniAnaconda配置虚拟环境 。 先下载安装MiniAnaconda工具,然后用命令行创建虚拟环境 :
conda create -n py27 python=2.7
conda activate py27
直接安装windows-build-tools大概率会失败,可按以下流程操作:
在执行npm install -g node-gpy后使用命令:
npm install -g --production windows-build-tools@4.0.0
更多关于serialport的功能可参考官方文档。
串口调试助手是最核心的当然是串口数据收发与显示的功能,pzh-py-com借助的是pySerial库实现串口收发功能,今天痞子衡为大家介绍pySerial是如何在pzh-py-com发挥功能的。
pySerial是一套基于python实现serial port访问的库,该库的设计者为Chris Liechti,该库从2001年开始推出,一直持续更新至今,pzh-py-com使用的是pySerial 3.4。
pySerial的使用非常简单,可在其官网浏览一遍其提供的API: https://pythonhosted.org/pyserial/pyserial_api.html,下面痞子衡整理了比较常用的API如下:
class Serial(SerialBase):
# 初始化串口参数
def __init__(self, *args, **kwargs):
# 打开串口
def open(self):
# 关闭串口
def close(self):
# 获取串口打开状态
def isOpen(self):
# 设置input_buffer/output_buffer大小
def set_buffer_size(self, rx_size=4096, tx_size=None):
# 获取input_buffer(接收缓冲区)里的byte数据个数
def inWaiting(self):
# 从串口读取size个byte数据
def read(self, size=1):
# 清空input_buffer
def reset_input_buffer(self):
# 向串口写入data里所有数据
def write(self, data):
# 等待直到output_buffer里的数据全部发送出去
def flush(self):
# 清空output_buffer
def reset_output_buffer(self):
pySerial常用参数整理如下,需要特别强调的是任何运行时刻对如下参数进行修改,均是直接应用生效的,不需要重新调用open()和close()去激活,因为参数的修改在pySerial内部是通过与参数同名的方法实现的,而这些方法均调用了Serial里的一个叫_reconfigure_port()的方法实现的。
参数名
功能解释
备注/可设值
port
设备名
/dev/ttyUSB0 on GNU/Linux or COM3 on Windows
baudrate (int)
波特率
/
bytesize
数据位bit个数
FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS
stopbits
停止位
STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO
parity
奇偶校验位
PARITY_NONE, PARITY_EVEN, PARITY_ODD PARITY_MARK, PARITY_SPACE
timeout (float)
接收超时
None:blocking mode
0: non-blocking mode
x: 超时时间x秒
write_timeout (float)
发送超时
同timeout
串口功能代码实现主要分为三大部分:配置功能实现、接收功能实现、发送功能实现。在实现这些功能之前首先需要import两个module,分别是serial、threading,serial就是pySerial库;threading是python自带线程库,其具体作用下面代码里会介绍。
除此以外还定义两个全局变量,s_serialPort和s_recvInterval,s_serialPort是串口设备object实例,s_recvInterval是线程间隔时间。
import serial
import threading
s_serialPort = serial.Serial()
s_recvInterval = 0.5
串口配置里主要就是实现GUI界面上"Open"按钮的回调函数,即openClosePort(),软件刚打开时所有可用Port默认是Close状态,如果用户选定了配置参数(串口号、波特率...),并点击了"Open"按钮,此时便会触发openClosePort()的执行,在openClosePort()里我们需要配置s_serialPort的参数并打开指定的串口设备。
class mainWin(win.com_win):
def setPort ( self ):
s_serialPort.port = self.m_textCtrl_comPort.GetLineText(0)
def setBaudrate ( self ):
index = self.m_choice_baudrate.GetSelection()
s_serialPort.baudrate = int(self.m_choice_baudrate.GetString(index))
def setDatabits ( self ):
# ...
def setStopbits ( self ):
# ...
def setParitybits ( self ):
# ...
def openClosePort( self, event ):
if s_serialPort.isOpen():
s_serialPort.close()
self.m_button_openClose.SetLabel('Open')
else:
# 获取GUI配置面板里的输入值赋给s_serialPort
self.setPort()
self.setBaudrate()
self.setDatabits()
self.setStopbits()
self.setParitybits()
# 打开s_serialPort指定的串口设备
s_serialPort.open()
self.m_button_openClose.SetLabel('Close')
s_serialPort.reset_input_buffer()
s_serialPort.reset_output_buffer()
# 开启串口接收线程(每0.5秒定时执行一次)
threading.Timer(s_recvInterval, self.recvData).start()
上述代码里需要特别讲一下的是串口接收线程,我们知道串口设备s_serialPort一旦打开之后,只要该串口设备的RXD信号线上有数据传输,pySerial底层会自动将其存入s_serialPort对应的input_buffer里,但并不会主动通知我们。那我们怎么知道input_buffer里有没有数据?此时就需要我们开启一个定时执行的线程,线程里会去查看input_buffer里是否有数据,如果有数据便显示出来,因此在串口设备打开的同时我们需要创建一个串口接收线程recvData()。
串口接收功能其实在串口配置里已经提到了,主要就是串口接收线程recvData()的实现,recvData()实现很简单,只有一个注意点,那就是threading.Timer()的用法,这是个软件定时器,它只能超时触发一次任务的执行,如果想让任务循环触发,那么需要在任务本身里添加threading.Timer()的调用。
def clearRecvDisplay( self, event ):
self.m_textCtrl_recv.Clear()
def setRecvFormat( self, event ):
event.Skip()
def recvData( self ):
if s_serialPort.isOpen():
# 获取input_buffer里的数据个数
num = s_serialPort.inWaiting()
if num != 0:
# 获取input_buffer里的数据并显示在GUI界面的接收显示框里
data = s_serialPort.read(num)
self.m_textCtrl_recv.write(data)
# 这一句是线程能够定时执行的关键
threading.Timer(s_recvInterval, self.recvData).start()
串口发送功能相比串口接收功能就简单多了,串口发送主要就是实现GUI界面上"Send"按钮的回调函数,即sendData(),代码实现比较简单,不予赘述。
def clearSendDisplay( self, event ):
self.m_textCtrl_send.Clear()
def setSendFormat( self, event ):
event.Skip()
def sendData( self, event ):
if s_serialPort.isOpen():
# 获取发送输入框里的数据并通过串口发送出去
lines = self.m_textCtrl_send.GetNumberOfLines()
for i in range(0, lines):
data = self.m_textCtrl_send.GetLineText(i)
s_serialPort.write(str(data))
else:
self.m_textCtrl_send.Clear()
self.m_textCtrl_send.write('Port is not open')
目前串口收发与显示实现均是基于字符方式,即发送输入框、接收显示框里仅支持ASCII码字符串,关于Char/Hex显示转换的功能(setRecvFormat()/setSendFormat())并未加上,后续优化里会进一步做。
至此,串口调试工具pzh-py-com诞生之串口功能实现便介绍完毕了
*请认真填写需求信息,我们会在24小时内与您取得联系。