智能水表(RB软工移203 李龙基)
成果展示
小项目全部部署在基于openharmony,效果如图:
图片仅供演示,系统在设备中实际运行良好
一、确定功能
确定实现功能如下:
- 获取传感器数据(水流量传感器)并实时渲染在屏幕上。
- 可以对水阀进行控制(发送控制指令)。
- 自动(手动)上报数据,发送给服务器/上位机以供后续处理。
- 其他(如统计数据,计算费用等…)。
所有功能实现的代码都将贴在文章末尾,以供编译参考。
二、添加南向接口并调用
注意:南向部分需要自己实现,本项目内容可以参考 基于 OpenHarmony 的水流量监测系统。
其本质依然是gpio操作和一些基础知识组合,会点led后就可以放心大胆的上啦~
依照物联网项目的基本架构:端管云用,我们北向也可以类比依照这四点实现功能设计(不涉及后端)。
- 端:即感知识别层,用于信息生成。
- 管:信息的传输,用于信息传输,具体为调用通信接口与上位机通信。
- 云:信息处理。
- 用:信息应用:如微信小程序设计。
我们参考Led点灯的接口,在 @system.app.d.ts 末尾添加如下接口声明:
static ledcontrol(options: { code: number; success?: (res: string) => void; fail?: (res: string, code: number) => void; complete?: () => void; }): void;
没错,就是led灯的接口连名字都不带改的,我们利用回调函数返回的对象,在res对象里添加传感器数据:值 作为data内容,发送的命令码,即为对水阀的控制开关指令。
在处理页面逻辑的文件上,我们也要添加主动调用接口的方法:
/*index.js*/app.ledcontrol({ code:led.open, success(res){ //解析数据并保存 }, fail(res,code){ }, complete(){ }})
三、添加通信接口(可选)
如有需要可以添加网络请求接粗体口,在api7(及以前)可以用@system.fetch,aip6后推荐使用@ohos.net.http (fetch不在维护,建议弃用)
//首先要导入鸿蒙的网络请求模块import fetch from '@system.fetch';try{ fetch.fetch({ url: 'http://127.0.0.1'+'?data='+this.rate, //填写服务器地址 responseType: 'json', success: res => { this.code3="已连接" let data = JSON.parse(res.data); //必须要加上 console.log(res.data) } }); console.log("手动上报数据") } catch(e){ console.log(e); this.code3="连接失败" }
如果报错,尝试在配置文件config.json里修改网络权限。
默认在module模块下:
"reqPermissions": [ { "name": "ohos.permission.GET_NETWORK_INFO" }, { "name": "ohos.permission.SET_NETWORK_INFO" }, { "name": "ohos.permission.INTERNET" } ],
轻量级穿戴设备似乎不支持网络通信接口,最后弃用改为南向上传(此处代码仅供参考)。
提醒:由于需要传输的是南向部分传输过来的传感器数据,所以建议也在南向部分处理数据上传,以减小时延和精度误差等。
四、其他
上位机(这里以微信小程序示例)主要负责远程监控与管理,设计如下:
控制面板页面设计 |
统计页面设计 |
|
|
页面仅代表功能演示,不代表实际数据。
项目源代码:
index.hml
退出 运行时长 0d 0h {{min}}m {{sec}} s 设备{{info}} {{ title }} {{rate_L}} L {{rate}} ml 运行状态 工作状态 : {{code1}} 水阀状态 : {{code2}} 上位机 : {{code3}} 手动上报 关闭水阀 开阀计水
index.css
.container { width: 100%; height: 100%; flex-direction: column; align-items: center;}swiper{ height: 60%; width: 100%; background-color: greenyellow;}.main{ margin: 10px; width: 100%; height: 60%; flex-direction:row; align-items: center; /*background-color: cadetblue;*/}.title_l { width: 50%; height: 280px; font-size: 40px; text-align: center; flex-direction: column;}.title_r { margin-top: 10px; width: 40%; height: 240px; font-size: 40px; text-align: center; background-color: black; border-radius: 50px; flex-direction: column; opacity: 0.9; border-color: white; border-width: 2px; padding: 10px; margin: 10px;}.text{ font-size: 40px; text-align: center; width: 100%; height: 35%;}.text_small{ font-size: 33px; text-align: center; width: 100%; height: 20%;}.text_s{ font-size: 30px; left: 12px; width: 100%; margin-left:10px ;}.ledImg{ width: 200px; height: 150px; margin-top: 10px; font-size: 40px;}.ledAction{ height: 50px; width: 100%; flex-direction: row; justify-content: space-around; align-items: center;}.ledAction-view{ width: 120px; height: 50px; flex-direction: column; justify-content: center; align-items: center;}.ledAction-img{ width: 60px; height: 60px;}.ledAction-btn{ width: 120px; margin-top: 10px; font-size: 30px; text-align: center;}.title-view{ width: 100%; height: 60px; margin: 1px; flex-direction: column; display: flex; background-color: midnightblue;}.title{ width: 100%; height: 300px; margin: 1px; flex-direction: row; display: flex; background-color: darkblue;}.top-view{ height: 60px; width: 100%; flex-direction: row; justify-content: center; align-items: center; display: flex; text-align: center;}.back-img{ height: 30px; width: 30px; margin-left: 10px;}.back-btn{ font-size: 30px; width: 25%; padding: 20px;}.date{ font-size: 30px; width: 50%;}.deviceid{ font-size: 30px; width: 20%;}
index.js
var led = {open:1,close:0,change:2}import app from '@system.app';import router from "@system.router";export default { data: { title: '当日累计:', statu:'0', rate: 0, rate_L:0, sec:0, min:0, info:"正常", tmp_rate:-1, curr_rate:0, code1:0, code2:"关闭", code3:"未连接", timer:0, time:0, }, onInit(){ //初始化 //this.openDoor() this.startTimer() this.info="初始化.." }, exit(e){ console.log("terminate!") app.terminate() }, startTimer() { this.time= setInterval(()=>{ //this.getRate() this.runtime() },1000); }, runtime(){ this.sec++ if(this.sec===60){ this.sec=0 this.min++ } }, getRate(){ let that=this try{ //that.rate++ app.ledcontrol({ code:led.open, success(res){ //console.log("data show1") that.tmp_rate=that.rate that.rate = (Number(JSON.stringify(res.led_status))) that.curr_rate=that.rate-that.tmp_rate that.code1=that.curr_rate }, fail(res,code){ console.log("get fail") }, complete(){ } }) }catch(e){ console.log(err) this.device_id="err" } }, openDoor(e){ console.log("open") this.info="运行中" this.code2="开启" //function function closeapp() { console.log("close") //clearInterval(timer); //数据归0 } const ShowRate = ()=> { this.time++ this.device_id++; //console.log(this.time) //if(this.time===300)closeapp() let that=this app.ledcontrol({ code:led.open, success(res){ //console.log("data show1") that.rate = (Number(JSON.stringify(res.led_status))) }, fail(res,code){ }, complete(){ } }) } this.rate=-1; try{ this.timer= setInterval(function(){ShowRate()},100); }catch(e){ console.error("err"+e) this.info="计时器故障" } /*这里有一个关于类里This的指向问题,这里使用匿名函数处理 ()=>{} 不可以使用function(),因为由于类特性,在function里的this指向change_per_second的rate变量,而无法访问到default类的rate变量 但是匿名函数里this会逐层上找 */ }, close(e){ console.log("close timer") this.info="关闭" this.code2="关闭" try{ clearInterval(this.timer) }catch{ this.info="故障" } //this.rate =0; let that = this app.ledcontrol({ //关闭水阀命令 code:led.close, success(res){ that.statu = res.led_status }, fail(res,code){ }, complete(){ } }) }, datalist(){ }, senddata(){ try{ fetch.fetch({ url: 'http://127.0.0.1'+'?data='+this.rate, //填写服务器地址 responseType: 'json', success: res => { this.code3="已连接" let data = JSON.parse(res.data); //必须要加上 console.log(res.data) } }); console.log("手动上报数据") } catch(e){ console.log(e); this.code3="连接失败" } },}