树莓派综合项目1:智能温度测量系统实验

一、介绍

  本系统中,将使用常见的几种模块来构建一个简单的智能温度测量系统。

二、组件

★Raspberry Pi 3主板*1

★树莓派电源*1

★40P软排线*1

★有源蜂鸣器模块*1

★RGB LED 模块*1

★DS18B20 温度传感器模块*1

★PCF8591 AD/DA转换模块*1

★PS2 操作手柄模块*1

★面包板*1

★跳线若干

三、实验原理

RGB LED模块
有源蜂鸣器模块
PCF8591数模转换模块
PS2操纵杆
DS18B20温度传感器

  我们可以在编程时通过操纵杆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()

  实验结果示例:


实验结果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343