更新记录

1.0.0(2026-01-07)

  • 支持hex、字符串收发

平台兼容性

uni-app(4.31)

Vue2 Vue3 Chrome Safari app-vue app-nvue app-nvue插件版本 Android Android插件版本 iOS 鸿蒙
- - × × - 1.0.0 7.0 1.0.0 × ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 快应用-华为 快应用-联盟
× × × × × × × × × - -

uni-app x(4.31)

Chrome Safari Android Android插件版本 iOS 鸿蒙 微信小程序
× × 7.0 1.0.0 × × ×

biming-USBCH340

一个基于 CH34xUARTDriver 实现的 Android USB 转串口通信插件。

平台支持

  • [x] Android
  • [x] nvue、uvue

使用方法

UVUE 示例

<template>
  <view style="overflow-y: scroll;height: 100vh">
    <view class="log-view">
      <text v-for="(log, index) in logs" :key="index">{{ log }}</text>
    </view>

    <button @click="getUSBList">获取USB列表</button>
    <view v-if="usbDevices.length > 0">
      <radio-group @change="onDeviceChange">
        <label v-for="(device, index) in usbDevices" :key="index">
          <radio :value="index.toString()" />
          <text>{{ device }}</text>
        </label>
      </radio-group>
    </view>

    <button @click="openDevice" :disabled="selectedDeviceIndex < 0">打开设备</button>
    <button @click="configDevice" :disabled="!isDeviceOpen">配置串口</button>
    <view>
      <input v-model="hexInput" placeholder="输入16进制数据" />
      <button @click="sendData" :disabled="!isDeviceOpen">发送Hex数据</button>
    </view>
    <view>
      <input v-model="stringInput" placeholder="输入字符串数据" />
      <button @click="sendStringData" :disabled="!isDeviceOpen">发送字符串数据</button>
    </view>
    <button @click="readData" :disabled="!isDeviceOpen">读取Hex数据</button>
    <button @click="readStringData" :disabled="!isDeviceOpen">读取字符串数据</button>
    <button @click="closeDevice" :disabled="!isDeviceOpen">关闭设备</button>
  </view>
</template>

<script lang="ts">
import {
  getUSBList,
  openPort,
  setSerialParameter,
  syncWriteHexStringData,
  syncWriteStringData,
  readData,
  readStringData,
  disconnect,
  registerDataCallback,
  registerStringDataCallback,
  removeDataCallback,
  isConnected
} from "@/uni_modules/biming-USBCH340";
import { Looper, Handler } from "android.os";

export default {
  data() {
    return {
      logs: [] as string[],
      usbDevices: [] as string[],
      selectedDeviceIndex: 0,
      isDeviceOpen: false,
      hexInput: '55030214',
      stringInput: 'Hello World',
      mainHandler: null as Handler | null
    };
  },
  created() {
    this.mainHandler = new Handler(Looper.getMainLooper());
  },
  methods: {
    runOnUI(action: () => void) {
      if (this.mainHandler != null) {
        this.mainHandler!.post(action);
      }
    },
    addLog(log: string) {
      // 使用 Handler 确保在主线程更新 UI,避免多线程并发修改 logs 导致 ConcurrentModificationException
      this.runOnUI(() => {
        this.logs.unshift(log);
        // 限制日志数量,防止列表过长
        if (this.logs.length > 200) {
          this.logs.pop();
        }
        console.log(log);
      });
    },
    getUSBList() {
      this.addLog("正在获取USB设备列表...");
      getUSBList(
        (result) => {
          this.runOnUI(() => {
            this.addLog("获取USB列表成功");
            this.usbDevices = [];
            if (result != null) {
              // In UTS/Kotlin, 'values' is a property on a Map that returns a Collection
              for (const value of result.values) {
                this.usbDevices.push(value as string);
              }
            }
            if (this.usbDevices.length > 0) {
              this.addLog(`发现 ${this.usbDevices.length} 个设备`);
            } else {
              this.addLog("未发现USB设备");
            }
          });
        },
        (error) => {
          this.addLog(`获取USB列表失败: ${error}`);
        }
      );
    },
    onDeviceChange(event: Record<string, any>) {
      // this.selectedDeviceIndex = parseInt(event['detail'].value);
      this.addLog(`选择了设备: ${this.usbDevices[this.selectedDeviceIndex]}`);
    },
    openDevice() {
      if (this.selectedDeviceIndex < 0) {
        this.addLog("请先选择一个设备");
        return;
      }
      this.addLog(`正在打开设备 ${this.selectedDeviceIndex}...`);
      openPort(this.selectedDeviceIndex, (result, message) => {
        this.runOnUI(() => {
          this.addLog(`打开设备结果: ${result}, ${message}`);
          if (result) {
            this.isDeviceOpen = true;
            this.registerCallback();
          }
        });
      });
    },
    configDevice() {
      this.addLog("正在配置串口...");
      // 波特率: 9600, 数据位: 8, 停止位: 1, 校验位: 0 (None)
      setSerialParameter(this.selectedDeviceIndex, 0, 115200, 8, 1, 0, false, (result, message) => {
        this.addLog(`配置串口结果: ${result}, ${message}`);
      });
    },
    sendData() {
      this.addLog(`正在发送Hex数据: ${this.hexInput}`);
      syncWriteHexStringData(this.selectedDeviceIndex, 0, this.hexInput, 1000, (length, message) => {
        this.addLog(`发送Hex数据结果: 长度 ${length}, ${message}`);
      });
    },
    sendStringData() {
      this.addLog(`正在发送字符串数据: ${this.stringInput}`);
      syncWriteStringData(this.selectedDeviceIndex, 0, this.stringInput, 1000, (length, message) => {
        this.addLog(`发送字符串数据结果: 长度 ${length}, ${message}`);
      });
    },
    readData() {
      this.addLog("正在读取Hex数据...");
      readData(this.selectedDeviceIndex, 0, 100, (data, length) => {
        if (length > 0) {
          let hexString = '';
          for (let i:Int = 0; i < length; i++) {
            let hex = (data[i] & 0xFF).toString(16);
            if (hex.length === 1) {
              hex = '0' + hex;
            }
            hexString += hex.toUpperCase() + ' ';
          }
          this.addLog(`读取到Hex数据: ${hexString}`);
        } else {
          this.addLog("未读取到Hex数据");
        }
      });
    },
    readStringData() {
      this.addLog("正在读取字符串数据...");
      readStringData(this.selectedDeviceIndex, 0, 100, (data, length) => {
        if (length > 0) {
          this.addLog(`读取到字符串数据: ${data}`);
        } else {
          this.addLog("未读取到字符串数据");
        }
      });
    },
    registerCallback() {
      this.addLog("注册数据回调...");
      // 注册Hex数据回调
      /*registerDataCallback(this.selectedDeviceIndex, (data, length) => {
        let hexString = '';
        for (let i:Int = 0; i < length; i++) {
          let hex = (data[i] & 0xFF).toString(16);
          if (hex.length === 1) {
            hex = '0' + hex;
          }
          hexString += hex.toUpperCase() + ' ';
        }
        this.addLog(`[Hex Callback] 收到数据: ${hexString}`);
      }, (error) => {
        this.addLog(`注册Hex回调失败: ${error}`);
      });*/

      // 注册字符串数据回调 (注意:通常只需要注册一种回调,这里为了演示都注册了,实际使用请根据需求选择)
      registerStringDataCallback(this.selectedDeviceIndex, (data, length) => {
        this.addLog(`[String Callback] 收到数据: ${data}`);
      }, (error) => {
        this.addLog(`注册String回调失败: ${error}`);
      });
    },
    closeDevice() {
      this.addLog("正在关闭设备...");
      removeDataCallback(this.selectedDeviceIndex, (result, message) => {
        this.addLog(`移除回调结果: ${result}, ${message}`);
      });
      disconnect(this.selectedDeviceIndex, (result, message) => {
        this.runOnUI(() => {
          this.addLog(`关闭设备结果: ${result}, ${message}`);
          this.isDeviceOpen = false;
        });
      });
    },
    checkConnection() {
      if (this.selectedDeviceIndex >= 0) {
        isConnected(this.selectedDeviceIndex, (connected) => {
          this.runOnUI(() => {
            this.isDeviceOpen = connected;
          });
        });
      }
    }
  },
  mounted() {
    // You can automatically get the list on page load if you want
    // this.getUSBList();
  },
  beforeDestroy() {
    if (this.isDeviceOpen) {
      this.closeDevice();
    }
  }
};
</script>

<style>
.log-view {
  height: 200px;
  border: 1px solid #ccc;
  padding: 5px;
  overflow-y: scroll;
  display: flex;
  flex-direction: column-reverse;
}

button {
  margin: 5px;
}

input {
  border: 1px solid #ccc;
  padding: 5px;
  margin: 5px;
}
</style>

API

getUSBList(success, fail)

获取已连接的 USB 设备列表。

  • 参数:
    • success: (result: Map<string, string>) => void: 成功回调,返回设备映射表。
    • fail: (error: string) => void: 失败回调。

openPort(deviceIndex, callback)

打开指定的 USB 设备端口。

  • 参数:
    • deviceIndex: number: 设备索引。
    • callback: (result: boolean, message: string) => void: 操作结果回调。

setSerialParameter(deviceIndex, portIndex, baudRate, dataBit, stopBit, parity, flowControl, callback)

设置串口参数。

  • 参数:
    • deviceIndex: number: 设备索引。
    • portIndex: number: 端口索引(通常为 0)。
    • baudRate: number: 波特率(如 9600, 115200)。
    • dataBit: number: 数据位(如 8)。
    • stopBit: number: 停止位(如 1)。
    • parity: number: 校验位(0: None, 1: Odd, 2: Even, 3: Mark, 4: Space)。
    • flowControl: boolean: 是否开启流控。
    • callback: (result: boolean, message: string) => void: 操作结果回调。

syncWriteHexStringData(deviceIndex, portIndex, hexString, timeout, callback)

同步写入 16 进制字符串数据。

  • 参数:
    • deviceIndex: number: 设备索引。
    • portIndex: number: 端口索引。
    • hexString: string: 16 进制数据字符串。
    • timeout: number: 超时时间(毫秒)。
    • callback: (length: number, message: string) => void: 写入结果回调。

readData(deviceIndex, portIndex, maxLen, callback)

读取数据。

  • 参数:
    • deviceIndex: number: 设备索引。
    • portIndex: number: 端口索引。
    • maxLen: number: 最大读取长度。
    • callback: (data: ByteArray, length: number) => void: 读取结果回调。

registerDataCallback(deviceIndex, success, fail)

注册数据接收回调,用于监听串口数据。

  • 参数:
    • deviceIndex: number: 设备索引。
    • success: (data: ByteArray, length: number) => void: 接收到数据时的回调。
    • fail: (error: string) => void: 注册失败回调。

registerStringDataCallback(deviceIndex, success, fail)

注册字符串数据接收回调,用于监听串口数据,并按 \r\n 分割返回。

  • 参数:
    • deviceIndex: number: 设备索引。
    • success: (data: string, length: number) => void: 接收到完整字符串数据时的回调。
    • fail: (error: string) => void: 注册失败回调。

removeDataCallback(deviceIndex, callback)

移除数据接收回调。

  • 参数:
    • deviceIndex: number: 设备索引。
    • callback: (result: boolean, message: string) => void: 操作结果回调。

disconnect(deviceIndex, callback)

断开设备连接。

  • 参数:
    • deviceIndex: number: 设备索引。
    • callback: (result: boolean, message: string) => void: 操作结果回调。

isConnected(deviceIndex, callback)

检查设备是否已连接。

  • 参数:
    • deviceIndex: number: 设备索引。
    • callback: (connected: boolean) => void: 连接状态回调。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

<uses-permission android:name="com.example.usbch340.USBCH340"/>

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。