HarmonyOS鸿蒙Next移动应用有哪个库支持USB OTG 485

HarmonyOS鸿蒙Next移动应用有哪个库支持USB OTG 485 现在我在开发鸿蒙手机软件,打算引入支持USB串口的库,但发现官方提供的是物理层的,我想通过 USB OTG 转485,一端接手机,一端我的传输通信的终端(双向传输),但没有找到具体的库,想请教各位大哥能否提供一些建议?新手小白一枚

当前环境:HarmonyOS 6.0.2 Release SDK, inclusion of OpenHarmony SDK Ohos_sdk_public 6.0.2.130 (API Version 22 Release) as is.

10 回复

开发者您好,若您期望手机设备实现USB转串口并能够通过串口开发指导实现串口能力,请问您是在什么样的业务场景中使用该能力,交互流程是怎样的,在哪一个环节遇到了问题?方便说明能力不满足可能带来的影响:什么时间用到?是否高频?有无三方库可以做到?若提供该能力,是否会造成大工作量返工?请您注意提供的内容不要包含您或第三方的非公开信息,如给您带来不便,敬请谅解。

更多关于HarmonyOS鸿蒙Next移动应用有哪个库支持USB OTG 485的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


这个还真不知道,不过可以提供个思路给你,找个C++的库嵌入到应用内你试试能不能用。

找HarmonyOS工作还需要会Flutter技术的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17,

好的谢谢我试一下

在 HarmonyOS(你这个 API 22/Release SDK 的环境)里,目前没有一个“像 Android usb-serial 那样开箱即用的官方/通用三方库”专门支持 USB OTG 转 RS485。通常要能跑通,关键取决于你用的 OTG→485 转换器在 USB 侧呈现的设备类型

1) 最推荐的硬件选型:买“USB CDC-ACM(虚拟串口类)”的 OTG→485

如果你的 OTG 转 485 适配器在手机上枚举出来是 CDC-ACM(USB Class = Communications/CDC),那你就可以用 HarmonyOS 的 USB Host 能力按“类设备”的方式去做:

  • 枚举 USB 设备 → 找到目标 VID/PID / Interface class
  • requestPermission(用户授权)
  • openDeviceclaimInterface
  • 找到 Bulk IN/OUT endpoint 做双向传输(收发数据)
  • 若是标准 CDC-ACM,再额外用 control transfer 设置串口参数(波特率/数据位/校验/停止位等)

这种方案的优点是:不需要内核/vendor 驱动,应用层就能做数据收发,落地概率最高。

2) 最容易踩坑的情况:CH340 / CP210x / FTDI 这类“专用串口芯片”

市面上很多 USB 转串口/485 用的是:

  • WCH CH340/CH341
  • Silicon Labs CP210x
  • FTDI FT232

它们在 USB 侧往往不是标准 CDC-ACM,而是 vendor-specific。在这种情况下:

  • 你用 USB Host 仍然“看得见设备”
  • 串口参数配置/协议握手需要对应芯片驱动逻辑(等同自己在应用层写驱动)
  • 并且在 HarmonyOS 手机上的权限/接口能力上,未必允许你做到像 Android 那样成熟稳定(尤其涉及一些底层 ioctl/内核驱动能力时)

结论:如果你还没定硬件,尽量不要选这类,否则软件工作量会大很多,且适配风险高。

3) 软件实现建议(不依赖“现成库”的通用路径)

你可以把它当成“USB Bulk 设备通信”来做一个小型串口层:

  • 设备筛选:通过 VID/PID 或 interface class/subclass/protocol 判断是不是你要的 OTG→485。
  • 授权与连接:每次插入后走用户授权流程(USB Host 一般需要用户同意)。
  • 端点通信:Bulk OUT 写数据、Bulk IN 读数据(做线程/异步循环读)。
  • 协议层:你上层自己定义帧(长度/校验/重传),因为 485 链路上经常需要抗干扰与粘包处理。

RS485 本身只是物理层/电气层;你最终收发的数据帧(Modbus RTU 或自定义协议)都在应用层处理。

4) 替代方案(如果你要“稳定+省适配”)

如果你不是强依赖 USB OTG:

  • 终端侧改为 BLE / Wi‑Fi TCP/UDP(手机侧成熟很多,权限/后台也更可控)
  • 或用带网络的 485 网关(手机走 IP,网关转 485),工程上经常更稳

先理解下你问题的意思,是不是想用USB转485适配器连接手机和你的设备。在鸿蒙生态里,可以看看这些鸿蒙的服务库和API,《开发USB服务》《USB管理》《串口管理》结合《Driver Development Kit(驱动开发服务)》,这个《开发适用串口协议的设备驱动》USBSerialDDK提供了一系列主机侧访问设备的接口,包括主机侧打开和关闭接口、串口读写通信等。文档里有开发指导。

希望帮到你:)。

USB OTG 转 RS485 双向通信,优先用官方 serialManager。

你可看下官方的这个文档:[@ohos.usbManager.serial (串口管理)-设备管理-ArkTS API-Basic Services Kit(基础服务)-基础功能-系统 - 华为HarmonyOS开发者](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-serialmanager#serialmanagergetportlist)

基于HarmonyOS的串口管理(serialManager)模块提供完整的USB串口设备操作能力,涵盖权限控制、设备连接、数据传输等核心功能:

获取设备列表

  getPortList(): Readonly<SerialPort>[]

  返回当前连接的串口设备信息(含portIddeviceName):

&nbsp; import { serialManager } from '@kit.BasicServicesKit';

&nbsp; const portList: serialManager.SerialPort[] = serialManager.getPortList();

&nbsp; console.info(`设备列表: ${JSON.stringify(portList)}`);

权限申请

  操作设备前需动态申请权限(应用重启后失效):

&nbsp; // 检查权限

&nbsp; if (!serialManager.hasSerialRight(portId)) {

&nbsp; &nbsp; serialManager.requestSerialRight(portId).then((granted: boolean) => {

&nbsp; &nbsp; &nbsp; if (granted) console.info("授权成功");

&nbsp; &nbsp; &nbsp; else console.error("用户拒绝授权");

&nbsp; &nbsp; });

&nbsp; }

完整流程

// 1. 获取串口设备列表
const ports = serialManager.getPortList();
if (ports.length === 0) {
  console.error('未检测到USB串口设备');
  return;
}
const portId = ports[0].portId; // 取第一个设备

// 2. 申请权限
try {
  const granted = await serialManager.requestSerialRight(portId);
  if (!granted) {
    console.error('串口权限被拒绝');
    return;
  }
} catch (e) {
  console.error('申请权限失败', e);
  return;
}

// 3. 打开串口
try {
  serialManager.open(portId);
  console.info('串口打开成功');
} catch (e) {
  console.error('打开串口失败', e);
  return;
}

// 4. 配置串口参数(RS485常用)
const attr = {
  baudRate: 9600,
  dataBits: 8,
  stopBits: 1,
  parity: 0, // 0:无校验,1:奇校验,2:偶校验
  flowControl: 0 // 0:无流控
};
try {
  serialManager.setAttribute(portId, attr);
  console.info('串口参数设置成功');
} catch (e) {
  console.error('设置参数失败', e);
}

// 5. 发送数据(同步)
const sendData = new Uint8Array([0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x84, 0x0A]);
try {
  const writeLen = serialManager.writeSync(portId, sendData, 2000);
  console.info(`发送成功,长度: ${writeLen}`);
} catch (e) {
  console.error('发送失败', e);
}

// 6. 接收数据(异步循环)
async function readLoop() {
  const buffer = new Uint8Array(1024);
  while (true) {
    try {
      const readLen = await serialManager.read(portId, buffer, 1000);
      if (readLen > 0) {
        const recvData = buffer.slice(0, readLen);
        console.info('接收数据:', Array.from(recvData));
        // 在这里处理RS485数据
      }
    } catch (e) {
      console.error('读取失败', e);
      break;
    }
  }
}
readLoop();

// 7. 关闭(页面销毁时调用)
// serialManager.close(portId);

希望能解决你的问题

import { promptAction } from '@kit.ArkUI';
import { serialManager } from '@kit.';
import { BusinessError } from '@ohos.base';
import { JSON } from '@kit.ArkTS';
import usbManager from '@ohos.usbManager';
import usb from '@ohos.usb'

interface SerialConfig  {
  baudRate: number;
  databits: serialManager.DataBits;
  parity: serialManager.Parity;
  stopbits: serialManager.StopBits;
}

// 固定定义 CH340
const CH340_VID:number = 0x1A86;
const CH340_PID:number  = 0x7523;

@Preview
@Entry
@Component
export struct SerialPortPages {

  // 👇 修复1:补全缺失的核心变量
  @State isOpen: boolean = false;
  @State sendText: string = "";
  @State serialFd: number = -1;
  // USB串口句柄
  @State usbConnection: number = -1;
  @State usbDevices: usbManager.USBDevice[] = [];
  @State logInfo_: string = '';
  // 定义USB串口设备: DeviceName
  @State DeviceName:string = '请选择USB串口设备';
  @State options:Array<SelectOption>=[];

  // 定义串口设备波特率: DeviceBaud
  @State DeviceBaud:string = '115200';
  Baudoptions:Array<SelectOption>=[
    {value:'9600'},
    {value:'19200'},
    {value:'38400'},
    {value:'115200'},
    {value:'230400'}
  ];

  // 定义串口设备数据位:DeviceData
  @State DeviceData:string = '8';
  Dataoptions:Array<SelectOption>=[
    {value:'5'},
    {value:'6'},
    {value:'7'},
    {value:'8'}
  ];

  // 定义串口设备校验位:DeviceParity
  @State DeviceParity:string = 'None';
  Parityoptions:Array<SelectOption>=[
    {value:'None'},
    {value:'Odd'},
    {value:'Even'}
  ];

  // 定义串口设备停止位:DeviceStop
  @State DeviceStop:string = '1';
  Stopoptions:Array<SelectOption>=[
    {value:'1'},
    {value:'2'}
  ];


  async aboutToAppear() {
    this.getUSBDeviceList();
  }

  async getUSBDeviceList() {
    try {
      const devices: Array<usbManager.USBDevice> = usbManager.getDevices();
      this.usbDevices = devices;
      this.logInfo_ += '\n[INFO] 找到 USB 设备数量:' + devices.length;

      if (devices.length === 0) {
        this.logInfo_ += '\n[ERROR] 未找到任何USB设备,请插入CH340';
        return;
      }

      for (let item of devices) {
        this.logInfo_ += `\nVID: ${item.vendorId}  PID: ${item.productId}`;
        if (item.vendorId === CH340_VID && item.productId === CH340_PID) {
          // 修复2:修正串口设备路径
          this.DeviceName = `/dev/${item.name}`;
          this.logInfo_ += '\n✅ 找到 CH340 串口设备';
          this.requestDevicePermission(item.name);
          return;
        }
      }
      this.logInfo_ += '\n[ERROR] 未找到 CH340 设备';
    } catch (e) {
      this.logInfo_ += '\n[ERROR] 获取设备失败:' + JSON.stringify(e);
    }
  }

  // 申请USB权限
  async requestDevicePermission(deviceName: string):Promise<void> {
    try {
      const has: boolean = await usbManager.hasRight(deviceName);
      if (!has) {
        this.logInfo_ += '\n正在申请USB权限...';
        await usbManager.requestRight(deviceName);
        this.logInfo_ += '\n✅ 权限获取成功!';
      } else {
        this.logInfo_ += '\n✅ 已拥有USB权限';
      }
    } catch (e) {
      this.logInfo_ += '\n❌ 权限申请失败';
    }
  }

  // 修复3:重写打开串口(官方API,无报错)
  async openSerialPort(): Promise<void> {
    if (this.isOpen) {
      this.logInfo_ += '\n 串口已打开,请勿重复操作';
      return;
    }
    try {
      // 打开USB设备
      this.usbConnection = usbManager.openDevice(this.DeviceName);
      if (this.usbConnection < 0) throw new Error("打开设备失败");

      // 占用CH340接口
      usbManager.claimInterface(this.usbConnection, USB_INTERFACE, true);
      this.isOpen = true;
      this.logInfo_ += '\n CH340串口连接成功';

      // 启动读取线程
      this.readLoop();
    } catch (e: unknown) {
      this.logInfo_ += '\n 打开失败';
    }
  }

  // 修复4:读取串口数据
  async readLoop() {
    while (this.isOpen && this.serialFd >= 0) {
      try {
        let buf = serialManager.read(this.serialFd, 4096);
        let str = String.fromCharCode(...buf);
        this.logInfo_ += '\n[接收] ' + str;
      } catch (e) {}
    }
  }

  // 修复5:重写关闭串口
  async closeSerialPort() {
    if (!this.isOpen) {
      this.logInfo_ += '\n⚠ 串口未连接';
      return;
    }
    try {
      serialManager.closeSerial(this.serialFd);
      this.isOpen = false;
      this.serialFd = -1;
      this.logInfo_ += '\n✅ 串口已断开';
    } catch (err) {
      this.logInfo_ += '\n❌ 断开失败';
    }
  }

  // 修复6:重写发送指令
  async sendATCmd() {
    if (!this.isOpen) {
      promptAction.showToast({ message: "请先打开串口连接" });
      return;
    }
    if (this.sendText.trim() === '') {
      promptAction.showToast({ message: "请输入指令" });
      return;
    }
    try {
      let cmd = this.sendText + '\r\n';
      let buf = new Uint8Array(cmd.split('').map(c => c.charCodeAt(0)));
      serialManager.write(this.serialFd, buf);
      this.logInfo_ += '\n[发送] ' + cmd;
    } catch (err) {
      this.logInfo_ += '\n❌ 发送失败';
    }
  }

  build() {
    Column() {
      Text('串口配置区:')
        .fontSize(17)
        // 修复7:删除报错的对齐方式
        .fontWeight(FontWeight.Bold)

      Divider()
        .width('100%')
        .margin({top:10,bottom:10})

      Row(){
        Text('USB设备:')
          .fontSize(15)
          .width(90)
        Select([{ value: this.DeviceName }])
          .layoutWeight(1)
          .margin({ right: 6 })
        Button('更新串口')
          .width(110)
          .onClick(() => {this.getUSBDeviceList();})
      }
      .width('100%')

      Divider()
        .width('100%')
        .margin({top:10,bottom:10})

      Row() {
        Text('波特率:')
          .fontSize(15)
          .width(90)
        Select([{ value: this.DeviceBaud }])
          .layoutWeight(1)
      }
      .width('100%')

      Divider()
        .width('100%')
        .margin({top:10,bottom:10})

      Row() {
        Text('数据位:')
          .fontSize(15)
          .width(90)
        Select([{ value: this.DeviceData }])
          .layoutWeight(1)
      }
      .width('100%')

      Divider()
        .width('100%')
        .margin({top:10,bottom:10})

      Row() {
        Text('校验位:')
          .fontSize(15)
          .width(90)
        Select([{ value: this.DeviceParity }])
          .layoutWeight(1)
      }
      .width('100%')

      Divider()
        .width('100%')
        .margin({top:10,bottom:10})

      Row() {
        Text('停止位:')
          .fontSize(15)
          .width(90)
        Select([{ value: this.DeviceStop }])
          .layoutWeight(1)
      }
      .width('100%')

      Divider()
        .vertical(false)
        .width('100%')
        .height(10)
        .margin({top:10,bottom:10})

      // 修复8:输入框绑定变量
      Row(){
        TextInput({
          placeholder:"输入AT指令:",
          text: this.sendText
        })
          .onChange((value) => { this.sendText = value })
          .layoutWeight(1)
        Button('发送')
        // 修复9:绑定发送事件
          .onClick(() => { this.sendATCmd() })
      }
      .width('100%')

      Divider()
        .vertical(false)
        .width('100%')
        .height(10)
        .margin({top:10,bottom:10})

      // 修复10:绑定按钮点击事件
      Row({ space: 8 }) {
        Button('打开连接')
          .layoutWeight(1)
          .onClick(() => { this.openSerialPort() });

        Button('断开连接')
          .layoutWeight(1)
          .onClick(() => { this.closeSerialPort() })
      }

      Divider()
        .width('100%')
        .margin({top:10,bottom:10})
    }
    .height('100%')
    .width('100%')
    .padding(16)
    .backgroundColor('#E6E6FA')
  }
}

在HarmonyOS Next中,通过@ohos.serialport(串口管理模块)可支持USB OTG 485通信。该库提供串口打开、配置、数据收发等接口,适配RS-485协议。无需额外第三方库。,

HarmonyOS 没有专门的“485库”,因为市面上的 USB OTG 转 485 模块普遍遵循 USB CDC ACM 协议,在系统中被识别为标准 USB 串口设备。您可以直接使用系统提供的 USB Host API(@ohos.usbManager)进行通信,无需第三方库。

核心流程:

  1. 声明权限 ohos.permission.USB_MANAGE
  2. 通过 usbManager.getDevices() 获取设备列表,按 VID/PID 或类别过滤。
  3. 调用 usbManager.requestRight() 申请设备访问权限。
  4. 打开设备,获取 USBInterface 及对应的 USBEndpoint(通常有一个批量输入和一个批量输出端点)。
  5. 使用 usbManager.bulkTransfer() 进行双向数据收发。
  6. 若需控制 485 方向(半双工),通常通过控制传输设置 RTS/DTR 信号,可在设备初始化时完成。

整个过程就是标准的 USB 串口读写,官方 USB Host 能力已足够覆盖 OTG 转串口的全部需求。

回到顶部