Python 使用驱动硬件详解

在本章中,我们将介绍以下主题:

Raspberry Pi 计算机区别于大多数其他家庭/办公室计算机的一个关键特性是,它能够直接与其他硬件接口。Raspberry Pi 上的通用输入/输出GPIO)引脚可以控制范围广泛的低电平电子设备,从发光二极管LED)到开关、传感器、电机、伺服甚至额外的显示器。

本章将重点介绍如何使用一些简单的电路连接 Raspberry Pi,以及如何使用 Python 控制和响应连接的组件。

Raspberry Pi 硬件接口由位于电路板一侧的 40 个引脚组成。

The GPIO pins and their layout will vary slightly according to the particular model you have.

RespberryPi 3、RespberryPi 2 和 RespberryPi B+都具有相同的 40 针布局。

较旧的 Raspberry Pi 1 型号(非 PLUS 类型)具有 26 针头,与较新型号的 1-26 针头相同。

Raspberry Pi 2, Raspberry Pi B+, and Raspberry Pi Model Plus GPIO header pins (pin functions)

连接器的布局如上图所示;引脚编号如 GPIO 头引脚 1 所示。

引脚 1位于离 SD 卡最近的一端,如下图所示:

The Raspberry Pi GPIO header location

使用 GPIO 头时应小心,因为它还包括电源引脚(3V3 和 5V)以及接地接地)引脚。所有 GPIO 引脚都可以用作标准 GPIO,但也有几个引脚具有特殊功能;它们被标记并用不同的颜色高亮显示。

It is common for engineers to use a 3V3 notation to specify values in schematics in order to avoid using decimal places that could easily be missed (using 33V rather than 3.3V would cause severe damage to the circuitry). The same can be applied to the values of other components, such as resistors, for example, 1.2K ohms can be written as 1K2 ohms.

TXRX引脚用于串行通信,在电压电平转换器的帮助下,信息可以通过串行电缆传输到另一台计算机或设备。

我们还有SDASCL引脚,能够支持一个名为I2C的双线总线通信协议(Raspberry Pi 3 和 Model Plus 板上有两个 I2C 通道:【T12 通道 1 ARM】,用于通用,通道 0 VC,通常用于识别附在顶部(模块)上的硬件。还有SPI MOSISPI MISOSPI SCLKSPI CE0SPI CE1引脚,它们支持另一种称为SPI的高速数据总线协议。最后,我们有PWM0/1引脚,它允许生成脉宽调制信号,这对伺服和生成模拟信号非常有用。

但是,在本章中,我们将重点介绍仅使用标准 GPIO 函数。GPIO 引脚布局如下图所示:

Raspberry Pi GPIO header pins (GPIO.BOARD and GPIO.BCM)

与 Raspberry Pi 2 GPIO 布局相比,Raspberry Pi Rev 2(2014 年 7 月之前)具有以下差异:

  • 26 GPIO 引脚头(与前 26 个引脚匹配)。
  • 位于销头旁边的另一组八个孔(P5)。详情如下:

Raspberry Pi Rev 2 P5 GPIO header pins

  • 原始 Raspberry Pi Rev 1(2012 年 10 月之前)总共只有 26 个 GPIO 引脚(与当前 Raspberry Pi 的前 26 个引脚匹配,但以下细节除外:

Raspberry Pi Rev 1 GPIO header differences

RPi.GPIO库可以使用两种系统中的一种来参考树莓 Pi 上的管脚。中间显示的数字表示引脚的物理位置,也是GPIO.BOARD模式下RPi.GPIO库引用的数字。外部的数字(GPIO.BCM是处理器物理端口的实际参考号,用于指示哪些管脚已连接(这就是为什么它们没有按任何特定顺序连接)。当模式设置为GPIO.BCM时使用它们,它们允许控制 GPIO 头引脚以及连接到其他 GPIO 线路的任何外围设备。这包括 BCM GPIO 4 上附加摄像头上的 LED 和主板上的状态 LED。但是,这也可能包括用于读取/写入 SD 卡的 GPIO 线,如果受到干扰,将导致严重错误。

如果您使用其他编程语言访问 GPIO 引脚,则编号方案可能不同,因此,如果您知道 BCM GPIO 参考,即处理器的物理 GPIO 端口,这将很有帮助。

Be sure to check out the Appendix, Hardware and Software List, which lists all the items used in this chapter and the places that you can obtain them from.

hello world的硬件等价物是一个 LED 闪光灯,这是一个很好的测试,可以确保一切正常,并且您已经正确连接了它。为了让它更有趣一点,我建议使用红色、蓝色和绿色RGB)LED,但如果您只有这些,请随意使用单独的 LED。

您将需要以下设备:

  • 4 条杜邦阴螺纹至阳螺纹接线
  • 小型试验板(170 个连接点)或更大的试验板
  • RGB LED(公共阴极)/3 个标准 LED(理想情况下为红色、绿色和蓝色)
  • 试验板导线(实心)
  • 3 x 470 欧姆电阻器

前面的每个组件都不应该花费很多美元,并且可以在以后的其他项目中重用。试验板是一个特别有用的部件,允许您在不需要焊接的情况下尝试自己的电路:

Diagrams of an RGB LED, a standard LED, and an RGB circuit

下图显示了试验板电路:

The wiring of an RGB LED/standard LEDs connected to the GPIO header There are several different kinds of RGB LEDs available, so check the datasheet of your component to confirm the pin order and type you have. Some are RGB, so ensure that you wire accordingly or adjust the RGB_ pin settings in the code. You can also get common anode variants, which will require the anode to be connected to 3V3 (GPIO-pin 1) for it to light up (and they will also require RGB_ENABLE and RGB_DISABLE to be set to 0 and 1 respectively).

本书的实验板和部件图是使用名为Fritzingwww.Fritzing.org的免费工具创建的;这是伟大的规划自己的树莓 Pi 项目。

  1. 创建ledtest.py脚本,如下所示:
#!/usr/bin/python3 
#ledtest.py 
import time 
import RPi.GPIO as GPIO 
# RGB LED module 
#HARDWARE SETUP 
# GPIO 
# 2[======XRG=B==]26[=======]40 
# 1[=============]25[=======]39 
# X=GND R=Red G=Green B=Blue  
#Setup Active States 
#Common Cathode RGB-LED (Cathode=Active Low) 
RGB_ENABLE = 1; RGB_DISABLE = 0 

#LED CONFIG - Set GPIO Ports 
RGB_RED = 16; RGB_GREEN = 18; RGB_BLUE = 22 
RGB = [RGB_RED,RGB_GREEN,RGB_BLUE] 

def led_setup(): 
  #Setup the wiring 
  GPIO.setmode(GPIO.BOARD) 
  #Setup Ports 
  for val in RGB: 
    GPIO.setup(val,GPIO.OUT) 

def main(): 
  led_setup() 
  for val in RGB: 
    GPIO.output(val,RGB_ENABLE) 
    print("LED ON") 
    time.sleep(5) 
    GPIO.output(val,RGB_DISABLE) 
    print("LED OFF") 

try: 
  main() 
finally: 
  GPIO.cleanup() 
  print("Closed Everything. END") 
#End
  1. RPi.GPIO库需要sudo权限才能访问 GPIO 引脚硬件,因此您需要使用以下命令运行脚本:
sudo python3 ledtest.py  

当您运行脚本时,您应该看到 LED 的红色、绿色和蓝色部分(或者每个 LED,如果您使用的是单独的 LED)依次亮起。如果没有,请再次检查您的接线,或通过将红色、绿色或蓝色导线临时连接到 3V3 引脚(GPIO 头的引脚 1)来确认 LED 是否正常工作。

The sudo command is required for most hardware-related scripts because it isn't normal for users to directly control hardware at such a low level. For example, setting or clearing a control pin that is part of the SD card controller could corrupt data being written to it. Therefore, for security purposes, superuser permissions are required to stop programs from using hardware by accident (or with malicious intent).

为了使用 Python 访问 GPIO 引脚,我们导入了RPi.GPIO库,它允许通过模块函数直接控制引脚。我们还要求time模块将程序暂停设定的秒数。

然后,我们定义 LED 接线和激活状态的值(请参见本配方的部分中控制 GPIO 电流部分,还有更多…部分)。

在程序使用 GPIO 引脚之前,我们需要通过指定编号方法-GPIO.BOARD-和方向-GPIO.OUTGPIO.IN(在这种情况下,我们将所有 RGB 引脚设置为输出)来设置它们。如果一个引脚配置为输出,我们将能够设置引脚状态;类似地,如果将其配置为输入,我们将能够读取引脚状态。

接下来,我们使用GPIO.ouput()来控制管脚,方法是说明 GPIO 管脚的编号和我们希望它处于的状态(1=高/开和0=低/关)。我们打开每个 LED,等待五秒钟,然后再将其关闭。

最后,我们使用GPIO.cleanup()将 GPIO 引脚返回到其原始默认状态,并释放对引脚的控制,以供其他程序使用。

在 Raspberry Pi 上使用 GPIO 引脚时必须小心,因为这些引脚直接连接到 Raspberry Pi 的主处理器,没有任何额外的保护。必须小心,因为任何不正确的接线都可能损坏 Raspberry Pi 处理器并导致其完全停止工作。

或者,您可以使用可直接插入 GPIO 头引脚的众多模块之一(减少接线错误的机会):

For example, the Pi-Stop is a simple pre-built LED board that simulates a set of traffic lights, designed to be a stepping stone for those who are interested in controlling hardware but want to avoid the risk of damaging their Raspberry Pi. After the basics have been mastered, it also makes an excellent indicator to aid debugging.

只需确保更新ledtest.py脚本中的LED CONFIG管脚引用,以引用您正在使用的硬件所使用的管脚布局和位置。

树莓 Pi 硬件零售商名单见附录硬件和软件清单

每个 GPIO 引脚仅能在其耗尽之前处理特定电流(单个引脚的最大电流为 16 mA 或总共 30 mA),同样,RGB LED 应限制在不超过 100 mA。通过在 LED 之前或之后添加电阻器,我们将能够限制通过它的电流,并控制它的亮度(电流越大,LED 越亮)。

由于我们可能希望一次为多个 LED 供电,因此我们通常会将电流设置为尽可能低,同时仍提供足够的功率点亮 LED。

我们可以用欧姆定律来告诉我们提供一个特定电流需要多少电阻。该规律如下图所示:

Ohm's law: The relationship between the current, resistance, and voltage in electrical circuits

我们的目标是最小电流(3 毫安)和最大电流(16 毫安),同时仍能从每个 LED 发出合理的亮光。为了获得 RGB LED 的平衡输出,我测试了不同的电阻器,直到它们发出接近白光(通过卡观看时)。为每一个选择了 470 欧姆的电阻器(您的 LED 可能略有不同):

Resistors are needed to limit the current that passes through the LEDs

电阻器上的电压等于 GPIO 电压(Vgpio=3.3V)减去特定 LED 上的压降(Vfwd);然后,我们可以使用该电阻计算每个 LED 使用的电流,如以下公式所示:

We can calculate the current drawn by each of the LEDs

许多使用 Raspberry Pi 的应用程序需要激活操作,而不需要键盘和屏幕。GPIO 引脚为 Raspberry Pi 提供了一种很好的方式,可以由您自己的按钮和开关控制,而无需鼠标/键盘和屏幕。

您将需要以下设备:

  • 2 条杜邦阴螺纹至阳螺纹接线
  • 小型试验板(170 个连接点)或更大的试验板
  • 按钮开关(瞬时关闭)或用于接通/断开电路的接线
  • 试验板导线(实心)
  • 1K 欧姆电阻器

开关如下图所示:

The push-button switch and other types of switch The switches used in the following examples are single-pole, single-throw (SPST), momentary close, push-button switches. Single pole (SP) means that there is one set of contacts that makes a connection. In the case of the push switch used here, the legs on each side are connected together with a single-pole switch in the middle. A double-pole (DP) switch acts just like a SP switch, except that the two sides are separated electrically, allowing you to switch two separate components on/off at the same time.

单掷**ST表示开关只与一个位置连接;另一侧将保持打开状态。双掷DT**表示开关的两个位置将连接到不同的部件。

瞬间关闭表示按钮按下时关闭开关,松开时自动打开开关。一个锁定的按钮开关将保持关闭状态,直到再次按下。

The layout of the button circuit

我们将在本例中使用声音,因此您还需要将扬声器或耳机连接到 Raspberry Pi 的音频插座。

您需要使用以下命令安装一个名为flite的程序,该命令将使 Raspberry Pi 对话:

sudo apt-get install flite  

安装后,可以使用以下命令对其进行测试:

sudo flite -t "hello I can talk"  

如果声音太小(或太大),可以使用以下命令调整音量(0-100%):

amixer set PCM 100%  

创建btntest.py脚本,如下所示:

#!/usr/bin/python3 
#btntest.py 
import time 
import os 
import RPi.GPIO as GPIO 
#HARDWARE SETUP 
# GPIO 
# 2[==X==1=======]26[=======]40 
# 1[=============]25[=======]39 
#Button Config 
BTN = 12 

def gpio_setup(): 
  #Setup the wiring 
  GPIO.setmode(GPIO.BOARD) 
  #Setup Ports 
  GPIO.setup(BTN,GPIO.IN,pull_up_down=GPIO.PUD_UP) 

def main(): 
  gpio_setup() 
  count=0 
  btn_closed = True 
  while True: 
    btn_val = GPIO.input(BTN) 
    if btn_val and btn_closed: 
       print("OPEN") 
       btn_closed=False 
    elif btn_val==False and btn_closed==False: 
       count+=1 
       print("CLOSE %s" % count) 
       os.system("flite -t '%s'" % count) 
       btn_closed=True 
    time.sleep(0.1) 

try: 
  main() 
finally: 
  GPIO.cleanup() 
  print("Closed Everything. END") 
#End 

与上一个配方一样,我们根据需要设置 GPIO 引脚,但这次作为输入,我们还使用以下代码启用内部上拉电阻器(请参见中的上拉和下拉电阻器电路,此配方中的部分有更多……了解更多信息):

GPIO.setup(BTN,GPIO.IN,pull_up_down=GPIO.PUD_UP) 

设置 GPIO 引脚后,我们创建一个循环,使用GPIO.input()持续检查BTN的状态。如果返回值为false,则该引脚已通过开关连接到 0V(地),每次按下按钮时,我们将使用flite为我们大声计数。

由于我们已经在try/finally条件下调用了主函数,所以即使我们使用Ctrl+Z关闭程序,它仍然会调用GPIO.cleanup()

We use a short delay in the loop; this ensures that any noise from the contacts on the switch is ignored. This is because when we press the button, there isn't always perfect contact as we press or release it, and it may produce several triggers if we press it again too quickly. This is known as software debouncing; we ignore the bounce in the signal here.

RespberryPi GPIO 引脚必须小心使用;用于输入的电压应在特定范围内为 ,并且应使用 保护电阻器将从其引出的任何电流降至最低。

我们必须确保只连接 0(接地)和 3V3 之间的输入。一些处理器使用 0V 到 5V 之间的电压,因此需要额外的组件来安全地与它们连接。切勿连接使用 5V 电压的输入或组件,除非您确定它是安全的,否则会损坏 Raspberry Pi 的 GPIO 端口。

前面的代码将 GPIO 引脚设置为使用内部上拉电阻器。GPIO 引脚上没有上拉电阻器(或下拉电阻器),电压可以在 3V3 和 0V 之间自由浮动,实际逻辑状态仍不确定(有时为 1,有时为 0)。

Raspberry Pi 的内部上拉电阻为 50K-65K 欧姆,下拉电阻为 50K-65K 欧姆。GPIO 电路中经常使用外部上拉/下拉电阻器(如下图所示),出于类似原因,通常使用 10K 欧姆或更大的电阻(当它们未激活时,电流消耗非常小)。

上拉电阻器允许少量电流流过 GPIO 引脚,并在未按下开关时提供高电压。当按下开关时,小电流被流向 0V 的大电流所取代,因此我们在 GPIO 引脚上得到一个低电压。按下开关时,开关处于低电平激活状态,逻辑为 0。其工作原理如下图所示:

A pull-up resistor circuit

下拉电阻器的工作方式相同,但开关为高电平(按下时 GPIO 引脚为逻辑 1)。其工作原理如下图所示:

A pull-down resistor circuit

除开关外,电路还包括一个与开关串联的电阻器,以保护 GPIO 引脚,如下图所示:

A GPIO protective current-limiting resistor

保护电阻器的用途是在 GPIO 引脚意外设置为输出而非输入时保护该引脚。例如,假设我们的开关连接在 GPIO 和接地之间。现在 GPIO 引脚被设置为输出并打开(将其驱动到 3V3),只要我们按下开关,在没有电阻器的情况下,GPIO 引脚将直接连接到 0V。GPIO 仍将尝试将其驱动到 3V3;这将导致 GPIO 引脚烧坏(因为它将使用过多电流将引脚驱动到高状态)。如果我们在这里使用 1K 欧姆的电阻器,则可以使用可接受的电流量(I=V/R=3.3/1K=3.3 mA)将引脚驱动到高电平。

Raspberry Pi 应始终正确关闭,以避免 SD 卡损坏(在对 SD 卡执行写入操作时断电)。如果您没有连接键盘或屏幕(您可能正在运行自动化程序或通过网络远程控制它,但忘记关闭它),这可能会造成问题,因为您无法键入命令或查看正在执行的操作。通过添加我们自己的按钮和 LED 指示灯,我们可以轻松地命令关机和复位,然后再次启动以指示系统何时处于活动状态。

您将需要以下设备:

  • 3 根杜邦阴螺纹至阳螺纹接线
  • 小型试验板(170 个连接点)或更大的试验板
  • 按钮开关(瞬时关闭)
  • 通用 LED
  • 2 个 470 欧姆电阻器
  • 试验板导线(实心)

停机回路的整个布局如下图所示:

The controlled shutdown circuit layout

  1. 创建shtdwn.py脚本,如下所示:
#!/usr/bin/python3 
#shtdwn.py 
import time 
import RPi.GPIO as GPIO 
import os 

# Shutdown Script 
DEBUG=True #Simulate Only 
SNDON=True 
#HARDWARE SETUP 
# GPIO 
# 2[==X==L=======]26[=======]40 
# 1[===1=========]25[=======]39 

#BTN CONFIG - Set GPIO Ports 
GPIO_MODE=GPIO.BOARD 
SHTDWN_BTN = 7 #1 
LED = 12       #L 

def gpio_setup(): 
  #Setup the wiring 
  GPIO.setmode(GPIO_MODE) 
  #Setup Ports 
  GPIO.setup(SHTDWN_BTN,GPIO.IN,pull_up_down=GPIO.PUD_UP) 
  GPIO.setup(LED,GPIO.OUT) 

def doShutdown(): 
  if(DEBUG):print("Press detected") 
  time.sleep(3) 
  if GPIO.input(SHTDWN_BTN): 
    if(DEBUG):print("Ignore the shutdown (<3sec)") 
  else: 
    if(DEBUG):print ("Would shutdown the RPi Now") 
    GPIO.output(LED,0) 
    time.sleep(0.5) 
    GPIO.output(LED,1) 
    if(SNDON):os.system("flite -t 'Warning commencing power down 3 2 1'") 
    if(DEBUG==False):os.system("sudo shutdown -h now") 
    if(DEBUG):GPIO.cleanup() 
    if(DEBUG):exit() 

def main(): 
  gpio_setup() 
  GPIO.output(LED,1) 
  while True: 
    if(DEBUG):print("Waiting for >3sec button press") 
    if GPIO.input(SHTDWN_BTN)==False: 
       doShutdown() 
    time.sleep(1) 

try: 
  main() 
finally: 
  GPIO.cleanup() 
  print("Closed Everything. END") 
#End
  1. 为了让这个脚本自动运行(一旦我们测试了它),我们可以将脚本放在~/bin(如果我们只想复制它,我们可以使用cp而不是mv,并使用以下代码将它添加到crontab
mkdir ~/bin 
mv shtdwn.py ~/bin/shtdwn.py  
crontab -e 
  1. 在文件末尾,我们添加了以下代码:
@reboot sudo python3 ~/bin/shtdwn.py 

这一次,当我们设置 GPIO 引脚时,我们将连接到关机按钮的引脚定义为输入,将连接到 LED 的引脚定义为输出。我们打开 LED 以指示系统正在运行。

通过将DEBUG标志设置为True,我们可以测试脚本的功能,而不会导致实际关机(通过读取终端消息);我们只需要确保在实际使用脚本时将DEBUG设置为False

我们进入一个while循环,每秒检查一次引脚,看 GPIO 引脚是否设置为LOW(即检查开关是否被按下);如果是,我们进入doShutdown()功能。

程序将等待三秒钟,然后再次测试,以查看按钮是否仍在按下。如果不再按下按钮,我们将返回到上一个while循环。但是,如果三秒钟后仍在按下,程序将闪烁 LED 并触发关机(并使用flite提供音频警告)。

当我们对脚本的运行方式感到满意时,我们可以禁用DEBUG标志(通过将其设置为False,并将脚本添加到crontabcrontab是一个在后台运行的特殊程序,允许我们在系统启动时安排(在特定时间、日期或定期)程序和操作(@reboot。这使得脚本可以在每次 Raspberry Pi 通电时自动启动。当我们按住关机按钮超过三秒时,它会安全地关闭系统并进入低功率状态(LED 在此之前关闭,表示稍后可以安全地断开电源)。为了重新启动覆盆子皮,我们短暂地切断电源;这将重新启动系统,当覆盆子 Pi 加载后,LED 将亮起。

我们可以通过添加额外的功能和使用额外的 GPIO 连接(如果可用),使用重置标头进一步扩展此示例。

Raspberry Pi 具有用于安装复位集管的孔(Raspberry Pi 3/2 上标记为RUN,Raspberry Pi 1 型号 a 和型号 B 版本 2 上标记为P6。重置引脚允许使用按钮重置设备,而不是每次卸下 micro USB 接口以循环电源:

Raspberry Pi reset headers - on the left, Raspberry Pi Model A/B (Rev2), and on the right, Raspberry Pi 3

要使用它,您需要将一根导线或引脚头焊接到 Raspberry Pi 上,并将一个按钮连接到它(或者每次在两个孔之间短暂地触摸一根导线)。或者,我们可以扩展之前的电路,如下图所示:

The controlled shutdown circuit layout and reset button

我们可以将这个额外的按钮添加到我们的电路中,它可以连接到重置标题(这是最靠近 Raspberry Pi 3 中间的孔,或最靠近其他型号边缘的孔)。当该引脚通过接地(例如其旁边的孔或另一个接地点,例如 GPIO 收割台的引脚 6)临时拉低时,将重置 Raspberry Pi,并允许其在关机后再次启动。

由于我们现在有脚本一直监控关机按钮,我们可以同时添加额外的按钮/开关/跳线进行监控。这将允许我们只需更改输入即可触发特定程序或设置特定状态。以下示例允许我们轻松地在自动 DHCP 联网(默认联网设置)和使用直接 IP 地址之间切换,如第 1 章中的直接联网到笔记本电脑或计算机配方中使用的开始使用覆盆子 Pi 3 计算机,用于直接局域网连接。

将以下元件添加到上一个回路:

  • 470 欧姆的电阻器
  • 带跨接接头(或可选开关)的双针头
  • 试验板导线(实心)

在添加上述组件后,我们的受控关机电路如下所示:

The controlled shutdown circuit layout, reset button, and jumper pins

在前面的脚本中,我们添加了一个额外的输入,以使用以下代码检测LAN_SWA引脚(我们添加到电路中的跳线引脚)的状态:

LAN_SWA = 11    #2 

确保使用以下代码将其设置为gpio_setup()功能中的输入(带上拉电阻器):

GPIO.setup(LAN_SWA,GPIO.IN,pull_up_down=GPIO.PUD_UP) 

添加一个新功能,在 LAN 模式之间切换并读取新的 IP 地址。doChangeLAN()功能检查LAN_SWA引脚的状态自上次调用以来是否发生了变化,如果是,则将网络适配器设置为 DHCP 或相应地设置直接 LAN 设置(并使用flite说出新的 IP 设置,如果可用)。最后,设置为直接连接的 LAN 会导致 LED 在该模式激活时缓慢闪烁。使用以下代码执行此操作:

def doChangeLAN(direct): 
  if(DEBUG):print("Direct LAN: %s" % direct) 
  if GPIO.input(LAN_SWA) and direct==True: 
    if(DEBUG):print("LAN Switch OFF") 
    cmd="sudo dhclient eth0" 
    direct=False 
    GPIO.output(LED,1) 
  elif GPIO.input(LAN_SWA)==False and direct==False: 
    if(DEBUG):print("LAN Switch ON") 
    cmd="sudo ifconfig eth0 169.254.69.69" 
    direct=True 
  else: 
    return direct 
  if(DEBUG==False):os.system(cmd) 
  if(SNDON):os.system("hostname -I | flite") 
  return direct 

添加另一个功能flashled(),每次调用该功能时,只需切换 LED 的状态即可。此功能的代码如下所示:

def flashled(ledon): 
  if ledon: 
    ledon=False 
  else: 
    ledon=True 
  GPIO.output(LED,ledon) 
  return ledon

最后,我们将主循环调整为也调用doChangeLAN(),并使用结果决定是否使用ledon调用flashled(),以跟踪每次 LED 的先前状态。main()功能现在应更新如下:

def main(): 
  gpio_setup() 
  GPIO.output(LED,1) 
  directlan=False 
  ledon=True 
  while True: 
    if(DEBUG):print("Waiting for >3sec button press") 
    if GPIO.input(SHTDWN_BTN)==False: 
       doShutdown() 
    directlan= doChangeLAN(directlan) 
    if directlan: 
      flashled(ledon) 
    time.sleep(1) 

我们已经了解了如何监控 GPIO 上的输入以启动应用程序和控制 Raspberry Pi;然而,有时我们需要控制第三方程序。使用uInput库,我们可以模拟键盘上的按键(甚至鼠标移动),使用我们自己的定制硬件控制任何程序。

有关使用uInput的更多信息,请访问http://tjjr.fi/sw/python-uinput/

执行以下步骤安装uInput

  1. 首先,我们需要下载uInput

您需要使用以下命令从 GitHub(~50KB)下载uInputPython 库:

wget https://github.com/tuomasjjrasanen/python-uinput/archive/master.zip
unzip master.zip

库将解压缩到名为python-uinput-master的目录。

  1. 完成后,可以使用以下命令删除 ZIP 文件:
rm master.zip  
  1. 使用以下命令安装所需的软件包(如果您已经安装了它们,apt-get命令将忽略它们):
sudo apt-get install python3-setuptools python3-dev
sudo apt-get install libudev-dev  
  1. 使用以下命令编译并安装uInput
cd python-uinput-master
sudo python3 setup.py install  
  1. 最后,我们使用以下命令加载新的uinput内核模块:
sudo modprobe uinput  

为确保启动时加载,我们可以使用以下命令将uinput添加到modules文件中:

sudo nano /etc/modules  

uinput放在文件中的新行并保存(Ctrl+XY)。

  1. 使用以下设备创建以下电路:
    • 试验板(半尺寸或更大)
    • 7 条杜邦阴螺纹至阳螺纹接线
    • 六个按钮
    • 6 x 470 欧姆电阻器
    • 面包板钢丝(实心)

GPIO keypad circuit layout

通过将元件焊接到 Vero 原型板(也称为剥离板)中,键盘电路也可以内置到永久性电路中,如下图所示:

GPIO keypad Pi hardware module This circuit is available as a solder-yourself kit from PiHardware.com.

  1. 通过将适当的按钮与适当的引脚匹配,将电路连接至 Raspberry Pi GPIO 引脚,如下表所示:

| | 按钮 | GPIO 引脚 | | GND | | 6. | | v | B 下来 | 22 | | < | B 左 | 18 | | ^ | 加油 | 15 | | > | 对 | 13 | | 1. | B_1 | 11 | | 2. | B_2 | 7. |

创建一个gpiokeys.py脚本,如下所示:

#!/usr/bin/python3 
#gpiokeys.py 
import time 
import RPi.GPIO as GPIO 
import uinput 

#HARDWARE SETUP 
# GPIO 
# 2[==G=====<=V==]26[=======]40 
# 1[===2=1>^=====]25[=======]39 
B_DOWN  = 22    #V 
B_LEFT  = 18   #< 
B_UP    = 15   #^ 
B_RIGHT = 13   #> 
B_1  = 11   #1 
B_2  = 7   #2 

DEBUG=True 
BTN = [B_UP,B_DOWN,B_LEFT,B_RIGHT,B_1,B_2] 
MSG = ["UP","DOWN","LEFT","RIGHT","1","2"] 

#Setup the DPad module pins and pull-ups 
def dpad_setup(): 
  #Set up the wiring 
  GPIO.setmode(GPIO.BOARD) 
  # Setup BTN Ports as INPUTS 
  for val in BTN: 
    # set up GPIO input with pull-up control 
    #(pull_up_down can be: 
    #    PUD_OFF, PUD_UP or PUD_DOWN, default PUD_OFF) 
    GPIO.setup(val, GPIO.IN, pull_up_down=GPIO.PUD_UP) 

def main(): 
  #Setup uinput 
  events = (uinput.KEY_UP,uinput.KEY_DOWN,uinput.KEY_LEFT, 
           uinput.KEY_RIGHT,uinput.KEY_ENTER,uinput.KEY_ENTER) 
  device = uinput.Device(events) 
  time.sleep(2) # seconds 
  dpad_setup() 
  print("DPad Ready!") 

  btn_state=[False,False,False,False,False,False] 
  key_state=[False,False,False,False,False,False] 
  while True: 
    #Catch all the buttons pressed before pressing the related keys 
    for idx, val in enumerate(BTN): 
      if GPIO.input(val) == False: 
        btn_state[idx]=True 
      else: 
        btn_state[idx]=False 

    #Perform the button presses/releases (but only change state once) 
    for idx, val in enumerate(btn_state): 
      if val == True and key_state[idx] == False: 
        if DEBUG:print (str(val) + ":" + MSG[idx]) 
        device.emit(events[idx], 1) # Press. 
        key_state[idx]=True 
      elif val == False and key_state[idx] == True: 
        if DEBUG:print (str(val) + ":!" + MSG[idx]) 
        device.emit(events[idx], 0) # Release. 
        key_state[idx]=False 

    time.sleep(.1) 

try: 
  main() 
finally: 
  GPIO.cleanup() 
#End 

首先,我们导入uinput并定义键盘按钮的接线。对于BTN中的每个按钮,我们将其作为输入启用,并启用内部上拉。

接下来,我们设置uinput,定义要模拟的键,并将它们添加到uinput.Device()函数中。我们等待几秒钟,让uinput初始化,设置初始按钮和键状态,然后开始main循环。

main循环分为两部分:第一部分通过按钮检查并记录btn_state中的状态,第二部分将btn_state与当前key_state数组进行比较。这样,我们就可以检测到btn_state的变化并调用device.emit()来切换按键的状态。

为了让我们在后台运行这个脚本,我们可以使用&来运行它,如下面的 命令所示:

sudo python3 gpiokeys.py &  

The & character allows the command to run in the background, so we can continue with the command line to run other programs. You can use fg to bring it back to the foreground, or %1, %2, and so on if you have several commands running. Use jobs to get a list.

您甚至可以通过按Ctrl+Z将一个进程/程序置于等待状态,以进入命令提示符,然后用bg将其恢复(这将使其在后台运行)。

我们可以使用uinput为其他程序提供硬件控制,包括需要鼠标输入的程序。

您可以在文件中创建多个不同的密钥映射以支持不同的程序。例如,events_z80键映射对于频谱模拟器非常有用,例如Fuse(浏览至http://raspi.tv/2012/how-to-install-fuse-zx-spectrum-emulator-on-raspberry-pi 了解更多详细信息)。events_omx键映射适用于使用以下命令控制通过 OMXPlayer 播放的视频:

omxplayer filename.mp4  

您可以使用-k参数获取omxplayer支持的密钥列表。

用新的键映射替换定义events列表的行,并通过使用以下代码将它们分配给事件来选择不同的键映射:

events_dpad = (uinput.KEY_UP,uinput.KEY_DOWN,uinput.KEY_LEFT, 
              uinput.KEY_RIGHT,uinput.KEY_ENTER,uinput.KEY_ENTER) 
events_z80 = (uinput.KEY_Q,uinput.KEY_A,uinput.KEY_O, 
             uinput.KEY_P,uinput.KEY_M,uinput.KEY_ENTER) 
events_omx = (uinput.KEY_EQUAL,uinput.KEY_MINUS,uinput.KEY_LEFT, 
             uinput.KEY_RIGHT,uinput.KEY_P,uinput.KEY_Q) 

您可以在input.h文件中找到所有KEY定义;您可以使用less命令查看(按Q退出),如下命令所示:

less /usr/include/linux/input.h  

uinput库可以模拟鼠标和操纵杆事件,以及键盘按下。要使用按钮模拟鼠标,我们可以使用以下代码调整脚本以使用鼠标事件(以及定义mousemove以设置移动的步长):

MSG = ["M_UP","M_DOWN","M_LEFT","M_RIGHT","1","Enter"] 
events_mouse=(uinput.REL_Y,uinput.REL_Y, uinput.REL_X, 
             uinput.REL_X,uinput.BTN_LEFT,uinput.BTN_RIGHT) 
mousemove=1 

我们还需要修改按钮处理以提供连续的移动,因为我们不需要跟踪鼠标键的状态。为此,请使用以下代码:

#Perform the button presses/releases 
#(but only change state once) 
for idx, val in enumerate(btn_state): 
  if MSG[idx] == "M_UP" or MSG[idx] == "M_LEFT": 
    state = -mousemove 
  else: 
    state = mousemove 
  if val == True: 
    device.emit(events[idx], state) # Press. 
  elif val == False: 
    device.emit(events[idx], 0) # Release. 
time.sleep(0.01) 

本章的下一个示例演示了一些看似简单的硬件如果用软件控制,可以产生一些令人印象深刻的结果。为此,我们将回到使用 RGB LED。我们将使用五个接线的 RGB LED,这样我们只需要使用八个 GPIO 引脚,就可以使用一种称为硬件复用的方法来控制它们的红色、绿色和蓝色元素(请参阅中的硬件复用小节,本配方中还有更多的……部分)。

您需要下图所示的 RGB LED 模块:

The RGB LED module from PiHardware.com

如上图所示,中的 RGB LED 模块 http://pihardware.com/ 配有 GPIO 引脚和用于连接的杜邦阴对阴电缆。虽然有两组引脚标记为 1 到 5,但只需连接一侧。

或者,您可以使用五个公共阴极 RGB LED、3 x 470 欧姆电阻器和 Vero 原型板(或大型试验板)重新创建自己的电路。电路将如下图所示:

Circuit diagram for the RGB LED module Strictly speaking, we should use 15 resistors in this circuit (one for each RGB LED element), which will avoid interference from LEDs sharing the same resistor, and will also prolong the life of the LEDs themselves if switched on together. However, there is only a slight advantage in using this, particularly since we intend to drive each RGB LED independently of the other four to achieve multi-color effects.

您需要将电路连接到 Raspberry Pi GPIO 标头,如下所示:

| RGB 发光二极管 | | | | | | 1. | | 2. | 3. | | 4. | | | | | | | | | | | Rpi GPIO 引脚 | 2. | 4. | 6. | 8. | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | | Rpi GPIO 引脚 | 1. | 3. | 5. | 7. | 9 | 11 | 13 | 15 | 17 | 19 | 21 | 23 | 25 | 27 | 29 | 31 | 33 | 35 | 37 | 39 | | RGB 发光二极管 | | | | 5. | | R | G | B | | | | | | | | | | | | |

创建rgbled.py脚本并执行以下步骤:

  1. 导入所有必需的模块,并在以下代码的帮助下定义要使用的值:
#!/usr/bin/python3 
#rgbled.py 
import time 
import RPi.GPIO as GPIO 

#Setup Active states 
#Common Cathode RGB-LEDs (Cathode=Active Low) 
LED_ENABLE = 0; LED_DISABLE = 1 
RGB_ENABLE = 1; RGB_DISABLE = 0 
#HARDWARE SETUP 
# GPIO 
# 2[=====1=23=4==]26[=======]40 
# 1[===5=RGB=====]25[=======]39 
#LED CONFIG - Set GPIO Ports 
LED1 = 12; LED2 = 16; LED3 = 18; LED4 = 22; LED5 = 7 
LED = [LED1,LED2,LED3,LED4,LED5] 
RGB_RED = 11; RGB_GREEN = 13; RGB_BLUE = 15 
RGB = [RGB_RED,RGB_GREEN,RGB_BLUE] 
#Mixed Colors 
RGB_CYAN = [RGB_GREEN,RGB_BLUE] 
RGB_MAGENTA = [RGB_RED,RGB_BLUE] 
RGB_YELLOW = [RGB_RED,RGB_GREEN] 
RGB_WHITE = [RGB_RED,RGB_GREEN,RGB_BLUE] 
RGB_LIST = [RGB_RED,RGB_GREEN,RGB_BLUE,RGB_CYAN, 
            RGB_MAGENTA,RGB_YELLOW,RGB_WHITE] 
  1. 使用以下代码定义设置 GPIO 引脚的函数:
def led_setup(): 
  '''Setup the RGB-LED module pins and state.''' 
  #Set up the wiring 
  GPIO.setmode(GPIO.BOARD) 
  # Setup Ports 
  for val in LED: 
    GPIO.setup(val, GPIO.OUT) 
  for val in RGB: 
    GPIO.setup(val, GPIO.OUT) 
  led_clear()
  1. 使用以下代码定义我们的工具功能以帮助控制 LED:
def led_gpiocontrol(pins,state): 
  '''This function will control the state of 
  a single or multiple pins in a list.''' 
  #determine if "pins" is a single integer or not 
  if isinstance(pins,int): 
    #Single integer - reference directly 
    GPIO.output(pins,state) 
  else: 
    #if not, then cycle through the "pins" list 
    for i in pins: 
      GPIO.output(i,state) 

def led_activate(led,color): 
  '''Enable the selected led(s) and set the required color(s) 
  Will accept single or multiple values''' 
  #Enable led 
  led_gpiocontrol(led,LED_ENABLE) 
  #Enable color 
  led_gpiocontrol(color,RGB_ENABLE) 

def led_deactivate(led,color): 
  '''Deactivate the selected led(s) and set the required 
  color(s) will accept single or multiple values''' 
  #Disable led 
  led_gpiocontrol(led,LED_DISABLE) 
  #Disable color 
  led_gpiocontrol(color,RGB_DISABLE) 

def led_time(led, color, timeon): 
  '''Switch on the led and color for the timeon period''' 
  led_activate(led,color) 
  time.sleep(timeon) 
  led_deactivate(led,color) 

def led_clear(): 
  '''Set the pins to default state.''' 
  for val in LED: 
    GPIO.output(val, LED_DISABLE) 
  for val in RGB: 
    GPIO.output(val, RGB_DISABLE) 

def led_cleanup(): 
  '''Reset pins to default state and release GPIO''' 
  led_clear() 
  GPIO.cleanup()
  1. 创建测试功能以演示模块的功能:
def main(): 
  '''Directly run test function. 
  This function will run if the file is executed directly''' 
  led_setup() 
  led_time(LED1,RGB_RED,5) 
  led_time(LED2,RGB_GREEN,5) 
  led_time(LED3,RGB_BLUE,5) 
  led_time(LED,RGB_MAGENTA,2) 
  led_time(LED,RGB_YELLOW,2) 
  led_time(LED,RGB_CYAN,2)  

if __name__=='__main__': 
  try: 
    main() 
  finally: 
    led_cleanup() 
#End 

首先,我们通过定义启用禁用LED 所需的状态来定义硬件设置,具体取决于所使用的 RGB LED(公共阴极)的类型。如果您使用的是普通阳极装置,只需反转启用禁用状态即可。

接下来,我们定义到管脚的 GPIO 映射,以匹配我们之前所做的布线。

我们还通过将红色、绿色和/或蓝色组合在一起来定义一些基本颜色组合,如下图所示:

LED color combinations

我们定义了一系列有用的函数,第一个是led_setup(),它将 GPIO 编号设置为GPIO.BOARD,并定义要用作输出的所有管脚。我们还调用了一个名为led_clear()的函数,该函数将在禁用所有管脚的情况下将管脚设置为默认状态。

This means that the LED pins, 1-5 (the common cathode on each LED), are set to HIGH, while the RGB pins (the separate anodes for each color) are set to LOW.

我们创建了一个名为led_gpiocontrol()的函数,它允许我们设置一个或多个管脚的状态。isinstance()函数允许我们测试一个值,看看它是否匹配一个特定的类型(在本例中是一个整数);然后,我们可以设置单个管脚的状态,或者遍历管脚列表并设置每个管脚。

接下来,我们定义两个函数,led_activate()led_deactivate(),它们将启用和禁用指定的 LED 和颜色。最后,我们定义了led_time(),这将允许我们指定 LED、颜色和打开时间。

我们还创建led_cleanup()将引脚(和 LED)重置为默认值,并调用GPIO.cleanup()释放正在使用的 GPIO 引脚。

此脚本旨在成为一个库文件,因此在直接运行该文件时,我们将使用if __name__=='__main__'检查仅运行我们的测试代码:

By checking the value of __name__, we can determine whether the file was run directly (it will equal __main__) or whether it was imported by another Python script.

这允许我们定义一个特殊的测试代码,它只在我们直接加载并运行文件时执行。如果我们将此文件作为模块包含在另一个脚本中,则不会执行此代码。

与之前一样,我们将使用try/finally来允许我们始终执行清理操作,即使我们提前退出。

为了测试脚本,我们将设置 LED 以各种颜色依次点亮。

我们可以通过一次打开 RGB LED 的一个或多个部分来创建几种不同的颜色。然而,通过一些巧妙的编程,我们可以创建一个完整的颜色光谱。此外,我们可以在每个 LED 上同时显示不同的颜色。

LED 需要阳极侧的高电压和阴极侧的低电压才能点亮。电路中使用的 RGB LED 是公共阴极,因此我们必须在 RGB 引脚上施加高电压(3V3),在阴极引脚上施加低电压(0V)(连接到每个 LED 的引脚 1 至 5)。

阴极和 RGB 引脚状态如下所示:

Cathode and RGB pin states

因此,我们可以启用一个或多个 RGB 引脚,但仍然可以控制哪些 LED 点亮。我们启用要点亮的 LED 的引脚,禁用不需要的引脚。这使我们能够使用比单独控制 15 条 RGB 线所需的管脚少得多的管脚。

我们可以向库中添加新函数以产生不同的效果,例如生成随机颜色。以下函数使用randint()获取介于 1 和颜色数之间的值。我们忽略超过可用颜色数量的任何值,以便控制 LED 关闭的频率。执行以下步骤以添加所需的功能:

  1. 使用以下代码将random模块中的randint()函数添加到rgbled.py脚本中:
from random import randint
  1. 现在使用以下代码添加led_rgbrandom()
def led_rgbrandom(led,period,colors): 
   ''' Light up the selected led, for period in seconds, 
   in one of the possible colors. The colors can be 
   1 to 3 for RGB, or 1-6 for RGB plus combinations, 
   1-7 includes white. Anything over 7 will be set as 
   OFF (larger the number more chance of OFF).'''  
  value = randint(1,colors) 
  if value < len(RGB_LIST): 
    led_time(led,RGB_LIST[value-1],period) 
  1. 使用main()功能中的以下命令创建一系列 闪烁 LED:
for i in range(20): 
  for j in LED: 
    #Select from all, plus OFF 
    led_rgbrandom(j,0.1,20) 

到目前为止,我们在一个或多个 LED 上一次只显示一种颜色。如果你考虑电路是如何布线的,你可能会想知道我们如何能同时得到一个 LED 显示一种颜色和另一种颜色。简单的答案是,我们不需要,我们只需要快速完成!

我们所需要做的就是一次显示一种颜色,但来回改变它,如此之快,颜色看起来就像两种颜色的混合(甚至是三个红/绿/蓝 LED 的组合)。幸运的是,这是像 Raspberry Pi 这样的计算机可以很容易做到的事情,甚至允许我们将 RGB 元素组合在一起,在所有五个 LED 上形成多种颜色。执行以下步骤混合颜色:

  1. 在混合颜色定义之后,使用以下代码将组合颜色定义添加到rgbled.py脚本的顶部:
#Combo Colors 
RGB_AQUA = [RGB_CYAN,RGB_GREEN] 
RGB_LBLUE = [RGB_CYAN,RGB_BLUE] 
RGB_PINK = [RGB_MAGENTA,RGB_RED] 
RGB_PURPLE = [RGB_MAGENTA,RGB_BLUE] 
RGB_ORANGE = [RGB_YELLOW,RGB_RED] 
RGB_LIME = [RGB_YELLOW,RGB_GREEN] 
RGB_COLORS = [RGB_LIME,RGB_YELLOW,RGB_ORANGE,RGB_RED, 
              RGB_PINK,RGB_MAGENTA,RGB_PURPLE,RGB_BLUE, 
              RGB_LBLUE,RGB_CYAN,RGB_AQUA,RGB_GREEN] 

前面的代码将提供创建阴影所需的颜色组合,RGB_COLORS提供平滑的渐变。

  1. 接下来,我们需要创建一个名为led_combo()的函数来处理单个或多个颜色。该功能的代码如下所示:
def led_combo(pins,colors,period): 
  #determine if "colors" is a single integer or not 
  if isinstance(colors,int): 
    #Single integer - reference directly 
    led_time(pins,colors,period) 
  else: 
    #if not, then cycle through the "colors" list 
    for i in colors: 
      led_time(pins,i,period) 
  1. 现在我们可以创建一个新脚本rgbledrainbow.py,以利用rgbled.py模块中的新函数。rgbledrainbow.py脚本如下:
#!/usr/bin/python3 
#rgbledrainbow.py 
import time 
import rgbled as RGBLED 

def next_value(number,max): 
  number = number % max 
  return number 

def main(): 
  print ("Setup the RGB module") 
  RGBLED.led_setup() 

  # Multiple LEDs with different Colors 
  print ("Switch on Rainbow") 
  led_num = 0 
  col_num = 0 
  for l in range(5): 
    print ("Cycle LEDs") 
    for k in range(100): 
      #Set the starting point for the next set of colors 
      col_num = next_value(col_num+1,len(RGBLED.RGB_COLORS)) 
      for i in range(20):  #cycle time 
        for j in range(5): #led cycle 
          led_num = next_value(j,len(RGBLED.LED)) 
          led_color = next_value(col_num+led_num, 
                                 len(RGBLED.RGB_COLORS)) 
          RGBLED.led_combo(RGBLED.LED[led_num], 
                           RGBLED.RGB_COLORS[led_color],0.001) 

    print ("Cycle COLORs")         
    for k in range(100): 
      #Set the next color 
      col_num = next_value(col_num+1,len(RGBLED.RGB_COLORS)) 
      for i in range(20): #cycle time 
        for j in range(5): #led cycle 
          led_num = next_value(j,len(RGBLED.LED)) 
          RGBLED.led_combo(RGBLED.LED[led_num], 
                           RGBLED.RGB_COLORS[col_num],0.001) 
  print ("Finished") 

if __name__=='__main__': 
  try: 
    main() 
  finally: 
    RGBLED.led_cleanup() 
#End 

main()功能将首先在 LED 之间循环,在所有 LED 上设置RGB_COLORS阵列中的每种颜色。然后,它将在颜色之间循环,在 LED 上产生彩虹效果:

Cycling through multiple colors on the five RGB LEDs

视觉持久性POV)显示器可以产生一种近乎神奇的效果,通过非常快速地来回移动一行 LED 或绕圈子来显示空中的图像。这种效果之所以有效,是因为您的眼睛无法快速调整以分离出单个闪光,因此您观察到一个合并图像(显示的消息或图片):

Persistence of vision using RGB LEDs

此配方使用上一配方中使用的 RGB LED 套件;您还需要以下附加项目:

  • 试验板(半尺寸或更大)
  • 2 条杜邦阴螺纹至阳螺纹接线
  • 倾斜开关(滚珠轴承类型适用)
  • 1 x 470 欧姆电阻器(电阻保护)
  • 试验板导线(实心)

应将倾斜开关添加到 RGB LED(如多路复用彩色 LED配方的准备部分所述)。倾斜开关的接线如下所示:

The tilt switch is connected to GPIO Input (GPIO pin 24) and Gnd (GPIO pin 6)

要再现 POV 图像,您需要能够快速移动 LED 并前后倾斜开关。请注意倾斜开关的安装方式与侧面成一定角度,以便开关向左移动时打开。建议将硬件安装在一段木材或类似设备上。您甚至可以使用便携式 USB 电池组和 Wi-Fi 加密狗通过远程连接为 Raspberry Pi 供电和控制(参见第 1 章中的使用 SSH(和 X11 转发)通过网络远程连接到 Raspberry Pi 开始使用 Raspberry Pi 3 计算机,了解详细信息):

Persistence of vision hardware setup

您还需要完整的rgbled.py文件,我们将在如何操作中进一步扩展该文件。。。部分

  1. 创建一个名为tilt.py的脚本来报告倾斜开关的状态:
#!/usr/bin/python3 
#tilt.py 
import RPi.GPIO as GPIO 
#HARDWARE SETUP 
# GPIO 
# 2[===========T=]26[=======]40 
# 1[=============]25[=======]39 
#Tilt Config 
TILT_SW = 24 

def tilt_setup(): 
  #Setup the wiring 
  GPIO.setmode(GPIO.BOARD) 
  #Setup Ports 
  GPIO.setup(TILT_SW,GPIO.IN,pull_up_down=GPIO.PUD_UP) 

def tilt_moving(): 
  #Report the state of the Tilt Switch 
  return GPIO.input(TILT_SW) 

def main(): 
  import time 
  tilt_setup() 
  while True: 
    print("TILT %s"% (GPIO.input(TILT_SW))) 
    time.sleep(0.1) 

if __name__=='__main__': 
  try: 
    main() 
  finally: 
    GPIO.cleanup() 
    print("Closed Everything. END") 
#End 
  1. 您可以通过使用以下命令直接运行脚本来测试脚本:
sudo python3 tilt.py
  1. 将下面的rgbled_pov()函数添加到我们之前创建的rgbled.py脚本中;这将允许我们显示一行图像:
def rgbled_pov(led_pattern,color,ontime): 
  '''Disable all the LEDs and re-enable the LED pattern in the required color''' 
  led_deactivate(LED,RGB) 
  for led_num,col_num in enumerate(led_pattern): 
    if col_num >= 1: 
      led_activate(LED[led_num],color) 
  time.sleep(ontime) 
  1. 我们现在将创建以下名为rgbledmessage.py的文件,以执行显示消息所需的操作。首先,我们将导入使用的模块:更新的rgbled模块、新的tilt模块和 Pythonos模块。最初,我们将DEBUG设置为True,因此 Python 终端将在脚本运行时显示附加信息:
#!/usr/bin/python3 
# rgbledmessage.py 
import rgbled as RGBLED 
import tilt as TILT 
import os 

DEBUG = True 
  1. 添加一个readMessageFile()函数读取letters.txt文件的内容,然后添加processFileContent()生成每个字母 LED 图案的Python 字典
def readMessageFile(filename): 
  assert os.path.exists(filename), 'Cannot find the message file: %s' % (filename) 
  try: 
    with open(filename, 'r') as theFile: 
    fileContent = theFile.readlines() 
  except IOError: 
    print("Unable to open %s" % (filename)) 
  if DEBUG:print ("File Content START:") 
  if DEBUG:print (fileContent) 
  if DEBUG:print ("File Content END") 
  dictionary = processFileContent(fileContent) 
  return dictionary  

def processFileContent(content): 
  letterIndex = [] #Will contain a list of letters stored in the file 
  letterList = []  #Will contain a list of letter formats 
  letterFormat = [] #Will contain the format of each letter 
  firstLetter = True 
  nextLetter = False 
  LETTERDIC={} 
  #Process each line that was in the file 
  for line in content: 
    # Ignore the # as comments 
    if '#' in line: 
      if DEBUG:print ("Comment: %s"%line) 
    #Check for " in the line = index name   
    elif '"' in line: 
      nextLetter = True 
      line = line.replace('"','') #Remove " characters 
      LETTER=line.rstrip() 
      if DEBUG:print ("Index: %s"%line) 
    #Remaining lines are formatting codes 
    else: 
      #Skip firstLetter until complete 
      if firstLetter: 
        firstLetter = False 
        nextLetter = False 
        lastLetter = LETTER 
      #Move to next letter if needed 
      if nextLetter: 
        nextLetter = False 
        LETTERDIC[lastLetter]=letterFormat[:] 
        letterFormat[:] = [] 
        lastLetter = LETTER 
      #Save the format data 
      values = line.rstrip().split(' ') 
      row = [] 
      for val in values: 
        row.append(int(val)) 
      letterFormat.append(row) 
  LETTERDIC[lastLetter]=letterFormat[:] 
  #Show letter patterns for debugging 
  if DEBUG:print ("LETTERDIC: %s" %LETTERDIC) 
  if DEBUG:print ("C: %s"%LETTERDIC['C']) 
  if DEBUG:print ("O: %s"%LETTERDIC['O']) 
  return LETTERDIC
  1. 增加一个createBuffer()功能,将一条消息转换为每个字母的一系列 LED 图案(假设字母由letters.txt文件定义):
def createBuffer(message,dictionary): 
  buffer=[] 
  for letter in message: 
    try: 
      letterPattern=dictionary[letter] 
    except KeyError: 
      if DEBUG:print("Unknown letter %s: use _"%letter) 
      letterPattern=dictionary['_'] 
    buffer=addLetter(letterPattern,buffer) 
  if DEBUG:print("Buffer: %s"%buffer) 
  return buffer 

def addLetter(letter,buffer): 
  for row in letter: 
    buffer.append(row) 
  buffer.append([0,0,0,0,0]) 
  buffer.append([0,0,0,0,0]) 
  return buffer 
  1. 接下来,我们定义一个displayBuffer()功能,使用rgbled模块中的rgbled_pov()功能显示 LED 图案:
def displayBuffer(buffer): 
  position=0 
  while(1): 
    if(TILT.tilt_moving()==False): 
      position=0 
    elif (position+1)<len(buffer): 
      position+=1 
      if DEBUG:print("Pos:%s ROW:%s"%(position,buffer[position])) 
    RGBLED.rgbled_pov(buffer[position],RGBLED.RGB_GREEN,0.001) 
    RGBLED.rgbled_pov(buffer[position],RGBLED.RGB_BLUE,0.001) 
  1. 最后,我们创建一个main()函数来执行所需的每个步骤:
    1. 设置硬件组件(RGB LED 和倾斜开关)。
    2. 阅读letters.txt文件。
    3. 定义 LED 字母模式的字典。
    4. 生成一个缓冲区来表示所需的消息。
    5. rgbled模块显示缓冲器,用tilt模块控制:
def main(): 
  RGBLED.led_setup() 
  TILT.tilt_setup() 
  dict=readMessageFile('letters.txt') 
  buffer=createBuffer('_COOKBOOK_',dict) 
  displayBuffer(buffer) 

if __name__=='__main__': 
  try: 
    main() 
  finally: 
    RGBLED.led_cleanup() 
    print("Closed Everything. END") 
#End 
  1. 创建以下名为letters.txt的文件,以定义显示示例'_COOKBOOK_'消息所需的 LED 模式。请注意,此文件只需要为消息中的每个唯一字母或符号定义一个模式:
#COOKBOOK 
"C" 
0 1 1 1 0 
1 0 0 0 1 
1 0 0 0 1 
"O" 
0 1 1 1 0 
1 0 0 0 1 
1 0 0 0 1 
0 1 1 1 0 
"K" 
1 1 1 1 1 
0 1 0 1 0 
1 0 0 0 1 
"B" 
1 1 1 1 1 
1 0 1 0 1 
0 1 0 1 0 
"_" 
0 0 0 0 0 
0 0 0 0 0 
0 0 0 0 0 
0 0 0 0 0 
0 0 0 0 0 

第一个函数readMessageFile()将打开并读取给定文件的内容。然后,它将使用processFileContent()返回一个 Python 字典,其中包含所提供文件中定义的字母的对应模式。处理文件中的每一行,忽略包含#字符的任何行,并检查"字符以指示后面的 LED 图案的名称。处理完文件后,我们得到一个 Python 字典,其中包含'_''C''B''K''O'字符的 LED 模式:

'_': [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] 
'C': [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1]] 
'B': [[1, 1, 1, 1, 1], [1, 0, 1, 0, 1], [0, 1, 0, 1, 0]] 
'K': [[1, 1, 1, 1, 1], [0, 1, 0, 1, 0], [1, 0, 0, 0, 1]] 
'O': [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]] 

现在我们可以选择字母,我们可以使用createBuffer()功能创建一系列 LED 图案。顾名思义,该函数将通过查找消息中的每个字母并逐行添加相关图案来建立 LED 图案的缓冲区。如果在字典中找不到字母,则将使用空格代替。

最后,我们现在有一个准备好显示的 LED 模式列表。为了控制启动顺序的时间,我们将使用倾斜模块并检查倾斜开关的状态:

The tilt switch position when not moving (left) and moving (right)

倾斜开关由一个小滚珠轴承组成,该轴承封闭在一个中空的绝缘圆筒内;当钢球位于气缸底部时,两个销之间的连接闭合。当球移动到油缸的另一端,脱离销接触时,倾斜开关打开:

The tilt switch circuit with the switch closed and with the switch open

当开关闭合时,上述倾斜开关电路将允许 GPIO 针脚 24 接地。然后,如果我们读取 pin,它将在静止时返回False。通过将 GPIO 引脚设置为输入并启用内部上拉电阻器,当倾斜开关打开时,它将报告True

如果倾斜开关打开(报告True,则我们将假设装置正在移动,并开始显示 LED 序列,每次显示一行 LED 图案时,增加当前位置。为了使图案更鲜艳一点(因为我们可以!),我们用另一种颜色重复每一行。一旦TILT.tilt_moving()功能报告我们已停止移动或正在朝相反方向移动,我们将重置当前位置,准备重新启动整个模式:

The message is displayed by the RGB LEDs - here, we are using green and blue together

当 RGB LED 模块和倾斜开关前后移动时,我们会看到空中显示的信息!

尝试不同的颜色组合、速度和手臂波浪度,看看你能产生什么效果。您甚至可以创建一个安装在轮子上的类似设置,以产生连续的 POV 效果。

教程来源于Github,感谢apachecn大佬的无私奉献,致敬!

技术教程推荐

机器学习40讲 -〔王天一〕

软件工程之美 -〔宝玉〕

后端技术面试 38 讲 -〔李智慧〕

Kafka核心源码解读 -〔胡夕〕

恋爱必修课 -〔李一帆〕

数据分析思维课 -〔郭炜〕

Web漏洞挖掘实战 -〔王昊天〕

朱涛 · Kotlin编程第一课 -〔朱涛〕

Serverless进阶实战课 -〔静远〕