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