Python 物联网入门指南(三)(3)

本文涉及的产品
数据可视化DataV,5个大屏 1个月
可视分析地图(DataV-Atlas),3 个项目,100M 存储空间
简介: Python 物联网入门指南(三)

Python 物联网入门指南(三)(2)https://developer.aliyun.com/article/1507200

准备工作

这个配方使用了前一个配方中使用的 RGB LED 套件;您还需要以下额外的物品:

  • 面包板(半尺寸或更大)
  • 2 x DuPont 母对公跳线
  • 倾斜开关(适合滚珠类型)
  • 1 x 470 欧姆电阻(R_Protect)
  • 面包板线(实心线)

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

!倾斜开关连接到 GPIO 输入(GPIO 引脚 24)和 Gnd(GPIO 引脚 6)

为了重现 POV 图像,您需要能够快速移动 LED 并来回倾斜开关。请注意倾斜开关安装在侧面倾斜,因此当向左移动时开关将打开。建议将硬件安装在一根木头或类似设备上。您甚至可以使用便携式 USB 电池组和 Wi-Fi dongle 来通过远程连接为树莓派供电和控制(有关详细信息,请参见第一章中的*通过网络远程连接树莓派使用 SSH(和 X11 转发)*配方):

!持续视觉硬件设置

您还需要已完成的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模块和 Python os模块。最初,我们将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()函数来执行所需的每个步骤:
  2. 设置硬件组件(RGB LED 和倾斜开关)。
  3. 阅读letters.txt文件。
  4. 定义 LED 字母模式的字典。
  5. 生成一个缓冲区来表示所需的消息。
  6. 使用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()”返回一个包含文件中定义的字母对应的 LED 图案的 Python 字典。处理文件时,会处理文件中的每一行,忽略包含“#”字符的任何行,并检查“”字符以指示接下来的 LED 图案的名称。处理文件后,我们得到一个包含 LED 图案的 Python 字典,其中包含'_''C''B''K''O'字符。

'_': [[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 图案。为了控制我们何时开始序列,我们将使用 TILT 模块并检查倾斜开关的状态:

当倾斜开关不移动时的位置(左)和移动时的位置(右)

倾斜开关由一个小滚珠封闭在一个空心绝缘圆柱体中组成;当球静止在圆柱体底部时,两个引脚之间的连接闭合。当球移动到圆柱体的另一端,远离引脚的接触时,倾斜开关打开:

倾斜开关电路,开关闭合和开关打开时

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

如果倾斜开关是打开的(报告True),那么我们将假设单位正在移动,并开始显示 LED 序列,每次显示 LED 图案的一行时递增当前位置。为了使图案更加丰富多彩(只是因为我们可以!),我们会用另一种颜色重复每一行。一旦“TILT.tilt_moving()”函数报告我们已经停止移动或者我们正在向相反方向移动,我们将重置当前位置,准备重新开始整个图案:

消息由 RGB LED 显示 - 在这里,我们一起使用绿色和蓝色

当 RGB LED 模块和倾斜开关来回移动时,我们应该看到消息在空中显示!

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

第八章:感知和显示真实世界的数据

在本章中,我们将涵盖以下主题:

  • 使用 I2C 总线的设备
  • 使用模拟数字转换器读取模拟数据
  • 记录和绘制数据
  • 通过 I/O 扩展器扩展树莓派 GPIO
  • 在 SQLite 数据库中捕获数据
  • 查看来自您自己的 Web 服务器的数据
  • 感知和发送数据到在线服务

介绍

在本章中,我们将学习如何收集来自现实世界的模拟数据并对其进行处理,以便在程序中显示、记录、绘制和共享数据,并利用这些数据。

我们将通过使用树莓派的 GPIO 连接来扩展树莓派的功能,与模拟数字转换器(ADC)、LCD 字母显示器和数字端口扩展器进行接口。

使用 I2C 总线的设备

树莓派可以支持多种高级协议,可以轻松连接各种设备。在本章中,我们将专注于最常见的总线,称为 I-squared-C(I²C)。它提供了一个用于通过两根导线与设备通信的中速总线。在本节中,我们将使用 I²C 与 8 位 ADC 进行接口。该设备将测量模拟信号,将其转换为 0 到 255 之间的相对值,并将该值作为数字信号(由 8 位表示)通过 I²C 总线发送到树莓派。

I²C 的优势可以总结如下:

  • 即使在总线上有许多设备的情况下,也能保持低引脚/信号计数
  • 适应不同从设备的需求
  • 容易支持多个主设备
  • 包括 ACK/NACK 功能以改进错误处理

准备工作

并非所有树莓派镜像都启用了 I²C 总线;因此,我们需要启用模块并安装一些支持工具。Raspbian 的新版本使用设备树来处理硬件外围设备和驱动程序。

为了使用 I²C 总线,我们需要在bootconfig.txt文件中启用 ARM I²C。

您可以使用以下命令自动执行此操作:

sudo raspi-config

从菜单中选择高级选项,然后选择 I²C,如下截图所示。当询问时,选择是以启用接口,然后点击是以默认加载模块:

raspi-config 菜单

从菜单中选择 I2C,选择是以启用接口并默认加载模块。

raspi-config程序通过修改/boot/config.txt以包括dtparam=i2c_arm=on来启用I2C_ARM接口。另一种总线(I2C_VC)通常保留用于与树莓派 HAT 附加板进行接口(从板载存储器设备读取配置信息);但是,您也可以使用dtparam=i2c_vc=on来启用此功能。

如果您愿意,您还可以使用raspi-config列表启用 SPI,这是另一种类型的总线。

接下来,我们应该包括 I²C 模块在打开树莓派时加载,如下所示:

sudo nano /etc/modules  

添加以下内容并保存(Ctrl + X, Y, Enter):

i2c-dev
i2c-bcm2708  

类似地,我们还可以通过添加spi-bcm2708来启用 SPI 模块。

接下来,我们将安装一些工具,以便直接从命令行使用 I²C 设备,如下所示:

sudo apt-get update
sudo apt-get install i2c-tools 

最后,在连接硬件之前关闭树莓派,以便应用更改,如下所示:

sudo halt  

您将需要一个 PCF8591 模块(这些的零售商在附录硬件和软件清单中列出)或者您可以单独获取 PCF8591 芯片并构建自己的电路(有关电路的详细信息,请参阅*还有更多…*部分):

来自 dx.com 的 PCF8591 ADC 和传感器模块

将 GND、VCC、SDA 和 SCL 引脚连接到树莓派的 GPIO 引脚头,如下所示:

树莓派 GPIO 引脚上的 I2C 连接您可以通过研究设备的数据表找出要发送/读取的消息以及用于控制设备的寄存器,使用相同的 I²C 工具/代码与其他 I²C 设备。

操作步骤…

  1. i2cdetect命令用于检测 I²C 设备(--y选项跳过有关可能干扰连接到 I²C 总线的其他硬件的警告)。以下命令用于扫描两个总线:
sudo i2cdetect -y 0
sudo i2cdetect -y 1 
  1. 根据您的树莓派板子版本,设备的地址应该在总线 0 上列出(适用于 Model B Rev1 板)或总线 1 上(适用于树莓派 2 和 3,以及树莓派 1 Model A 和 Model B Revision 2)。默认情况下,PCF8591 地址是0x48
要使用的 I²C 总线号 总线 00 总线 11
树莓派 2 和 3 HAT ID(I2C_VC) GPIO(I2C_ARM)
Model A 和 Model B Revision 2 P5 GPIO
Model B Revision 1 GPIO N/A
  1. 以下屏幕截图显示了i2cdetect的输出:

PCF8591 地址(48)在总线 1 上显示

如果没有列出任何内容,请关闭并仔细检查您的连接(来自www.dx.com的 ADC 模块在上电时会打开红色 LED)。

如果收到错误消息,指出/dev/i2c1总线不存在,您可以执行以下检查:

  • 确保/etc/modprobe.d/raspi-blacklist.conf文件为空(即模块未被列入黑名单),使用以下命令查看文件:

sudo nano /etc/modprobe.d/raspi-blacklist.conf

  • 如果文件中有任何内容(例如blacklist i2c-bcm2708),请删除并保存
  • 检查/boot/config,确保没有包含device_tree_param=的行(这将禁用对新设备树配置的支持,并禁用对某些树莓派 HAT 附加板的支持)
  • 使用lsmod检查模块是否已加载,并查找i2c-bcm2708i2c_dev
  1. 使用检测到的总线号(01)和设备地址(0x48),使用i2cget从设备读取(上电或通道更改后,您需要两次读取设备才能看到最新值),如下所示:
sudo i2cget -y 1 0x48
sudo i2cget -y 1 0x48 
  1. 要从通道1读取(这是模块上的温度传感器),我们可以使用i2cset0x01写入 PCF8591 控制寄存器。同样,使用两次读取来从通道1获取新样本,如下所示:
sudo i2cset -y 1 0x48 0x01
sudo i2cget -y 1 0x48
sudo i2cget -y 1 0x48
  1. 要循环遍历每个输入通道,请使用i2cset将控制寄存器设置为0x04,如下所示:
sudo i2cset -y 1 0x48 0x04
  1. 我们还可以使用以下命令控制 AOUT 引脚,将其完全打开(点亮 LED D1):
sudo i2cset -y 1 0x48 0x40 0xff 
  1. 最后,我们可以使用以下命令将其完全关闭(关闭 LED D1):
sudo i2cset -y 1 0x48 0x40 0x00  

工作原理…

设备上电后的第一次读取将返回0x80,并且还将触发通道 0 的新样本。如果再次读取,它将返回先前读取的样本并生成新样本。每次读取都将是一个 8 位值(范围从0255),表示电压到 VCC(在本例中为 0V 到 3.3V)。在www.dx.com模块上,通道 0 连接到光传感器,因此如果用手遮住模块并重新发送命令,您将观察到值的变化(较暗表示较高的值,较亮表示较低的值)。您会发现读数总是滞后一步;这是因为当它返回先前的样本时,它捕获了下一个样本。

我们使用以下命令指定要读取的特定通道:

sudo i2cset -y 1 0x48 0x01  

这将更改要读取的通道为通道 1(在模块上标有AIN1)。请记住,您需要执行两次读取,然后才能从新选择的通道看到数据。以下表格显示了通道和引脚名称,以及哪些跳线连接器启用/禁用了每个传感器:

通道 0 1 2 3
引脚名称 AIN0 AIN1 AIN2 AIN3
传感器 光敏电阻 热敏电阻 外部引脚 电位器
跳线 P5 P4 P6

接下来,我们通过设置控制寄存器的模拟输出使能标志(第 6 位)来控制 AOUT 引脚,并使用下一个值来设置模拟电压(0V-3.3V,0x00-0xFF),如下所示:

sudo i2cset -y 1 0x48 0x40 0xff 

最后,可以将第 2 位(0x04)设置为自动递增,并循环通过输入通道,如下所示:

sudo i2cset -y 1 0x48 0x04

每次运行i2cget -y 1 0x48,下一个通道将被选择,从 AIN0 开始,然后从 AIN1 到 AIN3 再返回到 AIN0。

要理解如何设置值中的特定位,有助于查看数字的二进制表示。8 位值0x04可以用二进制b0000 0100来表示(0x表示值以十六进制表示,b 表示二进制数)。

二进制数中的位从右到左进行计数,从 0 开始 - 即,MSB 7 6 5 4 3 2 1 0 LSB。

第 7 位被称为最高有效位MSB),第 0 位被称为最低有效位LSB)。因此,通过设置第 2 位,我们最终得到b0000 0100(即0x04)。

还有更多…

I²C 总线允许我们只使用少量线路轻松连接多个设备。PCF8591 芯片可用于将自己的传感器连接到模块或仅连接芯片。

使用多个 I2C 设备

I²C 总线上的所有命令都是针对特定的 I²C 设备的(许多设备可以选择将一些引脚设为高电平或低电平以选择附加地址,并允许多个设备存在于同一总线上)。每个设备必须具有唯一地址,以便一次只有一个设备会做出响应。PCF8591 的起始地址是0x48,通过三个地址引脚可选择附加地址为0x4F。这允许在同一总线上使用多达八个 PCF8591 设备。

如果决定使用位于 GPIO 引脚 27 和 28(或位于 Model A 和 Revision 2 Model B 设备的 P5 标头)的 I2C_VC 总线,则可能需要在 I²C 线和 3.3V 之间添加 1k8 欧姆的上拉电阻。这些电阻已经存在于 GPIO 连接器上的 I²C 总线上。但是,一些 I²C 模块,包括 PCF8591 模块,已经安装了自己的电阻,因此可以在没有额外电阻的情况下工作。

I2C 总线和电平转换

I²C 总线由两根线组成,一根数据线(SDA)和一根时钟线(SCL)。两根线都通过上拉电阻被被动地拉到 VCC(在树莓派上,这是 3.3V)。树莓派将通过每个周期将时钟线拉低来控制时钟,数据线可以被树莓派拉低以发送命令,或者被连接的设备拉低以回应数据:

树莓派 I²C 引脚包括 SDA 和 SCL 上的上拉电阻

由于从机设备只能将数据线拉到GND,因此设备可以由 3.3V 甚至 5V 供电,而不会有驱动 GPIO 引脚电压过高的风险(请记住,树莓派 GPIO 无法处理超过 3.3V 的电压)。只要设备的 I²C 总线能够识别逻辑最大值为 3.3V 而不是 5V,这应该可以工作。I²C 设备不能安装自己的上拉电阻,因为这会导致 GPIO 引脚被拉到 I²C 设备的供电电压。

请注意,本章中使用的 PCF8591 模块已安装了电阻;因此,我们只能使用VCC = 3V3。双向逻辑电平转换器可用于克服逻辑电平的任何问题。其中一种设备是Adafruit I²C 双向逻辑电平转换模块,如下图所示:

Adafruit I²C 双向逻辑电平转换模块

除了确保任何逻辑电压适合您使用的设备之外,它还将允许总线在更长的导线上延伸(电平转换器还将充当总线中继)。

仅使用 PCF8591 芯片或添加替代传感器

下图显示了 PCF8591 模块不带传感器的电路图:

PCF8591 模块的电路图,不带传感器附件

如您所见,除了传感器外,只有五个额外的元件。我们有一个电源滤波电容(C1)和一个带有限流电阻(R5)的电源指示 LED(D2),所有这些都是可选的。

请注意,该模块包括两个 10K 上拉电阻(R8 和 R9)用于 SCL 和 SDA 信号。但是,由于树莓派上的 GPIO I²C 连接也包括上拉电阻,因此模块上不需要这些电阻(并且可以被移除)。这也意味着我们应该只将该模块连接到 VCC = 3.3V(如果我们使用 5V,则 SCL 和 SDA 上的电压将约为 3.56V,这对于树莓派的 GPIO 引脚来说太高)。

PCF891 模块上的传感器都是电阻性的,因此模拟输入上的电压电平将随着传感器电阻的变化在 GND 和 VCC 之间变化:

电位分压电路。这提供了与传感器电阻成比例的电压。

该模块使用一种称为电位分压器的电路。顶部的电阻平衡了底部传感器提供的电阻,以提供介于VCCGND之间的电压。

电位器的输出电压(V[out])可以计算如下:


R[t]和 R[b]分别是顶部和底部的电阻值,VCC 是供电电压。

模块中的电位器具有 10K 欧姆的电阻,根据调节器的位置在顶部和底部之间分割。因此,在中间,我们在每一侧都有 5K 欧姆和输出电压为 1.65V;四分之一的位置(顺时针),我们有 2.5K 欧姆和 7.5K 欧姆,产生 0.825V。

我没有显示 AOUT 电路,它是一个电阻和 LED。但是,正如您将发现的,LED 不适合指示模拟输出(除了显示开/关状态)。

对于更敏感的电路,您可以使用更复杂的电路,例如惠斯通电桥(它允许检测电阻的微小变化),或者您可以使用专用传感器,根据其读数输出模拟电压(例如TMP36温度传感器)。PCF891 还支持差分输入模式,其中一个通道的输入可以与另一个通道的输入进行比较(结果读数将是两者之间的差异)。

有关 PCF8591 芯片的更多信息,请参阅www.nxp.com/documents/data_sheet/PCF8591.pdf上的数据表。

使用模拟数字转换器读取模拟数据

在命令行中使用的 I²C 工具(在上一节中使用)对于调试 I²C 设备非常有用,但对于 Python 来说并不实用,因为它们会很慢并且需要大量的开销。幸运的是,有几个 Python 库提供了 I²C 支持,允许有效地使用 I²C 与连接的设备进行通信并提供简单的操作。

我们将使用这样的库来创建我们自己的 Python 模块,它将允许我们快速轻松地从 ADC 设备获取数据并在我们的程序中使用它。该模块设计得非常灵活,可以在不影响其余示例的情况下放置其他硬件或数据源。

准备工作

要使用 Python 3 使用 I²C 总线,我们将使用Gordon Henderson 的 WiringPi2(有关更多详细信息,请参见wiringpi.com/)。

安装wiringpi2的最简单方法是使用 Python 3 的pippip是 Python 的软件包管理器,其工作方式类似于apt-get。您希望安装的任何软件包都将从在线存储库自动下载并安装。

要安装pip,请使用以下命令:

sudo apt-get install python3-dev python3-pip  

然后,使用以下命令安装wiringpi2

sudo pip-3.2 install wiringpi2

安装完成后,您应该看到以下内容,表示成功:

成功安装 WiringPi2

您需要将 PCF8591 模块连接到树莓派的 I²C 连接上,就像之前使用的那样:

PCF8591 模块和引脚连接到树莓派 GPIO 连接器

如何做…

在下一节中,我们将编写一个脚本,以便我们可以收集数据,然后稍后在本章中使用。

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

  1. 首先,导入我们将使用的模块并创建变量,如下所示:
#!/usr/bin/env python3 
#data_adc.py 
import wiringpi2 
import time 
DEBUG=False 
LIGHT=0;TEMP=1;EXT=2;POT=3 
ADC_CH=[LIGHT,TEMP,EXT,POT] 
ADC_ADR=0x48 
ADC_CYCLE=0x04 
BUS_GAP=0.25 
DATANAME=["0:Light","1:Temperature", 
          "2:External","3:Potentiometer"] 
  1. 创建device类并使用构造函数进行初始化,如下所示:
class device: 
  # Constructor: 
  def __init__(self,addr=ADC_ADR): 
    self.NAME = DATANAME 
    self.i2c = wiringpi2.I2C() 
    self.devADC=self.i2c.setup(addr) 
    pwrup = self.i2c.read(self.devADC) #flush powerup value 
    if DEBUG==True and pwrup!=-1: 
      print("ADC Ready") 
    self.i2c.read(self.devADC) #flush first value 
    time.sleep(BUS_GAP) 
    self.i2c.write(self.devADC,ADC_CYCLE) 
    time.sleep(BUS_GAP) 
    self.i2c.read(self.devADC) #flush first value 
  1. 在类中,定义一个函数以提供通道名称列表,如下所示:
def getName(self): 
  return self.NAME
  1. 定义另一个函数(仍然作为类的一部分)以返回 ADC 通道的新样本集,如下所示:
def getNew(self): 
  data=[] 
  for ch in ADC_CH: 
    time.sleep(BUS_GAP) 
    data.append(self.i2c.read(self.devADC)) 
  return data 
  1. 最后,在设备类之后,创建一个测试函数来测试我们的新device类,如下所示。这只能在直接执行脚本时运行:
def main(): 
  ADC = device(ADC_ADR) 
  print (str(ADC.getName())) 
  for i in range(10): 
    dataValues = ADC.getNew() 
    print (str(dataValues)) 
    time.sleep(1) 
if __name__=='__main__': 
  main() 
#End 

您可以使用以下命令运行此模块的测试函数:

sudo python3 data_adc.py  

工作原理…

我们首先导入wiringpi2,以便稍后可以与我们的 I²C 设备通信。我们将创建一个类来包含控制 ADC 所需的功能。创建类时,我们可以初始化wiringpi2,使其准备好使用 I²C 总线(使用wiringpi2.I2C()),并使用芯片的总线地址设置一个通用 I²C 设备(使用self.i2c.setup(0x48))。

wiringpi2还有一个专用类,可与 PCF8591 芯片一起使用;但是,在这种情况下,更有用的是使用标准 I²C 功能来说明如何使用wiringpi2控制任何 I²C 设备。通过参考设备数据表,您可以使用类似的命令与任何连接的 I²C 设备进行通信(无论是否直接支持)。

与以前一样,我们执行设备读取并配置 ADC 以循环通过通道,但是我们使用wiringpi2I2C对象的readwrite函数,而不是i2cgeti2cset。初始化后,设备将准备好读取每个通道上的模拟信号。

该类还将有两个成员函数。第一个函数getName()返回一个通道名称列表(我们可以用它来将数据与其来源进行关联),第二个函数getNew()返回所有通道的新数据集。数据是使用i2c.read()函数从 ADC 读取的,由于我们已经将其放入循环模式,每次读取都将来自下一个通道。

由于我们计划稍后重用此类,因此我们将使用if __name__测试来允许我们定义在直接执行文件时要运行的代码。在我们的main()函数中,我们创建 ADC,这是我们新设备类的一个实例。如果需要,我们可以选择选择非默认地址;否则,将使用芯片的默认地址。我们使用getName()函数打印出通道的名称,然后我们可以从ADC(使用getNew())收集数据并显示它们。

还有更多…

以下允许我们在data_adc.py中定义设备类的另一个版本,以便可以在 ADC 模块的位置使用它。这将允许在本章的其余部分中尝试而无需任何特定的硬件。

无硬件收集模拟数据

如果您没有可用的 ADC 模块,则可以从树莓派内部获得大量可用数据,可以代替使用。

创建data_local.py脚本如下:

#!/usr/bin/env python3 
#data_local.py 
import subprocess 
from random import randint 
import time 
MEM_TOTAL=0 
MEM_USED=1 
MEM_FREE=2 
MEM_OFFSET=7 
DRIVE_USED=0 
DRIVE_FREE=1 
DRIVE_OFFSET=9 
DEBUG=False 
DATANAME=["CPU_Load","System_Temp","CPU_Frequency", 
          "Random","RAM_Total","RAM_Used","RAM_Free", 
          "Drive_Used","Drive_Free"] 
def read_loadavg(): 
  # function to read 1 minute load average from system uptime 
  value = subprocess.check_output( 
            ["awk '{print $1}' /proc/loadavg"], shell=True) 
  return float(value) 
def read_systemp(): 
  # function to read current system temperature 
  value = subprocess.check_output( 
            ["cat /sys/class/thermal/thermal_zone0/temp"], 
            shell=True) 
  return int(value) 
def read_cpu(): 
  # function to read current clock frequency 
  value = subprocess.check_output( 
            ["cat /sys/devices/system/cpu/cpu0/cpufreq/"+ 
             "scaling_cur_freq"], shell=True) 
  return int(value) 
def read_rnd(): 
  return randint(0,255) 
def read_mem(): 
  # function to read RAM info 
  value = subprocess.check_output(["free"], shell=True) 
  memory=[] 
  for val in value.split()[MEM_TOTAL+ 
                           MEM_OFFSET:MEM_FREE+ 
                           MEM_OFFSET+1]: 
    memory.append(int(val)) 
  return(memory) 
def read_drive(): 
  # function to read drive info 
  value = subprocess.check_output(["df"], shell=True) 
  memory=[] 
  for val in value.split()[DRIVE_USED+ 
                           DRIVE_OFFSET:DRIVE_FREE+ 
                           DRIVE_OFFSET+1]: 
    memory.append(int(val)) 
  return(memory) 
class device: 
  # Constructor: 
  def __init__(self,addr=0): 
    self.NAME=DATANAME 
  def getName(self): 
    return self.NAME 
  def getNew(self): 
    data=[] 
    data.append(read_loadavg()) 
    data.append(read_systemp()) 
    data.append(read_cpu()) 
    data.append(read_rnd()) 
    memory_ram = read_mem() 
    data.append(memory_ram[MEM_TOTAL]) 
    data.append(memory_ram[MEM_USED]) 
    data.append(memory_ram[MEM_FREE]) 
    memory_drive = read_drive() 
    data.append(memory_drive[DRIVE_USED]) 
    data.append(memory_drive[DRIVE_FREE]) 
    return data 
def main(): 
  LOCAL = device() 
  print (str(LOCAL.getName())) 
  for i in range(10): 
    dataValues = LOCAL.getNew() 
    print (str(dataValues)) 
    time.sleep(1) 
if __name__=='__main__': 
  main() 
#End 

前面的脚本允许我们使用以下命令从树莓派中收集系统信息(subprocess模块允许我们捕获结果并处理它们):

  • CPU 速度:
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq  
  • CPU 负载:
awk '{print $1}' /proc/loadavg
  • 核心温度(乘以 1,000):
cat /sys/class/thermal/thermal_zone0/temp  
  • 驱动器信息:
df  
  • RAM 信息:
free  

每个数据项都是使用其中一个函数进行采样的。在驱动和 RAM 信息的情况下,我们将响应拆分为一个列表(由空格分隔),并选择我们想要监视的项目(如可用内存和已用驱动器空间)。

这一切都打包成与data_adc.py文件和device类相同的方式运行(因此您可以选择在以下示例中使用data_adc包括或data_local包括,只需将data_adc包括替换为data_local)。

Python 物联网入门指南(三)(4)https://developer.aliyun.com/article/1507202

相关实践学习
钉钉群中如何接收IoT温控器数据告警通知
本实验主要介绍如何将温控器设备以MQTT协议接入IoT物联网平台,通过云产品流转到函数计算FC,调用钉钉群机器人API,实时推送温湿度消息到钉钉群。
阿里云AIoT物联网开发实战
本课程将由物联网专家带你熟悉阿里云AIoT物联网领域全套云产品,7天轻松搭建基于Arduino的端到端物联网场景应用。 开始学习前,请先开通下方两个云产品,让学习更流畅: IoT物联网平台:https://iot.console.aliyun.com/ LinkWAN物联网络管理平台:https://linkwan.console.aliyun.com/service-open
相关文章
|
7天前
|
数据采集 存储 JSON
Python爬虫开发:BeautifulSoup、Scrapy入门
在现代网络开发中,网络爬虫是一个非常重要的工具。它可以自动化地从网页中提取数据,并且可以用于各种用途,如数据收集、信息聚合和内容监控等。在Python中,有多个库可以用于爬虫开发,其中BeautifulSoup和Scrapy是两个非常流行的选择。本篇文章将详细介绍这两个库,并提供一个综合详细的例子,展示如何使用它们来进行网页数据爬取。
|
15天前
|
存储 JSON 安全
从入门到精通:Python中的OAuth与JWT,打造无懈可击的认证体系🔒
【8月更文挑战第4天】构建现代Web和移动应用时,用户认证与授权至关重要。Python集成OAuth和JWT技术,能轻松实现安全认证。本文从OAuth基础入手,介绍如何使用`requests-oauthlib`库简化流程,再到JWT进阶应用,利用`PyJWT`库生成及验证令牌。最后,探讨如何结合两者,创建无缝认证体验。通过代码示例,由浅入深地引导读者掌握构建坚固应用认证体系的方法。
35 2
|
23天前
|
数据采集 API 网络安全
Python Requests代理使用入门指南
《Python Requests 代理使用入门指南》将带你深入了解如何使用Python Requests库来配置HTTP代理,并灵活处理各种权限和服务器响应问题。从代理服务器的基础知识,到代理认证与授权设置,本指南为初学者提供了全面的教学内容。
Python Requests代理使用入门指南
|
4天前
|
数据采集 数据可视化 算法
GitHub星标68K!Python数据分析入门手册带你从数据获取到可视化
Python作为一门优秀的编程语言,近年来受到很多编程爱好者的青睐。一是因为Python本身具有简捷优美、易学易用的特点;二是由于互联网的飞速发展,我们正迎来大数据的时代,而Python 无论是在数据的采集与处理方面,还是在数据分析与可视化方面都有独特的优势。我们可以利用 Python 便捷地开展与数据相关的项目,以很低的学习成本快速完成项目的研究。
|
5天前
|
数据采集 数据可视化 算法
GitHub星标68K!Python数据分析入门手册带你从数据获取到可视化
Python作为一门优秀的编程语言,近年来受到很多编程爱好者的青睐。一是因为Python本身具有简捷优美、易学易用的特点;二是由于互联网的飞速发展,我们正迎来大数据的时代,而Python 无论是在数据的采集与处理方面,还是在数据分析与可视化方面都有独特的优势。我们可以利用 Python 便捷地开展与数据相关的项目,以很低的学习成本快速完成项目的研究。 今天给小伙伴们分享的这份Python数据分析入门手册本着实用性的目的,着眼于整个数据分析的流程,介绍了从数据采集到可视化的大致流程。
|
6天前
|
机器学习/深度学习 传感器 物联网
使用Python实现深度学习模型:智能物联网与智能家居
【8月更文挑战第13天】 使用Python实现深度学习模型:智能物联网与智能家居
16 2
|
13天前
|
机器学习/深度学习 开发者 Python
Python 与 R 在机器学习入门中的学习曲线差异
【8月更文第6天】在机器学习领域,Python 和 R 是两种非常流行的编程语言。Python 以其简洁的语法和广泛的社区支持著称,而 R 则以其强大的统计功能和数据分析能力受到青睐。本文将探讨这两种语言在机器学习入门阶段的学习曲线差异,并通过构建一个简单的线性回归模型来比较它们的体验。
37 7
|
15天前
|
Python
探索Python中的装饰器:从入门到实践
【8月更文挑战第4天】在Python的世界中,装饰器是一把双刃剑,它既能美化代码,又能提升效率。本文将带你一探究竟,通过实例学习如何定义、使用以及深入理解装饰器背后的原理。我们将一起揭开这层神秘的面纱,让装饰器成为你编程工具箱中的又一利器。
31 9
|
15天前
|
测试技术 开发者 Python
揭秘Python中的装饰器:从入门到精通
【8月更文挑战第4天】装饰器,在Python中是一块神奇的“画布”,它允许开发者在不修改原有函数代码的情况下增加额外的功能。本文将通过实际的代码示例,带你一探究竟,从基础使用到高级技巧,逐步揭开装饰器的神秘面纱。
|
13天前
|
机器学习/深度学习 数据采集 数据可视化
使用Python进行数据科学入门指南
【8月更文挑战第6天】使用Python进行数据科学是一条充满挑战和机遇的道路。本文提供了入门指南,帮助您快速上手Python数据科学。通过实践项目和资源推荐,您将能够深入学习Python数据科学,成为一名合格的数据科学家。