OpenHarmony的NAPI功能为开发者提供了JS与C/C++不同语言模块之间的相互访问,交互的能力,使得开发者使用C或者C++语言实现应用的关键功能。如操作开发板中某个GPIO节点的状态(OpenHarmony并没有提供直接操作GPIO口状态的API),点亮一个LED灯。
本篇基于前3篇NAPI的讲解总结,做一个控制开发板上LED灯状态的应用作为完结篇。
一、环境准备
设备:rk3568开发板
开发环境:DevEco Studio4.0.0.600
二、NAPI函数编写
1、基于NAPI的GPIO节点控制函数
OpenHarmony并没有提供直接操作GPIO口状态的API。若需要控制开发板上LED灯的亮灭, 需要通过NAPI的方式,写一个可以查询GPIO口状态、一个可以对节点写1和一个可以对节点写0的函数。
(1)main目录如下:
(2)bq_function.cpp
#include "napi/native_api.h"
#include "napi/native_api.h"
#include <cstring>
#include <js_native_api.h>
#include <sys/un.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
//对指定节点写1
static napi_value Bq_GPIO_On(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
napi_valuetype valuetype0;
napi_typeof(env, args[0], &valuetype0);
char *ledPath = new char[150]{};
size_t buffSize = 100;
size_t realSize = 0;
napi_get_value_string_utf8(env, args[0], ledPath, buffSize, &realSize);
int fd;
char *ret = new char[50]{};
fd = open(ledPath, O_RDWR | O_TRUNC | O_NOCTTY);
if (fd < 0) {
strcpy(ret, "fail to open file");
} else {
strcpy(ret, "1");
write(fd, "1", 1);
close(fd);
}
napi_value returnValue = nullptr;
napi_create_string_utf8(env, ret, strlen(ret), &returnValue);
return returnValue;
}
//对指定节点写0
static napi_value Bq_GPIO_Off(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
napi_valuetype valuetype0;
napi_typeof(env, args[0], &valuetype0);
char *ledPath = new char[150]{};
size_t buffSize = 100;
size_t realSize = 0;
napi_get_value_string_utf8(env, args[0], ledPath, buffSize, &realSize);
int fd;
char *ret = new char[50]{};
fd = open(ledPath, O_RDWR | O_TRUNC | O_NOCTTY);
if (fd < 0) {
strcpy(ret, "fail to open file");
} else {
strcpy(ret, "0");
write(fd, "0", 1);
close(fd);
}
napi_value returnValue = nullptr;
napi_create_string_utf8(env, ret, strlen(ret), &returnValue);
return returnValue;
}
//查询指定节点状态
static napi_value bq_GPIO_State(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
napi_valuetype valuetype0;
napi_typeof(env, args[0], &valuetype0);
char *ledPath = new char[150]{};
size_t buffSize = 100;
size_t realSize = 0;
napi_get_value_string_utf8(env, args[0], ledPath, buffSize, &realSize);
char *ret = new char[50]{};
FILE *fp;
char gpioValuePath[200];
char gpioValue[2];
// 构建GPIO口值文件路径
snprintf(gpioValuePath, sizeof(gpioValuePath), "%s", ledPath);
if (access(gpioValuePath, F_OK) == -1) {
strcpy(ret, "Error: the GPIO is not exist");
napi_value returnValue = nullptr;
napi_create_string_utf8(env, ret, strlen(ret), &returnValue);
return returnValue;
}
// 打开GPIO口值文件
fp = fopen(gpioValuePath, "r");
if (fp == NULL) {
perror("Error opening GPIO value file");
strcpy(ret, "Error opening GPIO value file");
} else {
// 读取GPIO口值
if (fgets(gpioValue, sizeof(gpioValue), fp) == NULL) {
perror("Error reading GPIO value");
fclose(fp);
strcpy(ret, "Error reading GPIO value");
} else {
strcpy(ret, gpioValue);
}
}
fclose(fp);
napi_value returnValue = nullptr;
napi_create_string_utf8(env, ret, strlen(ret), &returnValue);
return returnValue;
}
EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {
{"bq_GPIO_On", nullptr, Bq_GPIO_On, nullptr, nullptr, nullptr, napi_default, nullptr},
{"bq_GPIO_Off", nullptr, Bq_GPIO_Off, nullptr, nullptr, nullptr, napi_default, nullptr},
{"bq_GPIO_State", nullptr, bq_GPIO_State, nullptr, nullptr, nullptr, napi_default, nullptr}};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = ((void *)0),
.reserved = {0},
};
extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
2、NAPI函数与导出与配置
(1)index.d.ts
export const bq_GPIO_On: (ledPath: string) => Promise<string>;//节点写1
export const bq_GPIO_Off: (ledPath: string) => Promise<string>;//节点写0
export const bq_GPIO_State: (ledPath: string) => Promise<string>;//节点状态查询
(2)CMakeLists.txt
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(Demo_Led)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(entry SHARED bq_func.cpp)
target_link_libraries(entry PUBLIC libace_napi.z.so)
(3)build-profile.json5
(4)oh-package.json5
三、Openharmony UI交互界面编写
1、UI界面编写,NAPI函数引用
(1)index.ets
开发板控制LED灯节点地址为:/sys/class/leds/pilot_lamp/brightness
import NAPI from 'libentry.so';//引出NAPI接口
import promptAction from '@ohos.promptAction';
@Observed
class ledModel {
public ledName: string //LED灯名称
public ledAddress: string //LED灯的节点地址
public ledState: boolean //LED灯状态
public isLedExit: boolean //LED灯节点是否存在
public ledStateCheck: string //检测返回值
constructor(LedName: string, LedAddress: string, LedState: boolean = false, isLedExit: boolean = true, LedStateCheck: string = '') {
this.ledName = LedName
this.ledAddress = LedAddress
this.ledState = LedState
this.isLedExit = isLedExit
this.ledStateCheck = LedStateCheck
}
}
@Entry
@Component
struct Index {
@State message: number = 0;
@State led: ledModel = new ledModel('LED_Green',
'/sys/class/leds/pilot_lamp/brightness')//开发板控制LED灯节点地址'/sys/class/leds/pilot_lamp/brightness'
private setIntervalID: number = -1
async ledCheck() {
this.led.ledStateCheck = await NAPI.bq_GPIO_State(this.led.ledAddress)
if (this.led.ledStateCheck === '1') {
this.led.ledState = true
} else if (this.led.ledStateCheck === 'Error: the GPIO is not exist') {
this.led.isLedExit = false
} else {
this.led.ledState = false
}
}
async aboutToAppear() {
await this.ledCheck()
//状态扫描
this.setIntervalID = setInterval(async () => {
await this.ledCheck()
}, 500)
}
aboutToDisappear() {
clearInterval(this.setIntervalID)
}
build() {
Column() {
Text("点亮一个LED灯")
.size({ width: '100%', height: 35 })
.margin({ top: 10, bottom: 10 })
.textAlign(TextAlign.Center)
.fontSize(30)
.fontWeight(800)
Column({ space: 10 }) {
ledList({ item: this.led})
}
.margin({ top: 20, bottom: 10 })
.size({ width: '50%', height: 50 })
.justifyContent(FlexAlign.Start)
.alignSelf(ItemAlign.Center)
}
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.size({ width: '100%', height: '100%' })
}
}
@Component
struct ledList {
@ObjectLink item: ledModel
tip(str: string) {
let show: string
switch (str) {
case "1":
show = '已打开';
break;
case "0":
show = '已关闭';
break;
default:
show = `error: ${str}`
}
promptAction.showToast({
message: show,
duration: 800
})
}
build() {
Row() {
Text(`${this.item.ledName}`)
.size({ width: '70%', height: 35 })
.fontSize(30)
.fontWeight(700)
.fontColor(this.item.isLedExit === true ? Color.Black : '#c9c9cd')
Blank()
Toggle({ type: ToggleType.Switch, isOn: this.item.ledState })
.size({ width: 80, height: 35 })
.enabled(this.item.isLedExit)
.onChange(async (isOn) => {
if (isOn) {
let res: string = await NAPI.bq_GPIO_On(this.item.ledAddress)
this.tip(res)
} else {
let res: string = await NAPI.bq_GPIO_Off(this.item.ledAddress)
this.tip(res)
}
})
}.width('100%')
}
}
2、效果展示
(1)LED灭
(2)LED亮
四、工程代码
见附件