OpenHarmony3.0上采用ets开发HAP控制LED灯
想了解更多内容,采用请访问:
和华为官方合作共建的开控制鸿蒙技术社区
https://harmonyos.51cto.com
1. 开发环境
硬件:Hi3516DV300开发板
软件:OpenHarmony3.0系统
工具:DevEco Studio 3.0
2. 功能简介
OpenHarmony3.0采用了方舟开发框架arkUI,支持了基于TS扩展的采用声明式开发范式eTS,本文使用ets开发语言,开控制构造一个应用程序,采用实现通过上层HAP控制底层LED灯的开控制亮与灭。
3. 实现原理
如果在Android上实现,采用需要通过java调用jni实现对底层的开控制访问。但是采用在OpenHarmony上,HAP采用ets语言开发,开控制没有发现嵌入到HAP当中的采用类JNI语言,但是开控制系统也提供了一个访问底层的机制,叫做NAPI,采用不过这部分是开控制在系统层实现的,不随HAP一起发布。采用我们想要实现控制LED灯的功能,是在NAPI部分通过C语言实现的,然后编译为xxx.z.so动态库,它向上层提供了一个控制接口。绿色LED灯对应GPIO2_3,计算出编号:2*8+3=19,源码库所以直接控制gpio19下的value值就可以控制LED灯亮灭了。
4. 具体实现
整个功能的实现分为了上层HAP应用开发和底层.z.so库的开发两部分。
4.1 应用的开发
1.在HUAWEI DevEco Studio中,创建一个 [Standard]Empty Ability 。

开发语言选择 “eTS”,可以注意到API Version仅支持7,说明是在OpenHarmony3中新支持的,也仅在OpenHarmony3中支持,这些功能实际上都是测试版本,稳定了之后就会在HarmonyOS中使用了,但目前还没有发布。

工程创建完毕后,我直接在pages目录结构下右击新建了一个ets page,取名led。

我们页面的样式、布局和控制全都在led.ets这个文件里了,不再像js分为css、hml和js三个文件。
Led.ets 文件内容
import led from @ohos.led @Entry @Component struct Led { @State private imgpath: string = app.media.ledoff @State private isShow: boolean= false build() { Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { Text(LED 灯控制) .fontSize(25) .fontWeight(FontWeight.Bold) .margin({ bottom: 30}) Image($r(app.media.ledoff)) .objectFit(ImageFit.Contain) .width(150) .height(150) .visibility(this.isShow ? Visibility.None : Visibility.Visible) Image($r(app.media.ledon)) .objectFit(ImageFit.Contain) .width(150) .height(150) .visibility(this.isShow ? Visibility.Visible : Visibility.None) Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) { Button(关闭, { type: ButtonType.Capsule, stateEffect: true }).backgroundColor(0x317aff).width(150).height(50).backgroundColor("#aaaaaa") .onClick((event: ClickEvent) => { this.isShow = false led.switchLed(19, 0); }) Button(打开, { type: ButtonType.Capsule, stateEffect: true }).backgroundColor(0x317aff).width(150).height(50) .onClick((event: ClickEvent) => { this.isShow = true led.switchLed(19, 1); }) }.width("100%") .margin({ top: 50 }) } .width(100%) .height(100%) .padding(10) } }放了两个图片,表示LED灯状态的,放在了代码的entry\src\main\resources\phone\media目录。

导入的ohos.led 库,云服务器是自己添加的NAPI层的动态库,后面会继续介绍。
在config.json文件中,记得把led.ets放到js部分pages数组的第一位,因为它是要显示的首页面。
代码中使用了两个Button组件,一个打开,一个关闭,因为在ets中还没有类似js中的switch的组件,页面中有两个image组件,分别显示打开和关闭的图像,通过设置visibility属性来切换状态,本来想通过动态设置image的源来改变图像内容,但没找到有效的方法,应该是支持的,只是自己还没了解怎么用。
编译前记得一定要设置签名,否则编译出来的程序无法安装。服务器托管

最后可以编译程序了,

生成的最终HAP在 build\outputs\hap\debug\phone\entry-debug-standard-ark-signed.hap
4.2 动态库的开发
动态库需要在OpenHarmony源码中添加和编译,本文使用的是OpenHarmony3.0源码,在foundation/ace/napi/sample目录下,复制一份native_module_demo,重命名为native_module_led,里面的文件也相应的修改名字,注意文件里调用也相应的修改成正确的名字,否则编译会报错。

主要修改的文件有,
foundation/ace/napi/sample/native_module_led/BUILD.gn
foundation/ace/napi/sample/native_module_led/native_module_led.cpp
foundation/ace/napi/BUILD.gn
目录native_module_led下BUILD.gn文件:
import("//build/ohos.gni") ohos_shared_library("led") { include_dirs = [ "//third_party/node/src", "//foundation/ace/napi/interfaces/kits", ] sources = [ "led_javascript_class.cpp", "native_module_led.cpp", ] deps = [ "//foundation/ace/napi:ace_napi" ] relative_install_dir = "module" external_deps = [ "hiviewdfx_hilog_native:libhilog" ] subsystem_name = "ace" part_name = "napi" }目录native_module_led下native_module_led.cpp文件修改部分摘要:
包含的头文件和宏定义,
#include <stdlib.h> // standard library 标准库函数头文件 #include <stdio.h> // standard input output 标准输入输出函数 #include <stdint.h> // 定义了扩展的整数类型和宏 #include <unistd.h> // POSIX 系统 API 访问功能的头文件 #include <fcntl.h> // unix标准中通用的头文件 define O_WRONLY and O_RDONLY // #include <string.h> #define GPIO_DIR_IN (char*)"in" #define GPIO_DIR_OUT (char*)"out" #define GPIO_VAL_LOW 0 #define GPIO_VAL_HIGH 1添加函数SwitchLed的具体实现,
static napi_value SwitchLed(napi_env env, napi_callback_info info) { HILOG_INFO("hey, SwitchLed - 0"); size_t requireArgc = 2; size_t argc = 2; napi_value args[2] = { nullptr }; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments"); napi_valuetype valuetype0; NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); napi_valuetype valuetype1; NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1)); NAPI_ASSERT(env, valuetype0 == napi_number && valuetype1 == napi_number, "Wrong argument type. Numbers expected."); uint32_t gpio; NAPI_CALL(env, napi_get_value_uint32(env, args[0], &gpio)); uint32_t val; NAPI_CALL(env, napi_get_value_uint32(env, args[1], &val)); char direction[100] = { 0}; sprintf(direction,"echo out > /sys/class/gpio/gpio%d/direction", gpio); system(direction); char value[100] = { 0}; sprintf(value,"echo %d > /sys/class/gpio/gpio%d/value", val, gpio); system(value); napi_value sum; NAPI_CALL(env, napi_create_double(env, 1.0f, &sum)); return sum; }初始化部分,
EXTERN_C_START /* * function for module exports */ static napi_value Init(napi_env env, napi_value exports) { /* * Properties define */ napi_property_descriptor desc[] = { DECLARE_NAPI_FUNCTION("add", Add), DECLARE_NAPI_FUNCTION("minus", Minus), DECLARE_NAPI_FUNCTION("switchLed", SwitchLed), DECLARE_NAPI_FUNCTION("TestPromise", TestPromise), DECLARE_NAPI_FUNCTION("TestPromiseOrAsyncCallback", TestPromiseOrAsyncCallback), }; NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); DemoJavascriptClassInit(env, exports); return exports; } EXTERN_C_END模块定义及注册,
/* * Module define */ static napi_module ledModule = { .nm_version = 1, .nm_flags = 0, .nm_filename = nullptr, .nm_register_func = Init, .nm_modname = "led", .nm_priv = ((void*)0), .reserved = { 0 }, }; /* * Module register function */ extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&ledModule); }目录napi下BUILD.gn文件,
group("napi_packages_test") { testonly = true deps = [ "sample/native_module_demo:demo", "sample/native_module_netserver:netserver", "sample/native_module_storage:storage", "test/unittest:unittest", "sample/native_module_led:led", ] if (is_standard_system) { deps += [ "sample/native_module_ability:ability" ] } }最后在源码根目录下执行编译命令,
$./build.sh --product-name Hi3516DV300 --build-target make_test生成的文件为:
out/ohos-arm-release/ace/napi/libled.z.so5. 系统设置
需要授予应用访问gpio下export文件的权限,
device/hisilicon/hi3516dv300/build/rootfs/init.Hi3516DV300.cfg
"name" : "boot", "cmds" : [ "write /sys/class/gpio/export 19", "chmod 777 /sys/class/gpio/gpio19/direction", "chmod 777 /sys/class/gpio/gpio19/value",6. 系统部署
6.1 拷贝动态库
生成的.z.so动态库已经拷贝到PC上E:\libled.z.so
PC串口控制台:
#mount -o remount,rw /PC命令窗口cmd:
E:>hdc_std file send E:\libled.z.so /system/lib/module/PC串口控制台:
#chmod 666 /system/lib/module/libled.z.so6.2 安装应用
PC命令窗口cmd:
E:>hdc_std install E:\Projects\HarmonyProject\MyLed\build\outputs\hap\debug\phone\entry-debug-standard-ark-signed.hap7. 应用测试
点击打开按钮,LED图标变绿,同时LED灯亮,

点击关闭按钮,LED图标变灰,同时LED灯灭。

8. 动图展示

想了解更多内容,请访问:
和华为官方合作共建的鸿蒙技术社区
https://harmonyos.51cto.com