项目一 基于 SIM800 的时间、天气语音播报服务机器人
项目二 树莓派多环境参数监测站
等待后续添加……
文章目录
- 系列文章目录
- 前言
- 2 硬件设计
- 2.1 树莓派
- 2.1.1 树莓派软硬件问题
- 2.2 传感器
- 2.2.1 温湿度传感器 DHC1080
- 2.2.2 光强传感器 BH1750
- 2.2.3 PM2.5 传感器 GP2Y1014AU
- 2.2.4 CO2 传感器 KQM6600
- 3 软件设计
- 3.1 界面设计
- 3.2 matplotlib 画图
- 3.3 模拟环境数据
- 3.4 传感器采集数据
- 3.4.1 DHC1080
- 3.4.2 BH1750
- 3.4.3 gp2y1014au
- 3.4.4 kqm6600ta
- 3.5 传感器数据整合
- 4 总结
前言
使用树莓派打造一个具备图像界面、可以进行多种环境参数检测的环境参数监测站。
硬件使用树莓派加个几个传感器,软件使用 Python 语言开发。UI 界面包含曲线显示和表格显示两种模式。
2 硬件设计 2.1 树莓派 2.1.1 树莓派软硬件问题版本:最新版本树莓派已经发布到第 4 代了,这里对性能要求不高,不管是用 3 代还是 4 代都没问题。本项目使用的是最新版的 4B。
系统:小白用户最好是使用最新版本的官方带图像界面系统。本项目使用的是最新版本不带图像界面系统。注意:如果使用 ssh 连接不带图形界面的树莓派系统,需要依赖连接软件显示图形界面。我用的是 Mobaxterm ,使用免费的版本就行了。
2.2 传感器总共四个传感器,采集温度、湿度、光强、PM2.5、CO2浓度五种数据。原计划所有传感器都使用 IIC 接口,挂载到同一个 IIC 外设上。调试时用来来读 CO2 浓度的 SGP30 传感器怎么也调试不好,一直读不上了数据,最后改用 串口驱动的 KQM6600 获取 CO2 浓度数据。
2.2.1 温湿度传感器 DHC1080HDC1080是一种集成温度传感器的数字湿度传感器,具有出色的测量精度和超低的功耗。其具有14位测量分辨率,相对湿度精度为±2%,温度精度为±0.2°C,工作时平均电流在uA级。
BH1750FVI 是一种用于两线式串行总线接口的数字型光强度传感器集成电路。这种集成电路可以根据收集的光线强度数据来调整液晶或者键盘背景灯的亮度。利用它的高分辨率可以探测较大范围的光强度变化。
GP2Y1014AU粉尘传感器是夏普开发的一款光学灰尘监测传感器模块,在其中间有一个大洞,空气可以自由流过,它里面对角位置放着红外发光二极管和光电晶体管,红外发光二极管定向发送红外,当空气中有微粒阻碍红外时,光电晶体管接收到的红外产生变化,所以信号输出引脚电压发送变化。其属性值如下;
供电电压;5-7V
工作温度;-10-65℃
监测最小直径;0.8μm
灵敏度;0.5V/(0.1mg/m3)灰尘浓度每变化0.1mg/m3,输出电压变化0.5V。
树莓派 4B 上没有 ADC 外设,所以要加一个 ADC 芯片。
ADS1115是德州仪器推出的具有IIC接口的16位ADC转换器,超小型X2QFN或VSSOP 封装,低功耗(20uA),宽电压输入2.0V-5.5V,可编程数据转换速率8SPS-860SPS,四个单端输入或两个差分输入。可应用于,电池电压电流检测,低速便携式仪表以及温度测量系统中。
去网上买这个传感器会带有一个 150Ω 电阻和一个 220u 电容,按照下图接上去。 LED 引脚可以直接接在 GPIO 引脚上。AOUT 引脚直接接ADS1115 第一通道。反正我是这么接的。
本来是打算使用 SGP30 来采集 CO2 数据的,但是调试了很久驱动也调不好,最好只能无论换一个传感器。
KQM6600可UART输出VOC甲醛、CO2浓度数据,3-5V供电。使用起来非常简单,直接用树莓派的 UART 外设获取数据便可。
软件设计大体分主 UI 设计和传感器数据读取两部分,全部使用 Python 语言。
总所周知,Python 是跨平台的语言,所以 UI 部分可以先在 Windows 系统上完成,然后再去树莓派上跟 传感器数据读取部分结合到一起就行了。
作者水平有限,以前也没做过 UI 设计,一切化繁从简,怎么简单怎么来。
先选择一个 Python 图形界面引擎。首先想到的是 PyQt5,但是想想以前从来没接触过,学习时间成本太高。鉴于本次设计的图像界面很简单(简陋),直接使用 Python 原生自带的图像界面 tkinter 就够用了。曲线画图的话,用上 matplotlib 库。
3.1 界面设计设计两个界面,一个界面显示五种数据的曲线图,一个界面显示表格。完美。下面上效果图。
非常 Very 简单的一个界面。这个界面是我在 windows 系统上使用模拟的数据显示的,只要有 Python 环境就可以运行。
另外一个界面,把所有数据做成一个表格,一秒更新一次最尾部数据,并把所以的数据往前循环一个位置。
#!/user/bin/env python3
# -*- coding:utf-8 -*-
# author :Yi Zhenkai
# create date:2022-04-20
# update date:2022-04-28
# function :Windows shift
import tkinter as tk
from tkinter import ttk
import tkinter
from tkinter import *
import random
import time
import matplotlib.animation as animation
from matplotlib import pyplot as plt
import matplotlib
from draw import Draw
import getData
from numpy import *
import threading
FREQUENCY = 500
#用于显示正常中文标签
plt.rcParams['font.sans-serif']=['SimHei']
#用于正常显示负号
plt.rcParams['axes.unicode_minus'] = False
matplotlib.use('TkAgg')
class basedesk(object):
def __init__(self, master):
self.root = master
self.root.config()
self.root.title('环境参数监测')
root.iconbitmap('lens.ico') # 更改窗口图标
screenwidth = root.winfo_screenwidth() # 屏幕宽度
screenheight = root.winfo_screenheight() # 屏幕高度
width = 1000
height = 500
x = int((screenwidth - width) / 2)
y = int((screenheight - height) / 2)
self.root.geometry('1400x800') # 大小以及位置
#self.root.geometry('{}x{}+{}+{}'.format(width, height, x, y)) # 大小以及位置
self.root.resizable(0,0) #界面不能放大缩小
self.init = curveface(self.root)
class curveface(object):
def __init__(self, master):
self.master = master
self.master.config(bg='white')
#基准界面 curveface
self.curveface = tk.Frame(self.master,)
self.curveface.pack(fill='both', expand=True)
self.btn = tk.Button(self.curveface,text='表格显示',command=self.change)
self.btn.pack()
self.draw = Draw(self.curveface)
def change(self):
self.curveface.destroy()
tableface(self.master)
class tableface(object):
def __init__(self, master):
self.master = master
self.master.config(bg='white')
self.tableface = tk.Frame(self.master,)
self.tableface.pack(fill='both', expand=True)
self.btn_back = tk.Button(self.tableface,text='曲线显示',command=self.back)
self.btn_back.pack()
columns = ['温度', '湿度', '光强', 'CO2', 'PM2.5']
self.table = ttk.Treeview(
master=self.tableface, # 父容器
height=10, # 表格显示的行数,height行
columns=columns, # 显示的列
show='headings', # 隐藏首列
)
self.table.heading('温度', text='温度', ) # 定义表头
self.table.heading('湿度', text='湿度', ) # 定义表头
self.table.heading('光强', text='光强', ) # 定义表头
self.table.heading('CO2', text='CO2', ) # 定义表头
self.table.heading('PM2.5', text='PM2.5', ) # 定义表头
self.table.column('温度', width=100, minwidth=100, anchor=S, ) # 定义列
self.table.column('湿度', width=100, minwidth=100, anchor=S) # 定义列
self.table.column('光强', width=100, minwidth=100, anchor=S) # 定义列
self.table.column('CO2', width=100, minwidth=100, anchor=S) # 定义列
self.table.column('PM2.5', width=100, minwidth=100, anchor=S) # 定义列
self.table.pack(fill='both',pady=50)
temp, humi, light, CO2, PM2_5 = getData.get_env_data()
all_data = list(zip(temp, humi, light, CO2, PM2_5))
for index, data in enumerate(all_data):
self.table.insert('', END, values=data) # 添加数据到末尾
t = threading.Thread(target=self.table_insert)
t.start()
def table_insert(self):
while True:
temp, humi, light, CO2, PM2_5 = getData.get_env_data()
all_data = list(zip(temp, humi, light, CO2, PM2_5))
for x in range(10):
self.table.insert('', x, values=all_data[x])
time.sleep(1)
def back(self):
self.tableface.destroy()
self.init = curveface(self.master)
anim = animation.FuncAnimation(fig=self.init.draw.fig, func=self.init.draw.update, interval=FREQUENCY, blit=True, repeat=True)
anim.save() #writer='pillow'
if __name__ == '__main__':
root = tk.Tk()
base = basedesk(root)
anim = animation.FuncAnimation(fig=base.init.draw.fig, func=base.init.draw.update, interval=FREQUENCY, blit=True, repeat=True)
root.mainloop()
3.2 matplotlib 画图
画图是这个设计最难的部分,也是因为以前基本上没有用过 Python matplotlib 画图的原因吧,真是让我绞尽脑汁,几经放弃。
这里先放代码。这里使用 animation 画动图,然后使用子图的方式把四个动图放置到一个界面上。
有点基础的同学完全可以依葫芦画瓢,很简单就可以再加几个子图上去。
#!/user/bin/env python3
# -*- coding:utf-8 -*-
# author :Yi Zhenkai
# create date:2022-04-20
# update date:2022-04-28
# function :Draw curves
import random
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
import tkinter as tk
from tkinter import ttk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,NavigationToolbar2Tk
import matplotlib
import getData
#用于显示正常中文标签
plt.rcParams['font.sans-serif']=['SimHei']
#用于正常显示负号
plt.rcParams['axes.unicode_minus'] = False
matplotlib.use('TkAgg')
#常量
RANGE = 10
FREQUENCY = 500
class Draw(object):
def __init__(self, root):
self.fig = plt.figure(figsize=(5, 4), tight_layout=True)
ax_1 = self.fig.add_subplot(221)##设置坐标轴
ax_1.set_xlim(0, 10) #设置x轴的范围
ax_1.set_ylim(0, 100)#设置y轴的范围
ax_1.grid(axis='both')
ax_1.set_title('温湿度曲线')
my_x_ticks = np.arange(0, 11, 1)
my_y_ticks = np.arange(0, 110, 10)
plt.xticks(my_x_ticks)
plt.yticks(my_y_ticks)
self.t_x_line = [0]
self.t_y_line = [0]
self.h_x_line = [0]
self.h_y_line = [0]
self.line_t, = plt.plot(self.t_x_line, self.t_y_line, linestyle='-', label='温度', linewidth=1, color='red')
self.line_h, = plt.plot(self.h_x_line, self.h_y_line, label='湿度', linestyle='-', linewidth=1, color='green')
self.text_t = plt.text(3, 90, '', ha='center', va= 'bottom', fontsize=15)
self.text_h = plt.text(6, 90, '', ha='center', va= 'bottom', fontsize=15)
plt.ylabel("温湿度:℃ / %RH") # x轴的标签
plt.legend()
ax_2 = self.fig.add_subplot(222)
ax_2.set_xlim(0, 10) #设置x轴的范围
ax_2.set_ylim(0, 2000) #设置y轴的范围
ax_2.grid(axis='both')
ax_2.set_title('光强曲线')
my_x_ticks = np.arange(0, 11, 1)
my_y_ticks = np.arange(0, 2110, 300)
plt.xticks(my_x_ticks)
plt.yticks(my_y_ticks)
self.l_x_line = [0]
self.l_y_line = [0]
self.line_l, = plt.plot(self.l_x_line, self.l_y_line, linestyle='-', label='光强', linewidth=1, color='blue')
self.text_l = plt.text(5, 1860, '', ha='center', va= 'bottom', fontsize=15)
plt.ylabel("光强: lx") # x轴的标签
plt.legend()
ax_3 = self.fig.add_subplot(223)
ax_3.set_xlim(0, 10) #设置x轴的范围
ax_3.set_ylim(300, 1000) #设置y轴的范围
ax_3.grid(axis='both')
ax_3.set_title('CO2 曲线')
my_x_ticks = np.arange(0, 11, 1)
my_y_ticks = np.arange(300, 1100, 100)
plt.xticks(my_x_ticks)
plt.yticks(my_y_ticks)
self.c_x_line = [0]
self.c_y_line = [0]
self.line_c, = plt.plot(self.c_x_line, self.c_y_line, linestyle='-', label='CO2', linewidth=1, color='c')
self.text_c = plt.text(5, 900, '', ha='center', va= 'bottom', fontsize=15)
plt.ylabel("CO2: ppm") # x轴的标签
plt.legend()
ax_4 = self.fig.add_subplot(224)
ax_4.set_xlim(0, 10) #设置x轴的范围
ax_4.set_ylim(0, 200) #设置y轴的范围
ax_4.grid(axis='both')
ax_4.set_title('PM2.5 曲线')
my_x_ticks = np.arange(0, 11, 1)
my_y_ticks = np.arange(0, 201, 25)
plt.xticks(my_x_ticks)
plt.yticks(my_y_ticks)
self.p_x_line = [0]
self.p_y_line = [0]
self.line_p, = plt.plot(self.p_x_line, self.p_y_line, linestyle='-', label='PM2.5', linewidth=1, color='purple')
self.text_p = plt.text(5, 175, '', ha='center', va= 'bottom', fontsize=15)
plt.ylabel("PM2.5: ppm") # x轴的标签
plt.legend()
canvas = FigureCanvasTkAgg(self.fig, master=root) # A tk.DrawingArea.
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
# matplotlib的导航工具栏显示上来(默认是不会显示它的)
toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=tk.YES) # get_tk_widget()得到的就是_tkcanvas
def update(self, c):
getData.get_env_data()
self.t_x_line = [i for i in range(0, RANGE)]
self.line_t.set_data(self.t_x_line, getData.temp)
self.text_t.set_text("T = %d" % getData.temp[9])
self.h_x_line = [i for i in range(0, RANGE)]
self.line_h.set_data(self.h_x_line, getData.humi)
self.text_h.set_text("H = %d" % getData.humi[9])
self.l_x_line = [i for i in range(0, RANGE)]
self.line_l.set_data(self.l_x_line, getData.light)
self.text_l.set_text("L = %d" % getData.light[9])
self.c_x_line = [i for i in range(0, RANGE)]
self.line_c.set_data(self.c_x_line, getData.CO2)
self.text_c.set_text("CO2 = %d" % getData.CO2[9])
self.p_x_line = [i for i in range(0, RANGE)]
self.line_p.set_data(self.p_x_line, getData.PM2_5)
self.text_p.set_text("PM2.5 = %d" % getData.PM2_5[9])
return self.line_t, self.line_h, self.text_t, self.text_h, self.line_l, self.text_l, self.line_c, self.text_c, self.line_p, self.text_p # 将需要动态变化的内容return即可
if __name__ == "__main__":
top=tk.Tk()
top.geometry("780x585") #tkinter界面长宽,根据像素点来的,每个不同的分辨率电脑上显示不一样
top.title("环境数据监测系统") #设置tkinter界面名字
top.resizable(0,0) #界面不能放大缩小
draw = Draw(top)
anim = animation.FuncAnimation(fig=draw.fig, func=draw.update, interval=FREQUENCY, blit=True)
top.mainloop()
3.3 模拟环境数据
在 windows 系统上调试没有实际传感器采集到的数据,就先用几个随机数来模拟一下。
#!/user/bin/env python3 # -*- coding:utf-8 -*- # author :Yi Zhenkai # create date:2022-04-20 # update date:2022-04-28 # function :Get enviroment parameters import random import time from numpy import * import numpy as np temp = [random.randint(20,35) for i in range(10)] humi = [random.randint(20,35) for i in range(10)] light = [random.randint(100,500) for i in range(10)] CO2 = [random.randint(400,500) for i in range(10)] PM2_5 = [random.randint(20,40) for i in range(10)] def get_env_data(): l = temp.pop(0) temp.append(l) l = humi.pop(0) humi.append(l) l = light.pop(0) light.append(l) l = CO2.pop(0) CO2.append(l) l = PM2_5.pop(0) PM2_5.append(l) temp[9] = random.randint(20,35) humi[9] = random.randint(20,35) light[9] = random.randint(100,500) CO2[9] = random.randint(400,500) PM2_5[9] = random.randint(10,50) return temp, humi, light, CO2, PM2_5 if __name__ == "__main__": temp, humi, light, CO2, PM2_5 = get_env_data() print(temp, humi, light, CO2, PM2_5)3.4 传感器采集数据
在传感器数据采集在设计上分为四个独立模块,每一个都可以单独使用,减少系统耦合性。
3.4.1 DHC1080这部分代码是借鉴别人的,直接拿来就可以用。在这里先感谢一下!
DHC1080 的设备地址是 0x40。
#
# SDL_Pi_HDC1080
# Python 3
# Raspberry Pi Driver for the SwitchDoc Labs HDC1080 Breakout Board
#
# SwitchDoc Labs
# December 2018
#
# Version 1.1
#constants
# I2C Address
HDC1080_ADDRESS = (0x40) # 1000000
# Registers
HDC1080_TEMPERATURE_REGISTER = (0x00)
HDC1080_HUMIDITY_REGISTER = (0x01)
HDC1080_CONFIGURATION_REGISTER = (0x02)
HDC1080_MANUFACTURERID_REGISTER = (0xFE)
HDC1080_DEVICEID_REGISTER = (0xFF)
HDC1080_SERIALIDHIGH_REGISTER = (0xFB)
HDC1080_SERIALIDMID_REGISTER = (0xFC)
HDC1080_SERIALIDBOTTOM_REGISTER = (0xFD)
#Configuration Register Bits
HDC1080_CONFIG_RESET_BIT = (0x8000)
HDC1080_CONFIG_HEATER_ENABLE = (0x2000)
HDC1080_CONFIG_ACQUISITION_MODE = (0x1000)
HDC1080_CONFIG_BATTERY_STATUS = (0x0800)
HDC1080_CONFIG_TEMPERATURE_RESOLUTION = (0x0400)
HDC1080_CONFIG_HUMIDITY_RESOLUTION_HBIT = (0x0200)
HDC1080_CONFIG_HUMIDITY_RESOLUTION_LBIT = (0x0100)
HDC1080_CONFIG_TEMPERATURE_RESOLUTION_14BIT = (0x0000)
HDC1080_CONFIG_TEMPERATURE_RESOLUTION_11BIT = (0x0400)
HDC1080_CONFIG_HUMIDITY_RESOLUTION_14BIT = (0x0000)
HDC1080_CONFIG_HUMIDITY_RESOLUTION_11BIT = (0x0100)
HDC1080_CONFIG_HUMIDITY_RESOLUTION_8BIT = (0x0200)
I2C_SLAVE=0x0703
import struct, array, time, io, fcntl
HDC1080_fw= 0
HDC1080_fr= 0
class SDL_Pi_HDC1080:
def __init__(self, twi=1, addr=HDC1080_ADDRESS ):
global HDC1080_fr, HDC1080_fw
HDC1080_fr= io.open("/dev/i2c-"+str(twi), "rb", buffering=0)
HDC1080_fw= io.open("/dev/i2c-"+str(twi), "wb", buffering=0)
# set device address
fcntl.ioctl(HDC1080_fr, I2C_SLAVE, HDC1080_ADDRESS)
fcntl.ioctl(HDC1080_fw, I2C_SLAVE, HDC1080_ADDRESS)
time.sleep(0.015) #15ms startup time
config = HDC1080_CONFIG_ACQUISITION_MODE
s = [HDC1080_CONFIGURATION_REGISTER,config>>8,0x00]
s2 = bytearray( s )
HDC1080_fw.write( s2 ) #sending config register bytes
time.sleep(0.015) # From the data sheet
# 0x10(48) Temperature, Humidity enabled, Resolultion = 14-bits, Heater off
#config = HDC1080_CONFIG_ACQUISITION_MODE
#self._bus.write_byte_data(HDC1080_ADDRESS,HDC1080_CONFIGURATION_REGISTER, config>>8)
# public functions
def readTemperature(self):
s = [HDC1080_TEMPERATURE_REGISTER] # temp
s2 = bytearray( s )
HDC1080_fw.write( s2 )
time.sleep(0.0625) # From the data sheet
data = HDC1080_fr.read(2) #read 2 byte temperature data
buf = array.array('B', data)
#print ( "Temp: %f 0x%X %X" % ( ((((buf[0]<<8) + (buf[1]))/65536.0)*165.0 ) - 40.0 ,buf[0],buf[1] ) )
# Convert the data
temp = (buf[0] * 256) + buf[1]
cTemp = (temp / 65536.0) * 165.0 - 40
return cTemp
def readHumidity(self):
# Send humidity measurement command, 0x01(01)
time.sleep(0.015) # From the data sheet
s = [HDC1080_HUMIDITY_REGISTER] # hum
s2 = bytearray( s )
HDC1080_fw.write( s2 )
time.sleep(0.0625) # From the data sheet
data = HDC1080_fr.read(2) #read 2 byte humidity data
buf = array.array('B', data)
#print ( "Humidity: %f 0x%X %X " % ( ((((buf[0]<<8) + (buf[1]))/65536.0)*100.0 ), buf[0], buf[1] ) )
humidity = (buf[0] * 256) + buf[1]
humidity = (humidity / 65536.0) * 100.0
return humidity
def readConfigRegister(self):
# Read config register
s = [HDC1080_CONFIGURATION_REGISTER] # temp
s2 = bytearray( s )
HDC1080_fw.write( s2 )
time.sleep(0.0625) # From the data sheet
data = HDC1080_fr.read(2) #read 2 byte config data
buf = array.array('B', data)
#print "register=%X %X"% (buf[0], buf[1])
return buf[0]*256+buf[1]
def turnHeaterOn(self):
# Read config register
config = self.readConfigRegister()
config = config | HDC1080_CONFIG_HEATER_ENABLE
s = [HDC1080_CONFIGURATION_REGISTER,config>>8,0x00]
s2 = bytearray( s )
HDC1080_fw.write( s2 ) #sending config register bytes
time.sleep(0.015) # From the data sheet
return
def turnHeaterOff(self):
# Read config register
config = self.readConfigRegister()
config = config & ~HDC1080_CONFIG_HEATER_ENABLE
s = [HDC1080_CONFIGURATION_REGISTER,config>>8,0x00]
s2 = bytearray( s )
HDC1080_fw.write( s2 ) #sending config register bytes
time.sleep(0.015) # From the data sheet
return
def setHumidityResolution(self,resolution):
# Read config register
config = self.readConfigRegister()
config = (config & ~0x0300) | resolution
s = [HDC1080_CONFIGURATION_REGISTER,config>>8,0x00]
s2 = bytearray( s )
HDC1080_fw.write( s2 ) #sending config register bytes
time.sleep(0.015) # From the data sheet
return
def setTemperatureResolution(self,resolution):
# Read config register
config = self.readConfigRegister()
config = (config & ~0x0400) | resolution
s = [HDC1080_CONFIGURATION_REGISTER,config>>8,0x00]
s2 = bytearray( s )
HDC1080_fw.write( s2 ) #sending config register bytes
time.sleep(0.015) # From the data sheet
return
def readBatteryStatus(self):
# Read config register
config = self.readConfigRegister()
config = config & ~ HDC1080_CONFIG_HEATER_ENABLE
if (config == 0):
return True
else:
return False
return 0
def readManufacturerID(self):
s = [HDC1080_MANUFACTURERID_REGISTER] # temp
s2 = bytearray( s )
HDC1080_fw.write( s2 )
time.sleep(0.0625) # From the data sheet
data = HDC1080_fr.read(2) #read 2 byte config data
buf = array.array('B', data)
return buf[0] * 256 + buf[1]
def readDeviceID(self):
s = [HDC1080_DEVICEID_REGISTER] # temp
s2 = bytearray( s )
HDC1080_fw.write( s2 )
time.sleep(0.0625) # From the data sheet
data = HDC1080_fr.read(2) #read 2 byte config data
buf = array.array('B', data)
return buf[0] * 256 + buf[1]
def readSerialNumber(self):
serialNumber = 0
s = [HDC1080_SERIALIDHIGH_REGISTER] # temp
s2 = bytearray( s )
HDC1080_fw.write( s2 )
time.sleep(0.0625) # From the data sheet
data = HDC1080_fr.read(2) #read 2 byte config data
buf = array.array('B', data)
serialNumber = buf[0]*256+ buf[1]
s = [HDC1080_SERIALIDMID_REGISTER] # temp
s2 = bytearray( s )
HDC1080_fw.write( s2 )
time.sleep(0.0625) # From the data sheet
data = HDC1080_fr.read(2) #read 2 byte config data
buf = array.array('B', data)
serialNumber = serialNumber*256 + buf[0]*256 + buf[1]
s = [HDC1080_SERIALIDBOTTOM_REGISTER] # temp
s2 = bytearray( s )
HDC1080_fw.write( s2 )
time.sleep(0.0625) # From the data sheet
data = HDC1080_fr.read(2) #read 2 byte config data
buf = array.array('B', data)
serialNumber = serialNumber*256 + buf[0]*256 + buf[1]
return serialNumber
3.4.2 BH1750
没什么好说的,就几行代码的事。不得不说,linux 系统上的 IIC 驱动还是很好用的。
BH1750 的设备地址是 0x23。
#!/usr/bin/env python
# encoding: utf-8
import smbus
import time
#BH1750地址
__DEV_ADDR=0x23
#控制字
__CMD_PWR_OFF=0x00 #关机
__CMD_PWR_ON=0x01 #开机
__CMD_RESET=0x07 #重置
__CMD_CHRES=0x10 #持续高分辨率检测
__CMD_CHRES2=0x11 #持续高分辨率模式2检测
__CMD_CLHRES=0x13 #持续低分辨率检测
__CMD_THRES=0x20 #一次高分辨率
__CMD_THRES2=0x21 #一次高分辨率模式2
__CMD_TLRES=0x23 #一次分辨率
__CMD_SEN100H=0x42 #灵敏度100%,高位
__CMD_SEN100L=0x65 #灵敏度100%,低位
__CMD_SEN50H=0x44 #50%
__CMD_SEN50L=0x6A #50%
__CMD_SEN200H=0x41 #200%
__CMD_SEN200L=0x73 #200%
bus=smbus.SMBus(1)
bus.write_byte(__DEV_ADDR,__CMD_PWR_ON)
bus.write_byte(__DEV_ADDR,__CMD_RESET)
bus.write_byte(__DEV_ADDR,__CMD_SEN100H)
bus.write_byte(__DEV_ADDR,__CMD_SEN100L)
bus.write_byte(__DEV_ADDR,__CMD_PWR_OFF)
def getIlluminance():
bus.write_byte(__DEV_ADDR,__CMD_PWR_ON)
bus.write_byte(__DEV_ADDR,__CMD_THRES2)
time.sleep(0.2)
res=bus.read_word_data(__DEV_ADDR,0)
#read_word_data
res=((res>>8)&0xff)|(res<<8)&0xff00
res=round(res/(2*1.2), 2)
# percent = int((100.0/65535.0)*res)
#print() res percent
# print(res,percent)
#result="光照强度: "+str(res)+"lx"
#return result
return res
if __name__ == '__main__':
while True:
res = getIlluminance()
print(res)
time.sleep(2)
3.4.3 gp2y1014au
这里实际上是去都 ADS1115 采集到的电压数据。使用前要先把 gp2y1014au 和 ADS1115 接起来,然后再连接到树莓派上面去。
代码上直接使用了 Python 的 ads1x15 库,使用之前要先用 pip 安装一下。
里面有个计算,参考了一个大佬的研究。
# coding=UTF-8
import board
import busio
import adafruit_ads1x15.ads1115 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
import RPi.GPIO as GPIO
import time
import math
import smbus
density=0.0
voltage=0.0
def SendVideo():
GPIO.setmode(GPIO.BCM)
IN1 = 4
GPIO.setwarnings(False)
GPIO.setup(IN1,GPIO.OUT) # 初始化二极管灯
GPIO.output(IN1,GPIO.LOW) # 关闭二级管灯
# Create the I2C bus
i2c = busio.I2C(board.SCL, board.SDA)
# Create the ADC object using the I2C bus
ads0 = ADS.ADS1115(i2c)
ads1 = ADS.ADS1115(i2c, address = 0x48)
# Create single-ended input on channel 0
chan0 = AnalogIn(ads0, ADS.P0)
# while True:
GPIO.output(IN1,GPIO.HIGH) # 启动二极管灯
time.sleep(0.00028) # 等待0.28ms
AIN2_DATA=chan0.voltage
time.sleep(0.00004) # 持续采集0.04ms
GPIO.output(IN1,GPIO.LOW) # 关闭二级管灯
time.sleep(0.00986)
density = 0.172 * AIN2_DATA * 1000 - 0.9999
if(density < 0):
density = 0
density = round(density, 2)
return density
if __name__ == '__main__':
while True:
PM = SendVideo()
print(PM)
time.sleep(1)
3.4.4 kqm6600ta
使用树莓派串口0 读数据,使用之前要先打开树莓派的串口0。
怎么打开参考一下网上的文章。
树莓派4b串口设置教程
# # -*- coding:utf-8 -*-
import serial
import time
ser = serial.Serial("/dev/ttyS0", 9600)
ser.flushInput()
ser.write("begin".encode("utf-8"))
CO2 = 0
def get_CO2():
while True:
count = ser.inWaiting()
if count != 0:
recv = ser.read(count)
ser.flushInput()
if len(recv) > 6:
CO2 = (recv[5]<<8) + recv[6]
if(CO2 < 400):
CO2 = 400
return CO2
time.sleep(0.1)
if __name__ == '__main__':
get_CO2()
3.5 传感器数据整合
把所以传感器数据整合到 getData.py 模块里。
#!/user/bin/env python3
# -*- coding:utf-8 -*-
# author :Yi Zhenkai
# create date:2022-04-20
# update date:2022-04-28
# function :Get enviroment parameters
import random
import time
import bh1750
import gp2y1014au
import SDL_Pi_HDC1080
import kqm6600ta
from threading import Thread
CO2 = 0
temp = [random.randint(20,35) for i in range(10)]
humi = [random.randint(20,35) for i in range(10)]
light = [random.randint(100,500) for i in range(10)]
CO2 = [random.randint(400,500) for i in range(10)]
PM2_5 = [random.randint(20,40) for i in range(10)]
def get_env_data():
hdc1080 = SDL_Pi_HDC1080.SDL_Pi_HDC1080()
hdc1080.setTemperatureResolution(SDL_Pi_HDC1080.HDC1080_CONFIG_TEMPERATURE_RESOLUTION_14BIT)
hdc1080.setTemperatureResolution(SDL_Pi_HDC1080.HDC1080_CONFIG_TEMPERATURE_RESOLUTION_14BIT)
l = temp.pop(0)
temp.append(l)
l = humi.pop(0)
humi.append(l)
l = light.pop(0)
light.append(l)
l = CO2.pop(0)
CO2.append(l)
l = PM2_5.pop(0)
PM2_5.append(l)
temp[9] = round(hdc1080.readTemperature(), 2)
humi[9] = round(hdc1080.readHumidity(), 2)
light[9] = bh1750.getIlluminance()
CO2[9] = kqm6600ta.get_CO2()
PM2_5[9] = gp2y1014au.SendVideo()
return temp, humi, light, CO2, PM2_5
if __name__ == "__main__":
# t1 = Thread(target=kqm6600ta.get_CO2)
# t1.start()
while True:
temp, humi, light, CO2, PM2_5 = get_env_data()
print(temp, humi, light, CO2, PM2_5)
time.sleep(1)
4 总结
- 没有树莓派可以在 windows 系统上仿真。
- 传感器要先调试好。
- 树莓派系统带图像界面可以直接使用远程桌面连接运行程序,也可以用 mobxterm SSH 连接运行。



