项目介绍
本文档是在eTS项目hap包中实现串口访问的使用说明,通过JS接口开放给上层应用使用。
一、开发环境准备
安装OpenHarmony SDK
1. 在DevEco Studio菜单栏选择Tools->SDK Manager
2. OpenHarmony SDK选项中选择配备API版本进行安装
二、创建eTS项目
创建支持Native C++的eTS项目
三、NAPI库相关
生成串口NAPI库
1. 添加文件src/main/cpp/types/libserialhelper/serialhelper.d.ts
/*
* Copyright (C) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {AsyncCallback, Callback} from "basic";
declare namespace serialHelper {
/**
* Open serial port.
* @param dev Indicates the serial port dev.
*/
function openSerial(dev:string, callback: AsyncCallback<void>): void;
function openSerial(dev:string): Promise<void>;
/**
* Close serial port.
* @param dev Indicates the serial port dev.
*/
function closeSerial(dev:string, callback: AsyncCallback<void>): void;
function closeSerial(dev:string): Promise<void>;
}
export default serialHelper;
2. 添加文件src/main/cpp/types/libserialhelper/package.json
{
"name": "libserialhelper.so",
"types": "./serialhelper.d.ts"
}
3. 根据serialhelper.d.ts文件生成对应的c++源码
方式一:手动编写src/main/cpp/serial_helper.cpp
struct AsyncCallInfo{
napi_env env = nullptr;
napi_ref callbackRef = nullptr;
napi_deferred deferred = nullptr;
napi_async_work work = nullptr;
void *data = nullptr;
};
static void AsyncCallFinish(AsyncCallInfo* asyncCallInfo, int32_t result, napi_value *asyncResult)
{
if (asyncCallInfo->deferred) {
if (result == 0) {
napi_resolve_deferred(asyncCallInfo->env, asyncCallInfo->deferred,
asyncResult[1]==nullptr?asyncResult[0]:asyncResult[1]);
} else {
napi_reject_deferred(asyncCallInfo->env, asyncCallInfo->deferred, asyncResult[0]);
}
} else {
napi_value callback = nullptr;
napi_get_reference_value(asyncCallInfo->env, asyncCallInfo->callbackRef, &callback);
napi_call_function(asyncCallInfo->env, nullptr, callback, CALLBACK_ARGV_CNT, asyncResult, nullptr);
napi_delete_reference(asyncCallInfo->env, asyncCallInfo->callbackRef);
}
}
static napi_value Call_OpenSerial(napi_env env, napi_callback_info info)
{
size_t argc = 0;
napi_value args[DEFAULT_ARG_COUNT] = {0};
napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
...
napi_value resourceName = nullptr;
napi_create_string_utf8(env, "x_napi_tool", NAPI_AUTO_LENGTH, &resourceName);
napi_create_async_work(env, nullptr, resourceName,
[](napi_env env, void* data) {
AsyncCallInfo* asyncCallInfo = (AsyncCallInfo*)data;
OpenSerialValue* openValue = (OpenSerialValue*)asyncCallInfo->data;
//openValue->out = SerialClient::GetInstance()->OpenSerial(openValue->dev);
},
[](napi_env env, napi_status status, void* data) {
AsyncCallInfo* asyncCallInfo = (AsyncCallInfo*)data;
OpenSerialValue* openValue = (OpenSerialValue*)asyncCallInfo->data;
napi_value asyncResult[CALLBACK_ARGV_CNT]={nullptr, nullptr};
napi_create_int32(env, openValue->out, &asyncResult[0]);
AsyncCallFinish(asyncCallInfo, openValue->out,asyncResult);
napi_delete_async_work(env, asyncCallInfo->work);
delete openValue;
delete asyncCallInfo;
},
(void*)asyncCallInfo, &asyncCallInfo->work);
napi_queue_async_work(env, asyncCallInfo->work);
return retValue;
}
方式二:使用NAPI框架生成工具生成 工具链接
1)将serialhelper.d.ts、basic.d.ts复制到同一目录中,创建out目录
2)执行./napi_generator-linux -f serialhelper.d.ts -o out
3)将生成的源码文件复制到src/main/cpp
4. make文件:src/main/cpp/CMakeList.txt
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(XComponent)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${NATIVERENDER_ROOT_PATH})
add_library(serialhelper SHARED serial_helper.cpp)
target_link_libraries(serialhelper PUBLIC libace_napi.z.so libc++.a)
5. 添加项目依赖
entry/package.json
"devDependencies": {
"@types/libserialhelper.so": "file:./src/main/cpp/types/libserialhelper"
}
entry/package-lock.json
"dependencies": {
"@types/libserialhelper.so": {
"version": "file:src/main/cpp/types/libserialhelper",
"dev": true
}
}
6. 编译生成
修改编译项entry/build-profile.json5:
"buildOption": {
"externalNativeOptions": {
"path": "./src/main/cpp/CMakeLists.txt",
"arguments": "-v -DOHOS_STL=c++_shared",
"abiFilters": [
"armeabi-v7a",
],
"cppFlags": "",
}
}
四、实现串口异步回调
添加串口IPC客户端libserialport_service_api.z.so库,并且实现具体的异步回调功能
- 将libserialport_service_api.z.so复制到entry/libs/armeabi-a7v目录
- 将库的头文件复制到entry/src/main/cpp/include目录
- 继承SerialCallbackBase类,实现串口数据异步回调SerialAsyncCallback
class SerialAsyncCallback: public SerialCallbackBase {
public:
SerialAsyncCallback() = default;
~SerialAsyncCallback();
// 通知回调事件
void OnCallBackEvent() override;
// 接收到串口数据
void OnRecvData(const uint8_t *buffer, uint32_t length) override;
...
};
- 修改src/main/cpp/CMakeList.txt文件
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(XComponent)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include
)
link_directories(${NATIVERENDER_ROOT_PATH}/../../../libs/${CMAKE_OHOS_ARCH_ABI})
add_library(serialhelper SHARED serial_helper.cpp x_napi_tool.cpp serial_async_callback.cpp)
target_link_libraries(serialhelper PUBLIC libace_napi.z.so libc++.a libhilog_ndk.z.so libuv.so libserialport_service_api.z.so)
- 在napi函数中调用api函数,使用NAPI框架生成工具生成OpenSerial代码,如下:
struct OpenSerial_value_struct {
std::string in0;
int32_t out;
};
void OpenSerial_execute(XNapiTool *pxt, void *data)
{
OpenSerial_value_struct *vio = (OpenSerial_value_struct *)data;
vio->out = get_serial_client()->OpenSerial(vio->in0);
}
void OpenSerial_complete(XNapiTool *pxt, void *data)
{
OpenSerial_value_struct *vio = (OpenSerial_value_struct *)data;
napi_value result = nullptr;
result = NUMBER_C_2_JS(pxt, Int32, vio->out);
{
napi_value args[XNapiTool::ARGV_CNT] = {result, nullptr};
pxt->FinishAsync(vio->out, args);
}
delete vio;
}
napi_value OpenSerial_middle(napi_env env, napi_callback_info info)
{
XNapiTool *pxt = std::make_unique<XNapiTool>(env, info).release();
if (pxt->IsFailed()) {
napi_value err = pxt->GetError();
delete pxt;
return err;
}
struct OpenSerial_value_struct *vio = new OpenSerial_value_struct();
pxt->SwapJs2CUtf8(pxt->GetArgv(0), vio->in0);
napi_value result = pxt->StartAsync(OpenSerial_execute, vio, OpenSerial_complete,
pxt->GetArgc() == 2 ? pxt->GetArgv(1) : nullptr);
if (pxt->IsFailed()) {
result = pxt->GetError();
}
return result;
}
- 模块注册
static napi_value init(napi_env env, napi_value exports)
{
std::shared_ptr<XNapiTool> pxt = std::make_shared<XNapiTool>(env, exports);
//js函数与C++函数映射
pxt->DefineFunction("setOptions", OHOS::SerialPort::SetOptions_middle);
pxt->DefineFunction("openSerial", OHOS::SerialPort::OpenSerial_middle);
pxt->DefineFunction("closeSerial", OHOS::SerialPort::CloseSerial_middle);
pxt->DefineFunction("clearBuffer", OHOS::SerialPort::ClearBuffer_middle);
pxt->DefineFunction("sendData", OHOS::SerialPort::SendData_middle);
pxt->DefineFunction("recvData", OHOS::SerialPort::RecvData_middle);
pxt->DefineFunction("transmit", OHOS::SerialPort::Transmit_middle);
pxt->DefineFunction("on", OHOS::SerialPort::on_middle);
pxt->DefineFunction("off", OHOS::SerialPort::off_middle);
pxt->DefineFunction("setGPIODirection", OHOS::SerialPort::setGPIODirection_middle);
pxt->DefineFunction("setGPIOValue", OHOS::SerialPort::setGPIOValue_middle);
pxt->DefineFunction("getGPIOValue", OHOS::SerialPort::getGPIOValue_middle);
return exports;
}
static napi_module g_serialHelper_Module = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = init,
.nm_modname = "serialhelper",
.nm_priv = ((void *)0),
.reserved = {(void *)0},
};
extern "C" __attribute__((constructor)) void Register_serialHelper_Module(void)
{
napi_module_register(&g_serialHelper_Module);
}
- eTS调用接口验证
import serialHelper from "libserialhelper.so"
...
//打开串口this.tty /dev/ttyXRUSB0
serialHelper.openSerial(this.tty).then(()=>{
HiLog.i(TAG, "serial openSerial " + this.tty + " success")
this.status = '开'
}).catch((error)=> {
HiLog.i(TAG, "openSerial " + this.tty + " failed:" + error)
});
...
//设置为异步
serialHelper.on("/dev/ttyXRUSB0", (data) => {
var dataString = "";
for (var i = 0; i < data.length; i++) {
dataString += String.fromCharCode(data[i]);
}
HiLog.i(TAG, "ttyXRUSB0 len:" + data.length + " data:" + dataString);
})
应用启动后点击"打开/dev/ttyXRUSB0"按钮查看输出日志,出现serialport_client与serial_service_impl标志,表示访问串口服务成功
最后分享一份鸿蒙(HarmonyOS)开发学习指南需要的可以
关注VX公众号:Android 老皮
《鸿蒙(HarmonyOS)开发学习指南》
第一章 快速入门
1、开发准备
2、构建第一个ArkTS应用(Stage模型)
3、构建第一个ArkTS应用(FA模型)
4、构建第一个JS应用(FA模型)
5、........
第二章 开发基础知识
1、应用程序包基础知识
2、应用配置文件(Stage模型)
3、应用配置文件概述(FA模型)
4、.......
第三章 资源分类与访问
1、 资源分类与访问
2、 创建资源目录和资源文件
3、 资源访问
4、.......
第四章 学习ArkTs语言
1、初识ArkTS语言
2、基本语法
3、状态管理
4、其他状态管理
5、渲染控制
6、......
第五章 UI开发
1.方舟开发框架(ArkUI)概述
2.基于ArkTS声明式开发范式
3.兼容JS的类Web开发范式
4.......
第六章 Web开发
1.Web组件概述
2.使用Web组件加载页面
3.设置基本属性和事件
4.在应用中使用前端页面JavaScript
5.ArkTS语言基础类库概述
6.并发
7.......
11.网络与连接
12.电话服务
13.数据管理
14.文件管理
15.后台任务管理
16.设备管理
17......
第七章 应用模型
1.应用模型概述
2.Stage模型开发指导
3.FA模型开发指导
4.......