经历过装修的社会人,不免总是在担心一个事情,那就是环境中甲醛浓度到底现在是多少?会不会对人的生活有影响,我们今天一起来探讨甲醛的web界面显示:
本次我们使用到的甲醛模组是ze08-ch2o
又是一个串口,无非就是工作环境是啥,管脚定义是啥的?
工作电压要求是3.7V~5.5V的电压,(*附件:萤火工场·CEK8903飞腾派硬件规格书-V1.01.pdf)
就近原则,选择4,6管脚作为电源输入,8,10作为通讯口。
模块管脚定义,物理层妥妥的搞完。
协议层,妥妥的使用主动上传,省去发起的操作;
Django+pyserial实现后端数据的读取和存储
首先更改Django中的models.py
class CH2O(models.Model):
ch2o = models.FloatField()
add_time = models.DateTimeField(auto_now_add=True)
class Meta:
db_table='ze08'
处理一下数据库
python manage.py makemigrations chat
python manage.py sqlmigrate chat 0002
python manage.py migrate
用pyserial实现甲醛数据的获取,同时实现数据存储到数据库
import threading
import time
import serial
from .models import CH2O
class MySerial(object):
def __init__(self):
self.ser = None
def get_port_list(self):
port_list = list(serial.tools.list_ports.comports())
return port_list
def open_serial_port(self, port, baud):
try:
self.ser = serial.Serial(port, baud, timeout=0.3, interCharTimeout=0.05)
except Exception as e:
self.ser = None
raise Exception(e)
def close_serial_port(self):
if self.ser == None:
raise Exception("no selected serial")
try:
self.ser.close()
self.ser = None
except Exception as e:
raise Exception(e)
def read(self):
if self.ser == None:
raise Exception("no selected serial")
try:
time.sleep(0.001)
return self.ser.read(self.ser.in_waiting)
except Exception as e:
raise Exception(e)
class Ze08Ch2o(object):
def __init__(self, device, baud) -> None:
self.serial = MySerial()
self.serial.open_serial_port(device, baud)
self.recv_thread_ = threading.Thread(target=self.recv_thread)
self.recv_thread_.start()
def calc_checksum(self, bytes_value):
checksum = 0
for byte_ in bytes_value:
checksum += byte_
checksum = checksum % (0xff + 1)
if checksum == 0xff:
return True
return False
def recv_thread(self):
recv_buffer = bytes()
while True:
try:
recv_buffer += self.serial.read()
except Exception as e:
print(str(e))
if len(recv_buffer) < 9:
time.sleep(0.01)
continue
else:
if recv_buffer[0] != 0xff:
recv_buffer = recv_buffer[1:]
else:
if len(recv_buffer) >= 9:
phase_buffer = recv_buffer[:9]
recv_buffer = recv_buffer[9:]
if self.calc_checksum(phase_buffer) == True:
int_formaldehyde = int.from_bytes(phase_buffer[4:6], byteorder='big')
float_formaldehyde = int_formaldehyde /1000.0 *1.25
CH2O.objects.create(ch2o=float_formaldehyde)
print("formaldehyde: {:>.4f}".format(float_formaldehyde))
time.sleep(0.001)
#z = Ze08Ch2o('/dev/ttyAMA2', 9600)
在channels实现的websocket上添加关于甲醛浓度的代码,实现从数据库中读取甲醛浓度数据和aht10的温湿度数据
def my_send(self):
if self.clients:
wendu = Temperature.objects.order_by('-id').first()
ch2o = CH2O.objects.order_by('-id').first()
self.send(text_data=json.dumps({'humid': wendu.humid, 'thermo': wendu.thermo, 'formaldehyde': ch2o.ch2o}))
#self.send(text_data=json.dumps({"message": "hello world"}))
在chatroom的html中增加甲醛数据
<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="100" rows="20"></textarea><br>
<input id="chat-message-input" type="text" size="100"><br>
<input id="chat-message-submit" type="button" value="Send">
{{ room_name|json_script:"room-name" }}
<script>
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const chatSocket = new WebSocket(
'ws://'
+ window.location.host
+ '/ws/chat/'
+ roomName
+ '/'
);
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
document.querySelector('#chat-log').value += ("thermo: " + data.thermo.toFixed(2) + " humid:" + data.humid.toFixed(2)+ " formaldehyde: " + data.formaldehyde.toFixed(4) + '\n');
};
chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function(e) {
if (e.key === 'Enter') { // enter, return
document.querySelector('#chat-message-submit').click();
}
};
document.querySelector('#chat-message-submit').onclick = function(e) {
const messageInputDom = document.querySelector('#chat-message-input');
const message = messageInputDom.value;
chatSocket.send(JSON.stringify({
'message': message
}));
messageInputDom.value = '';
};
</script>
</body>
</html>
后端的实时甲醛数据已经搞定;
一段时间的甲醛数据的处理继续撸上;
class formaldehydeView(View):
def get(self, request):
# 当前时间
now = datetime.datetime.now()
# 显示10秒前的甲醛浓度
datetimelist = []
ch2o_list = []
start_time = datetime.datetime.strftime(now - datetime.timedelta(seconds=10),"%Y-%m-%d %H:%M:%S") # 当前时间
end_time = datetime.datetime.strftime(now,"%Y-%m-%d %H:%M:%S") # 结束时间
# 获取在开始时间之后 结束时间之前的的甲醛浓度
ch2o_fd = CH2O.objects.filter(add_time__gte=start_time,add_time__lte=end_time).all()
for hcho in ch2o_fd:
ch2o_list.append(hcho.ch2o)
datetimelist.append(datetime.datetime.strftime(hcho.add_time,"%Y-%m-%d %H:%M:%S.%f"))
msg=json.dumps({'datetimelist': datetimelist, 'ch2o_list': ch2o_list})
return HttpResponse(msg)
在project的路由中添加甲醛浓度的访问
"""
URL configuration for mysite project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path,include
from chat.views import ThermoHumidView, formaldehydeView
urlpatterns = [
path("chat/", include("chat.urls")),
path('admin/', admin.site.urls),
path('aht10/', ThermoHumidView.as_view()),
path('ch2o/', formaldehydeView.as_view()),
]
ok,后端正式做完。
VUE3的前端就这么简单处理以下把
<!--
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
-->
<template>
<!-- <header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" /> -->
<div>
当前的温度是:{{thermo}}℃,
当前的湿度是: {{humid}}%,
当前的甲醛浓度: {{formaldehyde}}mg/m3
<div id="thermo" style="width: 1500px;height: 300px;" />
<div id="humid" style="width: 1500px;height: 300px;" />
<div id="formaldehyde" style="width: 1500px;height: 300px;" />
</div>
<!-- </header> -->
</template>
<!--
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
-->
<script>
import axios from 'axios';
import * as echarts from 'echarts';
//import {init, EChartsOption} from 'echarts';
export default {
data() {
return {
thermo:0 ,
humid: 0,
formaldehyde: 0,
aht10: {
datetime_list: [],
thermo_list: [],
humid_list: [],
},
//datetimelist: [],
//thermolist: [],
//humidlist: [],
ch2o: {
formaldehyde_list: 0,
datetime_list: [],
},
response: ' ',
}
},
methods: {
thermoView(){
var chartDom = document.getElementById('thermo');
var myChart = echarts.init(chartDom);
let option = {
xAxis: {
type: 'category',
//data: this.aht10_datetimelist
data: this.aht10.datetime_list,
},
yAxis: {
type: 'value',
},
series: [{
//data: this.thermolist,
data: this.aht10.thermo_list,
type: 'line',
smooth: true
}]
};
myChart.setOption(option);
},
humidView(){
var chartDom = document.getElementById('humid');
var myChart = echarts.init(chartDom);
let option = {
xAxis: {
type: 'category',
//data: this.datetimelist
data: this.aht10.datetime_list,
},
yAxis: {
type: 'value',
},
series: [{
//data: this.humidlist,
data: this.aht10.humid_list,
type: 'line',
smooth: true
}]
};
myChart.setOption(option);
},
formaldehydeView(){
var chartDom = document.getElementById('formaldehyde');
var myChart = echarts.init(chartDom);
let option = {
xAxis: {
type: 'category',
//data: this.datetimelist
data: this.ch2o.datetime_list,
},
yAxis: {
type: 'value',
},
series: [{
//data: this.humidlist,
data: this.ch2o.formaldehyde_list,
type: 'line',
smooth: true
}]
};
myChart.setOption(option);
},
checkValue(){
//this.datetimelist = this.response.data.datetimelist;
//this.thermolist = this.response.data.thermolist;
//this.humidlist = this.response.data.humidlist;
console.log(this.datetimelist);
this.thermoView();
this.humidView();
this.formaldehydeView();
},
getList(){
axios
.get('/api/aht10/')
.then(response=>{
this.response = response;
this.aht10.datetime_list = response.data.datetimelist;
this.aht10.thermo_list = response.data.thermolist;
this.aht10.humid_list = response.data.humidlist;
console.log(response),
this.checkValue();})
.catch(function(error){
console.log(error);
});
},
getCh2o(){
axios
.get('/api/ch2o/')
.then(response=>{
this.ch2o.formaldehyde_list = response.data.ch2o_list;
this.ch2o.datetime_list = response.data.datetimelist;
console.log(response),
this.checkValue();})
.catch(function(error){
console.log(error);
});
},
getThermoHumid(){
var socket = new WebSocket("ws:10.43.60.15:8000/ws/chat/room/");
socket.onopen = function () {
console.log('connect success.');//成功连接上Websocket
};
socket.onmessage = (e=>{
let data = JSON.parse(e.data);//打印服务端返回的数据
this.thermo = data['thermo'].toFixed(2)
this.humid = data['humid'].toFixed(2)
this.formaldehyde = data['formaldehyde'].toFixed(4),
console.log(data['thermo'] + data['humid'] + data['formaldehyde'])
this.getList();
this.getCh2o();
})
socket.onclose=function(e){
console.log(e);
socket.close(); //关闭TCP连接
};
}
},
created() {
this.getThermoHumid()
//this.getList()
}
}
</script>
<style scoped>
header {
line-height: 1.5;
}
.aht10 {
width: 450px;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
}
</style>
来一张图片展示一下。
视频下次补上。。。待续。。。
更多回帖