1、
Serial接口是 Web Serial API的接口,提供了从网页查找和连接串口的属性和方法。注意:
只能在部分支持
Serial并且网站为安全上下文(HTTPS)中可用,或者是本机访问
requestPort----获取授权串口
open-----打开串口
close---关闭串口(串口关闭前,需要释放锁住的流)
cancel---立即退出读取的循环,然后去调用releaseLock,最后调用close方法
releaseLock---Reader和.Writer的释放方法
read---port.readable.getReader()的读取字节数组方法
write---port.writable.getWriter()的写入方法
MySerialPort.js 是封装的一个SerialPort的工具类
-
- export default class MySerialPort {
- constructor() {
- this.state = {
- portIndex: undefined,
- ports: [],
- isOpen: false,
- writeType: 1,
- readType: 1,
- isScroll: true,
- readValue: [],
- status:false,
- //port参数
- baudRate: "9600",
- dataBits: "8",
- stopBits: "1",
- parity: "none",
- flowControl: "none",
- };
- this.keepReading=false;
- this.getPorts = this.getPorts.bind(this);
- this.handleRequestPort = this.handleRequestPort.bind(this);
- this.handleChildrenChange = this.handleChildrenChange.bind(this);
- this.readText = this.readText.bind(this);
- this.writeText = this.writeText.bind(this);
- this.handleClear = this.handleClear.bind(this);
- this.a2hex = this.a2hex.bind(this);
- this.hex2a = this.hex2a.bind(this);
- this.reader={};
- this.closed;
- }
-
- async getPorts() {
- // 获取已授权的全部串口
- let ports = await navigator.serial.getPorts();
- this.setState({
- ports,
- });
- }
- async handleRequestPort() {
- // 请求授权
- try {
- await navigator.serial.requestPort();
- await this.getPorts();
- } catch (e) {
- this.$message.error(e.toString());
- }
- }
- async openPort(portIndex, isOpen,callBack=null) {
- // 打开串口
- let port = this.state.ports[portIndex];
- if (!isOpen) {
- // 关闭串口
- this.keepReading = false;
- this.reader.cancel();
- await this.closed;
- this.handlePortOpen({
- portIndex,
- isOpen,
- });
- } else {
- await port.open({
- baudRate: this.state.baudRate,
- dataBits: this.state.dataBits,
- stopBits: this.state.stopBits,
- parity: this.state.parity,
- flowControl: this.state.flowControl,
- });
- this.handlePortOpen({
- portIndex,
- isOpen,
- });
- this.keepReading = true;
- this.closed=this.readUntilClosed(portIndex,callBack);
- }
- }
- async readUntilClosed(portIndex,callBack=null) {
- let port = this.state.ports[portIndex];
- while (port.readable && this.keepReading) {
- this.reader = port.readable.getReader();
- try {
- while (true) {
- const { value, done } = await this.reader.read();
- if (done) {
- break;
- }
- this.readText(value);
- callBack(value)
- }
- } catch (error) {
- this.$message.error(error.toString());
- } finally {
- this.reader.releaseLock();
- }
- await port.close();
- }
- }
-
- handlePortOpen({ portIndex, isOpen }) {
- // 处理打开串口
- this.setState({
- portIndex,
- isOpen,
- });
- }
- handleChildrenChange(type, value) {
- this.setState({
- [type]: value,
- });
- }
- portWrite(value) {
- return new Promise(async (resolve, reject) => {
- if (!this.state.isOpen) {
- this.$message.error("串口未打开");
- reject();
- return;
- } else {
- let port = this.state.ports[this.state.portIndex];
- const writer = port.writable.getWriter();
- await writer.write(new Uint8Array(value));
- writer.releaseLock();
- resolve(value);
- }
- });
- }
- readText(value) {
- console.log(value, "读取");
- let newValue = this.state.readValue.concat({
- value,
- type: 1,
- });
- this.setState({
- readValue: newValue,
- });
- }
- writeText(value) {
- console.log(value, "写入");
- this.portWrite(value).then((res) => {
- let newValue = this.state.readValue.concat({
- value: res,
- type: 2,
- });
- this.setState({
- readValue: newValue,
- });
- });
- }
- handleClear() {
- this.setState({
- readValue: [],
- });
- }
- componentDidMount() {
- this.getPorts();
- }
- handleState(status) {
- this.setState({
- status,
- });
- }
- setState(obj){
- Object.keys(this.state).forEach(key => {
- if(obj[key]!=undefined){
- this.state[key]=obj[key]
- }
- });
- }
- //字节转字符串
- hex2a(arr) {
- return String.fromCharCode.apply(String,arr);
- }
- //字符转16进制
- a2hex(str) {
- return str.charCodeAt(0);
- }
- }
vue代码:
- <template>
- <div>
- <el-row
- type="flex"
- class="row-bg"
- justify="center"
- v-show="portsList.length == 0"
- >
- <el-col :span="7"
- ><div style="margin-top: 400px">
- <span style="display: block">
- 仅支持Chrome 89+或者Edge 89+浏览器(安全上下文(HTTPS)中可用)
- span>
- <el-button type="primary" @click="obtainAuthorization"
- >授权
- >
- div>
- >
- el-row>
- <el-form
- v-show="portsList.length > 0"
- ref="form"
- :model="form"
- label-width="100px"
- >
- <el-row>
- <el-col :span="6"
- ><div>
- <el-form-item label="串口">
- <el-select
- v-model="form.port"
- filterable
- placeholder="请选择串口"
- :disabled="isDisable"
- >
- <el-option
- v-for="item in portsList"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- >
- el-option>
- el-select>
- el-form-item>
- <el-form-item label="波特率">
- <el-autocomplete
- popper-class="my-autocomplete"
- v-model="form.baudRate"
- :fetch-suggestions="querySearch"
- placeholder="请输入波特率"
- :disabled="isDisable"
- >
- <i class="el-icon-edit el-input__icon" slot="suffix"> i>
- <template slot-scope="{ item }">
- <div class="name">{{ item.value }}div>
- <span class="addr">{{ item.address }}span>
- template>
- el-autocomplete>
- el-form-item>
- <el-form-item label="数据位">
- <el-select
- v-model="form.dataBits"
- placeholder="请选择数据位"
- :disabled="isDisable"
- >
- <el-option label="7" value="7">el-option>
- <el-option label="8" value="8">el-option>
- el-select>
- el-form-item>
- <el-form-item label="停止位">
- <el-select
- v-model="form.stopBits"
- placeholder="请选择停止位"
- :disabled="isDisable"
- >
- <el-option label="1" value="1">el-option>
- <el-option label="2" value="2">el-option>
- el-select>
- el-form-item>
-
- <el-form-item label="校验位">
- <el-select
- v-model="form.parity"
- placeholder="请选择校验位"
- :disabled="isDisable"
- >
- <el-option label="None" value="None">el-option>
- <el-option label="Even" value="Even">el-option>
- <el-option label="Odd" value="Odd">el-option>
- el-select>
- el-form-item>
-
- <el-form-item label="流控制">
- <el-select
- v-model="form.flowControl"
- placeholder="请选择流控制"
- :disabled="isDisable"
- >
- <el-option label="None" value="None">el-option>
- <el-option label="HardWare" value="HardWare">el-option>
- el-select>
- el-form-item>
-
- <el-form-item label="发送区设置">
- <el-radio-group v-model="form.type">
- <el-radio label="1">ASCIIel-radio>
- <el-radio label="2">HEXel-radio>
- el-radio-group>
- el-form-item>
-
- <el-form-item label="发送信息">
- <el-input type="textarea" v-model="form.sendMsg">el-input>
- el-form-item>
-
- <el-form-item>
- <el-button :type="btnType" @click="connectBtn">{{
- btnText
- }}el-button>
- <el-button type="info" @click="obtainAuthorization"
- >新增授权
- >
- <el-button type="primary" @click="sendCommon">发送el-button>
- el-form-item>
- div>
- el-col>
- <el-col :span="15"
- ><div>
- <el-form-item label="电子秤信息">
- <el-input
- type="textarea"
- v-model="form.desc"
- disabled
- :autosize="{ minRows: 25, maxRows: 50 }"
- >el-input>
- el-form-item>
- >el-col>
- el-row>
- el-form>
- div>
- template>
-
- <script>
- import MySerialPort from "./MySerialPort";
- export default {
- data() {
- return {
- input: "",
- keepReading: true,
- form: {
- baudRate: "9600",
- dataBits: "8",
- stopBits: "1",
- parity: "None",
- flowControl: "None",
- desc: "",
- type: "1",
- },
- btnType: "primary",
- btnText: "连接串口",
- restaurants: [],
- portsList: [],
- };
- },
- mounted() {
- navigator.serial.addEventListener("connect", (e) => {
- this.$message.success("设备已连接");
- this.getPorts();
- });
- navigator.serial.addEventListener("disconnect", (e) => {
- this.$message.error("设备已断开");
- });
- this.restaurants = this.loadAll();
- if ("serial" in navigator) {
- this.myserialport = new MySerialPort();
- this.getPorts();
- }
- },
- computed: {
- isDisable() {
- return this.btnType == "danger";
- },
- },
- methods: {
- //接受数据的回调
- callBack(value) {
- this.form.desc = this.myserialport.hex2a(value);
- },
- //连接
- async connectBtn() {
- if (this.btnType == "primary") {
- try {
- await this.myserialport.openPort(this.form.port, true, this.callBack);
- } catch (error) {
- this.$message.error("串口连接失败!请检查串口是否已被占用");
- }
- if (this.myserialport.state.isOpen) {
- this.$message.success("串口连接成功");
- this.btnType = "danger";
- this.btnText = "关闭串口";
- }
- } else {
- this.myserialport.openPort(this.form.port, false, this.callBack);
- this.$message.success("串口关闭成功");
- this.btnType = "primary";
- this.btnText = "连接串口";
- }
- },
- //授权
- async obtainAuthorization() {
- if ("serial" in navigator) {
- console.log("The Web Serial API is supported.");
- if (!this.myserialport) this.myserialport = new MySerialPort();
- try {
- await this.myserialport.handleRequestPort();
- this.$message.success("串口授权成功");
- this.getPortInfo(this.myserialport.state.ports);
- } catch (error) {
- this.$message.warning("未选择新串口授权!");
- }
- } else {
- this.$message.error(
- "当前为HTTP模式或者浏览器版本过低,不支持网页连接串口"
- );
- }
- },
- //串口列表初始化
- getPortInfo(portList) {
- this.portsList = [];
- portList.map((port, index) => {
- const { usbProductId, usbVendorId } = port.getInfo();
- if (usbProductId === undefined || usbVendorId === undefined) {
- this.portsList.push({ label: "未知设备" + index, value: index });
- }
- });
- },
- // 发送
- async sendCommon() {
- if (this.myserialport.state.isOpen) {
- if (this.form.sendMsg.length !== 0) {
- const writeType = this.form.type;
- let value = this.form.sendMsg;
- let arr = [];
- if (writeType == 1) {
- // ASCII
- for (let i = 0; i < value.length; i++) {
- arr.push(this.myserialport.a2hex(value[i]));
- }
- } else if (writeType == 2) {
- // HEX
- if (/^[0-9A-Fa-f]+$/.test(value) && value.length % 2 === 0) {
- for (let i = 0; i < value.length; i = i + 2) {
- arr.push(parseInt(value.substring(i, i + 2), 16));
- }
- } else {
- this.$message.error("格式错误");
- return;
- }
- }
- this.myserialport.writeText(arr);
- } else {
- this.$message.warning("请输入发送的信息");
- }
- } else {
- this.$message.warning("串口处于关闭状态,请连接串口");
- }
- },
- async getPorts() {
- await this.myserialport.getPorts();
- this.getPortInfo(this.myserialport.state.ports);
- },
- querySearch(queryString, cb) {
- var restaurants = this.restaurants;
- var results = queryString
- ? restaurants.filter(this.createFilter(queryString))
- : restaurants;
- // 调用 callback 返回建议列表的数据
- cb(results);
- },
- createFilter(queryString) {
- return (restaurant) => {
- return (
- restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) ===
- 0
- );
- };
- },
- loadAll() {
- return [
- { value: "110" },
- { value: "300" },
- { value: "600" },
- { value: "1200" },
- { value: "2400" },
- { value: "4800" },
- { value: "7200" },
- { value: "9600" },
- { value: "14400" },
- { value: "19200" },
- { value: "28800" },
- { value: "38400" },
- { value: "56000" },
- { value: "57600" },
- { value: "76800" },
- { value: "115200" },
- { value: "230400" },
- { value: "460800" },
- ];
- },
- },
- };
- script>
-
- <style scoped>
- /* /deep/ .el-textarea__inner {
- height: 320px !important;
- width: 80% !important;
- } */
- style>
三:示例截图

授权界面:

授权成功后:

使用串口工具调试发送和接收:
1、使用vspd创建一个对虚拟串口,com1和com2
2、网页的连接com1,sscom连接com2就可以进行通信了

-
相关阅读:
基于粒子群优化的BP神经网络(分类应用) - 附代码
【微信小程序】文章设置
从一次性销售到持续收益:低代码服务商的转型之路
RFID服装管理系统改善零售供应链
【C语言笔记】dll库“__declspec”属性关键字总结,dllexport,dllimport
JD(按关键字搜索商品)API接口
MIME type备忘
js基础算法
vue报错信息汇总
Android学习之路(18) 数据存储与访问
-
原文地址:https://blog.csdn.net/ZhangY1217/article/details/125889000