OpenHarmony 串口服务访问

项目介绍

本文档是在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
javascript 复制代码
/*
 * 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
json 复制代码
{
  "name": "libserialhelper.so",
  "types": "./serialhelper.d.ts"
}
3. 根据serialhelper.d.ts文件生成对应的c++源码

方式一:手动编写src/main/cpp/serial_helper.cpp

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);
    }
}

cpp 复制代码
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
html 复制代码
# 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

json 复制代码
"devDependencies": {
	"@types/libserialhelper.so": "file:./src/main/cpp/types/libserialhelper"
}

entry/package-lock.json

json 复制代码
"dependencies": {
   "@types/libserialhelper.so": {
      "version": "file:src/main/cpp/types/libserialhelper",
      "dev": true
   }
}
6. 编译生成

修改编译项entry/build-profile.json5:

json 复制代码
"buildOption": {
  "externalNativeOptions": {
    "path": "./src/main/cpp/CMakeLists.txt",
    "arguments": "-v -DOHOS_STL=c++_shared",
    "abiFilters": [
      "armeabi-v7a",
    ],
    "cppFlags": "",
  }
}

四、实现串口异步回调

添加串口IPC客户端libserialport_service_api.z.so库,并且实现具体的异步回调功能

  1. 将libserialport_service_api.z.so复制到entry/libs/armeabi-a7v目录

  2. 将库的头文件复制到entry/src/main/cpp/include目录

  3. 继承SerialCallbackBase类,实现串口数据异步回调SerialAsyncCallback

    cpp 复制代码
    class SerialAsyncCallback: public SerialCallbackBase {
    public:
        SerialAsyncCallback() = default;
        ~SerialAsyncCallback();
    
        // 通知回调事件
        void OnCallBackEvent() override;
        // 接收到串口数据
        void OnRecvData(const uint8_t *buffer, uint32_t length) override;
        ...
    };
  4. 修改src/main/cpp/CMakeList.txt文件

    html 复制代码
    # 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)
  5. 在napi函数中调用api函数,使用NAPI框架生成工具生成OpenSerial代码,如下:

    cpp 复制代码
    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;
    }
  6. 模块注册

    cpp 复制代码
    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);
    }
  7. eTS调用接口验证

javascript 复制代码
    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 NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. ......

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门?:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. 构建第一个JS应用
  4. ......

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ......

基于ArkTS 开发:https://qr21.cn/FV7h05

1.Ability开发

2.UI开发

3.公共事件与通知

4.窗口管理

5.媒体

6.安全

7.网络与链接

8.电话服务

9.数据管理

10.后台任务(Background Task)管理

11.设备管理

12.设备使用信息统计

13.DFX

14.国际化开发

15.折叠屏系列

16.......

相关推荐
TechubNews31 分钟前
Base 发布首个独立 OP Stack 框架的网络升级 Azul,将是 L2 自主迭代的开端?
大数据·网络·人工智能·区块链·能源
多年小白1 小时前
中科院 Ouroboros 晶圆级存算一体芯片深度解析
大数据·网络·人工智能·科技·ai
发光小北2 小时前
IEC104 转 Modbus TCP 网关如何应用?
网络·网络协议·tcp/ip
山栀shanzhi3 小时前
在做直播时,I帧的间隔(GOP)一般是多少?
网络·c++·面试·ffmpeg
SPC的存折3 小时前
Cisco Packet Tracer 静态路由全网互通实验及详细教学文档,包括基础常识、实验信息、IP 地址规划和分步操作流程
网络·tcp/ip·智能路由器
东北甜妹3 小时前
网络服务-
网络·智能路由器
isyangli_blog3 小时前
openstack 下载打包整套环境
服务器·网络·openstack
treesforest3 小时前
IP 反欺诈查询怎么落地更稳?Ipdatacloud 适用场景与实战决策闭环
网络·数据库·网络协议·tcp/ip·网络安全
不一样的故事1263 小时前
SVN 权限已赋予但客户端看不到服务端文件
大数据·网络·安全
纯爱掌门人4 小时前
聊聊 HarmonyOS 上的应用内通知授权弹窗
前端·harmonyos·arkts