README.md
1# i2c结合OpenGL绘制渐变温度计
2
3## 实现介绍
4根据i2c读取温度,使用OpenGL动态显示室温,中间的温度线具有由蓝变红的渐变效果;达到某个值后呈现红色
5
6## 环境要求
7- 本示例仅支持在标准系统上运行。
8
9- SDK:Ohos_sdk_public 4.0.9.6 (API Version 10 )
10
11## 实现步骤
121. 实现OpenGL ES的温度计图示
132. 实现I2C——NAPI
143. 关于eTS的编写
15
16## 实现OpenGL ES的温度计图示
17OpenGL学习参考链接:https://learnopengl-cn.readthedocs.io/zh/latest/01%20Getting%20started/01%20OpenGL/
18### 实现步骤
19本实现主要对外实现四个接口,包括
201. OpenGL ES初始化
212. 温度值更新
223. 温度计图示随温度值更新
234. 回收退出OpenGL ES资源
24
25#### OpenGL ES初始化
26```C
27int32_t i2cOpengl::Init(void *window, int32_t width, int32_t height) {
28 LOGI("Init window = %{public}p, w = %{public}d, h = %{public}d.", window, width, height);
29 mEglWindow = reinterpret_cast<EGLNativeWindowType>(window);
30
31 mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
32 if (mEGLDisplay == EGL_NO_DISPLAY) {
33 LOGE("unable to get EGL display.");
34 return -1;
35 }
36
37 EGLint eglMajVers, eglMinVers;
38 if (!eglInitialize(mEGLDisplay, &eglMajVers, &eglMinVers)) {
39 mEGLDisplay = EGL_NO_DISPLAY;
40 LOGE("unable to initialize display");
41 return -1;
42 }
43
44 int version = 3;
45 mEGLConfig = getConfig(version, mEGLDisplay);
46 if (mEGLConfig == nullptr) {
47 LOGE("GLContextInit config ERROR");
48 return -1;
49 }
50
51 EGLint winAttribs[] = {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, EGL_NONE};
52 if (mEglWindow) {
53 mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mEglWindow, winAttribs);
54 if (mEGLSurface == nullptr) {
55 LOGE("eglCreateContext eglSurface is null");
56 return -1;
57 }
58 }
59
60 /* Create EGLContext from */
61 int attrib3_list[] = {
62 EGL_CONTEXT_CLIENT_VERSION, 3,
63 EGL_NONE};
64
65 mEGLContext = eglCreateContext(mEGLDisplay, mEGLConfig, mSharedEGLContext, attrib3_list);
66 if (!eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
67 LOGE("eglMakeCurrent error = %{public}d", eglGetError());
68 }
69 CreateProgram();
70 LOGI("Init success.");
71
72 return 0;
73}
74```
75
76初始化通过EGL接口实现,其中,`CreateProgram()`则为封装创建着色器程序,本实现使用了两类着色器程序,一类绑定温度值刻度显示,另一类绑定温度计纹理图片,初始化大致流程如下:
77
78
79
80#### 更新温度值
81接受来自ETS端的温度值,并用此温度值改变OpenGLES图示的刻度值
82```C
83void i2cTemp::SetTemp(double tempe) {
84 temp = (float)tempe;
85}
86```
87#### 温度计图示随温度值更新
88实现如下:
89```C
90void i2cOpengl::Update(void) {
91 glClearColor(1.0f, 1.0, 1.0f, 1.0f);
92 glClear(GL_COLOR_BUFFER_BIT);
93 temperatureLocation = glGetUniformLocation(shaderProgram, "currentTemperature");
94 a_positionLocation = glGetAttribLocation(shaderProgram, "a_position");
95 float currentTemperature = generateNormalizedTemperature(temp);
96 SetAndEnable();
97 LoadTexture();
98
99 glEnable(GL_DEPTH_TEST);
100 glUseProgram(shaderProgram);
101 glUniform1f(temperatureLocation, currentTemperature);
102
103 glBindVertexArray(vao[0]);
104 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
105 if ((err = glGetError()) != GL_NO_ERROR) {
106 LOGE("%{public}u %{public}d", err, __LINE__);
107 } else {
108 LOGI("success to draw %{public}u", err);
109 }
110
111 glUseProgram(shaderProgram1);
112 LOGI("Using shaderProgram1");
113 glBindVertexArray(vao[1]);
114 glActiveTexture(GL_TEXTURE0);
115 glBindTexture(GL_TEXTURE_2D, texture);
116 glUniform1i(glGetUniformLocation(shaderProgram1, "texture1"), 0);
117
118 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
119 if ((err = glGetError()) != GL_NO_ERROR) {
120 LOGE("%{public}u %{public}d", err, __LINE__);
121 }
122 LOGE("Finish Drawing");
123 eglSwapBuffers(mEGLDisplay, mEGLSurface);
124}
125```
126其中, `SetAndEnable()`函数链接相应VBO所需数组以及给着色器相应参数赋值并使能,`LoadTexture()`函数则设置了相应纹理属性以及载入纹理.然后通过两个着色器程序的切换画图,再进行交换缓冲.
127
128- 本纹理实现需要单头文件图像加载库stb_image.h,导库地址如下:
129https://github.com/nothings/stb/blob/master/stb_image.h
130
131- 另外,由于OpenGL的调用关系,纹理图片须得放在板载上且为需要为绝对路径,本实现按函数`LoadTexture()`的实现情况而言:
132
133
134
135本实现的温度计图片放在`entry/src/main/resourses/base/media`,开发者须将图片放置于有效路径并进行读取路径的更改
136完整的目录须包含:
137
138
139
140### 实现I2C温湿度的读取
141本实现的I2C部分使用`vendor\unionman\unionpi_tiger\sample\napi\napisubsys\i2cnapipart\i2cnapidemo`里提供的I2C NAPI接口
142
143参考:
144https://gitee.com/openharmony/vendor_unionman/tree/master/unionpi_tiger/sample/napi/napisubsys/i2cnapipart/i2cnapidemo
145
146另外:通过HDC工具查询I2C设备挂载在`/dev/i2c-5 `总线上,且设备地址为0x44;
147
148
149如若与程序:
150
151
152
153不一致,应适当修改程序至相应正确总线的相应设备地址
154
155正确编译成动态库后需将其送至板载`/system/lib/module`目录下
156```shell
157hdc_std shell mount / -o remount,rw
158hdc_std file send ./libi2cnapidemo.z.so /system/lib/module
159```
160另外:由于APP要调用I2C设备需要权限,则以下提供两种方法:
1611. 修改系统权限,使APP能够访问i2c驱动
162 修改`device\board\unionman\unionpi_tiger\config\init\arm\init.A311D.cfg `文件,在cmds中添加
163 ```json
164 "chmod 777 /dev/i2c-5",
165 ```
166 
167 更改后再烧录以及导库,这样,每次在开发板开机时,则会自动打开权限
1682. 每次开发板开机都手动更改权限
169 ```shell
170 chmod 777 /dev/i2c-5
171 ```
172
173
174至此,则完成了I2C库的导入.
175
176### 关于eTS的编写
177```TS
178 async aboutToAppear() {
179 Logger.info('aboutToAppear');
180 let resourceManager = getContext(this).resourceManager;
181 this.tempString = await resourceManager.getStringValue($r('app.string.mode_label').id);
182 i2cnapidemo.SoftReset();
183 i2cnapidemo.ModeSet(1,1);
184 }
185
186 getValue() {
187 i2cnapidemo.ReadData();
188 this.tempC = i2cnapidemo.ReadTemperatureC();
189 this.tempF = i2cnapidemo.ReadTemperatureF();
190 this.Hum = i2cnapidemo.ReadHumidity();
191 }
192
193 startWork() {
194 this.intervalID = setInterval(() => {
195 this.getValue();
196 }, 500)
197 }
198
199 Update() {
200 this.updateintervalID = setInterval(() => {
201 i2cOpengl.updateTemp(this.tempC);
202 }, 500)
203 }
204```
205本实现主要通过两个定时器,`startWork()`主要用于更新I2C读取的温湿度值,其中用到的接口来源于以上导入的I2C动态库,`Update()`则主要用于更新OpenGL的图示
206
207本OpenGLES图示主要显示在XComponent组件上,组件提供`onLoad()`与`onDestroy()`接口用于回调,本实现在`onLoad()`时调用`Update()`定时器进行更新图示,其中的`UpdateTemp()` NAPI接口主要调用`SetTemp(temp1)`更新数据以及调用`Update()`更新图示画面:
208```C++
209napi_value AppNapi::UpdateTemp(napi_env env, napi_callback_info info)
210{
211 LOGE("Update");
212 size_t argc = 1;
213 napi_value args[1] = {nullptr};
214
215 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
216
217 napi_valuetype valuetype0;
218 napi_typeof(env, args[0], &valuetype0);
219
220 double temp1;
221 napi_get_value_double(env, args[0], &temp1);
222
223 i2cOpengl_->SetTemp(temp1);
224
225 i2cOpengl_->Update();
226 i2cOpengl_->Update();
227
228 napi_value ret;
229 napi_create_double(env, temp1, &ret);
230
231 return ret;
232}
233```
234
235## 实现效果
236