(轉載請注明來源: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', width: _self.gaugeWidth * _self.pixelRatio, //儀表盤背景的寬度 startAngle: 0.75, endAngle: 0.25, startNumber: start, endNumber: end, splitLine: { fixRadius: 0, splitNumber: step, width: _self.gaugeWidth * _self.pixelRatio, //儀表盤背景的寬度 color: '#FFFFFF', childNumber: 5, childWidth: _self.gaugeWidth * 0.2 * _self.pixelRatio, //儀表盤背景的寬度 }, pointer: { width: _self.gaugeWidth * 0.8 * _self.pixelRatio, //指針寬度 color: 'auto' } } }, background: '#FFFFFF', pixelRatio: _self.pixelRatio, categories: chartData.categories, series: chartData.series, animation: true, width: _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>
- 手機遠程訪問效果圖: