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

相关推荐
qxqxa1 小时前
cfg80211是怎么配置无线设备的AP的?
网络·驱动开发
训山2 小时前
【11】纯血鸿蒙HarmonyOS NEXT星河版开发0基础学习笔记-模块化语法与自定义组件
笔记·学习·华为·harmonyos·鸿蒙系统
秋夫人2 小时前
http cache-control
网络·网络协议·http
helloxmg3 小时前
鸿蒙harmonyos next flutter混合开发之开发package
flutter·华为·harmonyos
不灭锦鲤4 小时前
ssrf学习(ctfhub靶场)
网络·学习·安全
weixin_548444264 小时前
2024年最新版本神马TV8.5影视APP源码 293TV影视点播系统源码搭建教程 神马TV8.2加强版反编译教程 保姆级小白可搭建 完整版本视频教程
网络
网络研究院7 小时前
如何安全地大规模部署 GenAI 应用程序
网络·人工智能·安全·ai·部署·观点
limengshi1383927 小时前
通信工程学习:什么是RIP路由信息协议
网络·网络协议·学习·智能路由器·信息与通信
limengshi13839211 小时前
通信工程学习:什么是TFTP简单文件传输协议
网络·网络协议·学习·信息与通信
麻辣韭菜13 小时前
网络基础 【HTTP】
网络·c++·http