树莓派编程(一) — 搭建交通信号灯

前面已经学习了一些树莓派系统和GPIO的基本知识了,现在可以开始动手做一些简单的东西了。因为一些外设的连接会涉及到一些电路的知识,如果操作不当可能会损坏外设或者是树莓派。所以在哦动手之前最好还是补充点电路相关的知识。我是花了点时间又把初中电路稍微复习了下,然后又乱七八糟看了点模拟电路、数字电路。所以最终应该也没有太多用处。

网上对于树莓派第一个项目基本就是接LED,然后控制。所以我第一个程序也使用LED,只不过想做的稍微复杂一点。 结合数码管和蜂鸣器来模拟十字路口的交通信号灯。使用的材料主要有:

  • 3个不同颜色的LED灯
  • TM1637四位数码管
  • 有源蜂鸣器
  • 若干100欧以上的电阻
  • 面包板和面包板连接线
  • 若干杜邦线

 

一 使用LED

 

点亮测试

 

首先实现信号灯中最简单的LED。第一次连接设备,为了安全,先只通电测试,不连接树莓派,点亮LED灯看看。

使用了面包板外接电源板,可以使用USB或9V的DC电源作为输入。有两组供电,按+/- 插在面包板上。 3组LED灯并联。 每一组串联的一个100欧的电阻(几十欧也可以,根据外接电压和电流来算一下)。 LED灯实际是一个发光二极管,压降一般在2V-3.5V之间,而电流一般控制在20ma。 超过40ma可能会烧毁。 这里我使用外接3.3V电压(和GPIO口电压一致)。通电或的效果如下。其中黄色等感觉偏暗,所以吧电阻换成了47欧,还是有点。

然后替换掉外接电源,直接从树莓派接触3.3V和GND2条线,连接到面包板的+/-导线上,LED也能正常发光。

 

编程控制

 

电路连接好了之后就可以开始通过代码来编程控制LED的开关了。 所以LED灯的正级不直接接入3.3V,而是接入GPIO接口,当接口发出高电平点亮LED,低电平时LED熄灭。

 

选择GPIO口

 

只需要控制电平,所以使用任意的GPIO接口都可以。

  • 红灯: GPIO25
  • 蓝灯: GPIO8
  • 绿灯: GPIO7

 

编写代码

 

这里我使用RPI.GPIO的python来开发,第一使用,先安装库。

sudo apt install rpi.gpio

安装完成之后就可以开始编码了,我在github上创建了一个仓库,这样就可以方便的把代码管理起来: https://github.com/cclover/RaspberrySample.git

本地把git仓库同步下来:

pi@raspberrypi-cc:~/source $ git clone https://github.com/cclover/RaspberrySample.git
Cloning into 'RaspberrySample'...
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 7 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (7/7), done.

创建相应的目录后就可以开始了, 这里创建了一个TrafficLight目录

pi@raspberrypi-cc:~/source cd RaspberrySample/
pi@raspberrypi-cc:~/source/RaspberrySample ls -l
total 16
-rw-r--r-- 1 pi pi 11349 Aug 18 16:48 LICENSE
-rw-r--r-- 1 pi pi    40 Aug 18 16:48 README.md
pi@raspberrypi-cc:~/source/RaspberrySample mkdir TrafficLight
pi@raspberrypi-cc:~/source/RaspberrySample cd TrafficLight/
pi@raspberrypi-cc:~/source/RaspberrySample/TrafficLight $

代码如下:

#-*- encoding=utf8 -*-

#import RPi.GPIO as GPIO
import sys
import time
import threading
import signal

sys.path.append('./timer.py')
from timer import LoopTimer

#define LED PIN
PIN_LIGHT_RED = 25
PIN_LIGHT_BLUE = 8
PIN_LIGHT_GREEN = 7

#LED Status
LED_STATUS_OFF = 0
LED_STATUS_ON = 1
LED_STATUS_BLINK = 2

#Traffic Light time
TIME_LIGHT_RED = 10
TIME_LIGHT_BLUE = 3
TIME_LIGHT_GREEN = 10
TIME_BLINK_GREEN = 3
TIME_INTERVAL = 1

#light list
light_list = [ PIN_LIGHT_RED, PIN_LIGHT_BLUE, PIN_LIGHT_GREEN ] 

#remain timer
reaminTime = 0



def initLED():
    print "Init LED!!"
    #GPIO.setup(light_list, GPIO.OUT)

def init():
    print "Init Traffic!!"
    #GPIO.setmode(GPIO.BCM)
    initLED()

def destory(signum, frame):
    print "Destory Traffic!!"
    #GPIO.cleanup()
    exit()

def switchLight(light, status):

    if not light in light_list:
        return
    if status == LED_STATUS_OFF:
        print "LIGHT OFF {0}".format(light)
        #GPIO.output(light, GPIO.LOW)
    elif status == LED_STATUS_ON:
        print "LIGHT ON {0}".format(light)
        #GPIO.output(light, GPIO.HIGH)

def blinkLight(light):

    print "blinkLight"
    switchLight(light, LED_STATUS_OFF)
    time.sleep(0.5)
    switchLight(light, LED_STATUS_ON)
    time.sleep(0.5)
    switchLight(light, LED_STATUS_OFF)
    time.sleep(0.5)
    switchLight(light, LED_STATUS_ON)


def redInterval():

    global reaminTime
    reaminTime = reaminTime - 1
    print "Read remain: {0}s".format(reaminTime)

def redTraffic():

    #red timer
    global reaminTime
    reaminTime = TIME_LIGHT_RED
    lightTimer = LoopTimer(TIME_LIGHT_RED, TIME_INTERVAL, redInterval, greenTraffic)
    
    #light red
    switchLight(PIN_LIGHT_GREEN, LED_STATUS_OFF)
    switchLight(PIN_LIGHT_RED, LED_STATUS_ON)
    switchLight(PIN_LIGHT_BLUE, LED_STATUS_OFF)
       
    # start time
    print "Light on red {0}s".format(reaminTime)
    lightTimer.start()



def blueInterval():

    global reaminTime
    reaminTime = reaminTime - 1
    print "Blue remain: {0}s".format(reaminTime)

def blueTraffic():

    #blue timer
    global reaminTime
    reaminTime = TIME_LIGHT_BLUE
    lightTimer = LoopTimer(TIME_LIGHT_BLUE, TIME_INTERVAL, blueInterval, redTraffic)

    #light blue
    switchLight(PIN_LIGHT_GREEN, LED_STATUS_OFF)
    switchLight(PIN_LIGHT_RED, LED_STATUS_OFF)
    switchLight(PIN_LIGHT_BLUE, LED_STATUS_ON)

    # start time
    print "Light on blue {0}s".format(reaminTime)
    lightTimer.start()



def greenInterval():

    global reaminTime
    reaminTime = reaminTime - 1
    print "Green remain: {0}s".format(reaminTime)
    if reaminTime <= TIME_BLINK_GREEN:
        blinkLight(PIN_LIGHT_GREEN)

def greenTraffic():

    #green timer
    global reaminTime
    reaminTime = TIME_LIGHT_GREEN
    lightTimer = LoopTimer(TIME_LIGHT_GREEN, TIME_INTERVAL, greenInterval, blueTraffic)
    
    #light green
    switchLight(PIN_LIGHT_GREEN, LED_STATUS_ON)
    switchLight(PIN_LIGHT_RED, LED_STATUS_OFF)
    switchLight(PIN_LIGHT_BLUE, LED_STATUS_OFF)

    # start time
    print "Light on green {0}s".format(reaminTime)
    lightTimer.start()

def registerExit():
    signal.signal(signal.SIGINT, destory)
    signal.signal(signal.SIGTERM, destory)


#main funciton
init()
registerExit()
print "Start traffic!!"
print "Press 'Ctrl+C' to exit..."
greenTraffic()

整体比较简单,绿灯显示10秒,最后3秒闪烁, 然后切换到蓝灯,蓝灯3秒后切换到红灯,红灯显示10秒。 python没用几次还不太熟,花了一下午时间。 线也连接的有点问题。

修改一下连线, 从树莓派GPIO口的7、8、25引出3根线,直接和对应的LED灯的+ 连接,从GND连接到面包板的 – 级。运行程序,可以按照预想工作了。

 

 

二 蜂鸣器

 

一般交通信号绿灯最后3秒闪烁的同时也会有声音提示,所以这里用蜂鸣器模仿一下。这里使用的是有源蜂鸣器,只需要给一个低电平就可以发声。这个外设有3个针脚,VCC、GND和I/O,VCC支持3.3~5V,所以I/O接口接上任意一个GIOP就行了。

 

选择GPIO口

 

只需要控制电平,所以使用任意的GPIO接口都可以。

  • I/O: GPIO24

 

编写代码

 

增加蜂鸣相关的代码,并在绿灯闪烁时调用一下

#define buzzer PIN
PIN_BUZZER = 24

def initBuzzer():
    print "Init Buzzer"
    GPIO.setup(PIN_BUZZER, GPIO.OUT, initial=GPIO.HIGH)

def beepBuzzer(trig):
    print "beepBuzzer"
    GPIO.output(trig,GPIO.LOW) 
    time.sleep(0.3)
    GPIO.output(trig,GPIO.HIGH) 
    time.sleep(0.3)
    GPIO.output(trig,GPIO.LOW) 
    time.sleep(0.3)
    GPIO.output(trig,GPIO.HIGH)

 

因为蜂鸣器需要3.3V电源和GPIO24,所以在之前连线基础上引出了一根3.3V连接到面包板的+ (左下), 蜂鸣器的GND和VCC和面包版的+ / – 相连。 I/O和GPIO24连接。

接通树莓派电源,执行python程序, 绿灯闪烁时蜂鸣器可以发出beep、beep的声音了。

 

 

三 数码管

 

最后一项就是显示出倒计时时间,这里使用了TM1637的4位数码管。DIO和CLK接口可以选择任意的GPIO接口,这里选择GPIO14和GPIO15

TM1637 Board Pin Function Raspberry Pin
GND Ground GND
VCC +5V Power 5V
DIO Data In GPIO 14
CLK Clock GPIO 15

 

这里要注意,这个设备使用的是5V的VCC,在使用5V外设的时候要注意GPIO口是INPUT还是OUTPUT,如果是OUTPUT问题不大,如果是INPUT,那么要注意外设产生的电平可能是5V,而树莓派GPIO口能承受的电平是3.3V,所以可能会损坏树莓派,这个时候就需要一个5V-3.3V的电平转换,可以自己用电阻做一个(网上搜索一下就有),也可以直接买一个8路5V-3.3V的电平转换。 不做数码管是一个输入设备,所以我们这里不需要。

 

编写代码

 

相比前面的LED灯和有源蜂鸣器,这个四位数码管就复杂很多了,不是高低电平就能搞定的。而RPI.GPIO没有直接提供控制TM1637的库,要想让TM1637显示一种办法找他的文档自己撸,另一种就是别人写好的库。 对于我们来说一般选择后一种了。google一下还是很多的,有C写的,也有python的。

最早下载的是这个源码,但是这个有问题,缺少方法,不知道那些用这个的人是怎么用的。。。

wget https://raspberrytips.nl/files/tm1637.py

然后找了一个可以用的,这个里面有测试代码,注释掉就好了

wget https://raw.githubusercontent.com/timwaizenegger/raspberrypi-examples/master/actor-led-7segment-4numbers/tm1637.py

在我们之前的代码中增加显示相关的代码:

#define TM1637 pin
PIN_DIO = 14
PIN_CLK = 15

def initDisplay():
    global display
    display = TM1637(PIN_CLK, PIN_DIO, 2)
    display.Clear()

def showTime(sec):
    global display
    display.ShowInt(sec)

另外在退出的时候要清空一下显示的内容

def destory(signum, frame):
    print "Destory Traffic!!"
    global display
    display.cleanup()
    GPIO.cleanup()
    exit()

 

TM1637需要4根线,为了方便吧风扇拔掉了,这样4个线就可以连在一起了,方便一点。最终效果图如下,可以正确的显示数字了。

 

最终效果

不知道怎么我这不能播放优酷视频了,那就贴上链接

http://v.youku.com/v_show/id_XMzc4NzUxNDA0NA==.html?spm=a2hzp.8244740.0.0

发现在绿灯倒数3S的时候,1S的时间变长了。看了下代码,应该是time.sleep导致timer时间变长,所以用2个单独的线程来跑一下就好了。

def greenInterval():

    global reaminTime
    reaminTime = reaminTime - 1
    print "Green remain: {0}s".format(reaminTime)
    showTime(reaminTime)
    if reaminTime <= TIME_BLINK_GREEN:
        threading.Thread(target=blinkLight,args=(PIN_LIGHT_GREEN))
        threading.Thread(target=beepBuzzer,args=(PIN_BUZZER))

 

项目地址:https://github.com/cclover/RaspberrySample/tree/master/TrafficLight


如果本文对您有帮助,可以扫描下方二维码打赏!您的支持是我的动力!
微信打赏 支付宝打赏

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注