一、项目背景:
因信创国产替代需求,公司准备慢慢向国产化转型,开始国产化的技术积累,需要一款国产移动终端设备替代原先的安卓终端,产品形态是安卓终端+STM32单片机结合的一款产品,选型国替代方案首先考虑到鸿蒙和星光麒麟,后面选择了鸿蒙,鸿蒙生态丰富,可跨端,不过OpenHarmony距离真正成熟商用还需要时间。
二、项目方案:
前期用华为P40 Pro harmony 4.2+STM32,通过USB Type-C的方式进行连接,手机做为Host,单片机做为Devices,通过OTG的转接进行连接。
三、开发过程中问题点:
(1)开发过程发现用ArkTS+ArkUi开发的鸿蒙应用,跑在P40上面界面卡顿、黑屏,用官网给出的例程也存在同样问题,在DevEco编译器中模拟器运行鸿蒙应用比较流畅,华为官网论坛开发者也有遇到这个问题,原因分析是跟低版本手机CPU兼容问题导致屏幕没刷新,目前还未完全适配低版本机型,可以用meta50或60系列手机调试进行规避。
(2)向公司申请meta60手机进行开发调试,应用运行卡顿、黑屏问题得到解决,但在开发USB的过程,可获取接入主设备的USB设备列表并打开USB设备,也可访问USB权限,但USBInterface获取不到,USBInterface包含USB输入和输出端点,通过端点才能读写数据进行传输,后面提交工单华为客服工程师建议升级next版本,然后升级next版本得到解决。
(3)HarmonyOS 的API版本管理策略存在不强制向下兼容的特点,不同设备搭载的 HarmonyOS 版本可能差异较大(如P30 pro用手机API 7,P40 pro用 API 9),且官网开发文档更新迭代较快,需自行根据实际开发调试的设备进行版本兼容适配。
建议开发者在开发前尽量用HarmonyOS next版本的终端进行开发工作,初次接触遇到的主要问题就是以上3点,后面项目是基于mate 60 HarmonyOS next 5.0.0进行开发,可以规避用4.2及以下双架构安卓和HarmonyOS系统的很多坑。
四、代码部分
参照官方文档https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/usb-guidelines-V5进行开发,代码如下:
import { BusinessError, systemDateTime, usbManager } from '@kit.BasicServicesKit';
import { Logger } from '@nzy/logger';
import { BasePacket } from '../parse/BasePacket';
import { McException } from '../rsu/McException';
import StringUtils from '../utils/StringUtils';
import UsbHostConfig from './UsbHostConfig';
/**
* usb管理类
*/
class UsbDeviceManger {
getUsbDevicesList() {
// 获取设备列表。
let deviceList: Array<usbManager.USBDevice> = usbManager.getDevices();
if (deviceList.length === 0) {
Logger.error("usb deviceList:" + deviceList);
throw new McException(UsbHostConfig.MY_USB_LIST_NULL, undefined, undefined);
}
Logger.error("usb deviceList length:" + deviceList.length);
let deviceName: string = deviceList[1].name;
Logger.error("usb deviceName:" + deviceName);
// 申请操作指定的device的操作权限。
usbManager.requestRight(deviceName).then((hasRight: boolean) => {
Logger.info("usb device request right result: " + hasRight);
this.openUsbDevice(deviceList);
}).catch((error: BusinessError) => {
Logger.error("usb device request right failed : " + error);
throw new McException(UsbHostConfig.MY_USB_REQ_RIGHT_FAILED, undefined, undefined);
});
}
private pipe?: usbManager.USBDevicePipe;
private interface1?: usbManager.USBInterface;
openUsbDevice(deviceList: Array<usbManager.USBDevice>) {
// 打开设备,获取数据传输通道。
this.pipe = usbManager.connectDevice(deviceList[1]);
this.interface1 = deviceList[1].configs[0].interfaces[0];
/*
打开对应接口,在设备信息(deviceList)中选取对应的interface。
interface1为设备配置中的一个接口。
*/
usbManager.claimInterface(this.pipe, this.interface1, true);
}
async sendData(cmd: Uint8Array): Promise<number> {
let sendState: number = 1;
if (this.interface1 === undefined) {
sendState = 1;
throw new McException(UsbHostConfig.MY_USB_DEV_NO_INTERFACE, undefined, undefined);
}
Logger.info("usb data send:" + StringUtils.uint8ArrayToHexString(cmd))
let outEndpoint: usbManager.USBEndpoint = this.interface1.endpoints[1];
// Logger.info("usb deviceList:" + this.interface1.endpoints[1].number)
// 发送数据,在device信息中选取对应数据发送的endpoint来做数据传输。(endpoint.direction == 0)
let result: Promise<number> = usbManager.bulkTransfer(this.pipe, outEndpoint, cmd, 1000);
await result.then((dataLength: number) => {
if (dataLength >= 0) {
Logger.info("usb writeData result write length : " + dataLength);
sendState = 0;
} else {
Logger.error("usb writeData failed");
sendState = 1;
throw new McException(UsbHostConfig.MY_USB_SEND_DATA_FAILED, undefined, undefined);
}
}).catch((error: BusinessError) => {
Logger.error("usb writeData error : " + JSON.stringify(error));
throw new McException(UsbHostConfig.MY_USB_SEND_DATA_ERROR, undefined, undefined);
});
return sendState;
}
revDataArray: Uint8Array = new Uint8Array(1024);
async receiveData(): Promise<BasePacket> {
/**************读取数据 START****************/
if (this.interface1 != undefined) {
let inEndpoint: usbManager.USBEndpoint = this.interface1.endpoints[0];
/*
读取数据,在device信息中选取对应数据接收的endpoint来做数据传输
(endpoint.direction == 0x80);dataUint8Array是要读取的数据,类型为Uint8Array。
*/
let currentTime = systemDateTime.getTime(false);
while (currentTime + 2000 > systemDateTime.getTime(false)) {
let dataLength: number = await usbManager.bulkTransfer(this.pipe, inEndpoint, this.revDataArray, 200);
if (dataLength > 0) {
let relDataArr: Uint8Array = this.revDataArray.slice(0, dataLength)
Logger.info("usb readData result Length : " + dataLength + ",data:" +
StringUtils.uint8ArrayToHexString(relDataArr));
//此处可自行根据业务进行解析
} else {
Logger.error("usb readData failed : " + dataLength);
}
}
throw new McException(UsbHostConfig.MY_USB_REV_DATA_FAILED, undefined, undefined);
}
throw new McException(UsbHostConfig.MY_USB_REV_DATA_ERROR, undefined, undefined);
}
releaseUsbDevice() {
if (this.pipe !== undefined || this.interface1 !== undefined) {
//释放注册过的通信接口
usbManager.releaseInterface(this.pipe, this.interface1);
//关闭设备消息控制通道
usbManager.closePipe(this.pipe);
}
}
}
export default new UsbDeviceManger
五、HarmonyOS适配OpenHarmony
修改app icon图标(harmonyos 图标显示规则和OpenHarmony不一致,可能不显示,不要用前景背景合成)
兼容OpenHarmony设备上运行,签名不用华为签名,用OpenHarmony签名;修改SDK版本和runtimeOS。