diff --git a/docs/OpenHarmonyCarSystem_OHCar/README.md b/docs/OpenHarmonyCarSystem_OHCar/README.md new file mode 100644 index 0000000000000000000000000000000000000000..88bc023506fb629f0acdfdf3954beac52bc9cc4d --- /dev/null +++ b/docs/OpenHarmonyCarSystem_OHCar/README.md @@ -0,0 +1,420 @@ +``` + Copyright (c) HelloKun (https://space.bilibili.com/475950146). + Demo Instruction: OpenHarmony Center Control System of Car (OHCar) + Environment: OpenHarmony 3.1Release API 8 + Test Device: DAYU200 + Author: HelloKun + 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. + demo仅展示车机UI以及控制逻辑,南向开发以及鸿蒙车辆管家其他参考(https://ost.51cto.com/posts/14399): +OHCar_phone_app: 移动端车辆管家-源码(API6 鸿蒙2.0) +OHCar_nfc: 车辆管家南向开发-源码(基于小熊派版本OpenHarmony源码开发) +OHCar_tcp: 车载系统南向开发-源码(基于OpenHarmony1.0.1 release源码开发) +``` + +@[toc](【HelloKun】开源鸿蒙车机系统OHCar) + +# 样例介绍 +OHCar是运行于DAYU200上的模拟车机,提供操控车辆、车辆仪表区、车载APP(导航、音视频)、系统设置等功能。 +## 开发环境 +dayu200: OH系统版本:OpenHarmony3.1Release ;IDE: 3.0.0.900 ; APP:ArkUI -ets +hi3861: OpenHarmony 1.0.1 Release ,C语言 +车管家(手机端): HarmonyOS API 6 ,JS语言 +## 运行效果 +![hcar.jpg](https://dl-harmonyos.51cto.com/images/202207/22edead69eade715d66076a3f4be4e343c549d.jpg?x-oss-process=image/resize,w_820,h_2268) +想象一下,现在你正忙完一天的工作准备下班回家,担心车辆能源不足?天气太热或太冷?于是在走到楼下停车位上的Dream Car前,拿出鸿蒙手机打开车辆专属的管家APP,提前检测剩余能量百分比,打开车载空调。到达车前的你不愿意掏出钥匙,顺手用手机碰一碰车门,只需点击弹出窗口中的解锁按钮即可进入车中。 +![doorpeng.gif](https://dl-harmonyos.51cto.com/images/202207/86354ed79df6193f313158b1970cbb029f56df.gif)![door.gif](https://dl-harmonyos.51cto.com/images/202207/556162905d725989a3c790326ea2b5981f8d23.gif) +坐进驾驶舱,OHCar又一次为你开启贴心服务。加载页面中红黑经典表盘与激情澎湃背景视频,短暂而又丝滑的过度只为让你忘却一天的疲惫。进入系统后,手指轻轻一戳,一键唤醒你的DreamCar。 +![start.gif](https://dl-harmonyos.51cto.com/images/202207/a360e2861faab7905a7780905b45cbd243c8ed.gif)![cover.gif](https://dl-harmonyos.51cto.com/images/202207/a15b6d9824a7ee539e6302c6d0ba1fd54b2f67.gif) +出发前,你打算先来一首音乐,或者刷一段冰冰的甜美笑容,又或者看一段Jay的最新MV,这些,OHCar都能给你。 +![musicqq.png](https://dl-harmonyos.51cto.com/images/202207/227339303d41d1f8cab186392d828e8c59c4aa.png?x-oss-process=image/resize,w_820,h_1453)![bilibili.png](https://dl-harmonyos.51cto.com/images/202207/a5666f616a9645808a8044dea6e0663f07c04b.png?x-oss-process=image/resize,w_820,h_1542)![jay.jpg](https://dl-harmonyos.51cto.com/images/202207/197180e710d71e5070e30087a9840301b73242.jpg?x-oss-process=image/resize,w_820,h_4032) + +当然,安全出行第一步。嫌弃屏幕太暗?打开设置,亮度一步到位。终于,你已经释放掉50%的疲惫感,准备一脚油门回到家中。别急,马路拥挤,实时导航能不能有? 当然,OHCar一直为你保驾护航。 +![amap.jpg](https://dl-harmonyos.51cto.com/images/202207/e9ddb62121e8b143faa874d050f20bc53f9744.jpg?x-oss-process=image/resize,w_820,h_1516) +![image.png](https://dl-harmonyos.51cto.com/images/202207/b672daa4048b94209ca221efe61f4cc8d6709d.png?x-oss-process=image/resize,w_432,h_728) +下班回家的路上,一切操作丝般顺滑,你享受着空调,听着Jay的音乐,踩着油门一路向北! + +![move.gif](https://dl-harmonyos.51cto.com/images/202207/93e2949403a89b7549855789465dede0c122d4.gif)![poiler.gif](https://dl-harmonyos.51cto.com/images/202207/35d4b11619c625617a926013feeb15e6bcce95.gif) +便捷而又炫酷的智慧生活极致体验,由OpenHarmony为你打造。再想象一下,通过OHCar,对话家里的[MRobot](https://ost.51cto.com/posts/12984),为你开启下一段贴心服务! +# 1. OHCar项目简介 +## 1.1 项目说明 +- Dayu200体验官活动之初决定做车载中控,设想是在汽车模型中实现车辆信息监测、无钥匙解锁、车辆控制能力、导航地图以及影音娱乐等功能,并配备移动端车辆管家。OHCar便是该设想的落地,基于现有开源鸿蒙的系统能力,基本实现了上述功能,只需手机碰一碰解锁车辆,还可在APP中控制车载空调、查看剩余电量、获取车辆位置等信息,进入车辆后,可在dayu200上操控车辆,仪表区、车载APP、系统设置一应俱全。 +- 从技术角度而言,车机中使用ArkUI框架中的ets语言开发,结合条件渲染,让Video、Web等组件实现同框显示,自定义数据类实现动画;智能车控硬件方面,dayu200做车辆中控,再使用两个hi3861 iot核心板、一个Arduino Nano协同实现车辆控制,电源测量模块获取电量,GPS模块测量位置信息,笔记本风扇模拟车载空调,为实现影音功能,使用音频解码与放大板实现音响。总的来说,OHCar软硬件均属鸿蒙生态,展示出开源鸿蒙在车载系统中极具潜力。本文从OHCar的功能角度分解说明项目,并在其中讲解使用到的关键技术,更多技术细节移步Gitee仓库。demo视频[OHCar视频](https://ost.51cto.com/show/14482) +- 注: + dayu200: OH系统版本:OpenHarmony3.1Release ;IDE: 3.0.0.900 ; APP:ArkUI -ets + hi3861: OpenHarmony 1.0.1 Release ,C语言 + 车管家(手机端): HarmonyOS API 6 ,JS语言 +## 1.2 OHCar软硬件简介 +OHCar项目框架组成如下图,dayu200作为上层的控制终端,提供车辆仪表信息显示、影音娱乐等服务,使用TCP传输驾驶员的操控指令;②号hi3861接收到数据后进行预处理(如获取车辆温湿度,车内烟雾监测),非本地指令则通过uart转给车辆底层的mcu;①号hi3861则提供无钥匙解锁、电量、定位的信息获取、车载空调操控等功能。底层mcu直接负责车辆的操控,如电机控制,实时性强,同时也方便实现软硬件隔离。各模块分工明确但又紧密配合。所有各模块(除dayu200外)均安装于经典2003款 Ferrari Enzo 模型中,只为直观展示上述功能。 +![image.png](https://dl-harmonyos.51cto.com/images/202207/b5e8143043845945bae32857c8972d10b36d45.png?x-oss-process=image/resize,w_820,h_875) +# 2. OHCar车载系统实现 +以APP形式模拟OHCar车载系统,APP基于ArkUI框架中的est语言开发,运行在dayu200之上,APP包括系统启动页、操控UI页、系统设置页、本地视频播放页。启动页是模拟车载系统开机;操控UI页面包含系统展示窗、车载APP(音乐、地图、Blibili)、仪表盘、车辆操控。系统设置页目前支持屏幕亮度调节功能;本地视频播放页用于播放视频文件,提高影音娱乐功能。 +车载系统模拟APP架构总结如下图: +![UI布局.png](https://dl-harmonyos.51cto.com/images/202207/968274362de7f1dae286763547ad7b6954d1d2.png?x-oss-process=image/resize,w_820,h_747) +打开DevEco Studio 3.0.0.900, 新建一个基于ets的工程,API为8。详情见[环境搭建](https://developer.harmonyos.com/cn/develop/deveco-studio#download_beta)。下面对OHCar项目中的关键功能模块的实现进行说明。 +## 2.1 系统启动页面 +系统启动页**模拟车机开机启动画面**,使用到Video组件展示加载视频,Canvas绘制表盘,结合定时器实现指针摆动,最后使用Stack容器实现覆盖效果。关键代码如下,具体实现可参考这篇帖子[首页aito视频&Canvas绘制仪表盘](https://ost.51cto.com/posts/14312) +```javascript +Stack() + { + Video({ + src: this.srcs, + previewUri: this.previewUris, + currentProgressRate: this.currentProgressRates, + controller: this.controller + }).width('100%').height('100%') + .objectFit(ImageFit.Cover) + .autoPlay(true) + .controls(this.controlsss) + .onFinish(() => { + console.error('onFinish'); + router.push({url:'pages/gauge'}) + }) + // 仪表盘 + Row({ space: 0 }) { + //油门表 + MyGauge() + Column() { + Canvas(this.car_gauge) + .width('45%') + .height('20%') + .backgroundColor('#000000') + .onReady(() => { + //表环-车速 + this.car_gauge.clearRect(-100, -100, 600, 600); + this.car_gauge.beginPath() + this.car_gauge.translate(0, 0) + this.car_gauge.shadowBlur = 30 + this.car_gauge.shadowColor = this.car_gauge_col + this.car_gauge.arc(this.gauge_speed_x, this.gauge_speed_y, 100, 0, 6.28) + this.car_gauge.fillStyle = 'black' + this.car_gauge.fill() + this.car_gauge.closePath() + //数字环、指针 操作类似,略过 + //车速 + Text(this.car_velocity+" KM/H") + .fontSize(40).height(40).fontStyle(FontStyle.Italic).textAlign(TextAlign.Center) + .backgroundColor('black').fontColor('white') + } + //电量表 + Gauge({ value: this.fuel_value, min: 0, max: 120 }) + .startAngle(240) + .endAngle((this.fuel_value)) + .colors([[0x5BA854, 0.5],[0xCFB53B, 0.5],[0xF01020, 0.5] ]) + .strokeWidth(30) + .width(120) + .height(120) + .margin({top:30}) + } + } + } +``` + +## 2.2 操控页面——系统展示窗 +操控UI页面的系统展示窗**模拟车辆的中控屏**,用于显示车辆状态、内置app。使用条件渲染将自定义component(音乐、地图、Blibili)展示出来。内置车载APP用web组件模拟(dayu200 联网后可实现网页加载。) +关键实现代码如下: +```js +Column({ space:0 }) { + if(this.display_flag==1) { + Video({ + src: this.srcs, + currentProgressRate: this.currentProgressRates, + controller: this.controller + }) + .width('100%') + .height('80%') + .objectFit(ImageFit.Fill) + .autoPlay(this.autoPlays) + .controls(this.controlsss) + .onStart(() => { + console.error('onStart'); + }) + } + else if(this.display_flag==2) + { + Column() + { + qqmusic() + }.height('80%').alignItems(HorizontalAlign.Center) + } + else if(this.display_flag==3) + { + Column() + { + amap() + }.height('80%').alignItems(HorizontalAlign.Center) + } + else if(this.display_flag==4) + { + Column() + { + Bilibili() + }.height('80%').alignItems(HorizontalAlign.Center) + + } +//web实现如下: +@Component +struct Bilibili { + @State message: string = 'Hello World' + controller: WebController = new WebController(); + build() { + Column() + { + Web({ src: 'https://www.bilibili.com/', controller: this.controller }) + } + .width('100%') + .height('100%') + .backgroundColor('black') + } +} +``` + +## 2.3 操控页面——仪表盘 +![gauge.png](https://dl-harmonyos.51cto.com/images/202207/826a44f30d64032a360592f5e4e691e3447cbf.png?x-oss-process=image/resize,w_272,h_142)![image.png](https://dl-harmonyos.51cto.com/images/202207/560db8c20a017853625584de67bfee1fe32144.png?x-oss-process=image/resize,w_286,h_130) +车辆仪表区用于**模拟车机的仪表盘**,从左到右依次用于展示能源余量、时速、油门大小。其中时速表盘、油门大小可随操控按钮(2.4节介绍)动态加载。时速表盘使用Canvas画布实现,油门表使用Gauge组件实现。具体实现可参考这篇帖子[首页aito视频&Canvas绘制仪表盘](https://ost.51cto.com/posts/14312) + +## 2.4 操控页面——车辆控制 +车辆控制区用于**模拟车辆实际操控**,如一键启动、驾驶(油门、方向、刹车)、开门、灯光、甚至是升降Dream Car的尾翼。UI使用Buttom、Image基础组件布局,实现比较简单。控件触发事件后,调用Socket接口,将控制量发送到目标ip中(hi3861中),下面代码**举例说明如何将一键启动触发后将消息通过socket接口发送出去**,关于Socket tcp通信实现可参考该文档[Socket连接](https://docs.openharmony.cn/pages/v3.1/zh-cn/application-dev/reference/apis/js-apis-socket.md/)。 +![image.png](https://dl-harmonyos.51cto.com/images/202207/e8cf561537de71d6da3936d4d52f7f95676645.png?x-oss-process=image/resize,w_364,h_183) +```js + tcpSend() { + tcp.getState().then((data) => { + if (data.isConnected) { + //发送消息 + tcp.send( + { data: this.message_send, } + ).then(() => { + prompt.showToast({message:"send message successful"}) + }).catch((error) => { + prompt.showToast({message:"send failed"}) + }) + } else { + prompt.showToast({message:"tcp not connect"}) + } + }) + } + //一键启动 + Button({ type: ButtonType.Circle, stateEffect: true }) { + Image($r('app.media.engine')).objectFit(ImageFit.Contain) + } + .width(90) + .height(90) + .margin({ top: 1, left: 80 }) + .backgroundColor(this.engine_btn_col) + .onClick(() => { + this.car_gauge_col = 'white' + this.srcs = $r('app.media.ferrari_start') + this.controller.start() + this.click_times += 1 + this.tcpConnect() + if (this.click_times % 2 != 0) { + this.engine_btn_col = 0x32c5ef + this.update_canvas(); + } + else + { + this.engine_btn_col = 0xCBD3D0 + this.click_times = 0; + } + prompt.showToast({ + message: "Start Engine:" + this.car_velocity, + }) + }) +``` +## 2.5 系统设置与视频播放 +系统设置功能页面主要使用brightness接口调节屏幕亮度。另外,还使用class自定义车辆信息类,为动态展示车辆提供参考。视频播放使用Video组件实现,音频的输出经过车载的音频放大器播放,关键代码如下: +```js +import brightness from '@ohos.brightness'; + Row() + { + Text('亮度').fontColor('blue').fontSize('35').width('10%').borderRadius(30).margin({top:10,left:30}) + + Slider({ + value: this.brightness, + min: 100, + max: 255, + step: 1, + style: SliderStyle.OutSet + }) + .width('80%').height('100%') .blockColor('blue').trackColor(Color.Black) + .onChange((value: number, mode: SliderChangeMode) => { + this.brightness = value + brightness.setValue(this.brightness); + console.info('value:' + value + 'mode:' + mode.toString()) + }).width('75%') + Text(this.brightness.toFixed(0)).fontSize(30).width('15%').fontColor('blue') + }.height('10%').backgroundColor('white') +``` + +# 3. OHCar车辆管家介绍 +智能时代,车载系统不应该仅仅是独立的系统,基于OpenHarmony的OHCar可为用户提供优质全面的服务。车辆管家运行于HarmonyOS 2.0 的移动端,依托NFC短距通信协议,通过碰一碰的交互方式,将手机和OHCar连接起来。然后通过手机端的原子化服务能力,快速完成配网、连接OHCar,提供无钥匙开门、监测车辆信息的服务。 类似的开发案例可参考本人这篇教程 [碰一碰系列分享总贴](https://ost.51cto.com/posts/12658)。 +## 3.1 UI开发 +UI布局使用js开发,具体方式参考该文档 [JS开发APP](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide-low-code-0000001158284713)。车辆管家UI直观,展示信息如下图,电量、位置信息的获取来自于hi3861端,空调、车门控制消息也发送至hi3861端。 +![车辆管家.jpg](https://dl-harmonyos.51cto.com/images/202207/f778e3967a825d96285170c2f949660e3550d5.jpg?x-oss-process=image/resize,w_820,h_4032) +## 3.2 碰一碰与数据传输 +车辆管家的技术核心是调用JS接口完成设备配网、消息发送。获取设备ID、发送消息接口核心代码如下(可参考本贴[JS通信接口](https://ost.51cto.com/posts/12772)): +```js +//需引入 import {getApp} from '../../common.js'; + sendMessage() + { + var message = this.app_msg; + let commonInfo = { + sessionId: getApp(this).ConfigParams.deviceInfo.sessionId + }; + getApp(this).NetConfig.sendMessage(commonInfo, message,(result)=>{ + if(result.code ==0) { prompt.showToast({message:'发送成功'})} + else{prompt.showToast({message:'发送失败'})} + }); + }, +``` + +# 4. OHCar南向开发 +南向开发分为三部分,分别对应车载系统UI南向开发、车辆管家APP南向开发、车辆硬件实时控制系统实现(电机、灯光等) +## 4.1 车载系统协同 +车载系统协同依靠dayu200与hi3861之间的TCP通信,南向开发也主要是针对该通信数据进行处理,hi3861端作为TCP服务器,接收dayu200发送的操作指令。hi3861也可采集车辆温湿度、烟雾信息,有需求可上报至dayu200端。如何建立二者之间的TCP连接可参考本人这篇教程 [#DAYU200#体验官Hi3861与dayu200通信](https://ost.51cto.com/posts/13300) 。下图是南向功能框架。 +![image.png](https://dl-harmonyos.51cto.com/images/202207/5417ee03912be6c5623810b0cbc107931e26e7.png?x-oss-process=image/resize,w_820,h_348) +下面给出将dayu200数据发送值车载mcu、将温湿度烟雾发送至dayu200的关键代码: +```c +while (1) + { + AHT20_Calibrate(); + AHT20_StartMeasure(); + AHT20_GetMeasureResult(&EnvData.temp_val, &EnvData.humi_val); + EnvData.ppm_val = Get_MQ2_PPM(); + if ((ret = recv(new_fd, recvbuf, sizeof(recvbuf), 0)) == -1) + { + printf("recv error \r\n"); + } + printf("recv :%s\r\n", recvbuf); + const unsigned char msg_cmd = recvbuf[0]; + //hi_uart_write(1, &msg_cmd, 1); + UartWrite(1, &msg_cmd, 1); + if (msg_cmd == 'x') + { + GpioSetOutputVal(WIFI_IOT_IO_NAME_GPIO_14, 0); + hi_udelay(80000); + GpioSetOutputVal(WIFI_IOT_IO_NAME_GPIO_14, 1); + } + if (msg_cmd == 'y') + { + GpioSetOutputVal(WIFI_IOT_IO_NAME_GPIO_8, 0); + hi_udelay(80000); + GpioSetOutputVal(WIFI_IOT_IO_NAME_GPIO_8, 1); + } + Float2String(buf, EnvData.humi_val, 2); + if ((ret = send(new_fd, buf, strlen(buf) + 1, 0)) == -1) + { + perror("send : "); + } + Float2String(buf, EnvData.ppm_val, 2); + if ((ret = send(new_fd, buf, strlen(buf) + 1, 0)) == -1) + { + perror("send : "); + } + Float2String(buf, EnvData.ppm_val, 2); + if ((ret = send(new_fd, buf, strlen(buf) + 1, 0)) == -1) + { + perror("send : "); + } + GpioSetOutputVal(WIFI_IOT_IO_NAME_GPIO_14, 0); + GpioSetOutputVal(WIFI_IOT_IO_NAME_GPIO_8, 0); + sleep(1); + } +``` + +## 4.2 车辆管家协同 +车载系统协同依靠移动端NFC短距通信,碰一碰车门上的NFC标签后,启动原子化服务后与hi3861通信,hi3861将GPS模块采集位置信息、电压采集模块采集汽车电量传输至车辆管家APP端;同时也可接收开门指令,实现无钥匙解锁。该部分南向工程架构如下(详细见附件): +![image.png](https://dl-harmonyos.51cto.com/images/202207/98e0061255f3c43e1fc0708ee2f62dac2aff5f.png?x-oss-process=image/resize,w_633,h_329) +NAN配网关键接口如下,具体参考本人这篇教程[设备配网](https://ost.51cto.com/posts/12786) +```c +include: +│ ├── netcfg.h // 无感配网注册相关接口 +│ ├── network_config_service.h //无感配网相关头文件。 +libs: +├── libs +│ ├── libhilinkadapter_3861.a // 无感配网相关库文件。 +│ └── libnetcfgdevicesdk.a // 无感配网相关库文件。 +src: + ├── netcfg.c // NAN相关操作和联网动作 +const char *g_ssid = "OHCar "; +const char *g_pinCode = "11111111"; +const char *g_productId = "1"; +const char *g_sn = "0123/.,.,4567890123450123456789012345"; +··· +devInfo[0].key = "productId"; +devInfo[1].key = "sn"; +devInfo[0].value = g_productId; +devInfo[1].value = g_sn; +ret = StartNetCfg(devInfo, DEVICE_INFO_NUM, NETCFG_SOFTAP_NAN); //SoftAP and NAN模式 + +//上报电量、位置信息、控制模拟空调 + if (strcmp(app_msg, "turn off air") == 0) + { + IoTGpioSetOutputVal(FAN_IO1, IOT_GPIO_VALUE0); + IoTPwmStart(1, 0, 80000); + printf("turn off air \r\n"); + app_msg[0] = '9'; + return; + } + + if (strcmp(app_msg, "car location") == 0) + { + // Bluetooth_read(location2app, 18) //室内GPS信号弱 + strncpy(location2app, "N:110.20 E:19.220 addr", 18); + SendRawData(&location2app); // 将消息发到FA + printf("car location\r\n"); + app_msg[0] = '9'; + return; + } + + if (strcmp(app_msg, "car fuel") == 0) + { + fuel_val = GetVoltage(); + float percent_vol = 1000 * fuel_val / FULL_FUEL; + Float2String(percent_vol, &temp_str, 2); + strncpy(fuel2app, temp_str, 5); + SendRawData(&fuel2app); // 将消息发到FA + printf("get car fuel \r\n"); + app_msg[0] = '9'; + return; + } +``` +## 4.3 车辆控制 +![image.png](https://dl-harmonyos.51cto.com/images/202207/51d378f711a75739b645474fcfb446437733bb.png?x-oss-process=image/resize,w_820,h_945)![iot2.jpg](https://dl-harmonyos.51cto.com/images/202207/b672daa4048b94209ca221efe61f4cc8d6709d.png?x-oss-process=image/resize,w_432,h_728) +车辆电机、车门、尾翼以及灯光控制使用另一块io接口多一些的mcu实现(5组灯光、6个舵机),mcu实时接收两块hi3861的控制指令,完成最底层的控制。从车载系统到车辆管家,再到hi3861,最终到mcu,遵循的报文如下,有助于了解项目: +```c +typedef enum MSG_CMD { + MOVE_GO = 'a', + MOVE_BACK, + MOVE_LEFT, + MOVE_RIGHT, //移动 + OPEN_LEFT_DOOR, + CLOSE_LEFT_DOOR, + OPEN_RIGHT_DOOR, + CLOSE_RIGHT_DOOR,//车门 + SPOILER_UP, + SPOILER_DOWN, //尾翼 + MOVE_GO_LIGHT, + MOVE_BACK_LIGHT, + WARRING_LIGHT_ON, + WARRING_LIGHT_OFF +}; +``` + +# 总结 +从技术上讲,实际的车载系统比文中说的要复杂、严苛很多。不过openHarmony作为万物互联时代下的产物,未来用于车载系统还是值得期待的。借此项目可了解openHarmony以及在dayu200上的开发方式,学习ARkUI框架、est语言。 +都说田家少闲月,五月人倍忙,自从疫情之后很多事被打乱,一到窗口期就是“5月",不知不觉dayu200体验官活动也接近尾声,感谢平台的支持与各位老师的直播分享,让我天马行空想法得以实现。 +# 附件说明: +OHCar_CenterSystem:dayu200运行的车载系统-源码 +OHCar_phone_app: 移动端车辆管家-源码 +OHCar_nfc: 车辆管家南向开发-源码 +OHCar_tcp: 车载系统南向开发-源码 + \ No newline at end of file