(转载请注明来源:cnblogs coder-fang)
- 开发说明:python开发websocket服务与树莓派GPIO控制,html做为前端展示,这里使用uni-app UI框架。
- 服务及架构图示:
- websocket服务代码:
from websocket_server import WebsocketServer import mymqtt import time,json class WebSock: def __init__(self,port): self.server = WebsocketServer(port, host='0.0.0.0') self.server.set_fn_new_client(self.new_client) self.server.set_fn_message_received(self.recv_fromclient) self.server.set_fn_client_left(self.client_left) self.clients=[] self.mqtt = mymqtt.MyMqtt(self.on_mqttconnect,self.on_mqttdisconnect,self.on_mqttmessage) self.status={"cmd":"gettemp","temp":25,"humi":33,"timespan":time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) } def on_mqttconnect(self,client, userdata, flags, rc): print("Conned broker with result code: " + str(rc)) def on_mqttdisconnect(self,client, userdata, flags, rc): print("DisConned broker with result code: " + str(rc)) def on_mqttmessage(self,client, userdata, msg): if msg.topic == "dev/up/temp": newmsg = str(msg.payload, encoding="utf-8") self.status = json.loads(newmsg) self.server.send_message_to_all(json.dumps(self.status)) def new_client(self,client, server): self.clients.append(client) print("newclient:"+str(len(self.clients))) def recv_fromclient(self,client, server, message): print("getsockreq:"+message) if(message =='gettemp'): self.mqtt.Pub('dev/get/temp',"") def client_left(self,client, server): self.clients.remove(client) print("client left:" +str(len(self.clients))) def Run(self): self.mqtt.Start("dev/up/temp") self.server.run_forever() def Stop(self): self.mqtt.Stop() self.server.shutdown()
- 树莓派dht11 GPIO代码:
import RPi.GPIO as GPIO import time class DHT11(): def __init__(self,pin): self.channel=pin GPIO.setmode(GPIO.BCM) #以BCM编码格式 time.sleep(1) #时延一秒 def GetTemp(self): try: data=[] j=0 GPIO.setup(self.channel, GPIO.OUT) GPIO.output(self.channel, GPIO.LOW) time.sleep(0.02) #给信号提示传感器开始工作 GPIO.output(self.channel, GPIO.HIGH) GPIO.setup(self.channel, GPIO.IN) while GPIO.input(self.channel) == GPIO.HIGH: continue while GPIO.input(self.channel) == GPIO.LOW: continue while GPIO.input(self.channel) == GPIO.HIGH: continue while j < 40: k = 0 while GPIO.input(self.channel) == GPIO.LOW: continue while GPIO.input(self.channel) == GPIO.HIGH: k += 1 if k > 100: break if k < 12: data.append(0) else: data.append(1) j += 1 humidity_bit = data[0:8] #分组 humidity_point_bit = data[8:16] temperature_bit = data[16:24] temperature_point_bit = data[24:32] check_bit = data[32:40] humidity = 0 humidity_point = 0 temperature = 0 temperature_point = 0 check = 0 for i in range(8): humidity += humidity_bit[i] * 2 ** (7 - i) #转换成十进制数据 humidity_point += humidity_point_bit[i] * 2 ** (7 - i) temperature += temperature_bit[i] * 2 ** (7 - i) temperature_point += temperature_point_bit[i] * 2 ** (7 - i) check += check_bit[i] * 2 ** (7 - i) tmp = humidity + humidity_point + temperature + temperature_point #十进制的数据相加 if check == tmp: #数据校验,相等则输出 print("temperature : "+str(temperature)+", humidity : " + str(humidity)) return True,temperature,humidity else: #错误输出错误信息,和校验数据 print("wrong") print("temperature : "+ str(temperature)+", humidity : " + str(humidity)+ " check : "+ str(check)+ " tmp : "+ str(tmp)) return False,-20,0 except: return False,-20,0 def Cleanup(self): GPIO.cleanup(self.channel)
- 树莓派mqtt客户代码:
import mymqtt,time,random,json from clients.dht11 import DHT11 class DevCtrl: def __init__(self): self.mqtt = mymqtt.MyMqtt(self.on_mqttconnect,self.on_mqttdisconnect,self.on_mqttmessage) self.dht11 = DHT11(4) def on_mqttconnect(self,client, userdata, flags, rc): print("Conned broker with result code: " + str(rc)) def on_mqttdisconnect(self,client, userdata, flags, rc): print("DisConned broker with result code: " + str(rc)) def on_mqttmessage(self,client, userdata, msg): if(msg.topic == "dev/get/temp"): print("gettemp reques") ret,temp,humi = self.dht11.GetTemp() status={"cmd":"gettemp","temp":temp,"humi":humi,"timespan":time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) } self.mqtt.Pub("dev/up/temp",json.dumps( status)) def Run(self): self.mqtt.Start("dev/get/temp",0,True) def Stop(self): self.dht11.Cleanup() self.mqtt.Stop(True)
- UI代码:
<template> <view class="content "> <view class=" uni-flex uni-row "> <text>上次更新时间:{{updatetext}}</text> <button @click="getData()" :disabled="isloading" style="height: 70%;margin: 10rpx;padding: 0rpx;" type="primary" :loading="isloading" size="mini">刷新</button> </view> <view class="qiun-charts "> <canvas canvas-id="canvasGauge" id="canvasGauge" class="charts"></canvas> </view> <view class="qiun-charts "> <canvas canvas-id="canvasGaugeshidu" id="canvasGaugeshidu" class="charts"></canvas> </view> <Prompt title="输入设备密码" :visible.sync="promptVisible" placeholder="输入密码" @confirm="clickPromptConfirm" mainColor="#e74a39"> <!-- 这里放入slot内容--> </Prompt> </view> </template> <script> import uCharts from '@/components/u-charts/u-charts/u-charts.js'; import Prompt from '@/components/zz-prompt/index.vue' var _self; var canvaGauge = null; export default { components: { Prompt }, data() { return { cWidth: '', cHeight: '', updatetext: '', isloading: true, pixelRatio: 1, gaugeWidth: 15, IsOpen: false, promptVisible: true } }, onLoad() { _self = this; this.cWidth = uni.upx2px(750); this.cHeight = uni.upx2px(500); //this.getServerData(); uni.connectSocket({ url: 'ws://192.168.3.71:40000', fail: () => { console.log("disconnected sock"); uni.showModal({ content: 'Can not connect web server', showCancel: false }); } }); uni.onSocketOpen(function() { console.log("WebSock opened"); _self.IsOpen = true; }); uni.onSocketClose(function(res) { console.log('WebSocket 已关闭!'); _self.IsOpen = false; }); uni.onSocketError(function(res) { uni.showModal({ content: 'Can not connect web server', showCancel: false }); _self.IsOpen = false; }); uni.onSocketMessage(function(res) { console.log("recvmsg:" + res.data); var ret = JSON.parse(res.data); if (ret.cmd == "gettemp") { _self.DrawData(ret.temp, ret.humi) _self.updatetext = ret.timespan } }); }, methods: { clickPromptConfirm(val) { if (val == "123123") { this.promptVisible = false; _self.getData(); _self.isloading = true } else { uni.showModal({ content: '密码不正确', showCancel: false }); } }, getData() { _self = this; _self.isloading = true; uni.sendSocketMessage({ data: "gettemp" }); setTimeout(() => { _self.isloading = false; }, 3000) }, DrawData(temp, humi) { let Gauge = { categories: [], series: [] }; //这里我后台返回的是数组,所以用等于,如果您后台返回的是单条数据,需要push进去 Gauge.categories = [{ "value": 0.43, "color": "#1890ff" }, { "value": 0.56, "color": "#2fc25b" }, { "value": 1, "color": "#f04864" }]; Gauge.series = [{ "name": "温度", "data": ((temp) / 100 + 0.2) / 0.8, "value": temp }]; let Gaugeshidu = { categories: [], series: [] }; //这里我后台返回的是数组,所以用等于,如果您后台返回的是单条数据,需要push进去 Gaugeshidu.categories = [{ "value": 0.45, "color": "#1890ff" }, { "value": 0.75, "color": "#2fc25b" }, { "value": 1, "color": "#f04864" }]; Gaugeshidu.series = [{ "name": "湿度", "data": humi / 100 }]; _self.showGauge("canvasGauge", Gauge, Gauge.series[0].value+"°C", -20, 60, 8); _self.showGauge("canvasGaugeshidu", Gaugeshidu, humi+"%"); }, showGauge(canvasId, chartData, name, start = 0, end = 100, step = 10) { canvaGauge = new uCharts({ $this: _self, canvasId: canvasId, type: 'gauge', fontSize: 11, legend: false, title: { name: name, color: chartData.categories[1].color, fontSize: 25 * _self.pixelRatio, offsetY: 50 * _self.pixelRatio, //新增参数,自定义调整Y轴文案距离 }, subtitle: { name: chartData.series[0].name, color: '#666666', fontSize: 15 * _self.pixelRatio, offsetY: -50 * _self.pixelRatio, //新增参数,自定义调整Y轴文案距离 }, extra: { gauge: { type: 'default', _self.gaugeWidth * _self.pixelRatio, //仪表盘背景的宽度 startAngle: 0.75, endAngle: 0.25, startNumber: start, endNumber: end, splitLine: { fixRadius: 0, splitNumber: step, _self.gaugeWidth * _self.pixelRatio, //仪表盘背景的宽度 color: '#FFFFFF', childNumber: 5, childWidth: _self.gaugeWidth * 0.2 * _self.pixelRatio, //仪表盘背景的宽度 }, pointer: { _self.gaugeWidth * 0.8 * _self.pixelRatio, //指针宽度 color: 'auto' } } }, background: '#FFFFFF', pixelRatio: _self.pixelRatio, categories: chartData.categories, series: chartData.series, animation: true, _self.cWidth * _self.pixelRatio, height: _self.cHeight * _self.pixelRatio, dataLabel: true, }); } } } </script> <style> /*样式的width和height一定要与定义的cWidth和cHeight相对应*/ .qiun-charts { width: 750upx; height: 500upx; background-color: #FFFFFF; } .charts { width: 750upx; height: 500upx; background-color: #FFFFFF; } .content { align-content: center; -webkit-box-align: center; margin-left: 50rpx; } .fixheader { align-items: center; z-index: 9000; background-color: transparent; position: fixed; width: 100%; height: auto; overflow-y: auto; } </style>
- 手机远程访问效果图: