Python 物联网编程实践(三)

原文:zh.annas-archive.org/md5/7FABA31DD38F615362E1254C67CC152E

译者:飞龙

协议:CC BY-NC-SA 4.0

第三部分:物联网游乐场-与物理世界互动的实际示例

这是我们涵盖物联网中的部分的部分。我们将探索和实验各种常见传感器,执行器和电子电路,我们将使用 Python 与物理世界进行交互。在这个过程中,我们将看到许多核心电子原理在第二部分中学到的实际应用。在本节的后半部分,我们还将结合我们从第一部分学到的知识(即物联网的互联网部分),使用各种不同的方法创建端到端的物联网应用程序。

本节包括以下章节:

  • 第七章,打开和关闭

  • 第八章,灯光,指示灯和显示信息

  • 第九章,测量温度,湿度和光照水平

  • 第十章,使用伺服,电机和步进电机进行运动

  • 第十一章,测量距离和检测运动

  • 第十二章,高级物联网编程概念-线程,AsyncIO 和事件循环

  • 第十三章,物联网可视化和自动化平台

  • 第十四章,将一切联系在一起-物联网圣诞树

第七章:打开和关闭东西

在上一章中,我们讨论了当您将数字和模拟电路与树莓派的 GPIO 引脚进行接口时将会使用的核心电子电路和概念。

在本章中,我们将介绍如何打开和关闭需要比树莓派安全使用的更高电压和电流东西。在电子领域,可以使用数百种不同的元件来进行控制和开关。它们可以以成千上万种不同的方式进行配置。我们将重点关注三种常见的补充—光耦、晶体管和继电器。

当与树莓派进行接口时,了解如何控制和开关电路是一个非常重要的话题。正如我们在第五章中讨论的那样,树莓派的 GPIO 引脚只能安全地提供少量毫安的输出电流和固定的 3.3 伏特。完成本章后,您对光耦、晶体管和继电器的了解将意味着您可以开始控制具有不同电流和电压要求的设备。

以下是本章的内容:

  • 探索继电器驱动电路

  • 确定负载的电压和电流

  • 使用光耦作为开关

  • 使用晶体管作为开关

  • 使用继电器作为开关

技术要求

要完成本章的练习,您将需要以下设备:

  • 树莓派 4 型 B

  • 树莓派操作系统 Buster(带桌面和推荐软件)

  • 至少 Python 版本 3.5

这些要求是本书中代码示例的基础。可以合理地期望,只要您的 Python 版本是 3.5 或更高,本书中的代码示例应该可以在树莓派 3 型 B 或不同版本的 Raspbian OS 上无需修改即可运行。

您将在 GitHub 存储库的chapter07文件夹中找到本章的源代码,链接在这里:github.com/PacktPublishing/Practical-Python-Programming-for-IoT

您需要在终端中执行以下命令来设置虚拟环境并安装本章代码所需的 Python 库:

$ cd chapter07              # Change into this chapter's folder
$ python3 -m venv venv      # Create Python Virtual Environment
$ source venv/bin/activate  # Activate Python Virtual Environment
(venv) $ pip install pip --upgrade        # Upgrade pip
(venv) $ pip install -r requirements.txt  # Install dependent packages

以下依赖项已从requirements.txt中安装:

本章练习所需的电子元件如下:

探索继电器驱动电路

电子开关的常见介绍是机械继电器 - 一种像普通开关一样工作的设备,只是通过给它供电来打开和关闭。不幸的是,直接连接继电器到树莓派是危险的!继电器通常需要太多的电流和电压,并且(如果它们确实切换)可能会损坏您的树莓派。因此,我们需要一个驱动电路,位于树莓派和继电器之间。这个电路的示例如图 7.1 所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.1 - 继电器驱动电路

这是我们将在本章逐步构建的电路。这个电路代表了您在 eBay、Banggood 和类似网站上找到的许多继电器控制模块。这些板确实很方便使用 - 当您让它们工作时。不幸的是,缺乏清晰的文档往往会使得让它们工作起来棘手和困难,特别是如果您是电子新手。

我们将要构建并探索图 7.1 中所示的三个子电路。这将帮助您了解光耦、晶体管和继电器作为开关的工作原理,以及它们为什么经常被串联在一起来控制继电器。这些知识还将帮助您在无法使其工作时逆向工程预制继电器控制模块。

在我们讨论光耦子电路之前,我们需要首先讨论负载电压和电流。

确定负载的电压和电流

负载是您想要控制的东西,或者在本章中,打开和关闭。LED、晶体管、光耦、继电器、灯、电动机、加热器、泵、自动车库门和电视都是负载的例子。如果您回到图 7.1,您会注意到图表右侧的“负载”一词。这是您连接想要打开或关闭的东西的地方。

晶体管、光耦继电器组件出现在上述负载列表中。回到图 7.1,继电器出现为晶体管子电路的负载,而晶体管子电路出现为光耦子电路的负载。

了解您想要控制的负载的两个属性是很重要的:

  • 负载需要多少电压?

  • 负载需要多少电流?

有时,这些属性可以在设备本身或其手册或数据表中找到。其他时候,它们需要被计算或者负载需要被手动测量。

知道这些属性很重要,因为它们影响了电路中选择哪些组件,包括适合的电源规格。在本章的整个电路构建过程中,我们将提到负载电流,因此稍后会有更多的上下文。现在,让我们看看如何测量直流电机的电流负载。

测量直流电机的电流需求

电机是人们想要控制的常见物品,它们是电流测量的一个很好的例子。让我们进行一个练习来测量我们的直流电机所使用的电流:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.2 - R130 直流电机

上面的照片显示了一个典型的 130(R130)直流电机,以及一组跳线引线焊接到电机的端子上,以便可以轻松地将其插入面包板。这个电机有一个红色的背面,但其他颜色也很常见 - 尤其是透明/白色。颜色对电机规格没有影响。

在进行以下步骤时,如果您不确定如何将万用表置于电流测量模式,请参考您的万用表手册。

以下是要遵循的步骤:

  1. 按照图 7.3 中所示的连接电路:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.3 - 使用万用表测量电流

我们假设这里的电机是章节开头的技术要求部分提到的电机。这个电机足够小,可以从面包板电源供电,通常可以提供 500mA 到 800mA。对于更大的电机(以及其他你不知道其额定值并想要测量的物品),你将需要一个更强大的电源。

如果你正在用 USB 手机充电器给面包板电源供电,请用万用表检查你的电源输出的 5 伏特电压,确保它提供的是大约 5 伏特。低功率充电器和质量差的 USB 电缆可能无法提供足够的电源使电源正常工作。理想情况下,阅读数据表并使用建议的电源适配器,通常是 7 到 12 伏特和 1 安培。

  1. 确保你的万用表设置为测量毫安mA),并且红色表笔连接到正确的引脚输入(通常标有 A 或 mA)。如果你的数字万用表有一个µA 输入,不要使用,否则可能会烧坏你的数字万用表的保护保险丝(保险丝可以更换)。

  2. 给电路供电,电机会旋转。

  3. 你的万用表将显示电机的电流消耗。记下这个值。这被称为连续自由电流,是电机在轴上自由旋转时使用的电流。

  4. 断开电机的电源。

  5. 用一把钳子,抓住电机的轴,使其无法旋转。

  6. 重新给电机供电,并迅速观察(并记下)数字万用表的读数。这个读数被称为堵转电流。电机在其轴被强行停止移动时会使用最大电流。

  7. 断开电机的电源。

我们现在已经测量了两种电流。我的 R130 电机的读数如下(你的可能会有所不同):

  • 连续或自由电流:~110mA 到200mA——随着电机使用而发热,它将使用更少;200mA 的测量是在电机冷却时进行的。一分钟后,它下降到了~110mA。

  • 堵转电流:这是~500mA 到~600mA。

这意味着我们的电机在正常运行时需要 200mA 到 600mA 的电流,任何我们希望用于电机的电路必须能够真实地处理 600mA,以免在电机停转时受到损坏(或者我们需要设计合适的保护,然而,这超出了我们的范围)。

有趣的是,还有一个启动电流,这是电机启动时发生的瞬时峰值电流,但我们无法用通用数字万用表来测量。

现在我们已经得到了 R130 电机的电流消耗,让我们收集继电器和 LED 的更多电流数据。

测量继电器和 LED 的电流需求

当我们到达标题为使用继电器作为开关的部分时,我们还将测量 LED 和继电器的电流消耗。你可以使用前一部分的步骤 1 到 4来测量 LED 和电阻对的电流消耗。执行这个测量的设置如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.4 - 测量通过电阻/LED 电路的电流

这是我们遵循的基本流程:

  1. 我们在图 7.3中所示的电机位置上连接了一个 LED 和一个 1kΩ电阻(或继电器)。

  2. 将你的万用表设置为毫安模式。

  3. 给电路供电。

  4. 用你的万用表测量电流。

一旦你完成了(并记下)你得到的测量值,从面包板上移除 LED 和电阻,接入继电器并进行相同的测量。

下图显示了 SRD-05VDC-SL-C 继电器以及你需要连接的继电器端子。请注意,你需要焊接排针(如图所示)或导线(一个很好的选择是将杜邦线剪成一半)到继电器的端子上,因为它不能直接插入面包板:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.5 - SRD-05VDC-SL-C 继电器

使用 5V 电源,您应该在万用表上获得类似以下数值:

  • 串联有 5mm 红色 LED 和 1000Ω电阻:3mA(来自欧姆计算的数值并向上取整I = (5V - 2.1V) / 1000Ω = 2.9mA)

  • 继电器:70mA 至 90mA(来自数据表并经过我的测量确认)

有关如何计算 LED 的电流的过程在第六章中已经讨论过,软件工程师的电子学 101。唯一的区别是,这里我们使用的是 5 伏电源和 1kΩ电阻,而不是在那一章中使用的 3.3 伏和 200Ω电阻。

请注意,我们将使用的光耦合器和 MOSFET 元件确实具有影响通过连接负载的电流的电压降方面。出于简洁起见,这些电压降的影响对我们的目的来说是不重要的,因此在本章的计算中不予考虑。

您现在已经学会了如何使用万用表测量直流电机、LED/电阻对和继电器的电流。了解您想要控制的设备以及您要连接的子电路的电流限制和期望,甚至是一个至关重要的信息,这样您在设计电路和选择合适的电源时可以选择合适额定的元件。

在本章中,我们将参考您在本节中进行的测量,探索光耦合器、MOSFET 和继电器。具体来说,我们将比较这些元件的电流额定值(在各自的数据表中找到)与我们的直流电机、LED/电阻和继电器的测量,并考虑可以用于直接控制哪个负载的元件。

我们将首先学习光耦合器及其用作开关的方法。

使用光耦合器作为开关

光耦合器(或光隔离器)是一种用于电气隔离两个电路的光控组件。这里显示了光耦合器的示意图和原理图符号:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.6 - 光耦合器符号和带有标记引脚的元件

光耦合器的两侧可以描述如下:

  • 输入端:我们将连接到树莓派 GPIO 引脚的一侧

  • 输出端:我们将连接到另一个电路的一侧

输入端的光耦合器内部有一个内部 LED(您将在图 7.6中的光耦合器符号中注意到 LED 符号),而在输出端有一个对 LED 光线做出响应的光电晶体管。这意味着控制(即开关)从输入端到外部端的传递是通过光线完成的,因此两侧之间没有物理电气连接。对我们来说,这意味着输出端的任何故障或意外不应对我们的树莓派造成损害。PC817 的隔离额定为 5000 伏,远远超出我们预期在物联网电子设备中使用的任何电压。

输入端 LED 关闭时,输出端光电晶体管关闭。然而,当 LED 被照亮(它在光耦合器元件内部,所以您看不到它)通过向引脚 1(阳极)和 2(阴极)施加电流时,光电晶体管被激活(打开),并允许电流在引脚 4(集电极)和 3(发射极)之间流动。

让我们创建一个简单的电路来演示 PC817 光耦合器,其规格如下:

  • 输入端(LED):具有以下数值:

  • 典型的**正向电压(V[F])**为 1.2 伏直流

  • 最大**正向电流(I[F])**为 50mA 直流

  • 输出端(光电晶体管):具有以下数值:

  • 最大集电极-发射极电压(V[CEO]):80 伏直流

  • 最大集电流(I[C]):50 毫安直流

  • **集电极-发射极饱和电压 V[CE(sat)]**在 0.1 至 0.2 伏范围内(基本上是电压降)

牢记这些规格,让我们开始搭建电路。

搭建光耦合器电路

我们将要构建以下图示的电路。这个电路使用 PC817 光耦器来电气隔离我们的树莓派和 LED 子电路:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.7 - 光耦器电路

这里的步骤编号与图 7.7中编号的黑色圆圈相匹配:

  1. 将 LED 放入你的面包板中,注意根据图示放置 LED 的阴极腿。

  2. 将一个 1kΩ电阻放入面包板中。这个电阻的一端与 LED 的阴极腿联通。

  3. 将 PC817 光耦 IC 放入面包板中。IC 上的白点表示 IC 的引脚 1。你的 IC 可能有也可能没有白点,但是 IC 上应该有一个明显的标记告诉你第一个引脚。请参考图 7.6以获取所有引脚编号信息。

  4. 将一个 1kΩ电阻放入你的面包板中。这个电阻的一端连接到 PC817 的引脚 1。

  5. 将 LED 的阳极腿连接到右侧电源轨的正电源。

  6. 将 PC817 的引脚 4 连接到你在步骤 2中放置的电阻的另一端。

  7. 将 PC817 的引脚 3 连接到右侧电源轨的负电源。

  8. 将 5 伏电源的正输出连接到右侧正电源轨。

  9. 将电源的负输出连接到右侧负电源轨。

  10. 将你在步骤 4中放置的电阻的另一端连接到树莓派上的 3.3 伏引脚。

  11. 最后,将 PC817 的引脚 2 连接到树莓派上的 GPIO 21。

图 7.7中,你可以直接将步骤 89(连接到外部电源)的导线连接到树莓派的+5 伏引脚和一个 GND 引脚。我们只是为红色 LED 使用了少量电流,但是对于更大的电流负载,你必须使用外部电源。树莓派上的+5 伏引脚直接连接到你用来给树莓派供电的电源。使用这个电源来为你的电路供电会有效地消耗树莓派可用的电流。如果消耗过多,你的树莓派将会重启!请注意(这很重要)这个行为的警告是****你将失去光耦器提供的电气隔离,因为你将会将输入输出端连接在一起(记住,输入输出端并不在光耦器内部电气连接,因为控制是通过光实现的)。

现在你已经完成了电路的搭建,我们将测试电路并探索使其工作的代码。

用 Python 控制光耦

首先运行chapter07/optocoupler_test.py文件中的代码,并观察 LED 的闪烁。以下是负责闪烁的代码部分:

# ... truncated ...
  pi.write(GPIO_PIN, pigpio.LOW) # On.     # (1)
  print("On")
  sleep(2)
  pi.write(GPIO_PIN, pigpio.HIGH) # Off.   # (2)
  print("Off")
  sleep(2)
# ... truncated ...

以下是发生的事情:

  • 在第(1)行,GPIO 21 是低电平,输入端的内部 LED 是开启的。输出端的光电晶体检测到这种光并被激活,允许电流在输出端的集电极(引脚 4)和发射极(引脚 3)之间流动,因此我们的红色 LED 亮起。

  • PC817 电路的输入端被布线为主动低电平,这就是为什么在第(1)行,GPIO 21 被设为低电平以打开电路,在第(2)行,GPIO 21 被设为高电平以关闭电路。另一种布线方式是主动高电平。如果你想要尝试并将电路改为主动高电平,你需要将图 7.7中的步骤 10的导线连接到 GND 引脚(而不是 3.3 伏引脚),并且在代码中颠倒pigpio.LOWpigpio.HIGH语句。

我们本可以使用更低阻值的电阻 R1 来驱动输入端 LED,但是 1kΩ的电阻提供了超过光耦合器电路内部 LED 所需的电流((3.3V - 1.2V)/1000Ω = 2.1mA)。您会看到 1kΩ、10kΩ和 100kΩ的电阻在许多电路中使用,因为这些都是比较常见的数值。我们还使用了 1kΩ的电阻 R2 来方便地驱动红色 LED。

您还记得上一章第六章中我们讨论过的内容吗?即我们不应该期望从树莓派 GPIO 引脚获得超过 8mA 的电流?是的,通过使用 PC817 光耦合器,我们现在可以通过在 GPIO 引脚和电路之间放置光耦合器来控制高达 50mA 的电流。此外,我们也不再受限于 GPIO 引脚的 3.3 伏电压,因为 PC817 可以处理高达 80 伏的电压。

记住,GPIO 引脚的主要作用是控制某物,而不是为其供电,因此始终要独立考虑控制电源需求。

在前一节中,我们计算(或测量)了我们的电机、继电器和 LED 的电流消耗。以下是在输出端使用 5 伏电源的 PC817 光耦合器的数据:

  • LED 和 1kΩ的电阻需要 3mA 的电流。

  • 继电器需要 70mA 至 90mA 之间的电流。

  • 电机需要约 500mA 至 600mA(堵转电流)。

LED 的 3mA 小于光耦合器输出端的最大额定 50mA,因此可以直接在输出端驱动 LED。然而,继电器和电机需要超出 PC817 限制的电流,因此在输出端使用它们可能会损坏光耦合器。

虽然我们可以使用光隔离器作为数字开关,但它们通常被用作隔离屏障来驱动其他组件,这些组件反过来可以驱动需要更高电流的负载。当我们从图 7.1构建完整的继电器驱动电路时,我们将在后面看到这一点,但现在,让我们学习如何将晶体管用作数字开关。

使用晶体管作为开关

晶体管是当今使用最广泛的电子元件,也是数字革命的支柱。它们可以以两种基本方式使用——作为放大器或数字开关。我们的重点将放在数字开关上,我们将使用一种名为金属氧化物半导体场效应晶体管MOSFET)的晶体管类型,具体来说是一种 N-沟道增强型 MOSFET——是的,这个名字有点拗口!

不要过分关注晶体管的冗长技术名称或存在的多种形式。这里的简单要点是,N-沟道增强型 MOSFET 作为数字开关效果很好,我们可以使用树莓派来控制它,或者后面将看到,也可以来自光耦合器等其他源。

场效应晶体管(FET)是电压控制的晶体管。另一种称为双极晶体管BJT)的晶体管是电流控制的晶体管。BJT 在树莓派上使用完全没问题,但需要额外考虑。在进一步阅读部分中,您会找到有关晶体管的更多学习资料的链接。

以下练习将使用 2N7000,一种 N-沟道增强型 MOSFET,如图 7.8所示。引脚名称为Source、Gate 和Drain。还有两种不同的封装样式,TO92 和 TO220。请注意,两种样式的 Source、Gate 和 Drain 引脚的排列方式是不同的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.8 - N-沟道增强型 MOSFET 符号和常见封装样式

2N7000 在其数据表中具有以下规格:

  • 最大**漏源电压(V[DSS])**为 60 伏直流

  • 最大**连续漏极电流(I[D])**为 200 毫安直流

  • 最大**脉冲漏极电流(I[DM])**为 500 毫安直流

  • **栅阈电压(V[GS(th)])**在 0.8 至 3 伏直流范围内

  • **漏极-源极开启电压(V[DS(on)])**在 0.45 至 2.5 伏特直流范围内(电压降)

以下是如何解释关于 2N7000 的这些参数:

  • 它可以安全地控制不超过 60 伏特(V[DSS])和持续 200 毫安(I[D])的负载,但脉冲 500 毫安(I[DM])是可以的。

  • 它理想情况下需要大于 3 伏特的电压才能打开(V[GS(th)])。

  • 它将在负载端电路上消耗 0.45 至 2.5 伏特(V[DS(on)])的电压。

2N7000(以及我们将很快讨论的 FQP30N06L)是逻辑电平可比较的 MOSFET。它们适用于树莓派,因为它们的最大门电压 V[GS(th)]小于 GPIO 引脚的 3.3 伏特。

让我们开始建立一个使用 2N7000 的电路与我们的树莓派。

构建 MOSFET 电路

我们将分两部分建立我们的电路,首先是在面包板上放置组件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.9 - MOSFET 晶体管电路(第一部分)

以下是我们建设的第一部分的步骤。步骤编号与图 7.9中编号的黑色圆圈相匹配:

  1. 将 MOSFET 放入面包板中,注意根据源极、栅极和漏极的正确方向放置组件。我们的示例布局假定了一个 2N7000 MOSFET。如果您需要帮助识别腿,请参阅图 7.8

  2. 将 100kΩ电阻放入面包板中。这个电阻的一端连接到 MOSFET 的栅极。

  3. 将 1kΩ电阻放入面包板中。这个电阻的一端也连接到 MOSFET 的栅极。

  4. 将 LED 放入面包板中,注意根据其阴极腿的显示方向放置组件。

  5. 将 1kΩ电阻放入面包板中。这个电阻的一端连接到 LED 的阴极腿。

  6. 将二极管放入面包板中,使组件朝向底部的阴极腿(二极管外壳上带有条纹的一端)。

现在我们已经将组件放入面包板中,让我们把它们全部连接起来:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.10 - MOSFET 晶体管电路(第二部分)

以下是第二部分建设的步骤。步骤编号与图 7.10中编号的黑色圆圈相匹配:

  1. 将树莓派的 GND 引脚连接到右侧电源轨的负极。

  2. 连接右侧和左侧电源轨的负极。

  3. 将 100kΩ电阻连接到负电源轨。

  4. 将 MOSFET 的源腿连接到负电源轨。

  5. 将 MOSFET 的漏极连接到 1kΩ电阻。

  6. 将 LED 的阳极腿连接到二极管的阴极腿。

  7. 将 LED 的阳极腿(和二极管的阴极腿)连接到右侧电源轨的正极。

  8. 将 1kΩ电阻连接到树莓派的 GPIO 21。

  9. 将电源供应的正输出端连接到右侧电源轨的正极。

  10. 将电源供应的负输出端连接到右侧电源轨的负极。

干得好。我们的电路建设完成了。在我们测试之前,让我们简要讨论一下这个电路。

请注意图 7.10(和图 7.1)中的 100kΩ电阻 R3。这是一个外部下拉电阻,确保当 GPIO 21 为高电平时,MOSFET 的 Gate 腿被拉到+3.3 伏特,而当它没有被拉到高电平时,它被连接到 GND(0 伏特)。MOSFET 具有电容电荷,因此如果没有下拉电阻,MOSFET 在从通(GPIO 21 为高电平)到断(GPIO 21 为低电平)的转换时可能会显得粘滞和缓慢,因为它在放电(请注意,此电路为主动高电平)。下拉电阻确保快速放电到断开状态。我们使用外部下拉电阻而不是在代码中激活的下拉电阻,以确保即使树莓派关闭电源或代码未运行时,MOSFET Gate 也被拉低。

您还会注意到 R1 和 R3 构成了一个电压分压器。1kΩ和 100kΩ的比例适合确保>3 伏特到达 MOSFET 的 Gate 腿以打开它。如果您需要关于下拉电阻和电压分压器的复习,我们在第六章中讨论过它们,软件工程师的电子学 101

在电路中添加电阻时,例如添加下拉电阻,始终要考虑更改的更广泛影响。例如,如果添加电阻由于现有电阻的存在而创建了电压分压器,那么您需要评估更改对周围电路的影响。对于我们的情况,这是为了确保足够的电压到达 MOSFET 的 Gate 腿以打开它。

在运行下一节中的代码后,尝试移除 R3 并再次运行代码。我不能保证您会在您的端看到任何东西,但您可能会观察到,当 GPIO 21 变为低电平时,红色 LED 不是立即熄灭而是缓慢消失,并且它的行为不是平稳地淡入淡出而是表现得不稳定。

与光耦示例一样,由于 LED 示例的电流要求较低,您可以将电线的外部电源连接到树莓派的+5 引脚和一个 GND 引脚。

在对 MOSFET 电路有了基本理解之后,让我们运行并探索一个与我们的电路交互的简单 Python 程序。

用 Python 控制 MOSFET

运行chapter07/transistor_test.py文件中的代码,红色 LED 将先点亮然后熄灭,然后再淡入淡出。一旦确认电路工作正常,让我们继续看代码:

# ...truncated ...
pi.set_PWM_range(GPIO_PIN, 100)             # (1)

try:
  pi.write(GPIO_PIN, pigpio.HIGH) # On.     # (2)
  print("On")
  sleep(2)
  pi.write(GPIO_PIN, pigpio.LOW) # Off.
  print("Off")
  sleep(2)

在这个例子中,我们使用 PWM。在第(1)行中,我们告诉 PiGPIO,对于 GPIO 21(GPIO_PIN = 21),我们希望其占空比受限于值范围 0 到 100(而不是默认的 0 到 255)。这是一个示例,说明了我们如何改变 PiGPIO 中占空比值的粒度。我们使用 0 到 100 只是为了使报告更容易,因为它映射到终端输出的 0%到 100%。

接下来,在第(2)行中,我们简单地打开和关闭 GPIO 一段时间,以测试晶体管电路,然后我们将看到 LED 在 2 秒延迟后点亮然后熄灭。

在以下代码的第(3)行中,我们使用 PWM 来淡入 LED,然后在前面代码块中的第(1)行设置的占空比范围内再次淡出它(第(4)行):

  # Fade In.
  for duty_cycle in range(0, 100):                  # (3)
      pi.set_PWM_dutycycle(GPIO_PIN, duty_cycle) 
      print("Duty Cycle {}%".format(duty_cycle))
      sleep(0.01)

  # Fade Out.
  for duty_cycle in range(100, 0, -1):              # (4)
      pi.set_PWM_dutycycle(GPIO_PIN, duty_cycle) 
      print("Dyty Cycle {}%".format(duty_cycle))
      sleep(0.01)
# ...truncated ...

让我们检查一下我们的继电器和电机是否可以安全地与这个晶体管电路一起使用,考虑到我们的 2N7000 额定电流为 200 毫安:

  • 继电器可以用来替代 LED,因为它只需要 70mA 到 90mA 之间的电流。

  • 电机需要~200mA 来自由旋转(连续电流),所以可能是安全的…或者不是?让我们看看。

在本章早些时候测试电机时,我们预计它在冷态时需要约 200mA(冷态时的连续电流)和约 500mA 至 600mA(堵转电流)之间的电流- 请记住这些是我的测量值,所以请用您的测量值替换。因此,原则上,我们的 2N7000 只要电机不承受负载就可以。实际上,一旦我们在电机轴上施加负载,它将需要超过 200mA 的连续电流。在这方面,2N7000 可能不是驱动这个电机的理想晶体管。我们需要寻找一个可以轻松处理 600mA 或更多连续电流的 MOSFET。我们很快就会看到 FQP30N06L MOSFET,它可以处理这个电流以及更多。

当 LED 在 PWM 相关代码中逐渐变暗时,如果您将电机连接到电路中以替代 LED/电阻对,您会注意到它会加速然后减速。您刚刚发现了如何使用 PWM 的占空比属性来控制电机的速度!我们将在第十章中更详细地介绍电机,使用舵机、电机和步进电机进行运动

要使用电机或继电器,必须使用外部电源,而不是树莓派上的+5 伏引脚。如果您尝试使用+5 伏引脚,可能会发现在运行代码时树莓派会重置。

我们不使用 PWM 控制继电器,因为它们切换速度太慢,而且如果它们工作(在非常低的 PWM 频率下),只会使它们耗损-但无论如何尝试一下,看看会发生什么;短暂的测试不会有害(尝试将代码中的频率从 8000 调整到 10,即pi.set_PWM_frequency(GPIO_PIN, 10))。

在我们的电路中,还有 1N4001 二极管 D1。这被称为反冲或抑制二极管。它的作用是保护电路免受在关闭电源时可能发生的继电器或电机等电磁元件的反向电压脉冲。诚然,我们的 LED 不是磁性的,但是有它在场也不会有任何害处。

每当您控制一个工作在电磁学上的组件(也称为感性负载)时,都要正确安装反冲抑制二极管。

图 7.8中,我们还有 FQP30N06L 的插图。这是一种能够驱动高电流负载的功率 N-通道增强模式 MOSFET。它在数据表中具有以下规格:

  • 最大**漏源电压(V[DSS])**为 60 伏特直流

  • 最大**连续漏极电流(I[D])**为 32A DC(安培而不是毫安!)

  • 最大脉冲漏极电流(I[DM])为 128A DC

  • **栅极阈值电压(V[GS(th)])**在 1 至 2.5 伏特直流范围内(<5 伏特,因此它是逻辑电平兼容的)

  • **漏-源导通电压(V[SD])**最大为 1.5 伏特直流

您可以在前面的电路中替换 FQP30N06L(或另一个 N-通道增强模式逻辑电平 MOSFET),它会起作用,但请记住以下几点:

  • FQP30N06L 的 G、D 和 S 引脚的顺序与 2N7000 不同,因此您需要调整接线。

  • 在处理更高电压和电流时,最好使用光耦隔离器将 MOSFET 与树莓派隔离开(我们将在讨论继电器时看到这种配置)。

  • 在高电流下,功率 MOSFET 可能会变得非常热-周围的元件和导线甚至是面包板都可能会熔化,因此在使用时要小心谨慎。

当控制高功率负载时,高功率 MOSFET 可能会变热,并且可以安装散热器,例如,FQP30N06L 的金属顶部上有一个散热器连接的孔。关于何时需要散热器的决定因素和计算超出了我们的范围,但是如果您的 MOSFET 变得太热(并且您在其数据表参数范围内使用它),那么请添加散热器。

如果您喜欢使用 MOSFET 控制更高电流负载的想法,您可能会喜欢在 eBay 等网站上研究现成的 MOSFET 模块。在学习了光耦合器和 MOSFET 之后,您现在可以理解这些模块是如何构建的 - 有些直接使用 MOSFET 直接连接到控制设备(即 GPIO 引脚),就像我们刚刚做的那样,而其他一些则在控制设备和 MOSFET 之间放置光耦合器。

您已经学会了如何使用 MOSFET 晶体管作为数字开关的基础知识。接下来,我们将把这些知识与光耦合器的学习结合起来,在面包板上构建我们的继电器驱动电路。

使用继电器作为开关

经典继电器是一种电机械元件,允许较小电流的设备开关更高电流的设备或负载的开关。原则上,它们就像我们之前使用的 MOSFET 或光耦合器一样。那么为什么要使用继电器?以下是一些原因:

  • 对于高电压和电流负载,它们往往比等效的 MOSFET 便宜得多。

  • 在高电流下,它们不会像 MOSFET 那样变得不可触摸的热。

  • 与光耦合器类似,继电器还提供输入和输出电路之间的电气隔离。

  • 它们只是电控开关,因此非电气工程师很容易理解和使用。

  • 它们经受住了时间的考验,被证明是控制高负载的简单而健壮的方式(尽管它们最终会磨损 - SRD-05VDC-SL-C 的数据表列出了其额定寿命为 100,000 次操作)。

还有一种被称为固态继电器SSR)的继电器类型,它没有移动部件,但通常比可比较的机械继电器更昂贵。

我们的第一个任务是创建我们的电路,接下来我们将完成这一步。

构建继电器驱动电路

让我们构建我们的继电器驱动电路。我们将分三部分进行,首先是放置组件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.11 - 继电器驱动电路(第一部分)

以下是建设的第一部分的步骤。步骤编号与图 7.11中的编号黑色圆圈相匹配:

  1. 将 PC817 放入面包板中,注意 IC 的引脚 1 连接到左侧面包板银行,如图所示。

  2. 将 1kΩ电阻器放入面包板中。电阻器的一端连接到 PC817 的引脚 1。

  3. 将 MOSFET 放入面包板中,注意正确放置源腿、栅极和漏极。我们的示例布局假定使用 2N7000 MOSFET。如果需要帮助识别腿部,请参阅图 7.8

  4. 将 1kΩ电阻器放入面包板中。这个电阻器的一端连接到 MOSFET 的栅极。

  5. 将 100kΩ电阻器放入面包板中。这个电阻器的一端也连接到 MOSFET 的栅极。

  6. 将二极管放入面包板中,注意正确放置组件,使带有标记的阴极腿(组件末端)指向面包板底部。

现在您已经放置了各个组件,接下来我们将连接这些组件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.12 - 继电器驱动电路(第二部分)

以下是第二部分建设的步骤。步骤编号与图 7.12中的编号黑色圆圈相匹配:

  1. 将您在上一个步骤 2中放置的电阻器连接到树莓派上的 3.3 伏引脚。

  2. 将 PC817 的引脚 2 连接到树莓派上的 GPIO 21。

  3. 将 PC817 的引脚 4 连接到右侧电源轨的正轨。

  4. 将 MOSFET 的源腿连接到右侧电源轨的负轨。

  5. 将连接到 MOSFET 的漏极的 100kΩ电阻器连接到右侧电源轨的负轨。

  6. 将 PC817 的引脚 4 连接到二极管的阴极腿。

  7. 将 MOSFET 的漏极引脚连接到二极管的阳极引脚。

最后,我们将连接电源和继电器:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.13 - 继电器驱动电路(3/3)

以下是构建的第三部分和最后一部分的步骤。步骤编号与图 7.13中的编号黑色圆圈相匹配:

  1. 将右侧电源轨道的正轨连接到 5 伏特电源的正输出端。

  2. 将右侧电源轨道的负轨连接到 5 伏特电源的负输出端。

  3. 将二极管的阳极引脚连接到继电器的一个线圈端子。

  4. 将二极管的阴极引脚连接到继电器的另一个线圈端子。

  5. 另一个5 伏特电源的负输出端连接到继电器的 com 端子。

步骤 5中,您必须为这个电路使用两个不同的外部电源,因为继电器线圈的电流要求和潜在的继电器负载很可能太大,无法从树莓派的电源中借用。

  1. 另一个5 伏特电源的正输出端连接到负载的正输入端(例如,电机上的一个端子)。

  2. 最后,将继电器的NO常开)端子连接到负载的正输入端。

在继电器上使用 NO 端子意味着负载默认处于关闭状态,只有在继电器吸合时才通电,这发生在 GPIO 21 为低电平时(记住这个电路是低电平有效)。如果将负载连接到继电器中的NC常闭)端子,负载将默认通电,即使树莓派关闭也是如此。

干得好!您完成了面包板电路,如图 7.13所示。这是与本章开头的图 7.1中显示的原理图相匹配的面包板电路。该面包板电路显示了一个 5 伏特的继电器线圈电源和一个 5 伏特的负载电源。然而,该电路可以使用不同的电源,但需要遵循以下几点:

  • 在这个电路中使用的电阻和 2N7000 MOSFET 能够驱动一个 12 伏特的继电器,比如 SRD-12VDC-SL-C。您只需要确保继电器线圈电源是 12 伏特而不是 5 伏特。

  • 负载电源被说明为 5 伏特,但是,如果您的负载需要更高的电压(在继电器规格内),可以增加。

现在我们有了一个完成的电路,让我们运行一个 Python 程序来控制继电器。

使用 Python 控制继电器驱动电路

运行以下代码,该代码位于chapter07/optocoupler_test.py文件中。继电器应该会发出点击声并在 2 秒后停止。这是我们创建和测试光耦电路时使用的相同代码,因为我们的树莓派连接到了光耦。

我们之前学习 MOSFET 时看到,我们可以直接将 MOSFET 连接到 GPIO 引脚并控制继电器,而无需光耦。那么,为什么前面的电路有一个光耦呢?

答案是我们的电路在技术上并不需要,而且有现成的继电器模块(尽管较少)不带有光耦。然而,有一个光耦存在并没有坏处,因为它提供了一定程度的电气隔离保护,以防继电器控制电路失效或在接线电源时发生意外。

最后,关于在 eBay 等网站上可以找到的具有多个继电器的继电器模块呢?通常只有一个继电器电路被复制多次——通常您可以为每个继电器计数一个晶体管和光耦对(尽管光耦和晶体管可以以芯片形式出现,即多个光耦或多个光耦在一个封装中,因此在某些模块上您可能只会看到芯片)。另外,请注意,有些模块将使用 BJT 而不是 MOSFET。如果您可以读取元件的零件号,您可以随时进行网络搜索以确定它们是什么。

最后,为了总结我们对打开和关闭的探索,这里是一个比较本章中使用的开关元件的表格:

- 光耦MOSFET继电器
- 结构固态固态
- 电流交流或直流(取决于光耦)仅直流(从 TRIACS 开始研究交流)
- 成本$ - $$ (低容量)至 (低容量)至 (低容量)至$$(高容量)
- 很热(不能触摸)对于高电流功率 MOSFET 是
- 控制电压/电流低(需要打开和关闭内部 LED)低(需要对栅极施加电压)
- 负载电压/电流低(例如,PC817 最大 50mA)低(例如,2N27000 为 200mA);高(例如,FQP30N06L 为 32A)
- 电气隔离
- 示例应用在控制电路和被控电路之间提供电气隔离允许低电流/电压电路控制更高电压/电流电路
- 寿命长寿命长寿命
- 使用 PWM

恭喜您完成了本章!您现在了解了多种控制具有电压和电流要求的负载的方法,这些要求超出了树莓派 GPIO 引脚的 3.3 伏特/8 毫安的限制。

总结

在本章中,我们学习了如何打开和关闭开关。我们首先简要回顾了典型的继电器驱动电路,然后学习了如何使用万用表测量直流电机、LED 和继电器的电流需求。接下来,我们讨论了光耦的特性,并学会了如何将其用作数字开关。然后,我们讨论了 MOSFET,并发现了如何将其用作开关和使用 PWM 进行电机速度控制。

本章中学到的信息、电路和练习将帮助您做出明智的决策,并进行必要的计算和测量,以选择合适的元件并创建可以安全地从树莓派引脚中获取更多电流和更高电压的电路,用于打开和关闭设备和其他负载。

我们对本章的方法是逐步探索和构建继电器驱动电路,这为您提供了一个实际示例,说明了为什么和如何将开关元件串联起来控制更高功率的元件和/或负载。此外,我们了解到光耦可以用于电气隔离电路,这可以是一种有用和实用的技术,可以帮助我们隔离和保护树莓派,以防电路故障或接线错误造成意外损坏。

在下一章中,我们将关注不同类型的 LED、蜂鸣器和可视元件,这些元件可以用来向用户发出信号或显示信息。

问题

最后,这里是一系列问题,供您测试对本章材料的了解。您将在本书的“评估”部分找到答案:

  1. 在控制晶体管时,MOSFET 和 BJT 有何不同?

  2. 您正在使用 MOSFET 控制电机,但是当您关闭 MOSFET(例如,使 GPIO 引脚低电平)时,电机并没有立即关闭,而是减速旋转。为什么?

  3. 您选择了一个随机的 MOSFET,想要从树莓派 3.3 伏特的 GPIO 控制它,但它不起作用。可能的问题原因是什么?

  4. 除了开关功能,光耦和继电器与晶体管不同的常见特征是什么?

  5. 主动低电平和主动高电平的 GPIO 有什么区别?

  6. 为什么我们更喜欢在 MOSFET 的栅极上使用物理下拉电阻,而不是在代码中激活的下拉电阻?

  7. 对于直流电机,失速电流代表什么?

  8. 直流电机的连续电流和自由电流有什么区别?

进一步阅读

以下教程是关于晶体管、它们的各种类型和应用的全面介绍:

  • [https://www.electronics-tutorials.ws/category/transistor](从 MOSFET 部分开始)

第八章:灯光、指示灯和显示信息

在上一章中,我们探讨并学习了如何使用光耦、晶体管和继电器电路,以及这三个组件如何共同工作以创建一个常见的继电器控制模块。我们还介绍了如何使用万用表测量负载的电流使用量,以便您可以就应该使用什么方法或组件来开关或控制外部负载做出明智的决定。

在本章中,我们将介绍使用 RGB LED 制作颜色的两种替代方法,并创建一个简单的应用程序来监视树莓派的 CPU 温度,并在 OLED 显示屏上显示结果。最后,我们将看到如何结合 PWM 和蜂鸣器来发出声音。

完成本章后,您将拥有知识、经验和代码示例,可以根据自己的项目需求来调整,以便在需要向用户显示信息、发出声音或简单地用灯光吸引他们的情况下使用。此外,您所学到的内容也可以适用于其他类型的兼容显示屏和照明设备,如果您希望进一步探索这些主题的话。

本章将涵盖以下主题:

  • 使用 RGB LED 制作颜色

  • 使用 SPI 控制多色 APA102 LED 灯带

  • 使用 OLED 显示屏

  • 使用蜂鸣器和 PWM 发出声音

技术要求

要完成本章的练习,您需要以下内容:

  • 树莓派 4 型 B

  • Raspbian OS Buster(带桌面和推荐软件)

  • 至少需要 Python 版本 3.5

这些要求是本书中代码示例的基础。可以合理地期望,只要您的 Python 版本是 3.5 或更高,代码示例应该可以在树莓派 3 型 B 或不同版本的 Raspbian OS 上无需修改即可运行。

您可以在此处的 GitHub 存储库的chapter08文件夹中找到本章的源代码:github.com/PacktPublishing/Practical-Python-Programming-for-IoT

您需要在终端中执行以下命令来设置虚拟环境并安装本章代码所需的 Python 库:

$ cd chapter08               # Change into this chapter's folder
$ python3 -m venv venv       # Create Python Virtual Environment
$ source venv/bin/activate   # Activate Python Virtual Environment
(venv) $ pip install pip --upgrade        # Upgrade pip
(venv) $ pip install -r requirements.txt  # Install dependent packages

以下依赖项已从requirements.txt中安装:

本章练习所需的电子元件包括以下内容:

让我们首先看看如何使用 PWM 来设置 RGB LED 的颜色。

使用 RGB LED 和 PWM 制作颜色

在本节中,我们将学习如何使用脉宽调制PWM)与 RGB LED 一起创建不同的颜色。作为提醒,PWM 是一种创建可变电压的技术,当应用于 LED 和电阻对时,可以用来改变 LED 的亮度。我们首先在第二章中讨论了 PWM 并用它来改变 LED 的亮度,使用 Python 和物联网入门。然后我们在第五章中更深入地讨论了 PWM,将树莓派连接到物理世界

RGB LED 是一个单一包装中的三个单色 LED(红色、绿色和蓝色),如图 8.1所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8.1 - RGB LED 品种

您会注意到显示了两种类型:

  • 共阴极:红色、绿色和蓝色 LED 共享一个公共的阴极腿,这意味着共腿连接到负电压或地面电压源 - 阴极 = 负极。

  • 共阳极:红色、绿色和蓝色 LED 共享一个公共的阳极腿,这意味着共腿连接到正电压源 - 阳极 = 正极。

共腿将是四条腿中最长的。如果最长的腿最靠近 LED 外壳的平面一侧,那么它是共阴极类型。另一方面,如果最长的腿靠近唇部(因此离平面一侧最远),那么它是共阳极类型。

我们之前在第五章*,将树莓派连接到物理世界*中学习了如何使用 PWM 来设置单个 LED 的亮度,但是如果我们改变 RGB LED 中三种单独颜色的亮度会发生什么?我们混合单独的颜色来创建新的颜色!让我们创建一个电路并开始混合。

创建 RGB LED 电路

在本节中,我们将创建一个简单的电路来控制 RGB LED,并且我们将使用共阴极RGB LED(即,三个单独的 LED 共享一个公共的 GND 连接)。

我们将首先按照面包板上显示的图 8.2构建电路:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8.2 - 共阴极 RGB LED 原理图

以下是我们即将构建的原理图的伴随面包板布局:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8.3 - 共阴极 RGB LED 电路

以下是要遵循的步骤,这些步骤与图 8.3中编号的黑色圆圈相匹配:

  1. 首先将 RGB LED 放入面包板中,注意 LED 的阴极腿的定位。

  2. 放置 200Ω电阻(R1)。这个电阻的一端连接到 LED 的红色腿。

  3. 放置第一个 15Ω电阻(R2)。这个电阻的一端连接到 LED 的蓝色腿。

  4. 放置第二个 15Ω电阻(R3)。这个电阻的一端连接到 LED 的绿色腿。

  5. 将树莓派上的一个地引脚连接到负电源轨道。

  6. 将树莓派上的 GPIO 16 连接到您在步骤 2中放置的 200Ω电阻(R1)的另一端。

  7. 将 RGB LED 的阴极腿连接到负电源轨道。

  8. 将树莓派上的 GPIO 20 连接到您在步骤 3中放置的 15Ω电阻(R2)的另一端。

  9. 将树莓派上的 GPIO 21 连接到您在步骤 4中放置的 15Ω电阻(R3)的另一端。

在我们测试 RGB LED 电路之前,让我们简要回顾一下我们是如何得到这个电路中的 200Ω和 15Ω电阻的。200Ω电阻(R1)是使用我们在第六章中介绍的相同过程得出的,软件工程师的电子学 101。R2 和 R3 的 15Ω电阻是使用相同的过程得出的,不同之处在于用于蓝色和绿色 LED 计算的典型正向电压为 3.2 伏特。如果你研究样本数据表,你会注意到蓝色和绿色 LED 的正向电压列出了最大正向电压为 4.0 伏特。即使在典型值 3.2 伏特下,我们也非常接近树莓派 GPIO 引脚的 3.3 伏特。如果你不幸需要超过 3.3 伏特的蓝色或绿色 LED 的 RGB LED,它将无法工作——尽管我从未遇到过这种情况…至少目前还没有。

现在我们准备测试我们的 RGB LED。

运行和探索 RGB LED 代码

现在你的电路已经准备好了,让我们运行我们的示例代码。我们的示例将点亮 LED 并使其交替显示不同的颜色。以下是要遵循的步骤:

  1. 运行chapter08/rgbled_common_cathode.py文件,你应该会看到 RGB LED 循环显示颜色。请注意前三种颜色,应该是红色、绿色,然后是蓝色。

要使用共阳RGB LED,它需要与图 8.2中显示的方式不同地接线——共阳腿必须连接到树莓派的+3.3V 引脚,而 GPIO 连接保持不变。另一个变化是在代码中我们需要反转 PWM 信号——你会在chapter08文件夹中找到一个名为rgbled_common_anode.py的文件,其中包含了已注释的差异。

  1. 如果你的前三种颜色不是红色、绿色,然后是蓝色,你的 RGB LED 可能与图 8.1中显示的 RGB LED 的引脚顺序不同,以及图 8.2中的电路。你需要做的是更改代码中的 GPIO 引脚编号(参见以下代码片段)并重新运行代码,直到颜色顺序正确为止。

  2. 在红色、绿色和蓝色循环之后,RGB LED 将以彩虹色动画显示,然后程序完成。

让我们讨论代码的有趣部分并看看它是如何工作的:

在第 1 行,我们从PIL.ImageColor模块导入getrgbgetrgb为我们提供了一种方便的方法,将常见的颜色名称(如红色)或 HEX 值(如#FF0000)转换为它们的 RGB 分量值(如(255, 0, 0)):

from time import sleep
import pigpio
from PIL.ImageColor import getrgb    # (1)

GPIO_RED = 16
GPIO_GREEN = 20
GPIO_BLUE = 21

pi.set_PWM_range(GPIO_RED, 255)      # (2)
pi.set_PWM_frequency(GPIO_RED, 8000)
# ... truncated ... 

从第 2 行开始,我们明确为每个 GPIO 引脚配置 PWM(占空比范围为 255,频率为 8000 是 PiGPIO 的默认值)。PWM 占空比范围从 0 到 255 完美地映射到 RGB 分量颜色值范围 0…255,我们很快就会看到这是我们如何设置每个颜色 LED 的亮度的方法。

在下面的代码中,在第 3 行,我们有set_color()的定义,它负责设置我们的 RGB LED 的颜色。color参数可以是一个常见的颜色名称,比如yellow,也可以是一个 HEX 值,比如#FFFF00,或者getrgb()可以解析的许多格式之一(请参阅rgbled_common_cathode.py源文件,了解常见格式的列表):

def set_color(color):                                 # (3)   rgb = getrgb(color)                               
  print("LED is {} ({})".format(color, rgb))
    pi.set_PWM_dutycycle(GPIO_RED,   rgb[0])          # (4)
  pi.set_PWM_dutycycle(GPIO_GREEN, rgb[1])
    pi.set_PWM_dutycycle(GPIO_BLUE,  rgb[2])

在第 4 行,我们看到如何使用 PWM 与单独的 GPIO 引脚来设置 RBG LED 的颜色。继续以黄色为例,我们看到以下内容:

  • GPIO_RED被设置为 0 的占空比。

  • GPIO_GREEN被设置为 255 的占空比。

  • GPIO_BLUE被设置为 255 的占空比。

绿色和蓝色的占空比值为 255 意味着这些 LED 完全开启,正如我们所知,混合绿色和蓝色会得到黄色。

当你浏览源文件时,你会在第 6 行和第 7 行遇到另外两个函数:

def color_cycle(colors=("red", "green", "blue"), delay_secs=1):   # (6)
    # ...truncated...

def rainbow_example(loops=1, delay_secs=0.01):                    # (7)
    # ...truncated...

这两种方法都委托给set_color()color_cycle()循环遍历其color参数提供的颜色列表,而rainbow_example()生成并循环遍历一系列颜色以产生彩虹序列。这些函数是我们在步骤 1中运行代码时生成光序列的原因。

我们的 RGB LED 电路有一些限制和缺点:

  • 首先,每个 RGB LED 需要三个 GPIO 引脚。

  • 其次,我们通过电阻将电流限制在 8mA,因此无法实现单个 LED 的最大亮度(我们需要约 20mA 才能实现全亮度)。

虽然我们可以引入晶体管(或适当的多通道 LED 驱动 IC)来增加电流,但我们的电路很快就会变得笨重!幸运的是,我们还有另一种方法可以用 LED 创建颜色,那就是可寻址 LED,我们将在下一节中讨论。

使用 SPI 控制多色 APA102 LED 灯带

APA102 是一种可寻址的多色(RGB)LED,使用串行外围接口(SPI)进行控制。简单来说,我们向 LED 发送指令询问它要显示什么颜色,而不是像在上一个例子中那样使用 PWM 单独控制 LED 的三个红绿蓝引脚。

如果您需要快速回顾 SPI,我们在第五章中已经涵盖了它,将您的树莓派连接到物理世界。我们还将在探索 APA102 特定代码后讨论 SPI,树莓派和 Python 的更多内容。

APA102 LED 也可以连接或串联在一起,以创建 LED 灯带或 LED 矩阵,从而创建动态和多 LED 的照明和显示解决方案。无论 LED 如何排列,我们都使用一种常见的技术来控制它们,即向一系列 APA102 LED 发送多组指令。每个单独的 LED 消耗一个指令,并将其余的传递给上游 LED 消耗。我们将在不久的将来使用 APA102 LED 灯带时看到这个想法。

APA102 LED 也被称为超级 LED、DotStar LED,有时也被称为下一代 NeoPixels。还有另一种可寻址 LED,WS2812,也被称为 NeoPixel。虽然原理和操作类似,但 WS2812 RGB LED 与 APA102 不兼容。

让我们创建一个电路并运行代码来控制我们的 APA102 LED 灯带。

创建 APA102 电路

在本节中,我们将创建我们的 APA102 电路,如下图所示。我们将在面包板上分两部分完成这个过程:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8.4 - APA102 LED 灯带电路原理图

让我们开始第一部分,即放置元件并连接逻辑电平转换器的低电压端:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8.5 - APA102 LED 电路(1/2)

以下是要遵循的步骤。步骤编号与图 8.5中编号的黑色圆圈相匹配:

  1. 将逻辑电平转换器(逻辑电平转换器)放入面包板中,将低电压端朝向树莓派。不同的逻辑电平转换器可能有不同的标记,但是低电压端应该是清楚的。在我们的示例中,一侧有一个LV(低电压)端子,另一侧有一个HV(高电压)端子,用于区分两侧。

  2. 连接左侧和右侧电源轨道上的负轨。

  3. 将树莓派上的 3.3 伏特引脚连接到左侧电源轨道的正轨。

  4. 将逻辑电平转换器上的 LV 端子连接到左侧电源轨道的正轨。

  5. 将树莓派上的MOSI(主输出从输入)引脚连接到逻辑电平转换器上的 A2 端子。

  6. 将树莓派上的SLCK(串行时钟)引脚连接到逻辑电平转换器上的 A1 端子。

  7. 将逻辑电平转换器上的 GND 端子连接到左侧电源轨的负轨。

  8. 将左侧电源轨上的负轨连接到树莓派的 GND 引脚。

现在我们已经将逻辑电平转换器的低电压端连接到了树莓派,接下来我们将把高电压端连接到 APA102 LED 灯带。作为提醒,树莓派的 GPIO 引脚工作在 3.3 伏(因此是电压),而 APA102 工作在 5 伏(因此是电压):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8.6 – APA102 LED 电路(2/2)

以下是我们搭建的第二部分的步骤。步骤编号与图 8.6中编号的黑色圆圈相匹配:

  1. 将逻辑电平转换器的 HV 端子连接到右侧电源轨的正轨。

  2. 从 B2 端子到面包板上未使用的一行放置一根跳线(在插图中,这显示在孔 G16 处)。

  3. 从 B1 端子到面包板上未使用的一行放置另一根跳线(在插图中,这显示在孔 H14 处)。

  4. 将逻辑电平转换器高电压端的 GND 端子连接到右侧电源轨的负轨。

  5. 将电源的正输出连接到右侧电源轨的正轨。

  6. 将电源的负输出连接到右侧电源轨的负轨。

  7. 将 APA102 LED 灯带的 VCC 端子或导线连接到右侧电源轨的正轨。

您的 APA102 必须正确连接。您会注意到 APA102 LED 灯带上的箭头,如图 8.4所示。这些箭头表示数据流的方向。确保您的 APA102 LED 灯带箭头与插图相匹配(即箭头指向面包板的反方向)。

如果您的 APA102 没有箭头,请查看端子的命名。LED 灯带的一侧可能有 CI/DI(I = 输入),而另一侧有 DO/CO(O = 输出)。我们需要连接的是输入端。

  1. 将 APA102 LED 灯带的CI(时钟输入)端子或导线连接到您在步骤 3中放置的连接回逻辑电平转换器的 B1 端子的导线。

  2. 将 APA102 LED 灯带的DI(数据输入)端子或导线连接到您在步骤 2中放置的连接回逻辑电平转换器的 B2 端子的导线。

  3. 最后,将 APA102 LED 灯带的 GND 端子或导线连接到右侧电源轨的负轨。

干得好!您现在已经完成了 APA102 LED 灯带电路。在完成这个电路搭建时,您会注意到我们使用了逻辑电平转换器。这是因为 APA102 需要 5 伏逻辑电才能正常运行。APA102 的数据表明明确提到最小逻辑电压为 0.7 VDD,即 0.7 x 5 伏=3.5 伏,这高于树莓派的 3.3 伏逻辑电平。

如果您需要关于逻辑电平和逻辑电平转换的复习,请参考第六章,软件工程师的电子学 101

让我们考虑一下(如果您在想)3.3 伏只比 3.5 伏略低一点——这肯定够接近了吧?您可以尝试用 3.3 伏来控制 APA102,它可能会带来一定程度的成功。然而,您可能也会遇到一些随机效果和混乱,例如,随机 LED 未按预期开启或关闭,LED 闪烁,或 LED 显示错误的颜色。不幸的是,APA102 是不兼容 3.3 伏的 5 伏逻辑设备之一,因此我们必须采取额外的步骤,使用逻辑电平转换器来满足其 3.5 伏最小逻辑电平要求。

现在您已经建立了 APA102 电路,接下来我们将讨论我们需要考虑的问题,以便为这个电路供电。

为 APA102 电路供电

在第七章*,打开和关闭东西*中,我们讨论了了解您正在使用的“负载”的电流要求的重要性。让我们将这个知识应用到我们的 APA102 LED 灯带上,以便我们可以正确地为其供电。我们的示例假设 LED 灯带包含 60 个 LED,但是您需要根据灯带上 LED 的数量调整计算。

举例来说,我们有以下内容:

  • 一个包含 60 个 LED 的 APA102 LED 灯带。

  • 每个 LED 使用(平均)最大 25mA(来自数据表并经过测量确认)。

  • LED 灯带在空闲时消耗大约 15mA(没有 LED 亮起)。

单个 RGB LED 在设置为白色时使用其最大电流,这是当每个 LED(红色、绿色和蓝色)都处于最大亮度时。

使用前面的值,我们可以计算出 60 个 LED 的预期最大电流需求,这超过了 1.5 安培:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果我们假设我们使用的是面包板电源供应,那么如果我们保守地假设我们的面包板电源供应最多只能提供大约 700mA,我们实际上不能将 60 个 LED 灯带上的所有 LED 都设置为全白。如果这样做,那么(取决于电源供应)它可能会在其内部过载保护启动时关闭,它可能会冒烟,或者它可能会限制其输出电流,这可能会导致 LED 看起来呈红色而不是白色。

让我们逆向工作,计算出我们可以从 700mA 电源供应中供电的 LED 的安全数量:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果我们然后减去 2 个 LED(50mA)作为一个小的安全缓冲区,我们得到 25 个 LED。记住这个数字(或者您计算的数字),因为我们在运行示例代码时将需要它。

计算出您可以使用的安全 LED 数量后,我们现在准备配置和运行我们的 Python 示例。

配置和运行 APA102 LED 灯带代码

现在您的电路已经准备好,我们知道 LED 灯带的预期电流使用情况,让我们配置并点亮 LED 灯带:

  1. 编辑chapter08/apa102_led_strip.py文件,并在文件顶部附近查找以下行。将数字调整为您之前计算的安全 LED 数量,或者如果您的灯带有足够能力的电源供应,则调整为灯带上的 LED 数量:
NUM_LEDS = 60     # (2)
  1. 保存您的编辑并运行代码。如果一切连接正确,您应该观察到 LED 灯带循环显示红色、绿色和蓝色,然后执行一些不同的光序列。

如果您的 LED 灯带没有工作,请查看本节后面的APA102 LED 灯带故障排除提示

如果您的灯带没有按照红、绿和蓝的顺序显示,那么您需要调整代码以设置正确的顺序——我将向您展示在代码的哪个部分可以调整 LED 的顺序。

现在我们在代码中配置了安全数量的 LED,让我们走一遍代码,看看它是如何工作的。

APA102 LED 灯带代码演示

从以下代码的第一行开始,我们有导入。我们将使用 Python 的deque集合实例(我只是简单地称之为数组)来在内存中模拟 APA102 LED 灯带——在将其应用于 LED 灯带之前,我们将在这个数组中构建和操作我们希望每个 LED 显示的颜色顺序。然后我们从 PIL 库中导入getrgb函数,用于处理颜色格式(就像我们在前面的 RGB LED 示例中所做的那样):

# ...truncated...
from collections import deque                                   # (1) from PIL.ImageColor import getrgb
from luma.core.render import canvas
from luma.led_matrix.device import apa102
from luma.core.interface.serial import spi, bitbang

最后,三个luma导入是用于 APA102 LED 灯带控制。Luma 是一个成熟的高级库,用于使用 Python 处理各种常见的显示设备。它支持 LCD、LED 灯带和矩阵等,还包括我们将在本章后面介绍的 OLED 显示器。

在本章中,我们只能浅尝 Luma 库的功能,所以我鼓励您探索其文档和各种示例——您将在本章末尾的进一步阅读部分找到链接。

接下来,我们来到下面代码的第 3 行,我们将color_buffer分配给deque的一个实例,该实例初始化为与我们条带中的 LED 数量相同的元素数。每个元素默认为黑色(即 LED 关闭):

# ...truncated...
color_buffer = deque(['black']*NUM_LEDS, maxlen=NUM_LEDS)      # (3) 

在下面代码的第 4 行中,我们开始创建我们的 APA102 的软件接口。在这里,我们创建了一个代表树莓派上默认硬件 SPI0 接口的spi()实例。要使用此接口,您的 APA102 必须连接到树莓派上的 SPI 引脚,如下所示:

  • DI 连接到 MOSI

  • CI 连接到 SCLK

在下面的代码片段中,port=0device=0与 SPI0 接口相关:

# ...truncated... serial = spi(port=0, device=0, bus_speed_hz=2000000)           # (4)  

bus_speed_hz参数设置了 SPI 接口的速度,对于我们的示例,我们将其从默认值 8,000,000 降低到 2,000,000,只是为了确保您的逻辑电平转换器能够工作。并非所有逻辑电平转换器都相同,它们将具有可以转换逻辑电平的最大速度。如果 SPI 接口的操作速度快于逻辑电平转换器可以转换的速度,我们的电路将无法工作。

在下面的代码中的第 5 行(已注释掉)中,我们有一个软件替代硬件 SPI 的选择,称为大砰,它可以在任何 GPIO 引脚上工作,但速度会受到影响。这类似于我们在第五章中讨论过的软件与硬件 PWM 的权衡,将您的树莓派连接到物理世界

# ...truncated... # serial = bitbang(SCLK=13, SDA=6)                             # (5)  # ...truncated... device = apa102(serial_interface=serial, cascaded=NUM_LEDS)    # (6)

在上述代码的第 6 行中,我们创建了一个apa102类的实例,指定了我们刚刚创建的serial实例和我们条带中 LED 的数量。从此刻开始,在代码中与 APA102 LED 条带进行交互,我们将使用device实例。

要初始化我们的 LED 条带,在下面代码的第 7 行中,我们调用device.clear()并将默认全局对比度设置为 128(即半亮度)。您可以调整此级别以找到您满意的亮度,记住更多的对比度/亮度意味着更多的电流使用。请注意,先前在计算安全 LED 数量时,计算中使用的每个 LED 的 25mA 假定最大亮度(即 255):

device.clear()                                                   # (7) contrast_level = 128 # 0 (off) to 255 (maximum brightness) device.contrast(contrast_level)

在下面代码的第 8 行中,我们有set_color()函数。我们使用此函数在color_buffer数组中将单个或所有元素设置为指定的颜色。这是我们在内存中构建 APA102 LED 条带要显示的颜色排列的方法:

def set_color(color='black', index=-1):                          # (8)
  if index == -1:
        global color_buffer
        color_buffer = deque([color]*NUM_LEDS, maxlen=NUM_LEDS)
    else:
        color_buffer[index] = color

现在,我们将跳转到下面代码块的第 12 行,到update()函数。这个函数循环遍历color_buffer,并使用代表我们 APA102 的 Luma device实例,使用draw.point((led_pos, 0), fill=color)来向设备提供要显示的颜色。这就是 Luma 库的魔力——它通过给我们一个非常简单的软件接口,使我们免受较低级别 APA102 和 SPI 数据和硬件协议的影响。

如果您想了解更多关于较低级别 SPI 使用和协议的知识,那么 APA102 是一个很好的起点。首先阅读 APA102 的数据协议的数据表,然后在pypi.org或 GitHub 上找到一个简单的 APA102 模块并查看其代码。在 PiGPIO 网站上也可以找到一个 APA102 的示例,进一步阅读部分中包含了链接。

重要的是要记住,在对color_buffer进行更改后需要调用update()

def update():                                                   # (12)
  with canvas(device) as draw:
        for led_pos in range(0, len(color_buffer)):
            color = color_buffer[led_pos]

            ## If your LED strip's colors are are not in the expected
 ## order, uncomment the following lines and adjust the indexes ## in the line color = (rgb[0], rgb[1], rgb[2]) # rgb = getrgb(color) # color = (rgb[0], rgb[1], rgb[2]) # if len(rgb) == 4: #     color += (rgb[3],)  # Add in Alpha    draw.point((led_pos, 0), fill=color)

如果出现 LED 灯带颜色不是标准的红、绿和蓝顺序,那么上面注释掉的代码部分可以用来改变颜色顺序。我从未遇到过非标准 APA102,但我读到过可寻址的 RGB LED 具有非标准顺序,所以我想我还是把那部分代码放进来,以防万一。

接下来是第(9)、(10)和(11)行,我们有三个简单操作color_buffer的函数:

def push_color(color):                                       # (9)   color_buffer.appendleft(color)

def set_pattern(colors=('green', 'blue', 'red')):           # (10)     range(0, int(ceil(float(NUM_LEDS)/float(len(colors))))):
        for color in colors:
            push_color(color)

def rotate_colors(count=1):                                 # (11)
    color_buffer.rotate(count)

push_color(color) 在第(9)行将一个新颜色推入color_buffer的索引 0,而第(10)行的set_pattern()用重复的颜色模式序列填充color_buffer。第(11)行的rotate_colors()旋转color_buffer中的颜色(并将它们包装起来——最后一个变成第一个)。你可以通过使用小于 0 的计数值向后旋转。

最后,在源代码的末尾,我们有以下函数,提供了你运行文件时看到的示例。这些函数使用之前讨论过的函数的组合来控制 LED 灯带:

  • cycle_colors(colors=("red", "green", "blue"), delay_secs=1)

  • pattern_example()

  • rotate_example(colors=("red", "green", "blue"), rounds=2, delay_secs=0.02)

  • rainbow_example(rounds=1, delay_secs=0.01)

我们将用一些结论性的笔记来完成对 APA102 使用 SPI 接口的覆盖。

APA102 和 SPI 接口的讨论

如果回想一下第五章,将树莓派连接到物理世界,我们讨论了串行外围接口 (SPI),你可能记得我们提到它使用四根导线进行数据传输。然而,如果你考虑我们在图 8.6中的电路,我们只使用了两根导线(DI 和 CI),而不是四根。怎么回事?

以下是 APA102 的 SPI 映射:

  • 树莓派上的Master-Out-Slave-In (MOSI) 连接到 APA102 上的Data In (DI)。在这里,你的树莓派是,向APA102 灯带发送数据。

  • Master-In-Slave-Out (MISO) 没有连接,因为 APA102 不需要将数据发送回树莓派。

  • 树莓派上的 SCLK 连接到 APA102 上的Clock In (CI)。

  • Client Enable/Slave Select (CE/SS) 没有连接。

最后一行重要的 CE/SS 值得进一步讨论。CE/SS 通道由主设备用于告诉特定的从设备它即将接收数据。正是这种机制允许单个 SPI 主控制多个 SPI 从。

但是,我们不会(也不能)使用 CE/SS 与 APA102,因为我们没有地方连接 CE/SS 引脚。这意味着 APA102 总是在等待来自主设备的指令,实际上占用了 SPI 通道。

如果我们使用 APA102(或任何没有 CE/SS 的设备),那么除非我们采取额外的步骤,否则我们不能将多个 SPI 设备连接到主硬件 SPI。以下是一些选项:

  • 如果性能降低没有不良影响,可以在通用 GPIO 引脚上使用大爆破。

  • 在树莓派上启用硬件 SPI1。它默认情况下是未启用的,需要编辑/boot/config.txt。如果搜索Raspberry Pi enable SPI1,你会在网上找到指南和提示。

  • 找到一个包括使能引脚的逻辑电平转换器,并编写代码手动控制这个引脚作为代理 CE/SS。

我们将用一些故障排除提示来结束关于 APA102 的部分。

APA102 LED 灯带故障排除提示

如果你无法点亮 APA102,或者发现随机 LED 未开启或关闭,或者显示意外颜色或随机闪烁,请尝试以下操作:

  • APA102 需要 5 伏逻辑电平:确保你使用的是逻辑电平转换器,并且连接正确——HV 连接到 5 伏,LV 连接到 3.3 伏。

  • 确保 APA102 的 DI/CI 端连接到逻辑电平转换器。

  • 确保您的电源可以提供足够的电流。例如,电流或电压的不足可能会使白色看起来更像红色。

  • 确保您的电源的地线连接到树莓派上的地线引脚。

  • 如果您正在使用大幅振荡,请转移到硬件 SPI。

  • 如果使用硬件 SPI(也就是创建spi()类的实例),请尝试以下操作:

  • 如果您收到错误消息SPI 设备未找到,请确保在 Raspbian OS 中已启用 SPI。我们在第一章中介绍了这一点,设置您的开发环境

  • 如果您之前已经使用 GPIO 8、9、10 或 11 进行常规 I/O 操作,那么要么按照前面的方法禁用并重新启用 SPI 接口,要么重新启动树莓派以重置硬件 SPI 接口。

  • 如果您的逻辑电平转换器无法跟上 SPI 总线速度,请尝试降低 SPI 总线速度——也就是说,它无法将 3.3 伏转换为 5 伏的信号,就像 SPI 接口产生它们一样(提示:将serial = spi(port=0, device=0, bus_speed_hz=2000000)中的bus_speed_hz参数降低到 1,000,000 或 500,000)。

  • 将 APA102 的 DI 和 CI 直接连接到树莓派上的 SDA 和 SCLK。这里的目标是绕过逻辑电平转换器,以排除它作为问题的可能性。

干得好!这是关于 APA102 的一个冗长而详细的部分。除了 APA102 本身之外,我们还涵盖了许多概念,包括如何计算 LED 灯带的功率需求以及 Luma 库的介绍,该库可以用于控制 APA102 之外的许多不同的照明和显示设备。然后,我们总结了在您的 APA102 电路、设置或代码第一次运行时的实际故障排除提示。

所有这些知识和经验都将适用于您进行的类似照明项目和 SPI-based 项目。特别是,它将是一个有用的参考,用于计算照明项目的功率需求,并在它们不起作用时排除电路和代码的故障。它还提供了我们将在下一节中构建的基本基础,我们将在其中研究如何将 OLED 显示器与我们的树莓派接口。

使用 OLED 显示器

OLED有机发光二极管显示器是一种用于制作屏幕的技术。我们的示例将使用 SSD1306,这是一种单色 128x64 像素显示器,但是这些信息也适用于其他 OLED 显示器。

我们的示例程序将读取您的树莓派的 CPU 温度,并将其与温度计图标一起显示在 OLED 显示器上。我们将假设 OLED 将使用 I2C 接口连接,但是,如果您使用spi()实例(就像在 APA102 示例中)用于serial对象,那么 SPI 接口设备也应该是兼容的。Luma 库更改交互方法的能力意味着您可以在最小更改代码的情况下重用现有代码与兼容的显示设备。

我们将首先连接 OLED 显示器到树莓派并验证它是否连接。

连接 OLED 显示器

让我们将您的 OLED 显示器连接到您的树莓派,如图 8.7所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8.7 - I2C OLED 显示电路关于为您的 OLED 供电的重要说明:我们的电路,如图 8.6所示,并且相关讨论使用了 5 伏的电源。如果您查阅本章开头提到的 SSD1306 OLED 数据表,您将发现它提到了最低供电电压为 7 伏。此外,您将找到其他来源和 SSD1306 OLED 模块,它们指出了不同的电压要求。请查阅文档或购买地点,以获取您的 OLED 的正确工作电压,并根据需要调整供电电压(步骤 78)。

您可以按照以下步骤连接 OLED,这些步骤对应于图 8.7中编号的黑色圆圈:

  1. 连接左侧和右侧电源轨道上的负极。

  2. 将你的树莓派的 SDA1(数据)引脚连接到面包板上的一个空行。

  3. 将你的 OLED 显示屏的 SDA(数据)端子或线连接到用于步骤 2的同一行。

  4. 将你的树莓派的 SCL1(时钟)引脚连接到面包板上的一个空行。

  5. 将你的 OLED 显示屏的 SCL(时钟)端子或线连接到用于步骤 4的同一行。

  6. 将你的树莓派的 GND 引脚连接到左侧电源轨道的负极。

  7. 将 5 伏电源的正输出连接到右侧电源轨道的正极。

  8. 将 5 伏电源的负输出连接到右侧电源轨道的负极。

  9. 将你的 OLED 显示屏的 GND 端子或线连接到右侧电源轨道的负极。

  10. 将你的 OLED 显示屏的 VCC 端子或线(也可能被命名为 VDD、Vin、V+或类似表示电压输入的名称)连接到右侧电源轨道的正极。

干得好!这完成了我们的 OLED 电路。正如你所看到的,我们正在用 5 伏电源为 OLED 供电,然而,SDA(数据)/SLC(时钟)通道直接连接到你的树莓派上。与我们在上一节中使用的 APA102 LED 灯带不同,SSD1306 OLED 兼容 3.3 伏逻辑,因此,在时钟和数据通道上我们不需要逻辑电平转换器来转换逻辑电平电压。

让我们简要考虑一下 SSD1306 OLED 的电流要求。我的测试结果如下:

  • 黑屏:~3mA

  • 白屏(每个像素都亮):~27mA

在最大电流使用量为~27mA 的情况下,你可以尝试将+5V 连接到树莓派的 5 伏引脚,但请记住这将从你的树莓派中取走保留电流(如果你的树莓派的电源供应不足,可能会在运行代码时重置)。

如果你需要回顾使用数字万用表进行电流测量,请参考第七章*,打开和关闭*。

接下来,将你的 OLED 连接到树莓派的 SDA 和 SCL 引脚,然后我们将使用i2cdetect实用程序验证树莓派是否检测到了它。

验证 OLED 显示屏是否连接

在第五章*,将你的树莓派连接到物理世界*中,我们使用了i2cdetect命令行工具来检查 I2C 设备是否连接,并验证其 I2C 地址。通过在终端中运行以下命令来检查你的树莓派是否能看到你的 OLED 显示屏:

$ i2cdetect -y 1

如果你的 OLED 已连接,你将看到以下输出,告诉我们 OLED 已被检测到,并具有十六进制地址0x3C

# ...truncated...
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- -- 
# ...truncated...

如果你的地址不同,没关系,我们只需要在代码中调整地址,接下来我们将这样做。

配置和运行 OLED 示例

我们即将探讨的代码包含在chapter08/oled_cpu_temp.py文件中。在继续之前,请先查看这个文件,以获得它包含的内容的整体视图:

  1. 如果你在前面得到的 OLED I2C 地址与0x3C不同,请在源代码中找到以下行,并更新地址参数以匹配你的 OLED I2C 地址:
serial = i2c(port=1, address=0x3C)
  1. 运行程序,你应该看到 OLED 显示屏上绘制的 CPU 温度和温度计图标。

一旦你在代码中配置了你的 OLED 显示屏地址并确认了示例在你的 OLED 上运行正常,我们就准备好审查代码并学习它是如何工作的。

OLED 代码演示

从导入开始,在第(1)行,我们从PILPillow)模块中导入类,用于创建我们想要在 OLED 显示屏上呈现的图像。我们还从 Luma 模块中导入与我们的 SSD1306 OLED 及其 I2C 接口相关的几个其他类(SPI 也被导入以供参考)。

我们看到如何在第(2)行创建一个代表我们 OLED 连接的接口的 I2C 实例。被注释掉的是 SPI 替代方案。在第(3)行,我们创建了一个代表我们 OLED 显示器的ssd1306实例,并将其分配给device变量。如果您使用的 OLED 显示器与 SSD1306 不同,您需要识别和调整ssd1306导入行以及第(3)行创建的设备实例:

from PIL import Image, ImageDraw, ImageFont         # (1)
from luma.core.interface.serial import i2c, spi
from luma.core.render import canvas
from luma.oled.device import ssd1306  #...truncated...

# OLED display is using I2C at address 0x3C serial = i2c(port=1, address=0x3C)                  # (2)
#serial = spi(port=0, device=0)
  device = ssd1306(serial)                            # (3)
device.clear()
print("Screen Dimensions (WxH):", device.size)

在第(4)行,我们遇到了get_cpu_temp()函数,该函数调用一个命令行实用程序来检索树莓派的 CPU 温度,然后解析并返回结果,我们将很快用来构建我们的显示图像:

def get_cpu_temp():     # (4)   temp = os.popen("vcgencmd measure_temp").readline() # Eg 62.5'C
  data = temp.strip().upper().replace("TEMP=", "").split("'")
    data[0] = float(data[0])

    if data[1] == 'F':  # To Celsius just in case it ever returns Fahrenheit
  data[0] = (data[0] - 32) * 5/9
  data[1] = 'C'    return (data[0], data[1])  # Eg (62.5, 'C') 

在第(5)行的以下代码中,我们定义了影响我们 OLED 显示器上显示图标的温度阈值。我们还将使用高阈值使 OLED 显示屏闪烁,以帮助创建视觉吸引力。

在第(6)行,我们加载了三个温度计图像,并从第(7)行开始将它们缩小到与我们的 SSD1306 OLED 的 128x64 像素尺寸相适应的大小:

# Temperature thresholds used to switch thermometer icons  temp_low_threshold = 60 # degrees Celsius                     # (5) temp_high_threshold = 85 # degrees Celsius   # Thermometer icons image_high = Image.open("temp_high.png")                        # (6)
image_med  = Image.open("temp_med.png")
image_low  = Image.open("temp_low.png")

# Scale thermometer icons (WxH) aspect_ratio = image_low.size[0] / image_low.size[1]            # (7)
height = 50 width = int(height * aspect_ratio)
image_high = image_high.resize((width, height))
image_med  = image_med.resize((width, height))
image_low  = image_low.resize((width, height))

接下来,我们从以下第(8)行开始定义了两个变量。refresh_secs是我们检查 CPU 温度并更新 OLED 显示屏的速率,而high_alert用于标记最高温度阈值的违反并开始屏幕闪烁:

refresh_secs = 0.5  # Display refresh rate                           #(8) high_alert = False # Used for screen blinking when high temperature   try:
    while True:
        current_temp = get_cpu_temp()
        temp_image = None    canvas = Image.new("RGB", device.size, "black")              # (9)
        draw = ImageDraw.Draw(canvas)                                # (10)
        draw.rectangle(((0,0), 
                   (device.size[0]-1, device.size[1]-1)), 
                   outline="white")

while循环中,在第(9)行,我们看到了 PIL 模块的使用。在这里,我们使用与 OLED 设备相同尺寸(即 SSD1306 的 128x64)创建了一个空白图像,并将其存储在canvas变量中。在随后的代码中,我们会在内存中操作这个画布图像,然后将其发送到 SSD1306 进行渲染。

在第(10)行创建的 draw 实例是我们用于在画布上绘制的 PIL 辅助类。我们使用这个实例来在画布上放置一个边界矩形,并稍后用它来向画布添加文本。draw实例还可以用于绘制许多其他形状,包括线条、弧线和圆形。PIL API 文档的链接可以在进一步阅读部分找到。

以下代码从第(11)行开始的代码块将使我们的 OLED 显示在high_alertTrue时闪烁:

  if high_alert:                                     # (11)
            device.display(canvas.convert(device.mode))
            high_alert = False   sleep(refresh_secs)
            continue 

从第(12)行开始,我们将从get_cpu_temp()获得的温度读数与之前定义的阈值进行比较。根据结果,我们更改将显示的温度计图像,并且对于高阈值违规,我们将high_alert = True。将high_alert设置为True将导致 OLED 显示在下一个循环迭代中闪烁:

 if current_temp[0] < temp_low_threshold:           # (12)
            temp_image = image_low
            high_alert = False     elif current_temp[0] > temp_high_threshold:
            temp_image = image_high
            high_alert = True     else:
            temp_image = image_med
            high_alert = False  

我们从以下第(13)行开始构建我们的显示。我们计算image_xy是我们的温度计图像在显示器上居中的点,然后使用image_x_offsetimage_x_offset变量来偏移该点,以将图像移动到我们希望它呈现的位置。

在第(14)行,我们将温度计图像粘贴到画布上:

# Temperature Icon image_x_offset = -40                    # (13) image_y_offset = +7 image_xy = (((device.width - temp_image.size[0]) // 2) + 
        image_x_offset, ((device.height - temp_image.size[1]) // 2) 
        + image_y_offset)
canvas.paste(temp_image, image_xy)      # (14)

在以下代码块的第(15)行,我们创建了要显示在 OLED 屏幕上的文本,并使用与图像相同的技术在第(17)行将文本定位在画布上。请注意使用draw.textsize()来获取文本的像素尺寸。

在第(16)行,我们设置font = None,以便在示例中使用默认系统字体,因为我无法完全确定您的树莓派上有哪些字体可用。在第(16)行之后被注释掉的行显示了使用自定义字体的示例。

在终端中运行fc-list命令,可以查看树莓派上安装的字体列表。

最后,在第(18)行,我们在画布上绘制文本:

# Temperature Text (\u00b0 is a 'degree' symbol)                 # (15) text = "{}\u00b0{}".format(current_temp[0], current_temp[1]) # Eg 43'C   font = None # Use a default font.                                # (16)
# font = ImageFont.truetype(font="Lato-Semibold.ttf", size=20) 

text_size = draw.textsize(text, font=font)                       # (17)
text_x_offset = +15 text_y_offset = 0 text_xy = (((device.width - text_size[0]) // 2) + text_x_offset, 
((device.height -  text_size[1]) // 2) + text_y_offset)
draw.text(text_xy, text, fill="white", font=font)                # (18)

我们现在已经到达了 while 循环的尾端。在以下代码的第(19)行,我们使用代表 SSD1306 OLED 显示器的device实例来显示canvascanvas.convert(device.mode)调用将我们创建的画布图像转换为 SSD1306 可用的格式:

# Render display with canvas device.display(canvas.convert(device.mode))        # (19)
sleep(refresh_secs)

在我们完成对 OLED 的探索之前,我想向您指出更多示例。Luma 库包含许多示例,涵盖了使用 OLED 显示器的许多方面。可以在进一步阅读中找到示例的链接。

OLED 显示器成本低廉,体积小,耗电量低,因此经常用于电池操作设备。如果您想探索树莓派的其他显示选项,您可能想调查一下可用的树莓派 TFT 显示器范围(只需在 eBay.com 或 Banggood.com 等网站上搜索该术语)。这些是树莓派的全彩迷你监视器,甚至还有触摸屏选项可用。

这样我们就结束了关于使用树莓派和 Python 进行照明和显示的覆盖范围。到目前为止,您所学到的知识将使您能够使用和正确供电自己的简单 LED 照明项目,并利用各种 OLED 显示器,用于那些您希望向用户显示文本和图形信息的项目。

为了完成本章的练习,接下来,我们将简要回顾脉宽调制(PWM),并看看我们如何使用它来产生声音。

使用蜂鸣器和 PWM 发出声音

在本章的最后一节中,我们将演示如何使用 PWM 制作简单的声音和音乐。我们的示例程序将在蜂鸣器上演奏一个音阶,并且我们将使用一种名为**Ring Tone Text Transfer Language (RTTTL)**的音乐记谱格式,这是由诺基亚在智能手机时代之前开发用于创建手机铃声的。随着我们的学习,我们可以使用一个简单的 Python 库来解析 RTTTL 音乐记谱,并将其音符转换为 PWM 频率和持续时间,然后可以用来关联蜂鸣器以创建可听的旋律。

要使用 PWM 发出声音,我们需要一种形式的扬声器,我们将使用所谓的被动蜂鸣器。蜂鸣器有两种基本形式:

  • 主动蜂鸣器:这些蜂鸣器包含一个内部振荡器,可以产生单一的音调。您只需要给主动蜂鸣器施加直流电压,它就会发出声音。

  • 被动蜂鸣器:这些蜂鸣器不包含任何内部智能来使它们工作,因此振荡必须由控制设备完成。这样做的好处是我们可以根据需要设置和更改音调,并且我们可以使用 PWM 来实现这一点。

现在我们了解了如何使用蜂鸣器发出声音,让我们继续创建我们的发声电路。

建立 RTTTL 电路

在本节中,我们将建立一个驱动被动蜂鸣器的电路。这个电路如图 8.8所示,与我们在第七章中介绍的 MOSFET 电路非常相似,只是这次连接了一个蜂鸣器作为负载:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8.8 - 蜂鸣器驱动电路原理图

我们将通过将组件放入面包板来开始我们的电路构建:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8.9 - 蜂鸣器驱动电路(1/2)的一部分

以下步骤编号与图 8.9中编号的黑色圆圈相匹配:

  1. 将 MOSFET 放在面包板上,注意组件与引脚的方向。如果您需要帮助识别 MOSFET 的引脚,请参阅第七章中的图 7.7打开和关闭设备

  2. 将 100kΩ电阻(R2)放入面包板中。这个电阻的一端与 MOSFET 的栅极(G)腿共享同一排。

  3. 将 1kΩ电阻(R1)放入面包板中。这个电阻的一端也与 MOSFET 的栅极(G)腿共享同一排。

  4. 将二极管放入面包板中,带有阴极腿(带子朝向的一端)指向面包板的一端。

  5. 将蜂鸣器的正极线连接到与二极管阴极腿共享的同一排中。

  6. 将蜂鸣器的负极线连接到一个空闲的面包板行。

现在我们已经放置了组件,让我们将它们连接起来:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8.10 – 蜂鸣器驱动电路(第二部分)

以下步骤编号与图 8.10中编号的黑色圆圈相匹配:

  1. 将左侧电源轨的负极连接到 1kΩ电阻(R2)。

  2. 将 MOSFET 的源腿(S)连接到左侧电源轨的负极。

  3. 将左侧电源轨的负极连接到树莓派上的 GND 引脚。

  4. 将 100kΩ电阻(R1)的末端连接到树莓派的 GPIO 12/PWM0 上。提醒一下,GPIO 12 在其替代功能中是通道 PWM0,是一个硬件 PWM 引脚。

  5. 将 MOSFET 的漏极(D)连接到二极管的阳极腿。

  6. 将二极管的阳极腿连接到蜂鸣器的负极线。

  7. 将蜂鸣器的正极线/二极管的阴极腿连接到右侧电源轨的正极。

  8. 连接左侧和右侧电源轨的负极。

  9. 将电源供应的正极输出连接到右侧电源轨的正极。

  10. 将电源供应的负极输出连接到右侧电源轨的负极。

现在您已经完成了这个电路搭建,我们将继续运行我们的 Python 示例,这将制作一些音乐!

运行 RTTTL 音乐示例

运行chapter08/passive_buzzer_rtttl.py文件中的代码,您的蜂鸣器将播放一个简单的音阶。

执行此操作的代码非常简单。在以下代码的第(1)行中,我们使用rtttl模块将 RTTTL 音乐乐谱解析为由频率和持续时间定义的一系列音符。我们的乐谱存储在rtttl_score变量中:

from rtttl import parse_rtttl
rtttl_score = parse_rtttl("Scale:d=4,o=4,b=125:8a,8b,        # (1)
    8c#,8d,8e,8f#,8g#,8f#,8e,8d,8c#,8b,8a")

接下来,在第(2)行,我们循环遍历rtttl_score中解析的音符,并提取频率和持续时间:

    for note in rtttl_score['notes']:                        # (2)
        frequency = int(note['frequency'])   duration = note['duration'] # Milliseconds
        pi.hardware_PWM(BUZZER_GPIO, frequency, duty_cycle)  # (3)
        sleep(duration/1000)                                 # (4)

在第(3)行,我们使用 PWM 在蜂鸣器的 GPIO 引脚上设置频率,并在第(4)行保持音符的持续时间,然后继续到下一个音符。

在第(3)行,请注意我们正在使用 PiGPIO 的hardware_PWM(),并且BUZZER_GPIO 必须 是一个硬件兼容的 PWM 引脚。 PiGPIO 的硬件定时 PWM(可用于任何 GPIO 引脚)不适用于音乐创作,因为它受限于一系列离散的频率。如果您需要 PWM 技术的复习,请重新阅读第五章,将您的树莓派连接到物理世界

使用 RTTTL 制作音乐听起来非常电子化,并且是资源有限的微控制器中的一种流行技术。但是,请记住,对于我们的树莓派来说,我们有足够的资源和内置硬件来播放丰富的媒体,如 MP3。

尝试在网络上搜索RTTTL 歌曲,您会找到许多歌曲、复古电脑游戏和电视电影主题的乐谱。

如果您想通过 Python 探索播放和控制 MP3,您会发现网络上有许多资源、教程和示例。不幸的是,有许多方法可以实现这个任务(包括 Raspbian OS 的不同版本之间的更改),因此有时在可靠地设置和配置您的 Raspberry Pi 和 Raspbian OS 时可能会有点棘手。如果您选择这条路线,我的建议是首先在命令行上探索播放 MP3 和控制音频(即更改音量)。一旦您有了稳定可靠的设置,然后再探索基于 Python 的方法。

摘要

在本章中,我们学习了如何使用 PWM 来设置 RGB LED 的颜色,以及独立的单个 RGB LED 需要三个专用的 GPIO 引脚来工作——分别用于红色、绿色和蓝色。然后,我们探索了另一种类型的 RGB LED,即 APA102,它是一种 2 线 SPI 可控设备,可以串联在一起创建 LED 灯带。接下来,我们学习了如何使用 OLED 显示器,创建了一个示例应用程序,显示了树莓派的 CPU 温度。最后,我们通过解析 RTTTL 音乐乐谱,使用 PWM 和被动蜂鸣器制作声音。

在本章中学到的知识将使您能够为自己的项目添加可视和可审计的反馈。您还将能够相对容易地将您的学习扩展到其他类型的显示器,因为我们在本章中使用的 Luma 库能够与 APA102 LED 灯带和 SSD1306 OLED 设备以外的其他显示器类型和型号一起工作。

在下一章中,我们将研究用于测量环境条件(包括温度、湿度和光照)的组件和技术。

问题

随着我们的结束,这里有一些问题供您测试对本章材料的了解。您将在书的评估部分找到答案:

  1. 你的 APA102 LED 灯带设置为显示所有 LED 为白色,但实际上所有 LED 看起来都是红色的。可能是什么问题?

  2. APA102 对 SPI 有什么限制?

  3. 当您使用逻辑电平转换器时,您的 APA102 无法工作,但当您直接连接到树莓派的 MOSI 和 SCK 引脚时(因此绕过逻辑电平转换器),它似乎可以工作。问题的一些可能原因是什么?

  4. 使用 Luma OLED 库在 OLED 显示器上创建和显示图像的基本过程是什么?

  5. 什么是 RTTTL?

进一步阅读

APA102 是一个很好的选择,可以开始学习较低级别的数据协议和通信。在查看 APA102 数据协议的数据表后(请参见本章开头的技术要求下的链接),下一个逻辑步骤是查看一些较低级别的代码。 PiGPIO 的 APA102 示例是一个起点,但您会在 PyPi.org 上找到其他示例:

Luma 系列库提供了许多高级模块,用于将常见显示器与树莓派集成,超出了本章中涵盖的 APA102 和 SSD1306 OLED。此外,Luma 还包含大量的示例:

Luma 使用 PIL(Python Imaging Library)/Pillow 兼容的 API 来绘制和操作显示器。我们在我们的 OLED 示例中特别使用了ImageDraw。您可以在以下链接找到 PIL API 文档:

如果您想进一步探索 RTTTL 格式,可以从其维基百科网站开始:

第九章:测量温度、湿度和光照水平

在上一章中,我们探讨了使用 RGB LED 制作颜色的两种方法 - 使用普通的 RGB LED 和可寻址的 APA102 RGB LED 条。我们还学习了如何使用简单的 OLED 显示屏,以及如何使用 PWM 来使用无源蜂鸣器播放音乐。

在本章中,我们将研究一些常见的组件和电路,用于收集环境数据,包括温度、湿度、光线暗或亮以及如何检测湿度。

学习的电路和代码示例将对构建和实验自己的环境监测项目非常有用。这些电路可以被视为测量环境条件的输入或传感器电路。例如,您可以结合第七章中的电路思想和示例,打开和关闭东西,当土壤干燥时打开水泵浇水,或者在天黑时打开低电压 LED 灯。事实上,在第十三章中,我们有一个可视化平台的示例,IoT 可视化和自动化平台,我们将使用本章中的一个电路来捕获、记录和可视化历史温度和湿度数据!

此外,在本章中,我们将看到模拟电子学和相关概念的实际示例,例如电压分压器,在第六章中学到的。

以下是本章的内容:

  • 测量温度和湿度

  • 检测光线

  • 检测湿度

技术要求

要执行本章的练习,您需要以下内容:

  • 树莓派 4 型 B

  • Raspbian OS Buster(带桌面和推荐软件)

  • 最低 Python 版本 3.5

这些要求是本书中代码示例的基础。可以合理地期望代码示例在树莓派 3 型 B 或不同版本的 Raspbian OS 上无需修改即可工作,只要您的 Python 版本是 3.5 或更高。

您将在github.com/PacktPublishing/Practical-Python-Programming-for-IoT的 GitHub 存储库的chapter09文件夹中找到本章的源代码。

您需要在终端中执行以下命令来设置虚拟环境并安装本章代码所需的 Python 库:

$ cd chapter09              # Change into this chapter's folder
$ python3 -m venv venv      # Create Python Virtual Environment
$ source venv/bin/activate  # Activate Python Virtual Environment
(venv) $ pip install pip --upgrade        # Upgrade pip
(venv) $ pip install -r requirements.txt  # Install dependent packages

以下依赖项从requirements.txt中安装:

本章练习所需的电子元件如下:

  • 1 x DHT11(较低精度)或 DHT22(较高精度)温度和湿度传感器

  • 1 x LDR(光敏电阻,也称为光电池或光敏电阻)

  • 电阻:

  • 1 x 200Ω 电阻

  • 1 x 10kΩ 电阻

  • 1 x 1kΩ 电阻

  • 1 x 100kΩ 电阻

  • 1 x 红色 LED

  • 1 x ADS1115 模数转换器模块

  • 外部电源 - 至少需要一个 3.3V/5V 面包板可安装电源供应器。

测量温度和湿度

测量温度和相关环境属性是一个常见的任务,有许多不同类型的传感器可用于测量这些属性,包括热敏电阻(温度相关电阻)、通过 SPI 和 I2C 连接的复杂分支模块,以及 DHT11 或 DHT22 传感器等传感器种类,我们将在示例中使用。

当涉及准确性、响应时间(从中我们可以快速获取数据的速度)和成本时,所有传感器都有其相对的优势和劣势。

正如图 9.1所示,DHT 传感器价格便宜,耐用且易于使用:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9.1 – DHT11 和 DHT22 温度和湿度传感器

DHT11 是一种非常常见的低成本传感器。DHT22 是它的高精度表亲。两者都是引脚兼容的,并且适用于我们的示例。如前图所示,这些传感器的引脚分配如下:

  • Vcc:3 到 5 伏电源来源

  • Data:连接到 GPIO 引脚的数据引脚

  • NC:未连接,表示未使用此引脚

  • GND:连接到地面

以下是 DHT11 和 DHT22 之间的核心相似之处和不同之处:

DHT 11DHT 22
工作电压3 到 5 伏3 到 5 伏
工作电流µA(微安)µA(微安)
温度范围0 到 50 摄氏度- 40 到 125 摄氏度
温度精度±2%±0.5%
湿度范围20 - 80%0 - 100%
湿度精度±5%±2%到 5%
最大采样率更快 – 每秒一次(1Hz)更慢 – 每 2 秒一次(0.5Hz)

如前所述,DHT11 和 DHT22 传感器的引脚兼容。它们只在测量精度和范围上有所不同。任一传感器都适用于我们即将创建的用于测量温度和湿度的电路。

创建 DHT11/DHT22 电路

我们将从在我们的面包板上创建的图 9.2中的电路开始:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9.2 – DHT 传感器原理图

以下是我们即将构建的电路的面包板布局:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9.3 – DHT 传感器电路

以下是要遵循的步骤,与图 9.3中编号的黑色圆圈相匹配:

  1. 将您的 DHT11 或 DHT22 传感器放入面包板中。

  2. 将 10kΩ电阻(R1)放入面包板中。电阻的一端与 DHT 传感器的 DATA 引脚共用一行。我们将在完成电路构建后讨论这个电阻以及为什么它被标记为可选的图 9.2

  3. 将树莓派上的 3.3 伏引脚连接到电源轨的正极。

  4. 将 10kΩ电阻(R1)连接到正电源轨。

  5. 将 DHT Vcc 引脚连接到正电源轨。

  6. 将树莓派上的 GND 引脚连接到负电源轨。

  7. 将 DHT 传感器的 GND 引脚连接到负电源轨。

  8. 最后,将 DHT 传感器的 DATA 引脚连接到树莓派上的 GPIO 21。

现在我们的 DHT 传感器电路已经完成。

在我们的电路中,Vcc 连接到 3.3 伏,这使得 DHT 数据引脚以这个电压工作。DHT11 和 DHT22 额定为 5 伏;但是,如果您将 Vcc 连接到 5 伏,数据引脚就会成为一个 5 伏逻辑引脚,这对于树莓派的 3.3 伏 GPIO 引脚来说是不安全的。

10kΩ上拉电阻是可选的,因为我们使用的 DHT 软件库已经默认启用了树莓派的内部上拉电阻。我在电路原理图中包括了上拉电阻,因为它包含在许多 DHT11/DHT22 数据表的电路示例中。如果您需要关于上拉电阻的复习,请重新阅读第六章,软件工程师的电子学 101

在我们的电路和 DHT11/DHT22 中,标有NC的引脚表示未连接。 NC 是一个常用的缩写,用于表示传感器、IC 或元件的引脚或端子没有内部连接到任何东西。但是,当我们处理开关(包括继电器)时,标有 NC 的元件引脚或端子表示通常关闭连接路径…因此,始终根据您正在查看的元件的上下文来解释 NC。

一旦您创建了电路,我们就准备好运行和探索代码来测量温度和湿度。

运行和探索 DHT11/DHT22 代码

运行chapter09/dht_measure.py文件中的代码,测量的温度和湿度将被打印到您的终端,类似于以下内容:

(venv) python DHT_Measure.py
{'temp_c': 21, 'temp_f': 69.8, 'humidity': 31, 'valid': True}

在这里,我们有以下内容:

  • temp_c是摄氏度温度。

  • temp_f是华氏度温度。

  • humidity是相对湿度百分比。

  • valid表示通过内部传感器校验和检查是否认为读数有效。读数中value == False的必须被放弃。

源文件中的代码简洁,并在此处完全复制。

在第 1 行,我们导入 DHT 传感器库,并在第 2 行实例化它。更新该行以匹配您使用的 DHT11 或 DHT22 传感器:

from pigpio_dht import DHT11, DHT22   # (1)

SENSOR_GPIO = 21 
sensor = DHT11(SENSOR_GPIO)           # (2)
#sensor = DHT22(SENSOR_GPIO)

result = sensor.read(retries=2)       # (3)
print(result)

result = sensor.sample(samples=5)     # (4)
print(result)

在第 3 和第 4 行,我们使用pigpio-dht库从传感器请求温度和湿度测量。对read()的调用将查询传感器的测量,并在测量结果为valid == False时重试retries次。另一种测量方法是sample()方法,它将获取温度和湿度的多个单独样本并返回归一化的测量值。

sample()的优势,特别是对于精度较低的 DHT11 传感器,是温度和湿度读数更一致,因为异常值读数(随机尖峰)被移除;然而,它显著增加了读取测量所需的时间-请参考本节开头表格中的最大采样率行。

例如,对于最大采样率为 1 秒的 DHT11,对于 5 个样本,sample(samples=5)调用将花费大约1 秒 x 5 个样本 = 5 秒才能返回,而具有 2 秒采样率的 DHT22 将花费约 10 秒。

DHT11 和 DHT22 是引脚兼容的;然而,由于每个传感器编码其数据的方式不同,它们在软件上不兼容。例如,使用 DHT11 库的 DHT22 传感器将生成不准确的结果(这将是非常明显的-例如,说您的房间温度为 650+摄氏度!)

多么简单!DHT 系列是流行的低成本传感器,可以测量温度和湿度。对于那些需要进行更快速读数的情况,或者需要将传感器安装在恶劣环境中,例如水中或户外,直接暴露在外部环境中,您肯定能找到适合您需求的传感器。

以下是其他温度(和类似环境)传感器连接到树莓派的快速概述:

  • 热敏电阻是温度敏感电阻,非常小,非常适合狭小空间,并且您可以获得密封包装以供室外和液体使用。您可以将它们与电压分压电路一起使用(类似于我们将在下一节中介绍的光敏电阻LDR))。

  • 有许多种类的 I2C 和 SPI 传感器可用于快速查询,可能还具有其他额外的传感器,例如气压传感器。这些模块通常较大,可能最好不要直接暴露在外部环境中。

  • 1-wire温度传感器也是紧凑且易于密封的,其优势在于可以有长电线(100 米以上)。

通过这一部分,我们结束了关于测量温度和湿度的介绍。许多环境监测项目需要您测量温度和湿度,使用树莓派和 DHT11 或 DHT22 是一种简单且具有成本效益的方法。我们将在第十三章中再次讨论我们的 DHT11/22 电路,IoT 可视化和自动化平台,在那里我们将集成该传感器到 IoT 平台以收集和监测温度和湿度。

现在我们已经探讨了温度传感器,让我们学习如何检测光线。

检测光线

轻松实现检测光线的存在或不存在,使用一种特殊类型的电阻器,称为LDR。 LDR 是一种低成本的光传感器,我们可以在许多应用中找到它们,从光控开关和灯到在黑暗时调暗闹钟显示屏的电路的一部分,再到现金箱和保险柜上的警报电路。

您可能也会发现 LDR 被称为光敏电阻或光电池。

以下图显示了典型的 LDR 组件,以及几种 LDR 原理图符号。如果您仔细观察这些符号,您会注意到它们是带有向内箭头的电阻符号。您可以将这些箭头看作代表光照在电阻上:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9.4 - 一个物理 LDR 组件和各种原理图符号

LDR 随其检测到的相对光照变化其电阻。如果您将万用表的端子置于 LDR 上的欧姆模式下,您会发现(大约几秒钟后)以下情况:

  • 当 LDR 处于黑暗中(例如,如果您把它遮住),其电阻通常会测量许多兆欧姆。

  • 在一个正常照明的房间(例如,桌子上的吊灯亮着),LDR 的电阻将以千欧姆计。

  • 当 LDR 处于强光(直射阳光或照射手电筒),其电阻将测量几百欧姆或更低。

这为我们提供了明显的区域,使得我们可以确定光的存在或不存在。通过校准和微调,我们可以轻松地确定在黑暗和光之间的一个点,我们可以用它来触发一个事件。例如,您可以使用我们将在下一步创建的 LDR 电路来以编程方式控制我们在第七章中创建的开关电路。

LDR 只能很好地测量相对光照水平 - 光的存在或不存在。如果您想要绝对的测量,比如光照水平,甚至是检测颜色,那么有一系列的 I2C 或 SPI 断开模块形式的 IC 可以实现这一点。

利用这种基本理解,我们将构建我们的 LDR 电路来检测光线。

创建 LDR 光检测电路

正如讨论的那样,LDR 随其检测到的相对光照变化其电阻。为了在我们的树莓派上检测变化的电阻,我们需要采取一些在以前章节中讨论过的步骤:

  • 我们需要将变化的电阻转换为变化的电压,因为我们的树莓派 GPIO 引脚工作在电压上,而不是电阻。这是欧姆定律和电压分压电路的应用,我们在第六章中学习过,软件工程师的电子学 101

  • 我们的树莓派 GPIO 引脚只能读取数字信号 - 例如高(~3.3 伏)或低(~0 伏)信号。为了测量变化的电压,我们可以连接一个模数转换器ADC),例如 ADS1115。我们在第五章中介绍了 ADS1115 和相应的 Python 代码,将您的树莓派连接到物理世界

我们将在您的面包板上创建图 9.5中所示的电路。当检测到一定程度的黑暗时,这个电路和附带的代码将点亮 LED:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9.5 - 带有 ADS1115 ADC 原理图的 LDR 电路

我们将分两部分构建电路。对于第一部分,我们将把组件放在面包板上,如图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9.6 - 带有 ADS1115 ADC 电路的 LDR 电路(第一部分,共 2 部分)

以下是要遵循的步骤,这些步骤与图 9.6中编号的黑色圆圈相匹配:

  1. 将 LDR 放在面包板上。

  2. 将 10kΩ电阻(R1)放在面包板上。这个电阻的一端与 LDR 的一端共用同一行。

  3. 将 ADS1115 ADC 放在面包板上。

  4. 将一个 200kΩ电阻(R2)放在面包板上。

  5. 将 LED 放在面包板上,特别注意将 LED 的阴极引脚连接到与 200kΩ电阻的一根引脚共享的同一行。

现在我们已经放置了我们的组件,我们将把它们连接起来:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9.7 - 带 ADS1115 ADC 电路的 LDR 电路(第二部分)

以下是要遵循的步骤;这次它们与图 9.7中编号的黑色圆圈相匹配:

  1. 将正电源轨道连接到 LDR。

  2. 将树莓派的 3.3 伏引脚连接到电源轨道的正电源轨道。

  3. 将树莓派的 GND 引脚连接到负电源轨道的负端。

  4. 将负电源轨道连接到 10kΩ电阻(R1)。

  5. 将 ADS1115 的 Vdd 端连接到正电源轨道。

  6. 将 ADS1115 的 GND 端连接到负电源轨道。

  7. 将 LDR 和 10kΩ电阻(R1)的交接处连接到 ADS1115 的 A0 端口(你能看到 LDR 和电阻如何创建电压分压器,变化的电压输出现在连接到 A0 吗?)。

  8. 将树莓派的 SDA 引脚连接到 ADS1115 的 SDA 端子。

  9. 将树莓派的 SCL 引脚连接到 ADS1115 的 SCL 端子。

  10. 将负电源轨道连接到 200kΩ电阻。

  11. 将 LED 的阳极引脚连接到树莓派的 GPIO 21 引脚。

希望你能看到由 LDR 和 10kΩ电阻 R1 形成的电压分压器。我们将在本章后面的LDR 配置摘要部分介绍 10kΩ电阻背后的原因。

由于 LDR 检测到的光的变化,它的电阻也会变化。这样做的效果是改变 R1(固定电阻)和 LDR 的电阻(变化电阻)的相对比例,从而改变在 LDR 和 R1 交叉处测量到的电压(这就是我们的 ADS1115 的 A(模拟输入)连接的地方,用于测量这个变化的电压)。

不要将 LED 放得太靠近 LDR。当 LED 发光时,LDR 可以检测到 LED 发出的光,这可能会干扰代码中的 LDR 读数。

现在您已经创建了 LDR 电路,我们将校准并运行我们的示例代码。

运行 LDR 示例代码

我们将要运行两个程序:

  • chapter09/ldr_ads1115_calibrate.py,这将帮助我们校准 LDR 读数

  • chapter09/ldr_ads1115.py,它监测光照水平,并在光线低于可配置水平时打开 LED

首先,我们应该检查 ADS1115 是否连接正确,并且树莓派能够识别它。在终端中运行i2cdetect命令。如果您的输出不包括一个数字(例如48),请验证您的接线是否正确。

$ i2cdetect -y 1
# ... truncated ...
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
# ... truncated ...

我们首先介绍了 ADS1115 模数转换器和第五章中的i2cdetect实用程序,将树莓派连接到物理世界

让我们从校准程序开始运行示例:

  1. 运行chapter09/ldr_ads1115_calibrate.py文件中的代码,并按照出现在终端上的说明进行操作,如下所示:

  2. “将 LDR 放在光线下并按 Enter 键”:在这个练习中使用环境光,并注意不要在 LDR 上投下阴影。在构建应用程序时,您将希望使用对您的目的有意义的光源,例如直射阳光、室内光或者用强光照射 LDR。

  3. “将 LDR 放在黑暗中并按 Enter 键”:我建议完全用黑布或杯子覆盖 LDR。用手指并不总是理想的,因为敏感的 LDR 可能仍然能够通过手指检测到一定程度的光:

(venv) python ldr_ads1115_calibrate.py Place LDR in the light and press Enter
Please wait...

Place LDR in dark and press Enter
Please wait...

File ldr_calibration_config.py created with:
# This file was automatically created by ldr_ads1115_calibrate.py
# Number of samples: 100
MIN_VOLTS = 0.6313
MAX_VOLTS = 3.2356 

校准程序从 ADS1115 在黑暗和光线条件下获取一定数量的样本(默认为100),并计算平均读数。接下来,程序将结果(也显示在终端中)写入ldr_calibration_config.py文件。这是我们示例的 Python 源文件,导入到我们实际的 LDR 和 LED 示例中,我们将在下一步中看到。

  1. 运行chapter09/ldr_ads1115.py文件中的程序,并观察终端上显示的输出,其中显示了 ADS1115 读取的电压:
LDR Reading volts=0.502, trigger at 0.9061 +/- 0.25, triggered=False

希望输出应该是triggered = False,LED 应该是关闭的。如果不是这种情况,请尝试重复步骤 1中的校准过程,或者继续阅读,您将发现如何在代码中调整触发点。

  1. 逐渐将手靠近 LDR,限制到达它的光线量。当您移动手时,您会注意到终端上的voltage读数发生变化,并且在某个电压水平时,将达到触发点,LED 将点亮:
LDR Reading volts=1.116, trigger at 0.9061 +/- 0.25, triggered=False
LDR Reading volts=1.569, trigger at 0.9061 +/- 0.25, triggered=True

您所见到的是电压分压器的功能,随着 LDR 的电阻随光线变化而变化。然后,ADS1115 读取这个电压。

您可能已经注意到,所产生的电压不是我们在第五章中使用 ADS1115 和电位计时产生的 0 伏特到 3.3 伏特的完整范围。我们受限范围是我们固定电阻(R1)和变阻(LDR)电路的副作用和限制,它无法变化到达~0 或~3.3 伏特所需的极端电阻。您将在电压分压器电路中遇到这种限制,因为它们将按设计包括固定电阻值。相比之下,我们的电位计是两个可变电阻器,创建电压分压器,我们可以有效地将分压器的一侧归零(接近 0Ω),这取决于我们将电位计的拨盘转向哪个方向,从而使我们能够接近 0 伏特和 3.3 伏特。

既然我们已经看到这段代码运行,让我们看看它是如何工作的。

LDR 代码演练

chapter09/ldr_ads1115_calibrate.pychapter09/ldr_ads1115_calibrate.py中的大部分代码是设置和配置 ADS1115 以及使用 PiGPIO 设置 LED 的样板代码。我们不会在这里重复常见的代码。如果您需要复习与 ADS1115 相关的代码,请查看第五章中的练习,“将您的树莓派连接到物理世界”。

让我们看看使我们的 LDR 工作的 Python 代码。

在第 1 行,我们看到我们正在导入之前使用校准程序创建的ldr_calibration_config.py文件。

接下来,在第 2 行,我们将校准值分配给LIGHT_VOLTS(LDR 在光线下被 ADS1115 检测到的电压)和DARK_VOLTS(您遮住 LDR 时检测到的电压)变量:

import ldr_calibration_config as calibration                   # (1)

# ... truncated ...

LIGHT_VOLTS = calibration.MAX_VOLTS                            # (2)
DARK_VOLTS = calibration.MIN_VOLTS

TRIGGER_VOLTS = LIGHT_VOLTS - ((LIGHT_VOLTS - DARK_VOLTS) / 2) # (3)
TRIGGER_BUFFER = 0.25                                          # (4)

在第 3 行,我们创建一个触发点。这是我们以后将在代码中使用的电压点来开启和关闭 LED。

您可以调整和实验TRIGGER_VOLTS的公式或值,以改变导致代码触发的照明条件。

第 4 行的TRIGGER_BUFFER变量用于在我们的触发器中创建缓冲区或滞后,电子术语中更为人所知的是滞后。这个值创建了一个小的窗口范围,检测到的电压可以在不引起触发或取消触发事件的情况下变化。如果没有这种滞后,触发器(和 LED)将在检测到的电压围绕TRIGGER_VOLTS触发电压振荡时快速打开和关闭。

要亲身体验这种效果,请将TRIGGER_BUFFER = 0,您会发现当您将手移动到 LDR 上方时,LED 对开和关非常敏感,并且在某一点甚至可能出现闪烁。当您增加TRIGGER_BUFFER的值时,您会注意到需要移动手才能使 LED 开关变得更加明显。

接下来,在第 5 行,我们来到了确定触发点是否已达到的代码。update_trigger()函数将 ADS1115 检测到的电压与调整为TRIGGER_BUFFERTRIGGER_VOLTS值进行比较,并在触发点被触发时更新triggered全局变量:

   triggered = False # (5)

   def update_trigger(volts):
       global triggered

       if triggered and volts > TRIGGER_VOLTS + TRIGGER_BUFFER:
           triggered = False
       elif not triggered and volts < TRIGGER_VOLTS - TRIGGER_BUFFER:
           triggered = True

在源文件的末尾附近,我们在第 6 行有一个while循环。我们正在读取 ADS1115 检测到的电压,更新全局triggered变量,然后将结果打印到终端:


trigger_text = "{:0.4f} +/- {}".format(TRIGGER_VOLTS, TRIGGER_BUFFER) 

  try:
      while True:                                                  # (6)
          volts = analog_channel.voltage

          update_trigger(volts)

          output = "LDR Reading volts={:>5.3f}, trigger at {}, triggered={}"
                   .format(volts, trigger_text, triggered)
          print(output)

          pi.write(LED_GPIO, triggered)                           # (7)
          sleep(0.05)

最后,在第 7 行,根据triggered的值切换 LED 的开关状态。

现在我们已经看到了如何使用我们的 LDR 电路和 Python 代码检测光线,我想简要介绍一下选择 LDR 电路的串联电阻的方法。

LDR 配置摘要

在使用 LDR 电路和代码时,您可能已经意识到有一些可调参数会影响电路和代码的工作方式,您是否想知道为什么我们使用了 10kΩ电阻?

没有两个 LDR 会给出相同的电阻-光线测量值,它们的电阻-光线范围也不是线性的。这意味着您的 LDR 以及您计划在其中使用它的照明条件可能会影响适当的固定电阻值。

以下是选择适当固定电阻的大致指南:

  • 如果您希望 LDR 在较暗的条件下更敏感,请使用更高值的电阻(例如,尝试 100kΩ)。

  • 如果您希望您的 LDR 在更明亮的条件下更敏感,请使用更低值的电阻(例如,尝试 1kΩ)。

请记住,这些只是建议,所以请随时根据自己的需求尝试不同的电阻。此外,每当更改固定电阻的值时,请重新运行校准代码。

还有一个名为 Axel Benz 的公式,可用于计算诸如 LDR 之类的模拟元件的参考电阻值。该公式表达如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

公式中的参数如下:

  • R[ref]是固定电阻 R1 的值。

  • R[max]是 LDR 的最大电阻(在黑暗中)。典型值可能为 10Ω。

  • R[min]是 LDR 的最小电阻(在强光下)。典型值可能为 10MΩ。

因此,如果我们使用典型值,我们得到了用于 R1 的 10kΩ值:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用万用表测量 LDR 的极端值,并查看您计算出的值。如果您的测量结果与典型的 10kΩ相差甚远,不要感到惊讶。考虑到我们使用的 LDR 欧姆范围约为10Ω至10,000,000Ω,差异可能仍然只是百分之一的一小部分!

我们之前在代码中看到两个变量影响我们的代码触发:

  • 更改TRIGGER_VOLTS的值以更改代码触发的点 - 例如,打开或关闭 LED。

  • 更改TRIGGER_BUFFER的值以改变触发器对光线变化的敏感度。

最后,请记住,LDR 以对数方式检测光线,而不是线性方式 - 例如,当您逐渐将手或物体放在 LDR 上以限制光线时,LDR 报告的电压不一定会与您限制的光线量成比例地变化。这就是为什么我们需要更改固定电阻值,如果我们希望 LDR 在更暗或更亮的条件下更敏感。

您可以尝试用可变电阻替换固定电阻 R1(例如,用 20kΩ可变电阻替换固定的 10kΩ,设置为 10kΩ。我们选择 20kΩ是因为我们可以将其调整到 10kΩ以上和以下。10kΩ可变电阻只能让我们降低电阻)。在代码校准为 10kΩ并定义了代码中的触发点后,您可以通过调整可变电阻来微调触发点。

这结束了我们对 LDR 的讨论。我们已经看到了如何与 ADS1115 ADC 一起构建简单的 LDR 电路,以及如何使用 Python 检测光线。您可以将这个简单的电路和配套的代码用于任何需要检测光线或黑暗的项目,例如光敏开关。

接下来,我们将学习如何检测湿度。

检测湿度

猜猜看……我们已经完成了检测湿度的繁重工作!这只是 LDR 电路和代码的另一个应用,只是我们用探针替换了 LDR。

对于这个练习,您可以使用两根带两端剥离的导线制作一组探针,并将它们安装在 LDR 的位置,如图 9.8所示。这与我们在上一节中构建的电路相同,并在图 9.7中显示,只是这一次,我们用两根导线替换了 LDR。现在让我们做出这个小改变:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9.8 - 湿度检测电路

以下是要遵循的步骤,这些步骤与图 9.8中编号的黑色圆圈相匹配:

  1. 从面包板上取下 LDR。

  2. 将一根带两端剥离的导线放入面包板的一行,该行先前连接到 LDR 的一条腿(在插图中,这根新导线连接回树莓派上的 3.3 伏特)。

  3. 将另一根带两端剥离的导线放入面包板的一行,该行先前连接了 LDR 的另一条腿(在插图中,这根新导线连接到与 10kΩ电阻(R1)共享的行)。

这个小改变 - 用裸导线替换 LDR - 将我们的电路变成了一个基本的湿度检测电路。让我们试试这个电路!

chapter09文件夹中,您会找到两个文件,名为moisture_calibrate.pymoisture_ads1115.py。这些文件与我们在上一节中使用的 LDR 文件集几乎相同,只是我已经将Light/Dark的措辞和变量名称更改为Wet/Dry。各个文件中的核心区别由注释标出。

鉴于相似性,我们不会详细介绍这些源文件和湿度电路;但是,供参考,以下是要遵循的步骤:

  1. 确保探针是干燥的。

  2. 运行moisture_calibrate.py并按照说明执行电压校准。

  3. 运行moisture_ads1115.py

  4. 检查终端输出是否指示trigger=False(代码在湿润条件下触发)。

  5. 将探针放入一杯水中(是的,这样做是安全的),观察终端上的电压读数的变化(如果探针意外短路也没关系,不会造成任何损坏)。

  6. 将探针浸入水中,检查终端输出是否显示trigger=True(探针湿润状态)。

  7. 如果触发器仍然为True,则需要在代码中调整TRIGGER_VOLTS的值。

您还可以将探针放入干燥的土壤中,并观察电压读数。慢慢地浇湿土壤,电压读数应该会改变。现在我们有了一个程序的基础,可以告诉您何时需要给植物浇水!

那么,为什么会起作用呢?很简单 - 水是电的导体,在我们的两个探针之间的行为就像一个电阻。

世界各地和不同来源的水 - 例如自来水与瓶装水 - 可能导电性不同。这意味着如果您的电路对 10kΩ电阻的反应不佳,您可能需要调整 R1 电阻的值。此外,您还可以尝试调整探针导线之间的距离和它们的大小。

我们将通过比较我们刚刚创建的内容与您可以购买的现成湿度检测器来结束我们对湿度检测的讨论。

比较检测选项

我们简单的电路和电线探针与您可以在 eBay 等零售网站上找到的水/湿度检测模块相比如何?这些产品通常包含某种探针,以及一个小型电子模块。这里显示了其中一个模块的图片,以及一些探针:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9.9 - 湿度检测模块和探针

这三个探针每个都有两个端子,只是电路板上的裸露铜线,类似于我们在图 9.8 中看到的裸露电线。一个关键的区别是这些探针暴露了更大的表面积,因此更敏感。此外,它们也可能比两根剥离的电线更不容易腐蚀(至少在短期到中期内)。

您可以直接将这些探针连接到我们在图 9.8 中显示的电路中的裸露电线,以扩展和增强电路的检测能力!

让我们讨论一下电子模块(在右侧的图 9.9 中放大并标记)。

除了 Vcc/Vin 和 GND 端子外,这些模块通常(不总是,但通常)还有两个输出端子或引脚,如下所示:

  • 模拟输出(在我们的示例中,这被标记为A

  • 一个数字输出(标记为S

请注意,我不会提供如何将先前显示的模块连接到您的树莓派的说明,而是会保持讨论的一般性。这些模块有许多变种,虽然它们的操作类似,但它们的接线方式可能有所不同。在本书的这个阶段,如果您对模拟与数字的基本原理、电压分压器和 ADC 感到满意,您就有了理解并对如何将这些模块接口到树莓派等设备做出明智决定的所有必要知识。一个很好的起点将是您模块的数据表或购买地点提供的任何信息。

模拟输出是一个通过到探针的通路。您可以直接将其连接到电压分压电路中,并使用诸如 ADS1115 之类的 ADC 测量可变电压 - 这正是我们在图 9.8 中创建的确切情景。如果您使用模拟通过,您将绕过模块上的所有其他电路(这就是为什么您可以直接将探针连接到我们的示例电路中)。

数字输出是使用模块电路的部分。典型的模块电路至少包括一个称为电压比较器的集成电路,一个固定电阻和一个可变电阻,这是一个触发点调节。固定电阻与探针一起创建一个电压分压器。电压比较器负责监视电压分压器上的电压,并在由调节触发的点上触发数字输出(例如,从“低”到“高”的转换)。图 9.9 中可以看到一个调节可变电阻的示例。

如果这种电压比较和触发听起来有点熟悉,那么您是正确的。这个模块及其电压比较器和可配置触发点原则上是我们创建的 LDR 和湿度电路以及 Python 代码的纯电子版本。是的,您可以在这些模块中使用 LDR 而不是探针!

因此,总之,什么更好 - 使用 ADS1115 和电压分压器类型的电路,如图 9.8 中所示,还是使用图 9.9 中所示的模块?没有一个最佳答案;然而,以下几点将帮助您做出自己的决定:

  • 使用类似图 9.8中的电路是一种模拟方法。传感器检测到的原始电压直接传递给您的 Raspberry Pi。这种方法的一个简单优点是您可以完全控制代码中的触发点。例如,您可以远程调整触发点从网页。这种方法的缺点是您需要一个涉及 ADS1115 和电压分压器的更复杂的电路。

  • 使用类似图 9.9中所示的模块作为数字方法,可以促进更简单的接口电路连接到您的 Raspberry Pi,只要模块的数字输出为 3.3 伏特。但缺点是您必须可以物理访问模块和调整电位器来更改触发点。

总结

在本章中,我们学习了如何使用常见的 DHT11 和/或 DHT22 传感器测量温度和湿度。我们还研究了如何使用 LDR 来检测光线,这使我们能够更详细地探索电压分压电路和 ADC。最后,我们改装了我们的 LDR 电路,以便我们可以检测湿度。

本章涵盖的示例电路和代码提供了使用现成传感器和简单电路测量环境条件的实际示例。您对这些传感器和电路的理解现在意味着您可以为自己的环境监测项目调整这些示例,包括将它们作为输入触发器与 Python 一起用于控制其他电路。

我们还看到了电压分压电路的新实际应用,以及它们在模拟电路中如何将可变电阻转换为可变电压,用于 ADC。这些示例和您对它们的理解代表了您可以适应并用于其他基于模拟的传感器的重要技能。

在下一章中,我们将学习如何更深入地控制直流电机,并学习如何控制舵机。

问题

在我们结束时,这里有一些问题供您测试本章材料的知识。您将在书的评估部分找到答案:

  1. 您能列举 DHT11 和 DHT22 温湿度传感器之间的两个区别吗?

  2. 为什么在我们的 DHT11/22 电路中外部 10kΩ上拉电阻是可选的?

  3. 描述 LDR 用于测量光线的基本电子原理。

  4. 您如何使 LDR 对特定光照条件更或者更不敏感?

  5. 您已经创建了一个 LDR 电路并校准了 Python 代码。现在,您更改了 LDR,并发现电压读数和代码中的触发点行为略有不同。为什么?

  6. 为什么将两根导线放入水中,配合电压分压器和 ADS1115 电路,可以作为基本的湿度检测器?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值