一、介绍
本系统中,将使用常见的几种模块来构建一个简单的智能温度测量系统。
二、组件
★Raspberry Pi 3主板*1
★树莓派电源*1
★40P软排线*1
★有源蜂鸣器模块*1
★RGB LED 模块*1
★DS18B20 温度传感器模块*1
★PCF8591 AD/DA转换模块*1
★PS2 操作手柄模块*1
★面包板*1
★跳线若干
三、实验原理
我们可以在编程时通过操纵杆PS2调整下限和上限值。操纵杆PS2有五个操作方向:向上、向下、向左、向右和向下按压。在这个项目中,我们将使用左右方向来控制上限值,上下方向来控制下限值。如果按一下操纵杆,系统将退出。
当实际温度值在下限和上限值之间时,LED灯显绿色,蜂鸣器无响声;当实际温度值超下限时,LED灯显蓝色,蜂鸣器蜂鸣3次,每次0.5秒;当实际温度值超上限时,LED灯显红色,蜂鸣器蜂鸣3次,每次0.1秒。
每个模块的详情资料请参考前面的文章:
树莓派基础实验2:RGB-LED实验
树莓派基础实验9:蜂鸣器实验
树莓派基础实验12:PCF8591模数转换器实验
树莓派基础实验14:PS2操纵杆实验
树莓派基础实验25:DS18B20温度传感器实验
四、实验步骤
第1步: 连接电路。
树莓派 | T型转接板 | DS18B20温度传感器 |
---|---|---|
GPIO7 | G4 | SIG |
5V | 5V | VCC |
GND | GND | GND |
树莓派 | T型转接板 | PCF8591数模转换模块 |
---|---|---|
SDA | SDA | SDA |
SCL | SCL | SCL |
5V | 5V | VCC |
GND | GND | GND |
PS2操纵杆 | T型转接板 | PCF8591数模转换模块 |
---|---|---|
Y | * | AIN0 |
X | * | AIN1 |
SW(按钮) | * | AIN2 |
VCC | 5V | * |
GND | GND | * |
树莓派 | T型转接板 | RGB LED模块 |
---|---|---|
GPIO0 | G17 | R |
GPIO1 | G18 | G |
GPIO2 | G27 | B |
GND | GND | GND |
树莓派 | T型转接板 | 有源蜂鸣器模块 |
---|---|---|
GPIO3 | G22 | SIG |
3.3V | 3.3V | VCC |
GND | GND | GND |
第2步: RGB LED模块程序。
#!/usr/bin/env python #告诉Linux本文件是一个Python程序
import RPi.GPIO as GPIO #导入控制GPIO的模块,RPi.GPIO
import time #导入时间模块,提供延时、时钟和其它时间函数
colors = [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF] #颜色列表
R = 11 #定义物理针脚号
G = 12
B = 13
def setup(Rpin, Gpin, Bpin):
global pins #在函数内部声明被其修饰的变量是全局变量
global p_R, p_G, p_B
pins = {'pin_R': Rpin, 'pin_G': Gpin, 'pin_B': Bpin}
GPIO.setmode(GPIO.BOARD) #设置引脚编号模式为板载模式,即树莓派上的物理位置编号
for i in pins:
GPIO.setup(pins[i], GPIO.OUT) # 设置针脚模式为输出(或者输入GPIO.IN)
GPIO.output(pins[i], GPIO.LOW) # Set pins to low(0 V) to off led
p_R = GPIO.PWM(pins['pin_R'], 2000) # set Frequece to 2KHz
p_G = GPIO.PWM(pins['pin_G'], 1999)
p_B = GPIO.PWM(pins['pin_B'], 5000)
p_R.start(0) # Initial duty Cycle = 0(leds off)
p_G.start(0)
p_B.start(0)
def map(x, in_min, in_max, out_min, out_max): #将颜色的刺激量转换为占空比对应的值。
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
def off():
for i in pins:
GPIO.output(pins[i], GPIO.LOW) # Turn off all leds
def setColor(col): # For example : col = 0x112233
R_val = (col & 0xff0000) >> 16 #先“与”运算只保留自己颜色所在位的值有效
G_val = (col & 0x00ff00) >> 8 #再“右移”运算将自己颜色所在位的值提取出来
B_val = (col & 0x0000ff) >> 0
R_val = map(R_val, 0, 255, 0, 100) #将颜色的刺激量转换为占空比对应的值
G_val = map(G_val, 0, 255, 0, 100)
B_val = map(B_val, 0, 255, 0, 100)
p_R.ChangeDutyCycle(R_val) # 更改占空比,调整该颜色的亮度
p_G.ChangeDutyCycle(G_val)
p_B.ChangeDutyCycle(B_val)
def loop():
while True:
for col in colors:
setColor(col)
time.sleep(1)
def destroy():
p_R.stop() #Turn off PWM
p_G.stop()
p_B.stop()
off() # Turn off all leds
GPIO.cleanup() #重置GPIO状态
if __name__ == "__main__":
try: #用try-except代码块来处理可能引发的异常
setup(R, G, B) #调用初始化设置LED灯的函数
loop() #调用循环函数
except KeyboardInterrupt: #如果遇用户中断(control+C),则执行destroy()函数
destroy() #调用清除LED状态的函数
第3步: 有源蜂鸣器模块程序。
#!/usr/bin/env python
import RPi.GPIO as GPIO
import time
Buzzer = 11 # pin11
def setup(pin):
global BuzzerPin
BuzzerPin = pin
GPIO.setmode(GPIO.BOARD) # Numbers GPIOs by physical location
GPIO.setup(BuzzerPin, GPIO.OUT)
GPIO.output(BuzzerPin, GPIO.HIGH)
def on():
GPIO.output(BuzzerPin, GPIO.LOW)
#低电平是响
def off():
GPIO.output(BuzzerPin, GPIO.HIGH)
#高电平是停止响
def beep(x): #响3秒后停止3秒
on()
time.sleep(x)
off()
time.sleep(x)
def loop():
while True:
beep(3)
def destroy():
GPIO.output(BuzzerPin, GPIO.HIGH)
GPIO.cleanup() # Release resource
if __name__ == '__main__': # Program start from here
setup(Buzzer)
try:
loop()
except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed.
destroy()
第4步: PCF8591数模转换模块程序。
#!/usr/bin/env python
#------------------------------------------------------
#
# 您可以使用下面语句将此脚本导入另一个脚本:
# “import PCF8591 as ADC”
#
# ADC.Setup(Address) # 查询PCF8591的地址:“sudo i2cdetect -y 1”
# i2cdetect is a userspace program to scan an I2C bus for devices.
# It outputs a table with the list of detected devices on the specified bus.
# ADC.read(channal) # Channal范围从0到3
# ADC.write(Value) # Value范围从0到255
#
#------------------------------------------------------
#SMBus (System Management Bus,系统管理总线)
import smbus #在程序中导入“smbus”模块
import time
# for RPI version 1, use "bus = smbus.SMBus(1)"
# 0 代表 /dev/i2c-0, 1 代表 /dev/i2c-1 ,具体看使用的树莓派那个I2C来决定
bus = smbus.SMBus(1) #创建一个smbus实例
#在树莓派上查询PCF8591的地址:“sudo i2cdetect -y 1”
def setup(Addr):
global address
address = Addr
def read(chn): #channel
if chn == 0:
bus.write_byte(address,0x40) #发送一个控制字节到设备
if chn == 1:
bus.write_byte(address,0x41)
if chn == 2:
bus.write_byte(address,0x42)
if chn == 3:
bus.write_byte(address,0x43)
bus.read_byte(address) # 从设备读取单个字节,而不指定设备寄存器。
return bus.read_byte(address) #返回某通道输入的模拟值A/D转换后的数字值
def write(val):
temp = val # 将字符串值移动到temp
temp = int(temp) # 将字符串改为整数类型
# print temp to see on terminal else comment out
bus.write_byte_data(address, 0x40, temp)
#写入字节数据,将数字值转化成模拟值从AOUT输出
if __name__ == "__main__":
setup(0x48)
#在树莓派终端上使用命令“sudo i2cdetect -y 1”,查询出PCF8591的地址为0x48
while True:
print '电位计 AIN0 = ', read(0) #电位计模拟信号转化的数字值
print '光敏电阻 AIN1 = ', read(1) #光敏电阻模拟信号转化的数字
print '热敏电阻 AIN2 = ', read(2) #热敏电阻模拟信号转化的数字值
tmp = read(0)
tmp = tmp*(255-125)/255+125
# 125以下LED不会亮,所以将“0-255”转换为“125-255”,调节亮度时灯不会熄灭
write(tmp)
time.sleep(2)
第5步: PS2操纵杆程序。
#!/usr/bin/env python
import PCF8591 as ADC
import time
def setup():
ADC.setup(0x48) # Setup PCF8591
global state
def direction(): #获取操纵杆方向结果
state = ['home', 'up', 'down', 'left', 'right', 'Button pressed']
i = 0
if ADC.read(0) <= 5:
i = 1 #up
if ADC.read(0) >= 250:
i = 2 #down
if ADC.read(1) <= 5:
i = 3 #left
if ADC.read(1) >= 250:
i = 4 #right
if ADC.read(1) >= 6 \ #由于未知原因,向左摇操纵杆会自动触发按键按下信号
and ADC.read(2) == 0: #所以加上ADC.read(1) >= 6这个限制,
i = 5 # Button pressed
if ADC.read(0) - 125 < 15 \
and ADC.read(0) - 125 > -15 \
and ADC.read(1) - 125 < 15 \
and ADC.read(1) - 125 > -15 \
and ADC.read(2) == 255:
i = 0 #home
return state[i]
def loop():
status = ''
while True:
tmp = direction()
if tmp != None and tmp != status:
print tmp #不为空和tmp值变化时打印
status = tmp
def destroy():
pass #pass语句就是空语句
if __name__ == '__main__': # Program start from here
setup()
try:
loop()
except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed.
destroy()
第6步: DS18B20温度传感器模块程序。
#!/usr/bin/env python
#----------------------------------------------------------------
# Note:
# ds18b20's data pin must be connected to pin7.
# replace the 28-XXXXXXXXX as yours.
#----------------------------------------------------------------
import os #导入操作系统的库os
ds18b20 = ''
def setup():
global ds18b20
for i in os.listdir('/sys/bus/w1/devices'):
#os.listdir(path) 返回path指定的文件夹包含的文件或文件夹的名字的列表
if i != 'w1_bus_master1':
#里面除了文件'w1_bus_master1',另外一个就是温度数据文件所在的文件夹
ds18b20 = i
#将温度数据文件所在的文件夹名赋值给全局变量ds18b20
def read():
location = '/sys/bus/w1/devices/' + ds18b20 + '/w1_slave'
#location是温度数据文件的地址
tfile = open(location)
#os.open(file, flags[, mode])打开一个文件
text = tfile.read()
# os.read(fd, n)从文件描述符 fd 中读取最多 n 个字节,返回包含
# 读取字节的字符串,文件描述符 fd对应文件已达到结尾, 返回一个空字符串。
tfile.close()
#os.close(fd)关闭文件描述符 fd
secondline = text.split("\n")[1]
# string.split(str="", num=string.count(str))
# 以 str 为分隔符切片 string,如果 num 有指定值,则仅分隔 num+ 个子字符串
#计算机里序号是从0开始计算,取1即是第二行
temperaturedata = secondline.split(" ")[9]
#以空格为分隔符,取序号为9的字符段,如:t=17375
temperature = float(temperaturedata[2:])
#取字符串(如:t=17375)第2位及以后部分,即数字部分17375
temperature = temperature / 1000
return temperature
def loop():
while True:
if read() != None:
print "Current temperature : %0.3f C" % read()
#以单精度浮点小数的形式输出,保留三位小数
def destroy():
pass
if __name__ == '__main__':
try:
setup()
loop()
except KeyboardInterrupt:
destroy()
第7步: 智能温度测量系统总控制程序。当实际温度值在下限和上限值之间时,LED灯显绿色,蜂鸣器无响声;当实际温度值超下限时,LED灯显蓝色,蜂鸣器蜂鸣3次,每次0.5秒;当实际温度值超上限时,LED灯显红色,蜂鸣器蜂鸣3次,每次0.1秒。
#!/usr/bin/env python
import RPi.GPIO as GPIO
import importlib #动态加载某个模块
import time
import sys
# 重新定义部分针脚位置
LedR = 11
LedG = 12
LedB = 13
Buzz = 15
#ds18b20 = '28-031467805fff'
#location = '/sys/bus/w1/devices/' + ds18b20 + '/w1_slave'
#导入模块
joystick = importlib.import_module('15_joystick_PS2')
ds18b20 = importlib.import_module('26_ds18b20')
beep = importlib.import_module('10_active_buzzer')
rgb = importlib.import_module('02_rgb_led')
#调用各个模块中的初始化函数
joystick.setup()
ds18b20.setup()
beep.setup(Buzz)
rgb.setup(LedR, LedG, LedB)
color = {'Red':0xFF0000, 'Green':0x00FF00, 'Blue':0x0000FF}
def setup():
"""初始化下限和上限值"""
global lowl, highl
lowl = 29
highl = 31
def edge():
"""根据摇杆方向的值设置上下限的值及退出"""
global lowl, highl
temp = joystick.direction()
if temp == 'Button pressed': #当按下摇杆时,程序退出
destroy()
quit()
if temp == 'up' and highl <= 125: #上限值不超过125
highl += 1
if temp == 'down' and lowl < highl-1: #保证上限值不能<=下限值
highl -= 1
if temp == 'right' and lowl < highl-1: #保证上限值不能<=下限值
lowl += 1
if temp == 'left' and lowl >= -5: #下限值不低于-5
lowl -= 1
def loop():
while True:
edge()
temp = ds18b20.read()
print 'The lower limit of temperature : ', lowl
print 'The upper limit of temperature : ', highl
print 'Current temperature : ', temp
print ''
if float(temp) < float(lowl):
rgb.setColor(color['Blue']) #温度超下限时LED灯显蓝色
for i in range(0, 3):
beep.beep(0.5) #蜂鸣3次,每次0.5秒
if temp >= float(lowl) and temp < float(highl):
rgb.setColor(color['Green']) #温度不超限时LED灯显绿色
if temp >= float(highl):
rgb.setColor(color['Red']) #温度超上限时LED灯显红色
for i in range(0, 3):
beep.beep(0.1) #蜂鸣3次,每次0.1秒
def destroy():
beep.destroy()
joystick.destroy()
ds18b20.destroy()
rgb.destroy()
GPIO.cleanup()
if __name__ == "__main__":
try:
setup()
loop()
except KeyboardInterrupt:
destroy()
实验结果示例: