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.......

相关推荐
爱码小白26 分钟前
网络编程(王铭东老师)笔记
服务器·网络·笔记
蜜獾云37 分钟前
linux firewalld 命令详解
linux·运维·服务器·网络·windows·网络安全·firewalld
柒烨带你飞1 小时前
路由器转发数据报的封装过程
网络·智能路由器
东方隐侠安全团队-千里2 小时前
网安瞭望台第17期:Rockstar 2FA 故障催生 FlowerStorm 钓鱼即服务扩张现象剖析
网络·chrome·web安全
沈剑心2 小时前
如何在鸿蒙系统上实现「沉浸式」页面?
前端·harmonyos
神一样的老师2 小时前
面向高精度网络的时间同步安全管理架构
网络·安全·架构
Georgewu2 小时前
【HarmonyOS】鸿蒙应用加载读取csv文件
前端·harmonyos
与海boy3 小时前
CentOS7网络配置,解决不能联网、ping不通外网、主机的问题
linux·网络·网卡
Georgewu3 小时前
【HarmonyOS】 鸿蒙图片或视频保存相册
前端·harmonyos
我叫czc4 小时前
【python高级】342-TCP服务器开发流程
服务器·网络·tcp/ip