## 防盗报警器 使用运动传感器检测入侵者并通过闪光灯和警报器发出警报 微控制器的另一个实际用途是在警报系统中。从让您在早上起床的闹钟到火警、防盗警报,甚至核电站出现问题时发出的警报,微控制器都有助于确保我们所有人的安全。 在本章中,您将构建自己的防盗警报器,其工作方式与商业版本完全相同:一个特殊的运动传感器会在任何不该进入房间的人进入时监视他们,并在他们不应该出现时闪烁拉响警报器以提醒人们注意入侵。无论您是在保护银行金库,还是只是想让间谍兄弟姐妹远离您的房间,防盗警报器肯定会派上用场。 对于这个项目,你需要你的 Pico;面包板;任何颜色的 LED;一个 330 Ω 电阻器;一个有源压电蜂鸣器;一个或多个 HC-SR501 被动红外 (PIR) 传感器;以及一系列公对公 (M2M) 和公对母 (M2F) 跳线。您还需要一根微型 USB 电缆,并将 Pico 连接到 Raspberry Pi 或其他运行 Thonny MicroPython IDE 的计算机。 HC-SR501 PIR 传感器 在前面的章节中,您一直在使用按钮开关形式的简单输入组件。这一次,您将使用称为被动红外传感器或 PIR 的专用输入。有数百种不同的 PIR 传感器可用; HC-SR501 成本低、性能高,可与您的 Pico 完美配合。 如果您选择了不同型号的传感器,请查看其文档以仔细检查哪些引脚是哪些;还要确保它在 3V3 逻辑电平下运行,就像您的 Pico – 如果您连接使用更高电压的传感器,例如 12 V 传感器,它将损坏您的 Pico,无法修复。一些传感器可能需要一个小开关或跳线来改变逻辑电压电平;这将在文档中注明。 被动红外传感器旨在检测运动,尤其是人和其他生物的运动。它的工作原理有点像照相机,但它不是捕捉可见光,而是将活体发出的热量作为红外线辐射。它被称为被动红外传感器,而不是主动红外传感器,因为就像相机传感器一样,它本身不会发出任何红外信号。 实际的传感器被埋在塑料透镜下面,通常形状像一个半球。镜头在技术上不是传感器工作所必需的,但可以提供更广阔的视野 (FOV);如果没有镜头,PIR 传感器将只能看到传感器正前方非常窄的角度内的运动。该镜头用于从更广的角度吸收红外线,这意味着单个 PIR 传感器能够观察大部分房间的运动。 在商业防盗报警系统中,PIR 传感器只是使用的传感器之一;其他包括可以判断窗户何时被砸碎的碎玻璃传感器、监控门是打开还是关闭的磁传感器、可以捕捉窃贼脚步声的声学传感器以及用于判断锁是否被强行打开的振动传感器.不过,一个简单的 PIR 传感器通常足以用于安全性较低的区域——想想接待室,而不是银行金库。 现在拿起您的 HC-SR501 传感器并查看它。首先要注意的是它有自己的电路板,很像你的 Pico——只是更小。除了传感器和镜头,还有其他几个组件:驱动传感器的小型黑色集成电路 (IC)、一些电容器和微型表面贴装电阻器。您可能还会看到一些小电位器,您可以用螺丝刀拧动它们以调整传感器的灵敏度以及触发时保持活动的时间;暂时保留这些。 您还会看到三个公针,与 Pico 底部的针完全相同。但是,您不能将它们直接推入面包板,因为板上的组件会妨碍您。相反,使用三根公对母 (M2F) 跳线并将母端插入 HC-SR501 上的引脚。 接下来,取公端并将它们连接到面包板和 Pico。 你需要 在将传感器连接到 Pico 时检查传感器的文档:许多不同的公司生产 HC-SR501 传感器,并且它们并不总是出于相同的目的使用相同的引脚。 对于图 7-1(背面)所示的传感器,引脚设置为接地 (GND) 引脚在底部,信号或触发引脚在中间,电源引脚在右侧; 您的传感器可能需要以不同的顺序放置电线! 从接地线开始:这需要连接到 Pico 的任何接地引脚。 在图 7-1 中,它连接到面包板第 3 行的第一个接地引脚。接下来,连接信号线:这应该连接到 Pico 的 GPIO 引脚 GP28。 最后,您需要连接电源线。 但不要将其连接到 Pico 的 3V3 引脚:HC-SR501 是 5 V 设备,这意味着它需要 5 伏电压才能工作。 如果您将传感器连接到 Pico 的 3V3 引脚,它将无法工作——该引脚根本无法提供足够的功率。 要为您的传感器提供所需的 5 V 电源,请将其连接到 Pico 的右上角引脚 – VBUS。 此引脚连接到 Pico 上的微型 USB 端口,并接入 USB 5 V 电源线,然后将其转换为 3.3 V 以运行 Pico 的微处理器。 所有三个 HC-SR501 引脚现在都应该连接到您的 Pico:接地、信号和电源。 ### 2. 编程报警器 您需要对 Pico 进行编程以识别传感器。 很方便,这并不比阅读按钮更难——事实上,你可以使用完全相同的代码。 首先创建一个新程序并导入机器库,以便您可以配置 Pico 的 GPIO 引脚,以及我们将用于在程序中设置延迟的 utime 库: import machine import utime 然后设置将 HC-SR501 传感器连接到的引脚,GP28: sensor_pir = machine.Pin(28, machine.Pin.IN, machine.Pin.PULL_DOWN) 就像你制作的反应游戏一样,防盗警报器的输入应该作为一个中断——停止程序做它正在做的任何事情,并在传感器被触发时做出反应。 和以前一样,首先定义一个回调函数来处理中断: def pir_handler(pin): utime.sleep_ms(100) if pin.value(): print("ALARM! Motion detected!") 确保您有正确的缩进。 utime.sleep_ms(100) 和 if pin.value(): 行用于防止来自 PIR 传感器的信号中的任何抖动触发警报。 正如我们在这里所做的那样,消除波动的过程被称为去抖动。 最后,设置中断本身。 请记住,这不是处理程序函数的一部分,因此您需要删除 Thonny 自动插入的任何额外空格: sensor_pir.irq(trigger=machine.Pin.IRQ_RISING, handler=pir_handler) 现在就足够了:请记住,无论程序的其余部分在做什么,中断都会保持活动状态,因此无需添加无限循环来保持程序运行。 单击运行图标并将程序作为 Burglar_Alarm.py 保存到您的 Pico。 缩小视野 PIR 传感器旨在覆盖尽可能宽的视野 (FOV),这样窃贼就不能简单地绕过房间的边缘。 如果您发现传感器在您不希望的情况下触发,有一种简单的方法可以修复它:从卫生纸中取出纸板内管并将传感器放在底部。 管子就像马的闪光灯,阻止传感器看到侧面的东西,这样它就可以专注于更远的东西。 在 PIR 传感器上方挥动您的手:外壳区域将打印一条消息,确认传感器看到您。如果您继续挥手,消息将继续打印 - 但每次打印之间都有延迟。 该延迟不是您的程序的一部分,而是内置于 HC-SR501 硬件本身:当检测到运动时,传感器将触发信号发送到 Pico 的 GPIO 引脚,并保持该信号开启几秒钟,然后再丢弃它。在大多数 HC-SR501 传感器上,延迟 使用其中一个小电位器进行调整:您可以插入螺丝刀并转动它 一种方法是减少延迟,另一种方法是增加延迟。检查您的传感器文档,了解哪个电位计控制延迟。 因为您的中断触发器设置为在信号的上升沿触发,所以只要 PIR 传感器发送其信号 1 或“高”,就会打印该消息。即使检测到更多运动,在内置延迟过去并且信号返回到 0 或“低”之前,中断也不会再次触发。 向 Shell 打印一条消息足以证明您的传感器正在工作,但这并不能产生多大的警报。 真正的防盗警报器有灯光和警报器,可以提醒周围的每个人出现问题——您可以将同样的内容添加到自己的警报器中。 首先将任何颜色的 LED 连接到 Pico,如图 7-2 所示。 较长的阳极,需要通过一个 330 Ω 电阻连接到引脚 GP15 - 请记住,如果没有这个电阻来限制通过 LED 的电流量,您可能会损坏 LED 和 Pico。 较短的腿,阴极,需要连接到 Pico 的接地引脚之一 - 为此使用面包板的接地导轨和两根公对公 (M2M) 跳线。 要设置 LED 输出,请在设置 PIR 传感器引脚的正下方添加新行: led = machine.Pin(15, machine.Pin.OUT) 这足以配置 LED,但您需要让它点亮。 将以下新行添加到您的中断处理程序函数的打印行下方(应缩进八个空格以匹配): for i in range(50): 您刚刚创建了一个有限循环,它将运行 50 次。 字母 i 代表一个增量,一个在每次循环运行时都会增加的值,并且由指令 range(50) 填充。 给你的新循环做一些事情,记住下面的这些行需要再缩进四个空格(总共十二个),因为它们构成了你刚刚打开的循环和中断处理函数的一部分: led.toggle() utime.sleep_ms(100) 些行中的第一行是机器库的一项功能,它允许您简单地更改输出引脚的值,而不是设置值——因此,如果引脚当前为 1 或高,则切换它会将其设置为 0,或者 低的; 如果引脚已经为 0 或低,则切换它会将其设置为高。 这两行代码以 100 毫秒(十分之一秒)的延迟使 LED 闪烁。 结果类似于您在第 4 章中编写的 LED 闪烁程序。 价值 VS 切换 有时使用切换功能来设置一个值是有意义的,例如闪烁 LED 时 - 但请确保您首先考虑过要实现的目标。 如果您的项目取决于在给定时间肯定打开或关闭的输出 - 例如警告灯或排水水箱的泵 - 始终明确设置该值而不是依赖切换。 你的程序现在看起来像这样: import machine import utime sensor_pir = machine.Pin(28, machine.Pin.IN, machine.Pin.PULL_DOWN) led = machine.Pin(15, machine.Pin.OUT) def pir_handler(pin): utime.sleep_ms(100) if pin.value(): print("ALARM! Motion detected!") for i in range(50): led.toggle() utime.sleep_ms(100) sensor_pir.irq(trigger=machine.Pin.IRQ_RISING, handler=pir_handler) 单击“运行”按钮,然后再次在 PIR 传感器上方挥手:您将看到通常的警报消息打印到外壳区域,然后 LED 将开始快速闪烁作为视觉警报。 等待 LED 停止闪烁,然后再次在 PIR 传感器上方挥手:消息将再次打印,并且 LED 将重复其闪烁模式。 为了使您的防盗警报器更具威慑力,即使没有检测到运动,您也可以使其缓慢闪烁 - 警告可能的入侵者您的房间正在被监视。 转到程序的最底部并添加以下行: while True: led.toggle() utime.sleep(5) 再次单击“运行”,但不要理会 PIR 传感器:您会看到 LED 现在亮起 5 秒钟,然后熄灭 5 秒钟。只要传感器没有被触发,这种模式就会持续下去;在 PIR 传感器上挥动你的手,你会看到 LED 再次快速闪烁,然后回到慢闪模式。 这突出了线程和中断之间的一个关键区别:如果您使用线程编写了这个程序,即使您的 PIR 处理程序正在打开和关闭 LED,您的程序仍会尝试以 5 秒的间隔打开和关闭 LED以 100 毫秒为间隔。那是因为线程并行运行。 对于中断,主程序会在中断处理程序运行时暂停——因此您编写的 5 秒切换代码会停止,直到处理程序完成 LED 闪烁,然后从停止处开始。您是否需要代码暂停或继续运行是您是否需要使用线程或中断的关键,这将取决于您的项目正在尝试做什么。 ### 输入和输出:把它们放在一起 您的防盗警报器现在有一个闪烁的 LED 来警告入侵者离开,并且可以查看它何时被触发,而无需查看 Shell 区域以获取消息。现在它所需要的只是一个警报器——或者至少是一个压电蜂鸣器,它可以在不震耳欲聋的情况下发出声音 你的邻居。 根据您购买的型号,您的压电蜂鸣器将有针脚伸出底部或连接到其两侧的短线。如果蜂鸣器有针脚,请将它们插入面包板,使蜂鸣器跨越中心分隔线;如果它有电线,请将它们放在面包板上,然后将蜂鸣器放在面包板上。 如果您的蜂鸣器有足够长的电线,您可以将它们连接到 Pico GPIO 引脚旁边的面包板;如果没有,请使用公对公 (M2M) 跳线连接蜂鸣器,如图 7-3 所示。红线或标有 + 符号的正极引脚应连接到 Pico 左下角的引脚 GP14,就在您用于 LED 的引脚上方。黑线或标有减号 (-) 符号或字母 GND 的负极引脚需要连接到面包板的接地轨。 如果您的蜂鸣器有三个引脚,将标有减号 (-) 或字母 GND 的支脚连接到面包板的接地轨,标有 S 或 SIGNAL 的引脚连接到引脚 GP14 在 Pico 上,其余的腿(通常是中间腿)连接到 Pico 上的 3V3 引脚。 如果你现在运行你的程序,什么都不会改变:蜂鸣器只有在从你的 Pico 的 GPIO 引脚获得电源时才会发出声音。返回程序顶部,将蜂鸣器设置在设置 LED 的正下方: buzzer = machine.Pin(14, machine.Pin.OUT) 接下来,更改您的中断处理程序以在 led.toggle() 下方添加新行——记住将其缩进 12 个空格以匹配: buzzer.toggle() 你的程序现在看起来像这样: import machine import utime sensor_pir = machine.Pin(28, machine.Pin.IN, machine.Pin.PULL_DOWN) led = machine.Pin(15, machine.Pin.OUT) buzzer = machine.Pin(14, machine.Pin.OUT) def pir_handler(pin): utime.sleep_ms(100) if pin.value(): print("ALARM! Motion detected!") for i in range(50): led.toggle() buzzer.toggle() utime.sleep_ms(100) sensor_pir.irq(trigger=machine.Pin.IRQ_RISING, handler=pir_handler) while True: led.toggle() utime.sleep(5) 单击“运行”并在 PIR 传感器上挥手:LED 会像以前一样快速闪烁,但这次会伴随蜂鸣器发出哔哔声。 恭喜:这足以吓跑入侵者,以免他们洗劫你的秘密糖果! 如果您发现蜂鸣器发出咔嗒声而不是哔哔声,那么您使用的是被动蜂鸣器而不是主动蜂鸣器。 有源蜂鸣器内部有一个称为振荡器的组件,它可以快速移动金属板以发出嗡嗡声; 无源蜂鸣器缺少此组件,这意味着您需要用自己的一些代码替换它。 警告 使用有源蜂鸣器时,只要它连接的引脚为高电平,它就会继续发声——换句话说,值为 1。因为您的循环运行偶数次,所以它会在蜂鸣器关闭的情况下结束 ; 但是,将循环更改为奇数次,它会在蜂鸣器仍然响起的情况下结束——按下停止按钮不会将其关闭。 如果发生这种情况,只需拔下 Pico 的微型 USB 电缆并重新插入 - 然后更改您的程序,以免再次发生! 如果您使用的是无源蜂鸣器,请尝试使用此版本的程序 - 它可以非常快速地打开和关闭连接到蜂鸣器的引脚,模拟有源蜂鸣器中振荡器的效果: import machine import utime sensor_pir = machine.Pin(28, machine.Pin.IN, machine.Pin.PULL_DOWN) led = machine.Pin(15, machine.Pin.OUT) buzzer = machine.Pin(14, machine.Pin.OUT) def pir_handler(pin): utime.sleep_ms(100) if pin.value(): print("ALARM! Motion detected!") for i in range(50): led.toggle() for j in range(25): buzzer.toggle() utime.sleep_ms(3) sensor_pir.irq(trigger=machine.Pin.IRQ_RISING, handler=pir_handler) while True: led.toggle() utime.sleep(5) 请注意,控制蜂鸣器的新循环不使用字母 i 来跟踪增量; 那是因为你已经在外循环中使用了这个字母——所以它使用了字母 j。 与此同时,只有三毫秒的短暂延迟意味着连接到蜂鸣器的引脚打开和关闭的速度足以使其发出嗡嗡声。 尝试将延迟更改为 4 毫秒而不是 3 毫秒,您会发现蜂鸣器的音调较低。 改变延迟会改变蜂鸣器的振荡频率:较长的延迟意味着它以较低的频率振荡,使其发出较低的音调; 更短的延迟使它以更高的频率振荡,使其成为更高音调的声音。 延长闹钟 防盗警报很少覆盖一个房间:相反,它们使用多个传感器网络从单个警报系统监控多个房间。基于 Pico 的防盗报警器可以以完全相同的方式工作,添加多个传感器以一次覆盖多个区域。 对于要覆盖的每个区域,您都需要一个 HC-SR501 传感器;在此示例中,您将再添加一个传感器,总共两个传感器,但您可以继续添加所需数量的传感器。您的两个传感器都需要 5 V 电源才能工作,但您已经在您的传感器上使用了 VUSB 引脚 第一个传感器的 Pico。如果您的面包板有足够的空间,您可以在连接到第一个传感器的那个旁边放置一根公对母 (M2F) 跳线,并将其用于第二个;不过,一种更简洁的方法是使用面包板上的电源轨。 从面包板末端断开第一个传感器的电源线,并将其插入红色或标有加号 (+) 符号的电源导轨。使用公对公 (M2M) 跳线并将相同的电源轨连接到 Pico 的 VUSB 引脚。接下来,使用公对母 (M2F) 跳线并将电源轨连接到第二个 PIR 传感器的电源输入引脚。 最后,像以前一样连接第二个 PIR 传感器的接地引脚和信号引脚——但这次将信号引脚连接到 Pico 上的引脚 GP22,如图 7-4(背面)所示。您的电路现在有两个传感器,每个传感器都连接到一个单独的引脚。 将程序设置为读取第二个传感器和第一个传感器就像添加两条新行一样简单。首先设置第二个传感器,在设置第一个传感器的位置下方添加新行: sensor_pir2 = machine.Pin(22, machine.Pin.IN, machine.Pin.PULL_DOWN) 然后在第一个中断的正下方创建一个新的中断: sensor_pir2.irq(trigger=machine.Pin.IRQ_RISING, handler=pir_handler) 请记住,您可以使用单个处理程序处理多个中断,因此无需更改程序的该部分。 单击运行,然后在第一个 PIR 传感器上挥手:您将看到警报消息、LED 闪烁和蜂鸣器正常发出声音。 等待它们完成,然后在第二个 PIR 传感器上挥手:您会看到防盗警报器以完全相同的方式响应。 为了使您的闹钟真正智能,您可以根据哪个引脚负责中断来自定义消息 - 它的工作方式与您之前编写的两人反应游戏完全相同。 返回您的中断处理程序并修改它,使其看起来像: def pir_handler(pin): utime.sleep_ms(100) if pin.value(): if pin is sensor_pir: print("ALARM! Motion detected in bedroom!") elif pin is sensor_pir2: print("ALARM! Motion detected in living room!") for i in range(50): led.toggle() buzzer.toggle() utime.sleep_ms(100) 就像在第 6 章的反应游戏项目中一样,这段代码使用了一个事实,即中断报告它是由哪个引脚触发的:如果连接到引脚 GP28 的 PIR 传感器负责,它将打印一条消息; 如果是连接到引脚 GP22 的 PIR 传感器,它将打印另一个。 您完成的程序将如下所示: import machine import utime sensor_pir = machine.Pin(28, machine.Pin.IN, machine.Pin.PULL_DOWN) sensor_pir2 = machine.Pin(22, machine.Pin.IN, machine.Pin.PULL_DOWN) led = machine.Pin(15, machine.Pin.OUT) buzzer = machine.Pin(14, machine.Pin.OUT) def pir_handler(pin): utime.sleep_ms(100) if pin.value(): if pin is sensor_pir: print("ALARM! Motion detected in bedroom!") elif pin is sensor_pir2: print("ALARM! Motion detected in living room!") for i in range(50): led.toggle() buzzer.toggle() utime.sleep_ms(100) sensor_pir.irq(trigger=machine.Pin.IRQ_RISING, handler=pir_handler) sensor_pir2.irq(trigger=machine.Pin.IRQ_RISING, handler=pir_handler) while True: led.toggle() utime.sleep(5) 如果您使用的是被动而不是主动蜂鸣器,请记住您需要将蜂鸣器切换为循环以使其发出哔哔声。 单击运行并在一个传感器上挥手,然后在另一个传感器上挥手,以查看这两条消息都打印到 Shell 区域。 恭喜:您现在知道如何构建能够覆盖所需区域的模块化防盗报警器了! 挑战:定制 您可以使用另一个 PIR 传感器扩展防盗警报吗? 添加另一个 LED 或另一个蜂鸣器怎么样? 您能否更改打印的消息以匹配您使用每个传感器覆盖的区域? 你能让蜂鸣器响得更久或更短吗? 除了 PIR 传感器之外,您能想到其他任何传感器可以很好地用于防盗警报吗?