树莓派家庭安全系统构建指南-全-

树莓派家庭安全系统构建指南(全)

原文:annas-archive.org/md5/aefcf00fd73bb0fc0edeadaf93dc8db4

译者:飞龙

协议:CC BY-NC-SA 4.0

序言

树莓派是一款强大且低成本的信用卡大小计算机,非常适合作为复杂家庭安防系统的控制器。利用现有的板载接口,树莓派可以扩展以连接几乎无限数量的安防传感器和设备。树莓派具备足够的处理能力和接口,能够构建复杂的家庭安防系统,但其成本仅为商业安防系统的一个小部分。

使用树莓派构建家庭安防系统 从向你展示树莓派及其如何设置基于 Linux 的操作系统开始。接着,本书引导你通过安全地将开关传感器和 LED 灯连接到原生的 GPIO 连接器,并展示如何使用简单的 Bash 脚本来访问这些设备。随着你深入了解,你将学习如何使用 I2C 接口和电源供应构建输入/输出扩展板,进而连接一个典型家庭安防系统所需的大量传感器。

本书配有清晰的图表和代码,列出了每个步骤,帮助你构建一个真正复杂且模块化的家庭安防系统。

本书内容

第一章,设置树莓派,从将树莓派从包装盒中取出并准备作为家庭安防系统的核心开始。在这个过程中,我们将安装和设置操作系统,将树莓派连接到网络并远程访问它。我们还将确保树莓派的安全,并确保它能正确地保持时间。

第二章,通过 GPIO 连接设备到树莓派,探讨了 GPIO 端口及其各种接口功能。我们将讨论如何通过 GPIO 将各种设备(如开关和传感器)连接到树莓派,开始构建我们的家庭安防系统。

第三章,扩展树莓派连接更多设备,探讨了扩展我们能连接到树莓派的设备数量的方法。我们通过挖掘 GPIO 上其他接口并构建自己的输入/输出扩展板,克服了仅有的八个数字引脚的限制。

第四章,添加磁性接触传感器,开始实际连接设备到我们的家庭安防系统,如磁性传感器和其他类型的接触设备。你将学习如何使用 Bash 脚本编程我们的 I2C 扩展端口,以便读取传感器的状态并开启警告 LED。我们还将开始开发控制脚本,使系统能够开关和设定延时计时器。

第五章, 添加被动红外运动传感器,介绍了被动红外运动探测器的工作原理,以及如何将有线和无线类型连接到我们的家庭安全系统。我们还将学习如何使用 Bash 脚本根据事件创建日志文件,以便我们能够维护探测器状态变化的历史记录。

第六章, 将摄像头添加到我们的安全系统,教你如何将 Raspberry Pi 摄像头模块和 USB 摄像头连接到我们的 Pi 板,以便在家庭安全系统需要时拍摄图像和视频。我们还将学习如何将图像叠加信息性文本,并立即通过电子邮件将文件发送给我们。

第七章, 构建基于网络的控制面板,开始着手构建模块,建立一个适用于移动设备的网络控制面板,用于我们的家庭安全系统。你将学会如何在 Raspberry Pi 上设置网页服务器并使用网页控制面板操作文件,这意味着我们将开始探索到目前为止所有元素如何作为我们最终系统的一部分整合在一起。

第八章, 杂项,探讨了其他一些内容,例如如何将与入侵者检测无关的其他传感器添加到我们的家庭安全系统中。我们还将讨论如何通过网页浏览器远程管理整个 Raspberry Pi 系统,并访问我们的家庭安全控制面板。

第九章, 将一切整合,是我们一直在等待的时刻;我们将把前面各章的所有元素和概念结合起来,组建出我们完整的系统,包括我们希望展示的各个元素。重点将是我们的 Bash 脚本,它们将把这些元素连接起来,并为整个系统提供控制逻辑。

本书所需的工具

你需要以下软件:

  • Gparted dd fake-hwclock

  • Win32 Disk Imager 0.9.5 PuTTY

  • i2c-tools

本书适合谁阅读

本书适合任何有兴趣使用 Raspberry Pi 板、基础电子元件、传感器和简单脚本从零开始构建模块化家庭安全系统的人。本书非常适合热心的初学者程序员、电子爱好者以及工程专业人士。如果你有一些基本的焊接技能,以便构建一些接口模块,那会更好。

约定

在本书中,你会看到多种文本样式,用以区分不同种类的信息。以下是这些样式的示例及其含义的解释。

文本中的代码词汇、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟 URL、用户输入以及 Twitter 用户名,均按如下方式显示:“将2015-09-24-raspbian-jessie.img提取到你的Home文件夹。”

代码块按如下方式设置:

# passwd
root@raspberrypi:/home/pi# passwd
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
root@raspberrypi:/home/pi#

任何命令行输入或输出都按如下方式编写:

$ sudo apt-get install fake-hwclock

新术语重要词汇以粗体显示。你在屏幕上看到的单词,如菜单或对话框中的内容,将像这样出现在文本中:“在主机名框中输入树莓派的 IP 地址,然后点击打开。”

注意

警告或重要说明会以这样的框显示。

提示

提示和技巧如下所示。

读者反馈

我们非常欢迎读者的反馈。告诉我们你对本书的看法——你喜欢或不喜欢的部分。读者反馈对我们非常重要,帮助我们开发出能让你最大化收益的书籍。

要向我们发送一般反馈,只需发送电子邮件到<feedback@packtpub.com>,并在邮件主题中提到书名。

如果你在某个领域有专长,并且有兴趣参与写作或为书籍做贡献,请参阅我们的作者指南,网址:www.packtpub.com/authors

客户支持

既然你已经成为了 Packt 书籍的骄傲拥有者,我们为你提供了一些帮助,以确保你最大程度地从购买中获益。

下载示例代码

你可以从你的 Packt 账户下载你购买的所有 Packt 书籍的示例代码文件。www.packtpub.com。如果你是在其他地方购买的这本书,可以访问www.packtpub.com/support并注册,文件将直接通过电子邮件发送给你。

勘误

尽管我们已尽力确保内容的准确性,但错误有时还是会发生。如果你发现我们书中的某个错误——无论是文本中的错误还是代码中的错误——我们将非常感激你能够报告给我们。这样一来,你可以避免其他读者的困扰,帮助我们改进后续版本的书籍。如果你发现任何勘误,请访问www.packtpub.com/submit-errata,选择你的书籍,点击勘误提交表格链接,填写勘误的详细信息。一旦勘误得到验证,你的提交将被接受,并且勘误会上传到我们的网站或加入到该书的勘误部分的现有列表中。

要查看之前提交的勘误,请访问www.packtpub.com/books/content/support,并在搜索框中输入书名。所需的信息将在勘误部分显示。

盗版

网络上对版权材料的盗版问题在所有媒体中都在持续存在。在 Packt,我们非常重视版权和许可证的保护。如果您在互联网上发现任何非法复制的我们作品的版本,无论形式如何,请立即向我们提供该位置地址或网站名称,以便我们采取补救措施。

如果您发现涉嫌盗版的材料,请通过<copyright@packtpub.com>与我们联系,并提供相关链接。

我们感谢您的帮助,以保护我们的作者和我们为您带来有价值内容的能力。

问题

如果您在本书的任何方面遇到问题,请通过<questions@packtpub.com>与我们联系,我们将尽力解决。

第一章:设置你的树莓派

在我们开始构建家庭安防系统之前,我们需要做一些准备工作,以确保树莓派能够正常运行。所以,我们将逐步完成准备工作,以便将树莓派做好开发的准备。

本章将涵盖以下主题:

  • 探索不同版本的树莓派

  • 为你的系统选择合适的树莓派版本

  • 准备 SD 卡并安装 Raspbian 操作系统

  • 学习如何通过家庭网络远程访问树莓派

  • 使用最新的软件包更新我们的操作系统

  • 探索树莓派上的时间管理选项

  • 设置用户和根密码以保护我们的树莓派

选择哪款 Pi?

自 2012 年树莓派发布以来,已经发布了几个版本的迷你 PC 板。接下来,我将逐一介绍发布的每个版本及其特点,帮助你选择最适合你项目的版本。

好消息是,在电力方面使用哪个版本并不重要,因为我们的家庭安防系统根据你希望系统执行的功能,实际上并不需要大量的处理能力(当然,具体取决于你希望系统执行的任务)。你可能还有一块旧板子可以使用。

另一个好消息是,GPIO 接口引脚布局在所有版本中都是相同的。虽然较新的版本有更多的引脚,但原有的 26 个引脚仍然保持在同一位置。

选择哪款 Pi?

最新的树莓派版本 2

树莓派 Model A

家庭中的“宝宝”是 Model A;它作为 Model B 的低成本版本发布,Model B 将在接下来的章节中介绍。它与 Model B 的主要区别在于只有 256Mb 的内存,并且没有以太网端口;所以,如果你想将这个板子连接到网络,你只能使用 USB Wi-Fi 加密狗。

树莓派 Model A

树莓派 Model A 板布局

树莓派 Model B

这是树莓派发布的第一个版本;后来发布了一个更新版,改善了电源系统和 USB 端口保护。它配备了 512Mb 的内存,并具有以太网端口,可以连接到网络。这个版本可能是最常见的,尤其是在需要快速启动、设置和配置树莓派时,拥有以太网端口非常有用,可以无需键盘和显示器快速设置。

树莓派 Model B

树莓派 Model B 布局

树莓派 Model B+和 Model 2

2014 年,树莓派基金会发布了一个新版本的板子,与之前的版本相比,做了一些根本性的变化。最根本的变化是板子布局、外形尺寸和安装点——这让许多外壳和配件制造商感到失望。

事实上,我们正在为一个基于树莓派的商业产品开发一个外壳原型——幸运的是,我们及时得知了板卡的更改,及时修改了外壳以支持即将发布的 Model B+。

此板的主要电子变化是增加了 2 个 USB 端口,能够为外设提供更多电力,扩展了 GPIO 接口,并移除了复合视频端口,现已将其整合到音频插孔中。它现在还使用了一个微型 SD 卡,并且卡槽更好。

2015 年 2 月,发布了更强大的树莓派:树莓派 Model 2。它在外形和接口上与 Model B+相似,但据称它比 Model B/B+快 6 倍,配备了升级版 ARM 处理器和 1Gb 内存。

以不到 30 英镑的低成本,它是一款非常出色的小型板卡,是嵌入式系统的强大动力源。

树莓派 Model B+和 Model 2

树莓派 Model B+和 Model 2 布局

模型对比表

Model A Model B Model B+ Version 2

|

Processor

|

ARM1176JZF-S 700 MHz processor, VideoCore IV GPU

|

Quad-core ARM Cortex-A7 CPU and a VideoCore IV dual-core GPU

|

|

Memory

|

256Kb

|

512Kb

|

512Kb

|

1Gb

|

|

USB Ports

|

2

|

2

|

4

|

4

|

|

Ethernet

|

No

|

Yes

|

Yes

|

Yes

|

|

No.GPIO Pins

|

26

|

26

|

40

|

40

|

|

Storage

|

SD Card

|

SD Card

|

Micro SD Card

|

Micro SD Card

|

那么,选择哪个呢?

本书中展示的模块基本上适用于任何版本的树莓派,但如果你想利用像摄像头这样的功能,这可能需要更多的处理能力和内存,或者想要有以太网连接,那么你需要使用 Model B。

如果你想开始连接额外的设备,比如 GSM 调制解调器,我建议你使用 Model B+,因为它能为这些设备提供更多电力,而无需额外的 USB 集线器。

如果你打算进行更多的视频和图像处理,或想连接更多设备,那么选择最新的Model 2板。我假设你已经选择了这个板,并且在本书中我将一直使用它;如果你选择使用早期的版本,请注意可能的限制。

注意

树莓派基金会网站有关于每个模型的更详细信息,你可以访问 www.raspberrypi.org/products

准备 SD 卡

树莓派仅从 SD 卡(或 B+和 v2 模型的 micro SD 卡)启动,无法从外部硬盘或 USB 闪存盘启动(虽然严格来说,这并不完全正确,但超出了本书的范围)。

推荐使用 Class 10 SD 卡以获得更好的性能,但对于本项目,Class 4 或 6 卡也足够。你需要至少 4Gb 的卡。

现在我们已经准备好了树莓派板和 SD 卡,我们需要专门为我们的家庭安全系统准备 SD 卡。我们将使用标准的 Raspbian 操作系统,因为没有理由使用其他发行版;它是树莓派的事实标准选择。

下载 Raspbian 映像

你需要从树莓派官网www.raspberrypi.org/downloads下载最新的Raspbian OS映像。

下载包含映像的 Raspbian OS ZIP 文件到你的 PC。

注意

在写这篇文章时,最新版本是 Raspbian Jessie 版本 4.1(2015-09-24-raspbian-jessie.zip)。

下载完成后,解压文件,你会得到文件2015-09-24-raspbian-jessie.img

接下来要做的就是将这个映像烧录到你的 SD 卡……

使用 Microsoft Windows

在 Windows PC 上,将映像烧录到 SD 卡的最佳方式是使用Win32 Disk Imager工具。可以从sourceforge.net/projects/win32diskimager下载。

注意

目前的版本(在撰写本文时)是 0.9.5。

它没有安装程序,可以直接从 EXE 文件启动。

现在,是时候创建你的 SD 卡映像了:

  1. 将 SD 卡插入 PC 并启动 Win32 Disk Imager。

  2. 选择 SD 卡设备的驱动器字母(确保选择正确!)。

  3. 选择你刚刚下载的 Raspbian 映像文件。

  4. 点击写入按钮来创建 SD 卡映像。使用 Microsoft Windows

使用 Linux

在 Linux PC 上,你需要使用gparteddd工具将映像烧录到 SD 卡上。

执行以下步骤来创建你的 SD 卡映像:

  1. 2015-09-24-raspbian-jessie.img解压到你的Home文件夹中。

  2. 将 SD 卡插入 PC。

  3. 如果你还没有打开终端窗口,打开一个(你可以在大多数图形桌面系统上使用Ctrl + Alt + T)。

  4. 在终端中输入以下命令:

    $ sudo fdisk -l 
    
    

    在列表中检查,你的 SD 卡显示为驱动器设备(例如,/dev/sdb)。在下一步中确保使用正确的设备是至关重要的。我们假设你的设备是/sdb

  5. 要将映像烧录到 SD 卡,输入以下命令:

    $ sudo dd if=2015-09-24-raspbian-jessie.img of=/dev/sdb
    
    
  6. 按下Enter键,去泡一杯茶或咖啡,因为这个过程可能需要一些时间。当命令($)提示符重新出现时,你就知道它已经完成。

  7. 当命令提示符重新出现时,输入以下命令:

    $ sudo sync
    
    
  8. 命令完成后,你可以从 PC 上取出 SD 卡。

启动你的 Pi

现在,你可以准备启动你的树莓派了。将崭新的 SD 卡插入树莓派并接通电源。

假设你已经将显示器连接到你的 Pi,你应该能够看到系统正常启动。虽然你可以等待它启动并通过终端会话连接到它(我们稍后会讨论),但我建议你至少在第一次启动时连接显示器,以确保一切正常。

在新的 Jessie 版本的 Raspbian 中,系统会直接启动到桌面 GUI,这与之前的版本有很大不同。在之前的版本中,系统首次运行时会进入 raspi-config 工具,那里你可以设置你的 Pi,并且非常重要的一点是,扩展文件系统以使用 SD 卡上的整个空间。

启动你的 Pi

Debian Jessie 默认启动到 GUI

扩展文件系统

当你首次创建 Raspbian SD 卡时,不管你的 SD 卡有多大,文件系统中只剩下大约 200Mb 的空间。这并不够用,因此我们需要扩展文件系统,使其使用卡上的所有可用空间。

幸运的是,现在在 Raspberry Pi 上这非常容易,因为该功能已经集成在桌面上的 Raspberry Pi 配置工具中。

要访问新的配置工具,进入 菜单,选择 首选项 | Raspberry Pi 配置

扩展文件系统

新的 Raspberry Pi 配置工具

提示

告别 GUI

我们的大部分工作将通过命令行界面(CLI)完成。因此,在我们稍后重启系统之前,让我们通过选择到 CLI选项来更改启动选项,如之前截图所示,以便以后启动到命令行。

无论如何,现在我们点击 扩展文件系统 按钮,几秒钟后,你会看到确认信息。文件系统将在系统下次重启时扩展。

使用 raspi-config 工具

如果你使用的是较旧版本的 Raspbian,或者你没有使用桌面 GUI,那么你需要使用 raspi-config 工具(这比我们以前必须在 shell 中手动执行要好得多)。首次启动时,你会直接进入 raspi-config 工具。

使用 raspi-config 工具

第一个选项是 扩展文件系统 选项;选择此项后,你会看到各种命令滚动在屏幕上。完成后,你会看到以下信息:

Root partition has been resized.
The filesystem will be enlarged upon the next reboot

点击 确定

在配置界面上选择完成,并在提示时重启你的 Pi。

当你的 Pi 通过扩展后的文件系统重启后,系统会直接进入 shell 提示符,你可以使用默认的用户名和密码登录。

登录: pi

密码: raspberry

设置你的 Pi

当你进入 shell 并连接了以太网时,Pi 应该已经连接到你的家庭网络,并从路由器获取了 IP 地址。如果是这种情况,你应该能在登录提示之前看到分配的 IP 地址,如下图所示:

设置你的 Pi

正如你从我的截图中看到的,它给出了我的 IP 地址,192.168.0.118。这很好,因为我现在可以通过安全 shell(SSH)客户端远程访问 Pi,舒适地坐在我的笔记本前。这在我的 Pi 在办公室时特别有用,我想坐在沙发上看电视,但仍然在操作它,这在我懒惰时经常发生。

提示

下载示例代码

你可以从你在www.packtpub.com的账户中下载所有你购买的 Packt Publishing 书籍的示例代码文件。如果你是在其他地方购买的此书,你可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给你。

要这样做,请下载PuTTY:一个允许你通过网络远程连接到 shell 终端的工具。你可以从www.putty.org下载它。

安装并启动PuTTY,你就可以舒适地从沙发上远程连接到你的 Pi。

设置你的 Pi

在“主机名”框中输入树莓派的 IP 地址,然后点击打开。你将通过远程终端窗口连接到你的 Pi。一旦登录后,你几乎可以在 Pi 上做任何事情,就像坐在它面前一样。

设置你的 Pi

从现在开始,我们假设我们做的大部分工作都会通过远程 shell 会话进行,除非另有说明。

如果你想使用命令行来启动树莓派远程 shell——例如,从另一个 Linux 系统——请从你的终端窗口使用以下命令:

# ssh pi@192.168.0.125

接下来,你将被提示输入 Pi 的密码,并进入一个 shell 会话。

设置你的 Pi

从 Debian 桌面终端窗口启动的 Pi shell 会话

更新系统

你应该养成定期更新操作系统的习惯;即使你可能已经安装了最新的镜像,仍然很有可能有可用的更新包。要更新操作系统,请输入以下命令:

$ sudo apt-get update

然后输入以下命令:

$ sudo apt-get dist-upgrade

这可能需要一些时间,具体取决于需要更新的数量。

获取正确的时间

树莓派没有内建的实时时钟硬件。这是为了降低板卡成本而故意省略的一个功能。相反,树莓派在启动时通过网络时间协议NTP)从互联网上的时间服务器获取时间。然而,如果在启动时没有互联网连接,时间就会不准确。

注意

在我们的安全系统中,保持时间准确非常重要,以确保日志文件和图像上的时间戳是正确的。

fake-hwclock

fake-hwclock 软件包已包含在最新的 Raspbian 发行版中,但在其他旧版本中并未包含。如果你需要安装它,请使用以下命令:

$ sudo apt-get install fake-hwclock

fake-hwclock是树莓派在没有网络连接时尝试保持时间的工具。它会定期保存当前时间,并在启动时恢复。这个方法的明显问题是,如果树莓派已经关机几天,那么时间将会设置为上次开机时的时间,使用的是fake-hwclock

如果你想查看最后一次登录的时间,请输入以下命令:

$ cat /etc/fake-hwclock.data

ntp

网络时间协议NTP)在有互联网连接时使用,它可以从互联网上的一个或多个时间服务器请求最新且最准确的时间。

默认情况下,ntp 服务在最新的 Raspbian 发行版中是启用的,但如果没有互联网连接,它将在启动时从fake-hwclock获取时间。某些情况下,可能需要强制 ntp 服务从互联网更新——例如,如果在启动后某个时刻恢复了互联网连接。

要强制让 ntp 服务从互联网上更新,请使用以下命令:

$ service ntp stop
$ ntpd –gq
$ service ntp start

说到安全……

如果系统本身不安全,那么拥有安防系统就毫无意义。所以,现在我们将更改pi用户的默认密码。

在提示符下,输入以下命令:

$ sudo passwd pi
pi@raspberrypi ~ $ sudo passwd pi
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

那么,这个 sudo 到底是啥意思?

你应该已经注意到,我们在终端窗口中运行的每个命令前都加上了sudo。这是为了让命令以root 用户身份执行——这是一种最高的安全级别。许多操作需要这种提升的安全权限。sudo实际上意味着超级执行

如果你不想每次都输入sudo,那么可以通过输入以下命令切换到超级用户:

$ sudo su

你会看到提示符从$变成了#,这表示你现在以根用户身份运行命令。

所以,现在可能是时候更改 root 用户密码了!要这样做,输入以下命令:

# passwd
root@raspberrypi:/home/pi# passwd
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
root@raspberrypi:/home/pi#

通过 Wi-Fi 连接

你还可以通过插入 USB 加密狗,将树莓派通过 Wi-Fi 连接到你的网络。为了实现这一点,还需要进行额外的配置步骤,超出了本章的范围,但有很多资源可以帮助你完成这个过程。

注意

你可以在 Rick Golden 所著的《树莓派网络食谱》中找到关于如何通过 Wi-Fi 连接树莓派的教程,该书由 Packt Publishing 出版 (www.packtpub.com/hardware-and-creative/raspberry-pi-networking-cookbook)。

总结

在本章中,我们将树莓派从盒子中取出,并准备将其作为我们家庭安防系统的核心。过程中,我们安装并设置了操作系统,将树莓派连接到网络,并实现了远程访问。我们还确保了树莓派的安全性,并确保它能够保持准确的时间。

在下一章中,我们将探讨 GPIO 端口及其所具有的各种接口。我们将了解如何通过 GPIO 端口连接到树莓派的各种设备,包括开关和传感器,随着我们开始构建家庭安全系统。

第二章:使用 GPIO 将设备连接到树莓派

树莓派有很多连接外设的方式,例如插入 USB 端口、连接设备到板载摄像头和显示端口,以及连接到组成GPIO 连接器的各种接口。作为我们家庭安全项目的一部分,我们将主要关注如何将设备连接到 GPIO 连接器。

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

  • 检查 GPIO 连接器以及每个引脚的功能

  • 学习将会在后续章节中使用的 I2C 和 SPI 总线

  • 安全地将 LED 和开关连接到数据引脚,并通过简单的脚本访问这些数据引脚

  • 理解 USB 端口及其局限性

前提条件

除了你的树莓派,你还需要以下部件来完成本章的项目:

  • 面包板

  • 一个 LED

  • 一个 220 欧姆电阻(红色、红色、黑色)

  • 一个 10K 欧姆电阻(棕色、黑色、橙色)

  • 一个按钮开关或切换开关

  • 一根连接线:前提条件

    我们的小配件集合

你好,GPIO

GPIO 连接器是树莓派板边缘的一组大引脚。在早期的型号中,这个连接器由 26 个引脚组成。但自从 B+型号开始,连接器有了 40 个引脚,尽管前 26 个引脚与以前的型号相同,我们将使用这 26 个引脚。你无需担心其余的引脚。

本质上,GPIO 连接器提供了对以下内容的访问:

  • 电源

  • 数字 I/O 引脚

  • I2C 总线

  • SPI 总线

  • UART 串行总线

GPIO 上的一些引脚具有多重功能,具体取决于它们的编程方式。以下图示是 GPIO 所有引脚的参考指南。黄色标签上的 GPIO 编号直接与 Broadcom 芯片上的编号对应,并且是脚本中通常使用的编号。

你好,GPIO

数字 I/O 引脚

GPIO 提供 8 个数字输入/输出引脚可供使用。这些引脚可以用于打开和关闭设备(输出模式),也可以用于检测外部设备的开关状态(输入模式)。每个引脚都可以独立配置为输入或输出操作,我在前面的图示中将其标记为D0D7

显然,如果我们使用每个引脚来驱动或感应一个独立的设备,我们将受到最多只能连接 8 个设备的限制。这在许多情况下可能不足够,因此在下一章中,我们将学习如何使用 GPIO 将更多设备连接到树莓派。

I2C 总线

互集成电路I2C)总线是一种低速接口,可以通过两线接口连接多个设备和简单传感器,而无需单独的时钟或设备选择线。通常情况下,这个总线的速度可以达到 100kbit/s。我们将在下一章中讨论这个内容,以帮助我们扩展数字输入/输出并连接更多设备。

SPI 总线

串行外设接口SPI)总线是一种同步双向串行连接,连接主设备和从设备。它可以用来访问更复杂的传感器或驱动显示器。

主设备提供同步,每次传输都通过SCLK(GPIO11/引脚 23)上的时钟脉冲进行同步。数据通过MOSI(主设备输出-从设备输入)和MISO(主设备输入-从设备输出)(分别是引脚 19 和 21)传输。

UART 串行总线

通用异步接收器和发射器UART)总线是一种通过串行数据连接与外部设备通信的方式,也是树莓派访问设备数据的一种常见方式,像 GPS 模块通常带有串行连接。由于它也与操作系统的串行控制台相连接,因此设置树莓派与 UART 连接的设备进行通信时可能会有些复杂。

USB 端口

我们可能都很熟悉通用串行总线USB)端口,因为我们用它们连接各种设备到我们的个人电脑,比如键盘、鼠标和硬盘。在树莓派上也是如此;我们可以连接键盘、鼠标和调制解调器,从而实现 Wi-Fi 和蓝牙连接。

USB 端口

官方树莓派 USB Wi-Fi 调制解调器

在早期的树莓派型号中,端口提供的电流非常低,如果连接的设备拉取过多电流,就会导致各种问题。从 B+型号开始,电流供应得到了显著改善,现在可以毫无问题地连接 GSM/LTE 调制解调器。

然而,如果你想连接硬盘驱动器等设备,仍然存在一些限制;这些设备的电流消耗可能超过树莓派 USB 端口所能提供的电流,因此建议在连接这类设备时使用带电源的 USB 集线器或 USB 电源注入器。

电源连接

GPIO 连接器还提供了对板载电源的访问。+5V 连接(引脚 2 和 4)基本上是从连接到微型 USB 电源端口的外部电源输入的+5V。这可以用来为小型外部电路供电,尽管如果需要较大电流,建议使用额外的外部+5V 电源。

+3.3V 电源(引脚 1 和 17)是板载 3.3V 稳压器的输出,提供最多 50mA 的小电流。如果你需要为外部电路提供超过 50mA 的电流,则应使用外部电源。我将在本书后面介绍如何制作一个外部电源。

注意

树莓派上的 I/O 引脚工作在 3.3V 电平上。如果将高于此电压的电压接入引脚,可能会永久性损坏你的 Pi。如果你遵循本书中的说明,一切应该没问题,但随机连接消耗大量电力的设备会损坏它!

熟悉 GPIO

在我们开始将许多东西连接到我们的 Pi 板之前,最好先通过一些简单的项目熟悉一下 GPIO,这将帮助我们理解如何使用 Shell 脚本与数字 I/O 引脚交互。

让这里有光

这个简单的小项目演示了如何将 GPIO 输出连接到 LED,并使用 Shell 命令打开和关闭它。

下图展示了如何使用面包板连接电路:

让这里有光

注意

你刚看到的漂亮电路图是使用 fritzing 这个免费软件工具制作的,fritzing 是一个开源硬件项目,旨在使电子学成为任何人都可以用作创意材料的领域。你可以从fritzing.org下载它。

LED 的阳极(正极)连接到D0数字 I/O(连接器的第 11 针或 GPIO17)。当该引脚被打开时,它将为 LED 提供 3.3V 电源。

LED 通过一个 220R 的电阻与接地引脚连接,电阻连接在阴极(负极)上。电阻限制了 LED 的电压和电流,否则它会烧坏,因为 LED 只能承受约 2V 的电压。对于一个 3.3V 供电,LED 的电流大约为 10mA,220R 的电阻可以很好地保护 LED 和 GPIO。

这是它的电路图:

让这里有光

注意

计算 LED 电阻值…

尽管本书并不是一本电子学理论课程,但我觉得展示如何使用欧姆定律计算 LED 的电阻值会很有用,因为我们稍后还会涉及这个内容。

正如我提到的,一个典型的 LED 在其两端会有约 2V 的电压降,尽管这取决于颜色和类型。这被称为设备的正向电压或 VLED。

一个 LED 所需的电流大约为 10mA,同样这取决于它的规格。我们称流过 LED 的电流为 ILED。

本质上,电阻上的电压将是供电电压减去 LED 的电压降(例如,2V)。所以,如果我们有一个 12V 的电源(VS),电阻上的电压将是 10V(VS – VLED)。

根据欧姆定律,电阻 R 是它两端的电压除以流过它的电流:R = V / I。由于我们需要 10mA 的电流流过它,且两端电压为 10V,因此所需的电阻值为 10V 除以 0.01A,即 1000 欧姆或 1K。

总结一下,R = (VS - VLED) / ILED。

现在,来控制 LED 的开关:GPIO 引脚实际上在 Linux 文件系统中被映射为设备,所以使用 Shell 命令非常简单,尽管有很多库可以让你使用 Python 等语言控制 GPIO。但为了避免学习新语言,我们将通过 Shell 命令完成所有操作。

我们连接的D0引脚实际上是 GPIO17(参考前面的图示)。我们需要做的第一件事是创建对这个 GPIO 引脚的文件访问。我们可以通过以下命令实现:

$ sudo echo 17 > /sys/class/gpio/export

然后,我们需要将引脚的方向设置为输出:

$ sudo echo out > /sys/class/gpio/gpio17/direction

接下来,我们可以将引脚设置为高电平,打开 LED:

$ sudo echo 1 > /sys/class/gpio/gpio17/value

要关闭 LED,我们使用这个命令:

$ sudo echo 0 > /sys/class/gpio/gpio17/value

一旦我们完成 GPIO 端口的操作,就可以移除它的文件访问:

$ sudo echo 17 > /sys/class/gpio/unexport

开始闪烁…

我们可以将这些命令组合成一个 Bash 脚本来创建一个闪烁的 LED。要创建闪烁脚本,请在nano或其他文本编辑器中创建一个新的文本文件。或者,如我通常做的那样(别忘了我有点懒),在你的笔记本上创建文本文件,然后通过WinSCP将其复制到远程树莓派上(如果你希望避免一些麻烦,请阅读下面的注释)。

以下是led-flash.sh的代码列表:

#!/bin/bash
sudo echo 17 > /sys/class/gpio/export
sudo echo out > /sys/class/gpio/gpio17/direction
# loop forever
while true
do
  sudo echo 1 > /sys/class/gpio/gpio17/value
  sleep 0.5
  sudo echo 0 > /sys/class/gpio/gpio17/value
  sleep 0.5
done

注:

如果你使用 Windows 来创建文件,请记得将文件保存为 Linux 格式的行尾(即单个0x0a或换行符字符),而不是 Windows 格式(0x0a + 0x0d或换行符加回车符),否则你可能会发现 Bash 脚本在树莓派上运行不正常。Windows 上的文本编辑器,如优秀的 Notepad++,会自动将脚本的行尾格式转换成 Linux 格式。

通过调用led-flash.sh来运行脚本(假设你把它命名为这个)。如果你与脚本位于同一目录下,可以通过输入以下命令来执行:

$ sudo bash ./led-flash.sh

由于这是一个无限循环,LED 在半秒间隔内闪烁,你需要通过使用CTRL + C来中断它,从而停止脚本。

别忘了使用以下命令将 GPIO 引脚从文件访问中移除:

$ sudo echo 17 > /sys/class/gpio/unexport

否则,如果你重新运行脚本,你会看到错误信息:echo: write error: Device or resource busy,因为第一行再次尝试为 GPIO17 设置文件访问。

添加开关

在这个项目中,我们将看到如何将开关连接到 GPIO 输入,并编写一个 Shell 脚本来读取开关的状态——也就是说,判断它是开启还是关闭。

将开关连接到你的树莓派的 GPIO27 引脚,如下图所示:

添加开关

振作起来

关于 GPIO 输入,有一点非常重要:它们处于所谓的浮空状态。这意味着,对于操作系统来说,除非它接收到已知电压,否则无法确定其参考状态。

这就是我们电阻发挥作用的地方——它将 GPIO 引脚拉至已知电压 3.3V,从而使其默认状态为高电平(或二进制1)。

当按钮开关被按下时,它会将 GPIO 引脚拉至 0V,即低电平(或二进制0)。

这是我们 GPIO 开关的电路图:

整理一下自己

检测脚本

现在我们已经将开关连接到树莓派,我们需要编写一个小脚本来检测开关是否被按下。

这与之前显示的 LED 脚本类似,但这次我们将 GPIO 引脚设置为输入并读取其逻辑电平。

在这个项目中,我们将开关连接到D2,即GPIO27(请参考之前的 GPIO 引脚图)。与之前一样,我们需要通过输入以下命令为引脚创建文件访问权限:

$ sudo echo 27 > /sys/class/gpio/export

现在,将它的方向设置为in

$ sudo echo in > /sys/class/gpio/gpio27/direction

现在我们准备好读取它的值了,可以使用以下命令来完成:

$ sudo cat /sys/class/gpio/gpio17/value

你会注意到它将返回1,或者说是高电平。这是由于我们之前提到的上拉电阻。意味着它的默认状态是高电平,当开关没有按下时。

当开关按下时,值应该读取为0或低电平。如果你有超过两只手,可以试试按下按钮并重新运行命令。或者,我们可以创建一个脚本来轮询开关状态。

poll-switch.sh的代码清单如下:

#!/bin/bash
sudo echo 27 > /sys/class/gpio/export
sudo echo in > /sys/class/gpio/gpio27/direction

# loop forever
while true
do
  # read the switch state
  SWITCH=$(sudo cat /sys/class/gpio/gpio27/value)

  if [ $SWITCH == 1 ]; then
    #switch not pushed so wait for a second
    sleep 1
  else
    #switch was pushed
    echo "You've pushed my button"
  fi
done

当你运行脚本并按下按钮时,你应该会看到你按下了我的按钮在控制台屏幕上滚动,直到你停止按下按钮。

别忘了,在完成 GPIO 端口的使用后,我们可以删除它的文件访问权限:

$ sudo echo 27 > /sys/class/gpio/unexport

我们现在已经看到了如何轻松读取开关输入,使用相同的电路和脚本可以读取其他传感器,例如门接触开关、簧片开关,或者任何其他具有开关状态的设备。

世界上最精密的灯光开关

通过将之前的两个小项目结合起来,我们现在可以创建一个系统,当按下按钮开关时执行一些有用的操作——例如,点亮我们也连接的 LED。虽然我们可以直接将 LED 连接到开关和电池,但那样不仅无聊,而且也违背了我们要做的事情的意义,那就是通过编程感知和控制事物。

这是我们精密灯光开关的面包板布局:

世界上最精密的灯光开关

这是电路图:

世界上最精密的灯光开关

照明脚本

我们的完整 Bash 脚本将展示如何控制精密灯光开关。该脚本将无限循环,检测开关的 GPIO 引脚状态,并在按下开关时打开 LED GPIO 引脚。

light-switch.sh的代码清单如下:

#!/bin/bash

#set up the LED GPIO pin
sudo echo 17 > /sys/class/gpio/export
sudo echo out > /sys/class/gpio/gpio17/direction

#set up the switch GPIO pin
sudo echo 27 > /sys/class/gpio/export
sudo echo in > /sys/class/gpio/gpio27/direction

# loop forever
while true
do
  # read the switch state
  SWITCH=$(sudo cat /sys/class/gpio/gpio27/value)

  #0=Pushed 1=Not Pushed
  if [ $SWITCH = "1" ]
  then
    #switch not pushed so turn off LED pin
    sudo echo 0 > /sys/class/gpio/gpio17/value
  else
    #switch was pushed so turn on LED pin
    sudo echo 1 > /sys/class/gpio/gpio17/value
  fi
  #short delay
  sleep 0.5
done

所以,现在我们有了一个脚本,它可以检测输入状态并做出响应;在这种情况下,它将打开一个 LED。我们现在正在构建如何组合我们的家庭安全系统的基础。

注意

记住,不要在 LED 位置上连接任何设备,例如蜂鸣器或其他大量消耗电流的设备。这可能会永久性地损坏你的板子。我们将在本书后面部分探讨如何控制需要更高功率的设备。

总结

在本章中,我们介绍了通过查看 GPIO 上可用的各种接口,将 Raspberry Pi 连接到外部世界的不同方法。我们了解了如何将设备连接到 Raspberry Pi GPIO 连接器上的数字引脚,并使用简单的 Bash 脚本进行控制和读取。特别是,我们已经安全且正确地将开关连接到数字输入引脚,这将为我们的家庭安全检测电路奠定基础。

在下一章中,我们将探讨如何扩展我们可以连接到 Raspberry Pi 的设备数量,通过利用 GPIO 上其他接口,并构建我们自己的输入/输出扩展板,克服只有 8 个数字引脚可用的限制。

第三章:扩展你的树莓派以连接更多设备

现在我们将探讨如何扩展可以连接到树莓派的设备数量,克服仅有 8 个数字引脚的限制。我们将通过制作自己的扩展板来实现这一目标,这样理论上可以提供无限数量的数字输入和输出。

我们还将通过构建自己的 +3.3V 电源来克服 +3.3V 电源的限制,这个电源从树莓派的 +5V 电源中提取。

本章将涵盖以下内容:

  • 详细了解 I²C 总线

  • 学习串并转换与并串转换

  • 构建一个 +3.3V 电源

  • 构建一个基于 I2C 的端口扩展器,提供更多的输入和输出

  • 了解替代的现成扩展板

前提条件

在本章中的项目中,除了树莓派,你还需要以下组件:

  • 一块铜条板(或 Veroboard®)

  • 一颗 LD1117V33 电压调节器

  • 一颗 2 x 100nF,16V 的陶瓷电容

  • 一颗 10uF,16V 的电解电容

  • 一颗 1 x MCP23017 16 位端口扩展器 IC

  • 一颗 4 x 10K 欧姆电阻

  • 一根连接线

I2C 总线

在上一章中,我们简要介绍了 I2C 总线(或称为互联集成电路总线),这是一种通过仅用两根线将多个设备连接在一起的方法。I2C 是由飞利浦公司在 1980 年代初发明的,作为一种将计算机外设通过统一协议连接在一起的方式。你可以将 I2C 看作一种早期形式的 USB。

与以太网(通常以高达 1Gbit/s 的速度运行)或 USB(最大可达 480Mbit/s)等接口相比,I2C 的速度通常较低,最高仅为 100kbit/s。然而,这对于连接基本传感器、显示设备或其他外设(如实时时钟)已经足够。事实上,I2C 协议还有一些更快的版本,某些设备可能会支持。

仅需 2 根线

I2C 是一种双向串行通信协议,通过两根线进行数据传输:

  • 串行数据线 (SDA) 负责在主设备与从设备之间传输数据。参考第二章《使用 GPIO 连接设备到树莓派》中的 GPIO 引脚参考,这是 GPIO 连接器的第 3 引脚。

  • 串行时钟线 (SCL) 负责控制总线上数据的时序和流量控制。这是 GPIO 连接器的第 5 引脚。

你应该记得我们在上一章提到过的上拉电阻,它确保 GPIO 数字输入被拉到已知状态。对于 I2C 总线的两条线路,这也是必须的,默认情况下应该使用电阻将线路拉高。然而,在树莓派上,这已经为我们完成了,因此在我们的情况下不需要担心这个问题。

你的地址是什么?

那么,如果我们只需要两根线就能与多个设备通信,树莓派如何知道与哪个设备通信呢?这就是 I2C 协议发挥作用的地方。每个连接到总线的设备都有自己独特的 ID 或地址,由 7 位或 10 位组成。有些设备允许您设置地址以确保它在系统中的唯一性,而其他设备则由制造商硬编码其地址。

两种寻址方法(7 位和 10 位)是兼容的,您可以在同一总线上连接使用任一方法的设备,因为树莓派本身支持这两种方法。因此,使用 10 位寻址方案,您会发现我们可以通过 I2C 总线连接许多设备,而不仅仅是 GPIO 上的有限数字引脚!

存在一个平行宇宙

数据通常以串行模式或并行模式传输,具体取决于所需的数据传输速度、线路距离以及功能等因素。大多数系统之间的数据通信是通过串行模式在几根线路上传输的,例如之前提到的 I2C 总线,但这也包括以太网、RS232/422 和 USB 等。

在计算机系统中,数据是通过宽度与数字系统的字长相匹配的总线以并行模式传输的。在并行模式下,数据字的所有位同时通过各自的线路在总线上传输,而不是像串行模式那样按顺序在单一线路上传输。

我们讨论过的数字 I/O 引脚(包括树莓派 GPIO 连接器上的引脚)通常作为并行总线组合在一起。在我们的系统中,我们将使用宽度为 8 位的并行总线(即 8 位数字 I/O 引脚组)。也就是说,总线有 8 根线路,可以使用 8 位二进制值(我们的字长)进行设置或读取。

存在一个平行宇宙

8 位数据总线的表示

因此,在前面的图示中,我们有总线上的 8 根数字 I/O 线路。如果我们希望使线路 0、1 和 4为高电平开启,而其余线路为低电平关闭,那么我们将对总线进行寻址并将其设置为以下值:

  • 用二进制表示,这将是 00010011

  • 用十六进制表示,这将是0x13

  • 用十进制表示,这将是 19(由16+2+1表示)

换句话说,要打开数据线 0、1 和 4,我们将字节值 19 发送到总线的地址。

串行转并行转换

那么,现在我们知道了如何通过我们的 I2C 总线,使用串行接口打开或关闭某些数字输出,或者读取某些数字输入,接下来该如何操作呢?

幸运的是,有许多集成电路ICs)可以帮助我们简单、轻松地完成这项工作。这些集成电路被称为移位寄存器,它们执行串行到并行转换,将来自 I2C 串行总线的数据转换为并行表示,通过设置每个并行总线输出。

当读取并行总线数据线作为输入时,反向操作发生了,将位数据转换为 I2C 总线上的串行形式;这被称为并行到串行转换。

这是一个相当简化的概述,市面上有许多资源可以解释这些操作;我们将在本章后面看到这些操作的实际应用,但首先……

给我电源

你会记得在上一章中提到,大多数与 GPIO 相关的操作都在+3.3V 电平上进行,而不是通常与数字电路相关的+5V 电平。我们的基于 I2C 的移位寄存器也是如此——它们需要在+3.3V 电平上工作,以便与树莓派兼容。

然而,你还会记得,树莓派直接提供的+3.3V 电源并不多——实际上只有 50mA。这对于我们的接口来说真的不够。因此,在继续之前,我们将自己构建一个+3.3V 电源,它足以满足我们系统的需求。

对于我们的电源,我们将使用一个基本的 3.3V 电压调节器(型号LD1117V33),它将从树莓派提供的稍多一些的+5V 电源调节为平稳的+3.3V 电源。我们应该能够从这个电源中提取几百毫安的电流——足够供我们安全系统的 I/O 电路使用。

我们电源所需的部件如下:

  • 一颗 LD1117V33 电压调节器

  • 一颗 100nF、16V 的陶瓷电容

  • 一颗 10uF、16V 的电解电容

这是我们+3.3V 电源的电路图:

给我电源

和我们所有的元件一样,LD1117V33 电压调节器可以从许多电子元件供应商那里广泛购买到。

我们的电源可以在一小块条形板上轻松构建,如下所示:

给我电源

注意

上述布局中的条形板是从顶部展示的。也就是说,铜轨位于板子的底面,元件从普通的顶面插入并焊接到底下的条形带中。在这种布局中,不需要切割条形板上的任何轨道。

构建 I2C 扩展器

好的,现在我们已经弄清楚了如何为我们提供更多的数字 I/O 引脚,并为此构建了电源,我们可以开始构建我们的扩展端口了。

为了做到这一点,我们将使用一颗专门为此设计的芯片:MCP23017,由 Microchip 制造,并且可以从电子供应商那里广泛购买到。

MCP23017 是一款集成电路,直接连接到 I2C 总线(我们之前提到的 SDA 和 SCL 引脚),为我们提供 16 个双向输入和输出引脚。如果需要,我们可以将最多 8 个此类芯片连接到同一总线上,从而提供最多 128 个输入和输出(是的,我知道我之前说过“几乎无限”,但稍后我会解释)。

构建 I2C 扩展器

MCP23017 集成电路引脚图

注意

MCP23017 的完整数据手册可以在 Microchip 网站上找到,网址是 www.microchip.com/MCP23017

I2C 端口扩展器电路

构建你的端口扩展器所需的基本元件如下:

  • 1 个 MCP23017 16 位端口扩展器 IC

  • 1 个 10K 欧姆电阻(4 个)

  • 1 个 100nF、16V 陶瓷电容

  • 1 块铜条板(或 Veroboard®)

  • 一根接线

这是我们 I2C 扩展端口电路的电路图。看起来有些复杂,但实际上大部分线路是用来连接外部世界的:

I2C 端口扩展器电路

让我们逐步解析电路

在右侧,连接器 CN1 是我们的 Raspberry Pi GPIO 连接器——注意我们只使用了四个引脚:

  • +5V 输出(引脚 2)

  • I2C SDA(引脚 3)

  • I2C SCL(引脚 5)

  • 0V/GND(引脚 6)

你会看到我的朋友,之前提到的 +3.3V 稳压器(U1、C1 和 C2)。它将来自 Raspberry Pi 的 +5V 输出转换为 +3.3V,以供电路的其余部分使用。

主要元件是 U2——我们的 MCP23017 端口扩展器 芯片。芯片的引脚 9 和 10 分别连接到 +3.3V 电源和 GND,C3 用作靠近芯片的去耦电容器,用于减少电源上的噪声。

MCP23017 可以作为 16 位扩展器使用,或者作为 2 个 8 位扩展器使用。在我们的电路中,我们将设备分为 2 个 8 位总线:I/O 总线 A 和 I/O 总线 B。每个总线上的引脚可以被编程为输入或输出。

小贴士

连接输入/输出引脚

我们总线上的输入和输出引脚通常不能直接连接到设备——它们提供的电流有限,需要正确接口连接到蜂鸣器、灯光等设备;还必须保护它们免受损坏的输入信号。下一章我们将学习如何安全地连接到 I/O 端口。

Raspberry Pi 的 I2C SDA/SCL 线连接到芯片的引脚 12 和 13。你会看到还有额外的 I2C 输出(PL1 到 PL3),这表明我们可以将其他设备连接到 I2C 总线,例如另一个 MCP23017 芯片,以提供更多的 16 个数字 I/O。

电阻 R1 用于将 RESET 引脚(18)保持高电平。将此引脚拉低可以重置芯片。

电阻 R2 至 R4 用于将地址引脚 A0 至 A2(引脚 15-17)保持低电平。

高电平与低电平

当我们使用术语来描述数字引脚或输入时,我们只是简单地描述引脚的逻辑电平分别是二进制 1 或 0。数字引脚不喜欢处于浮空状态——即既不高也不低——因为这会导致不可预测的操作。因此,我们始终确保它们保持在确定的逻辑电平。一般来说,将引脚连接到 0V(或地)可以确保它保持在逻辑电平 0,将其连接到正电源(如 3.3V)则可以确保它保持在逻辑电平 1。

记得我之前提到过,你可以将大量设备连接到 I2C 总线上,从而为我们提供几乎无限的 I/O 引脚?实际上,在许多情况下,这并不完全正确。这是因为 I2C 设备的寻址方式,使得当所有设备都连接到同一根两线时(通过其唯一的地址),所有设备都可以被识别。每个设备的地址是由制造商事先约定好的,以确保大家的设备能够在同一总线上正常工作,不会产生冲突。因此,地址是预先编程到设备中的。

MCP23017 已经赋予了一个唯一的基本地址,但可以通过将地址引脚 A0-A2 设置为高或低来修改;因此,实际上,它可以配置为 8 个地址中的一个。这就是为什么你只能在同一 I2C 总线上使用最多 8 个此类芯片,从而为我们提供理论上的最大 128 个 I/O 引脚(即 16 个 I/O x 8 个芯片)。

构建你的扩展板

这个电路可以轻松地在一小块条形板上构建。下面的图片显示了布局的示例,显得比电路图要简单一些。在下一章中,我们将学习如何连接我们的板并编程,以便检查它是否正常工作。

构建你的扩展板

提示

使用条形板时,确保你切断 MCP23017 两排针脚之间的走线,以免它们短路。你可以从许多电子供应商处购买走线切割工具,这样可以轻松完成这项任务。再次提醒,前面提到的布局中,铜条位于板子下方,元件则位于平面侧。

你可能还想将+3.3V 电源电路加到同一块条形板上,这样可以将所有部件集成在一起。

注意

在下一章中,我们将学习如何编程设备,以便我们能将其用于我们的家庭安防系统。

使用现成的扩展板

虽然自己动手制作设备更令人满足,但如果你还不自信使用焊接铁,或者只是简单地没有时间,你可能会想购买一些现成的扩展板用于你的家庭安防系统。

以下是一些现成的扩展板,你可以获取;它们应该可以在我们稍作修改脚本、以支持硬件所需的库后,作为我们家庭安防系统的一部分使用。

Hobbytronics MCP23017 扩展端口套件

这个套件几乎与本章前面部分中我们自己的电路完全相同。该套件包括一个 MCP23017 芯片、一个 PCB 和各种连接器。板卡设计成可以串联在一起,因此你可以拥有多个扩展板,以便为你提供更多的输入/输出端口。请注意,这个套件不是预组装的,需要焊接,但我认为我应该把它包括在内,因为它是我在原型设计时用来构建这种系统的板卡。你可以直接从 Hobbytronics 购买,bit.ly/mcp23017

PiFace 数字 I/O 扩展板

PiFace 数字 I/O 扩展板是我们板卡的预组装版本,但它使用的是MSP23S17芯片变体,该芯片通过SPI 总线工作,而不是 I2C 总线。该板设计有 8 个输入和 8 个输出,以及若干其他硬件组件,包括几个继电器、一些 LED 和开关。请注意,本书中针对我们系统的代码需要修改,以便与此板兼容,因为它使用了不同的接口和不同的库。你可以从 Farnell element14 购买,bit.ly/2434230

PiFace 数字 I/O 扩展板

PiFace 数字 I/O 扩展板

Gertboard

Gertboard是一个 Raspberry Pi 附加板,由 Gert van Loo 设计——他是参与原始 Raspberry Pi 设计的硬件工程师之一。

这是一款功能强大且价格合理的板卡,已完全组装,具备 12 个缓冲的输入/输出线,适用于开启需要较大电流的设备(如蜂鸣器和灯光)的开集电极驱动器,还包括一个数字转模拟转换器。

你只能将一个这样的板卡连接到你的 Raspberry Pi,因此如果需要更多的 I/O 线,你还需要使用其他设备。但它是一个非常适合实验的板卡。有趣的是,它配备了ATmega 微控制器,这是 Arduino 使用的相同微控制器,而且你实际上可以为该设备使用Arduino开发环境。

再次提醒,本书中针对我们系统的代码需要修改,以便与此板兼容。

Gertboard 可通过 Farnell element14 购买,bit.ly/2250034

Gertboard

组装好的 Gertboard

总结

我们现在已经详细了解了 I2C 总线,并学习了如何使用这种接口构建扩展端口,以便可以连接更多设备到我们的 Raspberry Pi,而不再仅限于 Raspberry Pi 的 GPIO 端口提供的 8 个数字 I/O 引脚。此外,我们还探索了其他现成的板卡,可以用于连接更多设备到 Raspberry Pi。我们还构建了一个电源供应器,可以提供比 Raspberry Pi 直接提供的更多的+3.3V 电力。

在下一章,我们将开始实际连接到我们的家庭安全系统,例如磁性传感器和其他类型的接触设备,并学习如何使用 Bash 脚本编程我们的 I2C 扩展端口,以便读取传感器的状态并开启警告 LED。我们还将开始开发系统的控制脚本,这将使我们能够布防和撤防系统,并添加延迟定时器。

第四章. 添加磁性接触传感器

现在我们已经搭建了端口扩展器硬件,我们需要学习如何编程,使我们的树莓派能够检测到连接到它的设备,作为我们家庭安全系统的一部分。我们将首先通过连接开关,以磁性传感器的形式将其接入系统——这是家庭安全系统中最常见的组件,用于通过门窗检测入侵。

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

  • 了解簧片开关及其作为门传感器的工作原理

  • 在树莓派上启用并设置 I2C 总线

  • 将我们的传感器连接到端口扩展器的输入端

  • 学习如何通过 Bash 脚本访问我们的 I2C 端口扩展器

  • 编写一个脚本,检测我们门传感器的状态

  • 查看其他类型的接触传感器,这些传感器也可以以相同的方式连接和编程

前提条件

本章练习所需的部件如下:

  • 我们的树莓派和端口扩展器板

  • 8 个 10K 欧姆电阻

  • 磁性门传感器和磁铁

  • 一根连接线

  • 一根 4 芯报警线

磁性接触传感器的工作原理

簧片开关本质上构成了我们的磁性接触传感器。簧片开关由两根金属触点组成,这些触点是由磁性材料(称为簧片)制成的,并置于一个玻璃外壳内。当触点接触时,开关处于开启状态;当触点弹开时,开关关闭,电路断开。控制这些触点的方式是通过磁场,当磁场靠近开关时,能够接通或断开电路。

一种常开(NO)类型的簧片开关通常处于关闭状态,直到磁铁靠近开关,将触点拉在一起。

常闭(NC)类型的簧片开关工作原理则与此相反,开关通常处于开启状态,直到磁铁靠近开关,将两个触点拉开。

磁性接触传感器的工作原理

一种典型的簧片开关

现在你可以看到,磁性簧片开关如何在安全应用中发挥作用,特别是在我们的家庭安全系统中,用于检测窗户的开关状态。我们只需要将一个簧片开关安装在门框上,并将其连接到我们的安全系统,磁铁则安装在实际门的开关对面。当门开关时,它就会接通或断开簧片开关的触点。

用于安全系统的簧片开关及其磁铁,通常被封装在小塑料外壳中,便于将其固定到门和门框上。

磁性接触传感器的工作原理

一个门框安装的磁性传感器,包含一个簧片开关(型号:Cherry MP201801)

磁性传感器安装在门框上(显然是为了能够连接到报警电路的电线),而对应的磁铁将被附在门上,位置足够接近边缘,以便当磁铁直接对着传感器时,传感器的接触会连接(或断开,取决于类型)。

磁性接触传感器的工作原理

对应的门装磁性驱动器(型号:Cherry AS201801)

设置 I2C 端口扩展器

既然我们已经构建了端口扩展器,接下来需要将其准备好,以便连接传感器。首先,我们需要在树莓派上安装工具,使我们能够使用 I2C 总线并编程连接到总线的设备,包括构成我们端口扩展器的 MCP23017 芯片。

注意

在设置好 I2C 总线之前,请不要将端口扩展器连接到树莓派。

启用 I2C 总线

很可能默认情况下并没有加载用于使用 I2C 总线的模块。幸运的是,这个过程相当简单,可以通过树莓派配置工具完成。请按照以下步骤操作:

  1. 使用以下命令启动树莓派配置工具:

    $ sudo raspi-config
    
    

    启用 I2C 总线

  2. 选择选项 8:高级选项启用 I2C 总线

  3. 选择选项 A7:I2C启用 I2C 总线

  4. 选择<Yes>

  5. 重启树莓派以使设置生效。

现在 I2C 总线已经启用,我们需要设置操作系统,使其每次启动时加载所需的模块。请按照以下步骤操作:

  1. 使用以下命令编辑 Modules 文件

    $ sudo nano /etc/modules
    
    
  2. 将以下行添加到文件中:

    i2c-bcm2708
    i2c-dev
    
    
  3. 保存文件并退出 Nano。

安装 I2C 工具软件包

为了方便我们使用 Bash 脚本访问 I2C 总线,我们需要安装 i2c-tools 软件包:

$ sudo apt-get install i2c-tools

安装完成后,我们需要关闭系统:

sudo shutdown –h now

在活动停止后,关闭树莓派电源,连接端口扩展器到 GPIO 端口,再重新开机,以便开始使用它。

作为一个快速的检查,您可以通过输入以下命令来查看是否加载了 I2C 支持:

$ ls /dev/i2c-*

如果模块已经加载,这应该会给出至少一个总线的列表,例如 /dev/i2c-1。如果没有,您可能会得到以下响应:

ls: 无法访问 /dev/i2c-*:没有此类文件或目录

在这种情况下,您需要回头检查之前的步骤,因为某些步骤没有正确完成。

查找我们的设备

i2c-tools 软件包安装了几种不同的工具,帮助我们使用连接到总线的端口扩展器。i2cdetect 工具让我们能够查找 I2C 总线和附加到总线上的设备。

要获取系统上 I2C 总线的列表,请输入以下命令:

$ sudo i2cdetect -l

您应该会得到以下响应:

pi@raspberrypi ~ $ sudo i2cdetect -l

i2c-1 i2c 20804000.i2c I2C 适配器

上述输出显示我们有一个 I2C 总线,它将连接到我们的 GPIO。注意,早期型号的树莓派可能会返回设备 ID 为 i2c-0

现在我们可以使用工具扫描所有连接到总线的设备。我们通过指定总线 ID 来实现这一点,如下命令所示:

$ sudo i2cdetect 1

如果 I2C 总线上没有连接任何设备(即没有连接我们的端口扩展器),我们预计会看到以下输出:

pi@raspberrypi ~ $ sudo i2cdetect 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x03-0x77.
Continue? [Y/n] Y
 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
pi@raspberrypi ~ $

I2C 总线上未发现任何设备

连接了我们的端口扩展器后,我们应该会看到以下输出:

pi@raspberrypi ~ $ i2cdetect 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x03-0x77.
Continue? [Y/n] Y
 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
pi@raspberrypi ~ $

我们的 I2C 端口扩展器从设备可以在地址 0x20(十进制 32)处找到。

注意

上述地址是我们连接到 I2C 总线的 MCP23017 芯片的位置。如果没有看到这个地址,可能是接线有问题,需要重新检查。

你会记得,我们可以通过将 A0-A2 引脚设置为唯一的地址,在 I2C 总线上最多添加 8 个此类设备。如果 A0 设置为high,则设备的地址将显示为 0x21(十进制 33)——如果所有引脚都是高电平,则最多为 0x27(十进制 39)。

设置端口扩展器

如前一章所讨论的,我们可以在端口扩展器上拥有 2 个 8 位总线,每个引脚都可以定义为输入或输出。在我们构建的扩展板上,我们将它们称为 I/O 总线 A 和 I/O 总线 B。

要配置 I2C 总线上的 MCP23017 芯片,我们可以使用我们之前安装的i2cset工具发送适当的命令。

在我们的家庭安防系统中,我们将把总线 A 上的所有引脚分配为输入,用于连接传感器。为此,我们使用以下命令:

$ sudo i2cset –y 1 0x20 0x00 0xFF

注意

这个命令是什么意思?

  • -y: 这将在不与用户交互的情况下运行命令。

  • 1: 这是总线的 ID(例如,i2c-1)。

  • 0x20: 这是芯片的地址。

  • 0x00: 这是芯片上的数据寄存器(在此情况下为 PORT A 引脚分配)。

  • 0xFF: 这是加载到数据寄存器中的值(在此情况下,所有引脚都作为输入——二进制%11111111)。

你可以通过以下方式检查数据寄存器是否已正确设置:

$ sudo i2cget –y 1 0x20 0x00

这应该返回一个值0xFF,即我们之前设置的值。

连接我们的磁性接触传感器

现在我们已经让端口扩展器与树莓派一起工作,我们可以开始将设备连接到它,并创建脚本来监控输入引脚上的传感器。

让我们回到上一章中构建的端口扩展器条形电路板并连接我们的磁性传感器。但首先,我们需要确保所有输入默认都被拉低,并使用 10KΩ的电阻。这可以防止它们处于悬空状态,从而在读取端口数据时产生无效数据。

注意

在下面的示意图中,我将下拉电阻外接,但你也可以直接将它们放在条形电路板上。书的最后部分,我们将提供一个新的电路板布局,将我们到目前为止所有的原型工作整合成一个完整的解决方案。

为了检查端口的输入值,我们使用i2cget命令:

$ sudo i2cget –y 1 0x20 0x12

这应该返回0x00,表示所有输入都关闭(二进制 %00000000)。

注意

这个命令是什么意思?

  • -y:此命令在无需用户交互的情况下运行。

  • 1:这是总线的 ID(例如 i2c-1)。

  • 0x20:这是芯片的地址。

  • 0x12:这是芯片上的数据寄存器(在此案例中为 PORT A 读取值)。

现在让我们将磁性传感器的簧片开关的一端连接到 BUS A 的数据引脚 0(我们将其称为 GPA0),另一端连接到+3.3V 电源线。默认情况下,开关是常开的(NO),这意味着输入仍然通过电阻拉低。

但是,当你将配套的磁铁靠近传感器开关时(例如,当门关闭时),开关将闭合,将输入拉高至+3.3V 电源线。如果你现在运行相同的命令读取端口的输入值,你应该会看到返回值是 0x01,表示第一个位是高电平(二进制 %00000001)。

连接我们的磁性接触传感器

监控传感器

现在,一切准备就绪,我们的磁性传感器能够检测门是否关闭,我们可以通过一个简单的 Bash 脚本来监控这个传感器,脚本将使用我们之前安装的 I2C 工具命令。

poll-magnetic-switch.sh的代码列表示如下:

#!/bin/bash
sudo i2cset –y 1 0x20 0x00 0xFF

# loop forever
while true
do
  # read the sensor state
  SWITCH=$(sudo i2cget –y 1 0x20 0x12)

  if [ $SWITCH == "0x01" ]
  then
    #contact closed so wait for a second
    echo "The door is closed!"
    sleep 1
  else
    #contact was opened
    echo "The door is open!"
  fi
done

当你运行脚本并按下按钮时,你应该会看到“门是开的!”在控制台屏幕上滚动,直到你停止按压按钮。

通过将此与我们在第二章中的复杂开关项目结合,我们可以在门打开时打开连接到 GPIO17 的 LED:

#!/bin/bash

#set up the LED GPIO pin
sudo echo 17 > /sys/class/gpio/export
sudo echo out > /sys/class/gpio/gpio17/direction

#set up port expander 
sudo i2cset –y 1 0x20 0x00 0xFF

# loop forever
while true
do
  # read the sensor state
  SWITCH=$(sudo i2cget –y 1 0x20 0x12)

  if [ $SWITCH == "0x01" ]
  then
    #switch not pushed so turn off LED pin
    sudo echo 0 > /sys/class/gpio/gpio17/value
  else
    #switch was pushed so turn on LED pin
    sudo echo 1 > /sys/class/gpio/gpio17/value
  fi
  #short delay
  sleep 0.5
done

注意

之后,随着我们将更多传感器连接到不同的输入引脚,我们需要能够检测到哪个传感器被触发。我们将在本书稍后的章节中编写一个 Bash 函数,它将解析从i2cget命令返回的十六进制值,并告诉我们到底是哪一个输入是高电平。

防篡改电路

如果你仔细观察我们的系统,可能会发现,根据你是检测常开还是常闭传感器开关,通过简单地切断电线是可能篡改传感器通道的。所以,在常开开关的情况下,如果电线被切断,监控系统不会激活,因为它始终显示为打开状态,即使开关已经关闭。

为了减少这种情况的发生,大多数报警系统采用 4 芯电缆系统,将传感器设备连接到主控板——其中两根线用于连接传感器,另外两根用于创建一个防篡改回路,该回路本身形成一个用于监控的传感器输入。

防篡改电路

4 芯报警电缆

请查看以下电路,以便更好地理解我的意思:

防篡改电路

在这个电路中,我们有两个传感器:一个用于监控窗户,另一个用于监控门。这些传感器连接到 I/O 总线 A 的输入端口,分别为 0 和 1(或我们喜欢称之为 GPA0 和 GPA1)。如前所述,它们通过电阻下拉至 0V,但当开关闭合时,正电压轨会使输入端口变高。

然而,我们在整个系统中还增加了一个防篡改回路,并将其连接到 GPA7 进行监控。这个回路通过连接传感器与控制板的每一条电缆进行菊花链连接。只要回路保持完整,输入端口 GPA7 将保持高电平,但如果电缆在任何地方被切断,电流就会停止流动,电阻 R3 将把输入端拉低。这将被监控脚本检测到。

许多安全传感器产品提供在其中终止防篡改回路电线的功能。

所以,在我们的家庭安全系统中,我们将分配 GPA7 作为我们的防篡改回路。

进入区域

你现在可能已经意识到,即使是一个适度大小的物业也可能需要大量的门窗传感器;因此,如果我们为每个传感器使用一个输入端口,我们很快就会用尽端口,除非我们在系统中加入越来越多的端口扩展器。市面上销售的安全系统也是如此。

因此,解决这个问题的方法是通过创建区域,每个区域包含一组传感器。例如,卧室可以定义为一个区域,其中有一个窗户传感器、一个门传感器和一个运动探测器组成该区域。在这种情况下,每个传感器通过串联(或菊花链)连接;如果其中一个被触发,它会通知监控系统该区域发生了触发。当然,触发源不一定是实际的探测器,这在大多数应用中并不会造成问题。

然而,在我们考虑在一个区域内混合使用常开和常闭类型传感器时,这可能会引入一些挑战,但这是我们将在本书后面探讨的问题。

你可以使用的其他传感器如下所示:

  • 霍尔效应传感器:霍尔效应传感器是用于检测附近磁场的简单电子芯片。它们与我们一直使用的簧片开关相似;然而,由于它们是电子设备,它们能够测量与磁铁的接近程度(或磁场强度),而不仅仅是像簧片开关那样处于开或关状态。而且,由于它们是固态的,可以看作比机械开关更可靠。进入区域

    一种低成本的霍尔效应传感器—Allegro Microsystems A1302KUA-T

  • 压力垫传感器:压力垫用于检测站立或行走在其上的人,并可以放置在地垫下以隐藏它们。它们甚至可以放置在椅子上,用于检测坐在椅子上的人。从本质上讲,它们是开关,就像簧片开关一样,只不过它们是通过踩踏产生的压力来激活的,因此可以与我们的磁性传感器电路以相同的方式接线和使用。进入区域

    压力开关可以放置在前门垫下

概述

在本章中,我们配置并使基于 I2C 的端口扩展器正常工作,并通过连接磁性传感器——安全系统中最常用的传感器之一——进行了实验。我们还学习了如何使用 Bash 脚本与 I2C 设备进行交互,以及如何读写这些设备的数据。

此外,我们现在应该开始理解安全系统的各个组成部分和基本构建块,包括防篡改环路和区域。这些概念将为我们后续章节做准备,届时我们将开始将这些元素组合在一起,构建最终的全面系统。

在下一章中,我们将了解被动红外运动探测器的工作原理,如何将有线和无线类型的探测器连接到我们的家庭安全系统。我们还将学习如何使用 Bash 脚本根据事件创建日志文件,从而记录探测器状态的变化历史。

第五章:添加被动红外运动传感器

在上一章中,我们开始将基本但常用的磁性开关传感器添加到我们的家庭安全系统中,并读取它们的状态,以保护门窗免受入侵。我们还探讨了如何将我们的家分成不同的区域,例如按房间划分,这样我们就可以将传感器分组到逻辑电路中,进而作为这些区域的一部分,而不是单独的传感器输入。

我们将以被动红外PIR)探测器的形式,将运动传感器添加到我们的系统中。这些探测器有多种类型,你可能曾在房间的角落看到过它们。基本上,它们的工作原理是相同的,即探测一定范围内的体热,因此它们通常用于当某人(或某物,如宠物猫)进入房间时触发报警系统。

添加被动红外运动传感器

一个典型的 PIR 运动传感器(型号:GardScan QX-PIR)

在这一章中,我们将:

  • 学习 PIR 探测器如何工作以及如何设置它们

  • 将有线 PIR 探测器连接到我们的端口扩展器的输入端口

  • 在我们的区域电路中开始使用 12V 电源,而不是 3.3V 电源

  • 学习如何通过我们的 GPIO 端口安全地与 12V 电路接口

  • 学习如何将一个 433 MHz 无线接收器连接到我们的 Raspberry Pi

  • 使用 433 MHz 无线信号将一个遥控开关连接到我们的系统

  • 编写一个脚本,当我们的探测器输入状态变化时,能够检测并记录该状态

前提条件

本章需要以下部件(除了上一章中使用的组件):

  • 一个有线类型的被动红外探测器(可以从任何 DIY 商店购买)

  • 一个 4N25/4N35 光耦合器

  • 一个 1N4148 二极管

  • 一个 1 千欧电阻

  • 一个 10 千欧电阻

  • 一个 433 MHz 接收模块和远程发射器(这是可选的)

  • 一个 12V 电源

  • 一根连接线

  • 一根 6 芯报警线

被动红外传感器解释

你可能没有意识到,但所有物体都会辐射热能(包括你的咖啡桌);只是你看不见它,因为热量本质上是红外波,而这些波对于人眼是不可见的(就像你的电视遥控器一样)。不过,这些波可以被为此目的设计的电子设备探测到,例如你的电视红外接收器,它能检测到遥控器按下按钮时发出的能量。

你可能已经意识到,像我们、我们的猫,甚至地下地板上的老鼠,都会产生相当多的热量。安防系统和自动照明中使用的被动红外运动传感器正是为了检测这种热量。之所以称之为被动,是因为传感器本身并不辐射任何能量来进行探测——相反,它们只是检测物体发出的红外辐射。这与超声波传感器和雷达设备明显不同,后者依赖于探测物体反射的能量脉冲。

PIR 传感器需要有一点智能,因为它们必须应对房间中不断变化的温度。它们会确定所在房间的背景温度,例如指向的墙壁或地板的温度。当一个物体,例如我们自己或我们的猫,在探测器单元与背景物体之间移动时,传感器前面的温度迅速升高到人体温度,从而触发系统。

设置 PIR 传感器

PIR 传感器设备有许多不同的形式,包括传感器芯片中使用的不同材料以及传感器视窗前的镜头,这些都可能大幅影响设备的范围、视场和灵敏度。因此,设置传感器时,通常最好的指导就是附带的那张小说明纸。

然而,无论你使用什么类型的 PIR 传感器,以下是一些关于安装传感器位置的常规指南,以避免误触发:

  • 确保设备安装在坚固的基础上,并且不受振动影响。

  • 切勿将其安装在能够直接或反射阳光照射到镜头的地方。

  • 同样,切勿将设备安装在面对或高于热源的地方。

  • 不要将设备安装在通风的地方,因为这会影响其背景温度的校准。

设备的安装位置还取决于你想要保护的区域。例如,你可能希望检测从走廊进入客厅的人,因此你的覆盖区域可以定义为从设备安装的房间角落到客厅门的位置。

PIR 传感器通常提供固定的视场(例如 90 度或 110 度),但其范围会根据传感器的指向角度和安装高度有所不同。

在我的系统中,我将使用 Gardscan QX PIR 探测器作为我的有线设备,这是一个非常不错的、低成本的设备,可以从 RS Components 购买到(订货代码是 493-1289)。该设备具有 110 度的视场,并且根据其安装时配置的下倾角,最远可达 12 米的范围。从其数据表中可以看到,这个设备的覆盖模式如下图所示。请注意,从这些模式来看,设备前方的所有区域并不是都被覆盖,这可能并不是你所预期的。因此,根据设备的数据表正确放置设备至关重要。

设置你的 PIR 传感器

GardScan QX-PIR 在其 110 度视场下的覆盖图案(顶部/平面视图)

这里还有一个侧视图的示意图:

设置你的 PIR 传感器

GardScan QX-PIR 覆盖图案取决于配置的角度以及一个“向下看的”窗口(侧视图)

给我电源(再来一次)

在我们将现成的安全设备连接到我们的报警系统之前,我们需要一个与这些设备兼容的电源。通常,报警电路及其设备使用 12V 电源,电流足够大以驱动所有设备和报警控制系统本身。

幸运的是,这并不太难解决,但这是我们现在必须做的事情;否则,我们将无法连接并为 PIR 传感器供电。最简单的方法是购买一个高质量的 12V 市电适配器,提供一个稳定的电源。这些适配器可以从网上商店或电子产品供应商处轻松购买到。或者,你可以自己制作一个 12V 稳压电源,并将其添加到我们在第三章中制作的电源条电路板上,扩展你的 Pi 连接更多设备

注意

另一种选择是使用电池供电的 PIR 传感器,这意味着你无需直接通过安全系统的面板为设备供电;然而,显然也意味着电池需要定期更换。本章稍后我们将讨论的无线 PIR 就是电池供电的。

我们将在本章稍后讨论如何处理更高电压的传感器电路,以避免损坏我们的家庭安全控制电路或 Raspberry Pi。

连接我们的 PIR 动作传感器

商用报警系统通过 4 芯或 6 芯报警电缆连接其设备。在上一章中,我们使用了 4 芯电缆,因为我们连接了一个需要两根线的开关,以及一个需要另外两根线的防拆回路。

对于我们的 PIR 传感器电路,我们需要相同的四根线;然而,我们还需要从控制面板为设备供电,因此需要额外的两根线来实现这一点——因此需要一个六芯电缆。

以下示意图展示了我的 GardScan PIR 传感器的接线方式,实际上这对于大多数现成的安全系统设备来说是典型的:

连接我们的 PIR 运动传感器

安全系统传感器设备的典型连接

与我们在上一章中看到的磁性接触传感器类似,设备可以有常闭NC)或常开NO)警报。这种特定的设备有一个常闭输出,这意味着当探测器被触发时,警报电路将被断开。这是我们传感器设备的首选配置,因为这意味着它们可以在每个区域内串联连接。

我们现在可以将这个传感器设备添加到我们在上一章中开始组建的警报电路中。以下示意图展示了到目前为止所有传感器都接入同一区域的电路:

连接我们的 PIR 运动传感器

我们区域的示意图,其中所有三个传感器和防拆回路都在同一区域内

到目前为止,我们使用+3.3V 电源通过传感器开关和警报电路。实际上,这并不是一个好主意,我们这么做只是为了方便测试 GPIO 输入。

实际上,在我们的最终系统中,我们确实应该使用 12V 电源来通过传感器和防拆电路。因为较高的电压更容易通过系统传输,且对噪声的抗干扰能力更强,这样可以避免触发失败或误触发。这也使得它与市面上现有的系统和配件兼容。

12V 警报区域电路

使我们的区域电路使用 12V 而不是 3.3V,只需要更换电源,实际上我们迄今为止使用的所有传感器都能处理通过开关传递的 12V 电源。

然而,如果我们将 12V 电路连接到树莓派的 GPIO 端口或端口扩展器的输入端口,我们可能会看到“神奇的烟雾”,并闻到烧焦的味道。所以,我们需要添加一些电路,以便能够使用 12V 警报电路,并保护我们的控制板输入。

警报电路保护

一种有效的保护我们区域输入免受 12V 警报输入干扰的方法是使用一个叫做光隔离器的小型低成本设备。顾名思义,它通过光隔离警报电路与控制板的数字输入。

光隔离器(也称为光耦合器)内部有一个红外 LED,当电流通过时,它会将光传递给光电晶体管,从而打开它。这些电路在电气上是隔离的,因为它们仅通过光控制。

警报电路保护

4N25(如前图所示)和 4N35 是低成本的 6 针光耦合器设备,大多数制造商倾向于使用以下示意图中的引脚布局:

警报电路保护

现在我们知道如何将 12V 报警电路与控制面板的输入连接,让我们构建整个电路,用于我们系统中每个添加的区域。

报警电路保护

一个与 GPIO 输入光电隔离的 12V 区域电路

它是如何工作的

此时,我们假设我们的区域电路是常闭的—也就是说,当电路断开时,报警会触发。

12V 电源通过光电隔离器的 LED 传递,电流通过 1 千欧电阻限制。1N4148 二极管以反向方式存在,目的是保护光耦免受反向电压的损害。

注意

1 千欧电阻是基于我们有 12V 电源,并且 LED 的正向电压降(Vf)为 1.2V,电流(If)约为 10mA 的事实计算得出的。

当报警电路闭合时,电流流动,LED 亮起。这使得晶体管保持导通,GPIO 端口的输入保持低电平。如果报警电路断开,光耦 LED 熄灭,进而使晶体管关闭。GPIO 输入然后通过 10 千欧电阻被拉高。

这个过程相当简单但有效,不是吗?

这个电路的另一个优点是它应该在故障时呈现正向—也就是说,如果光耦发生故障,GPIO 端口上的报警输入应该被拉高,从而触发报警,而不是静默故障。

无线 PIR 运动传感器

无线运动传感器现在普遍以低成本提供,使其几乎可以安装在任何地方,而无需从报警控制面板接线。它们中的一些仍然需要外部电源,但许多依靠电池工作。报警系统必须包含与无线传感器兼容的无线接收器。

在本节中,我们将查看如何将基于 Raspberry Pi 的安全系统与无线接收器设备配合使用。

433 MHz 无线报警系统

无线系统使用未授权的射频在报警系统的各个组件之间进行通信。在英国,最常用的频率是433 MHz868 MHz。尽管较新的系统现在使用 868 MHz 频率,433 MHz 仍然被广泛使用,因为它比 868 MHz 系统的范围稍长。然而,433 MHz 频段也被许多其他设备使用,这使得它变得拥挤,而 868 MHz 通常只用于报警系统。

虽然无线安全系统可能很方便,但理解使用无线而非有线系统的优缺点很重要。

优点如下:

  • 它们安装的简便性和速度

  • 它们的拆卸简便性,这意味着你可以随时带着它们走

  • 将来扩展系统可能更容易,大多数系统会自动检测新设备

缺点如下:

  • 它们比有线系统更昂贵,有时价格是有线系统的三到四倍

  • 它们不像有线系统那样安全,并且根据欧洲标准 BSEN 50131,安全等级不能超过二级(尽管这个等级适用于家庭财产)

  • 无线设备需要定期更换电池

  • 无线系统的可靠性较差,容易受到干扰,甚至是无线电干扰

连接 433 MHz 接收器

过去,您可以通过使用廉价的接收器(如 XY-MV-5V 模块)以及由 GitHub 贡献者 Mark Wolfe 整理的433-Util库,为树莓派推出自己的 433 MHz 接收器。基本上,他将与 433 MHz 通信相关的代码集合在一起,并将其全部放入这个库中。最初为 Arduino 开发的这个库,现在已经移植到树莓派上。

然后你可以使用一个现成的发射器,例如车钥匙或任何其他 433 MHz 的发射器,在按下发射器上的每个按钮时查看接收到的代码。

连接 433 MHz 接收器

一个 XY-MK-5V 通用 433 MHz 接收模块

寻找合适的 433 MHz 接收器应该很容易,因为像亚马逊和 eBay 这样的网站上有大量出售这些设备,它们的价格低至几英镑。

注意

请注意,433 MHz 频段是许多设备可以使用的自由频段。因此,有许多不同类型的接收器,尽管它们都标称为 433 MHz 接收器,但它们可能使用 AM 或 FM 工作,并且某些接收器只能检测某些类型的数据。比如 Quasar QAM 系列,可能还需要特殊的解码芯片才能读取传输的数据,并且可能仅与配对的发射器配合使用。

这个接收模块可以接收到来自车钥匙遥控器的信号,例如下图所示的这种遥控器(可以从任何当地的 DIY 商店的家庭安全区购买),它将输出一系列方波。这些方波随后被 433-Util 软件解码。

连接 433 MHz 接收器

一个 Novar/Blyss 433 MHz 无线遥控器

我喜欢这个遥控器,因为我觉得它作为我们家庭安全系统的布防撤防设备是非常合适的。我将在第八章中谈到布防和撤防,各种各样的事物,我们将在那里探讨实现这些功能的方法。

替代方法(因为我们别无选择)

我在上一节开始时提到“过去...”。这是因为最近,我无法使 433-Util 软件与接收模块一起正常工作,而这些接收模块过去是可以正常工作的。我不完全确定为什么会这样;然而,我只能猜测,由于该软件使用“位碰撞”(bit banging)技术解码传入的数据信号,导致时序不再正确,可能是因为后来的树莓派板卡更快,导致例程出错。

注意

什么是位碰撞?

位操作是一种通过软件进行串行通信的方式,而不是使用专用硬件。软件负责信号的所有参数,包括时序、电平和同步。位操作可以看作是一种“黑客”手段,但它确实允许以非常低的成本在不更改硬件的情况下实现不同的协议。

因此,为了让我们的生活更轻松(并且实际上让设备在所有版本的树莓派上都能正常工作),我们将使用一个专用的接收模块,你可以在亚马逊上以不到 5 英镑的价格购买,它不需要这些软件位操作的繁琐。你会从下图中注意到,它仍然使用类似的 XY-MK-5V 无线接收器;只是主板会为我们解码信号,并根据遥控器的命令开关继电器。

注意

如果你仍然对 433-Util 软件项目感兴趣,并且想尝试推出自己的接收器,你可以在github.com/ninjablocks/433Utils找到原始项目。

另一种方法(因为我们别无选择)

一款 Hielec 发射器钥匙扣和接收模块,可在亚马逊上购买

由于它只是简单地开关继电器,这意味着我们可以轻松地将其集成到我们的家庭安全系统中,因为它仅作为一个开关工作。当你按下发射器上的按钮时,继电器会接通触点;再按一次,继电器会断开。板上的螺丝端子为我们提供了继电器端子的接入。

接收器接线图

由于我们仅处理开关输入,我们可以使用与之前区域电路相同的电路,只不过这次连接到我们的开/关 GPIO 输入,具体设置我们将在第九章中讨论,将所有内容组合在一起

接收器接线图

接收模块与 GPIO 输入接口的电路

当接收模块将继电器开关打开时,它将通过开启光耦合器的 LED 来完成 12V 电路。这样,晶体管将 GPIO 引脚拉至地面,提供低电平输入。

你可以使用这种类型的电路,连接任何配对的接收器,用于你想在系统中使用的无线安全设备。

记录探测数据

对于任何系统,能够在某些事件发生时记录数据都是很有用的。我们也可以通过每次区域探测器被触发时写入日志文件来实现这一点。这样,你可以记录下每次有人进入房间的时间,即使系统没有启用,你也可以在之后查看。你还可以记录系统启用和禁用的时间。

这是一个简单的脚本,展示了如何在我们连接到 GPIO 输入的区域发生事件时进行处理:

#!/bin/bash

#set up the I2C expansion port
sudo i2cset –y 1 0x20 0x00 0xFF

#reset status
CURR_STATE="0x00"
LAST_STATE="0x00"

#path to the log file
LOG_FILE="/etc/pi-alarm/zones.log"

# loop forever
while true
do
  # read the gpio inputs
  CURR_STATE=$(sudo i2cget –y 1 0x20 0x12)

   #check if state has changed
   if [ "$CURR_STATE" != "$LAST_STATE" ]
  then
    #write change to log file
      TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"`
     echo "$TIMESTAMP Zone Status Changed from $LAST_STATE to $CURR_STATE" > $LOG_FILE
  fi
  $LAST_STATE = $CURR_STATE
  sleep 1
done

前面的例子非常简单,但通过实际写出在构成区域中通过解码i2cget命令返回的十六进制值来改变的区域或区域,它可以变得更加有用。

注意

在第九章,综合应用中,你将学习如何做到这一点,以便在网页上显示每个区域的个别状态。你可以使用完全相同的技术来处理日志文件,实际上,完全可以通过扩展相同的脚本来输出到日志文件。

总结

在这一章中,我们首先学习了如何使用被动红外传感器来检测运动,以保护预定义的覆盖区域免受入侵。然后,我们研究了如何通过光耦合器将这些传感器连接到我们的端口扩展器的输入端,因为我们现在将使用 12V 为报警区域电路供电。

然后,我们研究了在开放的 433-MHz 频段上运行的无线报警系统,这个频段通常用于安全设备。经过探索使用我们树莓派上的传统 433-Util 位编码软件解码通过简单接收器传输的设备信号的可能性后,我们决定使用配对的接收器设备,这样它就能轻松与我们的报警电路输入接口。

最后,我们创建了一个简单的脚本,它将把我们的报警输入的变化记录到一个文本文件中,稍后可以扩展该脚本来详细记录系统的实际情况。

第六章:向我们的安全系统添加摄像头

到目前为止,我们一直在组合能够将传感器连接到我们的报警系统的元素,使用开关或被动红外运动探测器来检测入侵,这些探测器将通知我们的树莓派在特定区域发生了某些事情。所有这些元素将在本书的后面结合成一个完整的系统。

随着相机的加入,我们的系统将变得更加复杂,可以拍摄图片和视频,并在检测到某些事件时立即通过电子邮件发送给我们。

我们还将使用电子邮件,在我们外出时,当系统中的任何传感器被触发时,向我们的智能手机发送警报。

本章将涉及以下主题:

  • 设置树莓派相机模块并学习如何捕获静态图像和视频图像

  • 学习如何将捕获的图像叠加文本和时间戳

  • 使用运动探测器触发图像捕获

  • 实时通过电子邮件发送图片和视频文件给我们

  • 理解白天和夜间拍摄图像的差异

  • 在需要时开启和关闭安全照明及其他高电流设备

  • 连接 USB 网络摄像头,替代原生相机模块

前提条件

除了上一章使用的组件外,你还需要以下零件来完成本章内容:

  • 树莓派标准相机模块

  • 树莓派 NoIR 相机模块

  • 红外 LED 阵列和/或可见 LED 阵列

  • 一台 USB 网络摄像头

树莓派相机模块

树莓派相机模块是官方的树莓派配件,适用于所有型号的树莓派,可用于拍摄高清静态图片和视频图像。它直接连接到树莓派主板的相机串行接口CSI)端口,这个端口专门用于这些模块以支持高速操作。

相机本身是一款 500 万像素固定焦距传感器,支持 1080p、720p 和 VGA 视频模式及静态图像拍摄。

树莓派相机模块

官方树莓派相机模块

你还可以购买相机模块的外壳,除非你打算为相机系统制作自己的外壳,否则我建议你使用现成的外壳。

树莓派相机模块

树莓派相机外壳有多种颜色和款式

连接相机模块

如前所述,该模块通过其专用的相机接口直接连接到树莓派主板,如下图所示。连接相机时,带状电缆的接触面朝向 HDMI 接口,电缆的蓝色面朝向网络接口。

连接相机模块

将相机模块连接到专用接口

正如你在下面的图片中看到的那样,排线连接器并不长,因此相机需要放置在 Raspberry Pi 附近。通过使用相机外壳,你实际上可以将相机直接安装在 Raspberry Pi 机箱上,如果这种方式适合你。

连接相机模块

相机模块,放置在外壳内

设置相机模块

在我们使用相机模块之前,我们需要在 Raspberry Pi 上启用相机支持。为此,我们使用 raspi-config 工具,就像我们在旅程中早些时候为 I2C 总线所做的那样。

  1. 通过 SSH 从沙发上懒洋洋地连接到你的 Raspberry Pi,或者直接使用键盘和显示器连接。

  2. 一旦你登录,使用以下命令启动配置工具:

    $ sudo raspi-config
    
    
  3. 然后,选择 5 启用相机设置相机模块

  4. 接下来,它会要求你确认是否要启用相机支持。设置相机模块

  5. 选择 <Enable>

  6. 然后,选择 Finish 并重启你的 Pi 以启用相机设置。

测试相机模块

一旦你的 Raspberry Pi 重启,相机应该已经启用。我们可以通过使用 raspistill 工具拍摄一张静态图像来测试:

$ raspistill –v -o test.img

这将延迟 5 秒钟后拍照,并显示各种信息,如下图所示:

测试相机模块

注意

相机模块在 Raspian 上需要至少 128 MB 的 GPU 内存才能正常运行。如果你遇到任何问题,首先确保 /boot/config.txt 配置文件中的 gpu_mem 设置为至少 128

如果一切顺利,你应该能在你的主文件夹中找到文件 test.jpg。由于你是通过 Shell 连接的,你不会看到命令运行时显示的 5 秒预览图像。

如果你将图像文件下载到你的 PC 上,你应该能看到由相机模块拍摄的高质量照片。

测试相机模块

Raspberry Pi 相机模块拍摄的测试照片

小贴士

如果你发现运行 raspistill 时输出错误,确保它在排线的两端都连接正确。另一个问题是,有时连接相机镜头组件与相机板上小连接器的排线可能会松动。只需确保它也牢固连接。我曾多次遇到这种问题,当相机模块从我的杂乱测试箱里拿出来时,连接会松动。

raspistill 工具有很多选项可以用来操作它捕获的图像,稍后我们将在捕获脚本中使用其中一些选项。与此同时,要查看可用选项,只需运行 raspistill 而不带任何选项,选项会被列出:

$ raspistill

成为视频明星

现在我们知道我们的摄像头模块正常工作,可以尝试捕捉一些视频。为此,我们将使用 raspivid 工具。以下命令将拍摄 5 秒钟的高清视频并将文件保存在你的树莓派中:

$ raspivid –o test.h264 –t 5000

你会注意到该文件名为 test.h264——这是因为视频是以原始 H.264 视频流的形式捕捉的。不幸的是,并不是很多媒体播放器可以处理这些文件(虽然 VLC 播放器可以——它非常棒,几乎可以处理你投给它的任何文件——你可以在www.videolan.org下载它)。

如果你希望在智能手机和常规媒体播放器上播放该文件,我们需要将其封装成容器格式,比如 MPEG-4,并将文件扩展名更改为 .mp4

为此,我们将使用 GPAC 包,它是一个开源的多媒体框架。它附带了一个名为 MP4Box 的工具,我们将使用这个工具来为我们的视频文件创建一个 MP4 容器:

  1. 首先,安装 GPAC 包:

    $ sudo apt-get install gpac
    
    
  2. 安装完成后,运行以下命令来转换我们创建的测试视频:

    $ MP4Box -fps 30 -add test.h264 test.mp4
    
    

现在你应该已经拥有了文件 test.mp4,你可以下载并在你的电脑或智能手机上播放。

小贴士

另一个常用的转换工具是 ffmpeg,我在 Windows 上经常使用它来转换视频文件;然而,它可能相当复杂,尽管树莓派上也有相应的包,但我实际上无法让它在树莓派上正确转换。MP4Box 要简单得多,更适合我们的需求。

捕捉到画面

所以,现如今我们已经有了捕捉静态图像和视频的方法,可以在我们的安全系统中使用。如果我们希望这一过程持续进行,可以编写脚本来持续拍摄视频,但这样很快就会填满我们的存储卡,并且效率不高。因此,我们将把摄像系统和之前连接的运动探测器结合起来。

在上一章中,我们创建了一个报警区,里面有一些传感器和一个连接到我们系统的运动探测器,输入端口是 GPA0。那么,让我们编写一个脚本,每当触发运动探测器时,就拍摄一个视频片段:

#!/bin/bash

#set up port expander 
sudo i2cset –y 1 0x20 0x00 0xFF

# loop forever
while true
do
  # read the GPA inputs
  GPA=$(sudo i2cget –y 1 0x20 0x12)

  # detect the zone on input 0
  if [ $GPA == "0x01" ]
  then
    #circuit normally closed so zone is OK
    #short delay
sleep 0.5

  else
    #zone is activated so take a 20 sec video clip

    #filename will be based on current timestamp
    sDate='date +%d%m%y'
    sTime='date +%T'
    echo "Zone 1 Activate at $sDate $sTime"

#take video clip
raspivid –o $sDate$sTime.h264 –t 20000

#convert to MP4
MP4Box -fps 30 -add $sDate$sTime.h264 $sDate$sTime.mp4
  fi
done

你有新的邮件

将图像存储在你的树莓派上其实用处不大——理想情况下,你会希望图像在捕捉到的瞬间就直接发送给你,这样你就可以在智能手机上查看它们。

一种简单、快速、可靠的方法是直接将其通过电子邮件发送给你。因此,我们将为我们的家庭安全系统添加一个电子邮件功能,将图像捕捉到的内容作为附件发送到你的电子邮件地址,之后你可以通过智能手机访问。这些图像可以从你的树莓派中删除,以防止 SD 卡空间被这些相对较大的文件占满。

设置电子邮件发送客户端

幸运的是,有一些很好的包可以帮助我们实现这一点。按照以下步骤安装我们所需的电子邮件包:

  1. 使用以下命令更新软件包安装器:

    $ sudo apt-get update
    
    
  2. 使用以下命令安装并设置 SMTP 客户端:

    $ sudo apt-get install ssmtp
    
    

    你现在需要设置客户端,通过你的电子邮件账户发送电子邮件。在以下配置文件中,我假设你使用的是 Gmail 账户。如果你使用其他邮件提供商,设置可能会有所不同。

  3. 使用 Nano 或其他文本编辑器打开 ssmtp 配置文件:

    $ sudo nano /etc/ssmtp/ssmtp.conf
    
    
  4. 用以下配置替换条目:

    root=<your-username>@gmail.com
    mailhub=smtp.gmail.com:587
    rewriteDomain=gmail.com
    AuthUser=<your-username>@gmail.com
    AuthPass=<your-password>
    FromLineOverride=YES
    UseSTARTTLS=YES
    
  5. ssmtp 可以单独使用,但在自动发送电子邮件时有点麻烦(默认情况下,你需要在命令行中手动输入电子邮件,或者创建一个文本文件),因此我们还需要安装 mailutils 包:

    $ sudo apt-get install mailutils
    
    
  6. 安装完成后,我们可以使用 mail 命令更轻松地发送电子邮件。通过我们之前设置的 (G)mail 账户发送一封测试电子邮件,使用以下命令确保你的设置正常:

    $ echo "Test Email" | mail –s "Test Pi-Mail" me@mydomain.com
    
    

如果一切顺利,你应该能在几秒钟内收到测试电子邮件。

发送附件

现在我们可以从家庭安全系统发送基本电子邮件了,让我们尝试发送之前拍摄的静态图像。但首先,我们需要再安装一个包来帮助我们完成这项工作:

$ sudo apt-get install mpack

安装完毕后,你可以使用以下命令发送我们之前拍摄的测试图像文件:

$ sudo mpack –s "Security Photo" test.jpg me@mydomain.com

现在我们已经具备了所有元素,能够通过电子邮件将警报和图像从家庭安全系统直接发送到我们的智能手机。

那是在哪里拍的?

通常,你可以在电子邮件中注明附加图像的拍摄时间和地点,但这不如在图像上叠加一些文字酷吧?那么,让我们借助 imagemagick 来做一些魔法,它是一个流行的命令行图像处理工具。使用以下命令安装它:

$ sudo apt-get install imagemagick

现在我们将使用命令行来拍摄我们之前拍的测试照片,使用 imagemagick 工具之一叠加一些文字,并将其保存到另一个文件:

$ convert test.jpg –fill red –pointsize 48 annotate +20+60 'Camera 1' annotated.jpg

几秒钟后,这将生成一个名为 annotated.jpg 的文件,文件中包含我们带有 Camera 1 红色文字的图像。当我们把这些元素组合到最终系统中时,我们还会在图像上叠加时间戳。

那是在哪里拍的?

注意

目前,raspistill 工具生成的图像文件比较大,因为它们是高分辨率的照片。这使得处理和发送它们在处理时间上有些耗时,因此当我们构建最终系统时,我们将使用 raspistill 的选项 –w–h–q 来减少图像的大小和质量,从而提高系统效率。

要捕获较小的图像文件,可以尝试使用以下命令:

$ raspistill -o test.img –h 768 –w 1024 –q 25

夜视

标准的树莓派摄像头非常适合拍摄白天人们走在花园小道上的照片,但在夜间拍摄时,它并不适用。应对这一问题有两种方法:第一种是在 PIR 探测器触发时,用强光照亮拍摄区域;第二种是使用树莓派NoIR 摄像头模块和红外 LED 阵列,让摄像头能够在黑暗中看见。稍后会详细介绍。

夜视

树莓派 NoIR 摄像头模块;它看起来与标准型号相似

照亮的体验

为了通过树莓派 GPIO 或我们的端口扩展电路打开灯或 LED 阵列,我们需要一些能够提供比 GPIO 端口本身更高电流和电压的设备。

一个合适的选择是TIP120 达林顿晶体管,它能让我们通过 GPIO 引脚开关最大 80V 和 5A 的负载。在稍后的完整系统中,我们将使用 MCP23017 端口扩展器的 B 端口来控制输出,但这个原理适用于我们所有的 GPIO 输出端口。

照亮的体验

TIP120 晶体管价格便宜,但可以驱动大负载。

以下电路展示了我们如何通过 GPIO 端口输出驱动大负载。

照亮的体验

在我们的示例电路中,我们使用一个 GPIO 输出引脚,通过一个 220 欧姆电阻控制晶体管的基极。当 GPIO 引脚为高电平时,晶体管被打开,允许 12V 电路通过 LED 阵列。

在上述电路中,LED 没有电流限制,因为它们是串联连接的,因此,九个 LED 每个大约降压 1.5V,这对于 12V 电源来说是合适的(是的,我知道这里仅包括了六个 LED,但这只是为了示意)。记得根据你的具体需求调整。这个电路也能轻松驱动其他负载,例如灯泡或警报器。

注意

如果你打算驱动大功率负载,可能需要将 TIP120 安装到散热器上,以散热防止过热和烧毁。然而,在我们之前展示的电路中,你可能不需要散热器,因为我们最多只驱动了几百毫瓦的功率。

复杂的灯光开关重访

再次扩展我们在前几章中提到的复杂灯光开关,我们可以再次编写一个 Bash 脚本,在 PIR 探测器触发时打开摄像头灯,拍摄一张照片,并将其通过电子邮件发送给我们。

对于以下脚本,我们假设控制 TIP120 晶体管输出的是树莓派的 GPIO17 引脚(D0 或我们连接器的第 11 引脚),它替代了我们早期设置中的 LED。PIR 触发器的输入再次连接到 MCP23017 端口扩展器的 GPA0(端口 A,数据引脚 0)。其他所有输入与之前一样,使用 10 K 电阻连接至低电平:

#!/bin/bash

#set up the High Load GPIO pin
sudo echo 17 > /sys/class/gpio/export
sudo echo out > /sys/class/gpio/gpio17/direction

#set up port expander Port A for inputs
sudo i2cset –y 1 0x20 0x00 0xFF

#clear the output by default to switch light off
sudo echo 0 > /sys/class/gpio/gpio17/value

# loop forever
while true
do
  # read the sensor state
  SWITCH=$(sudo i2cget –y 1 0x20 0x12)

  #PIR is normally closed so pin is held high
  if [ $SWITCH != "0x01" ]
  then
    #PIR was triggered – pin taken low

    #switch on lamp driver
    sudo echo 1 > /sys/class/gpio/gpio17/value
    sleep 0.5

#take a still image
    sudo raspistill –o –image.jpg –h 768 –w 1024 –q 25

    #email the image
    mpack –s "Security Alert Photo" test.jpg me@mydomain.com

    #switch off the lamp driver
    sudo echo 0 > /sys/class/gpio/gpio17/value

  fi
  #short delay
  sleep 0.5
done

pir-camera-trigger.sh

你现在可以看到,我们已经开始开发控制家庭安防系统的软件基础。

那是獾吗?

如果你不想在捕捉图像之前照亮某个区域,可以使用红外照明与兼容的摄像头配合使用。标准的树莓派摄像头模块无法与红外照明配合使用,因为它包含红外滤光片,但我们可以使用 NoIR 版本的摄像头模块来代替。

树莓派 NoIR 摄像头模块与标准模块完全相同,不同之处在于它没有内置红外滤光片,这意味着它在红外照明的帮助下能够在黑暗中看到。这使得它非常适合在夜间观察獾类动物,以及用于我们的家庭安防系统。

你将需要一个红外 LED 阵列或聚光灯来不可见地照亮你希望用摄像头捕捉的区域。这些设备有各种外形和强度,或者你可以通过购买单个红外 LED 来自己制作,通常可以在电子商店找到。

那是獾吗?

Kingbright 红外 LED 聚光灯需要 6V 电源,这意味着你可以将两个串联连接——分别安装在摄像头的两侧。

连接和驱动 LED 聚光灯模块的方式与上面提到的照明灯相同,都是使用 TIP120 驱动电路。唯一的区别是,我们人眼无法看到 LED 是否点亮。

使用 USB 摄像头

除了使用树莓派摄像头模块外,你还可以使用标准的 USB 网络摄像头拍摄静态图像。不过需要注意的是,专用的摄像头模块在图像质量方面远优于 USB 摄像头。尽管如此,你可能已经有一款闲置在杂物盒里的摄像头,为什么不试试呢?

安装摄像头

在将摄像头连接到树莓派的 USB 端口后,你可以使用 lsusb 命令检查它是否已被识别:

$ lsusb

我正在使用一款 Logitech 网络摄像头,它在 lsusb 中显示如下(设备 006):

pi@raspberrypi ~ $ lsusb
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 006: ID 046d:08d8 Logitech, Inc. QuickCam for Notebook Deluxe

注释

不是所有的网络摄像头都能与树莓派兼容。即使它被识别为一个 USB 设备,它也可能无法与操作系统正常工作并创建视频设备(例如,/dev/video0)。例如,我曾经使用的一款旧的便宜的 Trust 摄像头虽然被识别为 USB 设备,但无法捕捉任何图像。

你可以通过在 elinux.org/RPi_USB_Webcams 上查看你的摄像头品牌和型号来检查它是否可能与树莓派兼容。

现在,树莓派知道我们已经连接了一个网络摄像头设备,我们可以使用fswebcam工具来捕获图像帧。你可以在开发者网站上了解更多关于fswebcam的信息,网址为www.sanslogic.co.uk/fswebcam

使用以下命令安装fswebcam

$ sudo apt-get install fswebcam

拍摄照片

现在,你可以通过捕获一张静态图像来测试摄像头,这可以通过运行以下命令完成:

$ fswebcam test.jpg

你应该期待看到类似以下的输出:

拍摄照片

注意

fswebcam有很多选项可以调整图像的分辨率和质量。使用命令fswebcam -?可以查看所有选项。

拍摄,拍摄,拍摄

fswebcam不能接收视频流,但你可以将其设置为定期拍摄一系列。例如,为了每 10 秒拍一张照片,你可以使用以下命令:

$ fswebcam frame.jpg -l 10

一个如何实现这一功能的示例是通过设置网络摄像头每隔几秒钟拍一张照片(–q开关将fswebcam在后台运行)。当我们的安全系统被触发时,我们可以获取最新拍摄的照片,该照片可能是拍摄了你家门前的路径。

为了后续在本书中组建我们的整个系统,我们将重点介绍树莓派相机模块,但如果你想使用 USB 网络摄像头,你始终可以将代码替换为前面的示例。

你会注意到,fswebcamraspistill不同,它能够在图像上叠加时间戳信息,因此你不需要像之前那样担心叠加文本。查看fswebcam命令行选项以获取更多信息。

多摄像头设置

你可能会想到,树莓派只有一个相机模块输入。如果你希望在你的财产周围放置多个由运动探测器触发的摄像头,这显然是一个限制。

然而,没人阻止我们构建独立的设备,这些设备配有单独的树莓派主板、PIR 探测器、相机模块和网络连接,可以使用 Wi-Fi 加密狗或以太网连接。

由于你只需要一个输入端口来检测 PIR 运动传感器是否被触发,你可以使用树莓派的 GPIO 端口连接传感器,而不是使用端口扩展器。树莓派将通过网络发送警报,并在需要时可以警告主控制器 Pi——使其成为一个从属传感器设备。

你可以轻松获取到小型 PIR 探测器,例如下图所示的 Parallax 探测器,你可以将其与相机模块一起安装在树莓派机箱上,创建一个自包含的设备。

多摄像头设置

一个 Parallax PIR 运动传感器(型号 555-28027)

从属驱动程序

虽然为每个摄像头配备一个树莓派可能看起来相当复杂——想一想——但你实际上可以用大约 50 英镑的价格,购买包含所有组件的摄像头单元,这比购买一台无线智能摄像头便宜得多。如果你真的想变得聪明一点,还可以将其用作从本地单元接收进一步传感器输入的从设备。

没有什么可以阻止你将从设备的 GPIO 输出引脚连接到主控制器的输入引脚,并根据其本地传感器的状态来控制该引脚。通过在单元之间运行一条 6 芯电缆,如果你的电源足够强大(你需要一个输出 5V @ 1A 的电源来为从设备树莓派供电),你甚至可以为从设备提供电力。

我目前不会进一步深入探讨这种配置,但你可以挑战自己,使用多台树莓派以及本书中学到的构建模块和概念,创建一个完全分布式的家庭安防系统。

总结

在本章中,我们学习了如何将树莓派摄像头模块和 USB 摄像头连接到我们的树莓派板上,以便在家庭安防系统需要时捕捉图像和视频。我们还学习了如何在图像上叠加信息性文本,并将文件立即通过电子邮件发送给我们。

为了在夜间捕捉摄像头图像,我们还研究了如何使用可见光和红外光照亮捕捉区域,并能够根据需要通过使用高电流达林顿晶体管驱动器来开关照明。

在下一章,我们将开始实际搭建模块,构建一个针对移动设备优化的基于 Web 的家庭安防系统控制面板。我们将学习如何在树莓派上设置 Web 服务器,并使用我们的 Web 控制面板操作文件,这意味着我们将开始探索到目前为止我们遇到的所有元素如何结合在一起,成为最终系统的一部分。

第七章:构建基于 Web 的控制面板

我们现在已经把所有硬件元素都准备好了,以创建一个完整的家庭安全系统,系统包括门窗接触开关、运动探测器和摄像头,用来拍摄那些想当入侵者的“开心照”!我故意通过模块化的方式引导您完成这些步骤,以便您可以根据需求挑选和扩展适合的硬件传感器元素。在第九章,集成所有功能,我们将把这些组件接线,构建基于区域的完整系统。

所有家庭安全系统都需要一个控制面板,该面板可以让我们启动停止系统,并监控系统内各区域的状态。我们还可能想做一些事情,比如只启动某些区域,或者让系统在一天中的特定时间自动启动和停止。

所需的硬件,比如开关、LED 和 LCD 显示屏,可能会相当昂贵且耗时;它们也可能使系统变得不那么灵活和可配置。因此,在我们的系统中,我们将构建一个基于 Web 的控制面板,可以通过手机浏览器访问。这也意味着当我们不在家时,可以远程控制系统。

在本章中,我们将涵盖以下内容:

  • 定义我们的家庭安全系统的范围,包括我们将监控的区域数量以及我们将使用的 I/O 端口

  • 学习如何在我们的 Raspberry Pi 上安装和配置 web 服务器

  • 为我们的报警控制面板开发一个基本的 HTML5 网页

  • 学习如何使用 PHP 脚本动态配置我们系统的网页

安装 web 服务器

有几种可供选择的web 服务器可以安装在我们的 Raspberry Pi 上,它们都适用于我们的系统。但是我更喜欢lighttpd web 服务器,因为它易于使用且轻量级。lighttpd 通常被称作“Lighty”,说实话,比 lighttpd 这个名字更简短。

除了 web 服务器本身,我们还将安装PHP支持,这将使我们能够编写动态网页与 Linux 系统进行交互。说实话,虽然有很多原因让我不太喜欢 PHP 用于商业 Web 部署,但对于我们这样的嵌入式 Linux 系统,它是完美的,并且非常适用。如果您以前没有做过服务器端 Web 脚本,它也非常容易入门。

为了执行以下步骤,您需要通过终端控制台(例如 PuTTY)登录到 Raspberry Pi:

  1. 更新软件包安装器:

    $ sudo apt-get update
    
    
  2. 安装 lighttpd Web 服务器:

    $ sudo apt-get install lighttpd
    
    

    安装后,它将自动作为后台服务启动,并且每次 Raspberry Pi 启动时都会启动。

  3. 安装 PHP5 支持:

    $ sudo apt-get install php5-cgi
    
    
  4. 现在,我们需要在我们的 web 服务器中启用 PHP FastCGI 模块:

    $ sudo lighty-enable-mod fastcgi-php
    
    
  5. 最后,我们需要重启 Web 服务器:

    $ sudo /etc/init.d/lighttpd
    
    

就这样!现在你应该已经成功安装了 PHP Web 服务器。默认情况下,网页内容文件将被安装在 /var/www 位置,Lighty 会在此位置安装一个测试占位符页面,你可以通过在浏览器中输入树莓派的 IP 地址来访问它,截图如下所示:

安装网页服务器

Lighttpd 占位符页面

测试 PHP5 安装

在此过程中,我们还应该测试 PHP 安装情况,因为它对构建我们的控制台至关重要。可以通过编写一个简单的 PHP 脚本页面来完成这项测试,如果 PHP 安装正确,它将返回有关环境和配置的信息:

  1. 首先,进入网页内容文件夹:

    $ cd /var/www
    
    
  2. 在 Nano 中创建一个名为 phpinfo.php 的文件:

    $ sudo nano phpinfo.php
    
    
  3. 在编辑器中,输入以下单行命令,然后保存并退出 Nano:

    <?php phpinfo(); ?>
    
    

现在,在你的浏览器中,输入树莓派的 IP 地址后跟 /phpinfo.php,例如 http://192.168.0.110/phpinfo.php,应该会呈现如下页面:

测试 PHP5 安装

由 Web 服务器生成的 PHP 信息页面

现在我们知道我们的网页服务器正常工作,可以开始创建我们的控制台网页了。

控制中

为了知道我们在报警控制面板上需要哪些控制项,我们需要绘制出系统的地图,列出区域输入和控制输入输出的数量。正如你从第三章中记得的那样,扩展你的树莓派连接更多设备,我们可以使用 I/O 扩展器上的两个端口最多设置 16 个区域。我们还可以使用树莓派板上的八个 GPIO 引脚。因此,让我们现在分配这些输出并将它们记录在下表中。

我将使用 I/O 扩展板的 A 端口为我的报警输入系统设置 8 个区域,使用原生 GPIO 引脚来连接按钮和警报输出。这样配置的一个原因是,系统可以始终保持安全故障模式——即使扩展板发生故障,树莓派仍然能够通信并触发连接到它的警报器和蜂鸣器。

端口 I/O 引脚 标签/用途
扩展器 A 0 (A0) 区域 1 输入(进/出通道)
1 (A1) 区域 2 输入
2 (A2) 区域 3 输入
3 (A3) 区域 4 输入
4 (A4) 区域 5 输入
5 (A5) 区域 6 输入
6 (A6) 区域 7 输入
7 (A7) 区域 8 – 防拆环路输入
扩展器 B 0 (B0)
1 (B1)
2 (B2)
3 (B3)
4 (B4)
5 (B5)
6 (B6)
7 (B7)
R-Pi GPIO 0 (GP0) 启动/解除开关(输入)
1 (GP1)
2 (GP2)
3 (GP3)
4 (GP4) 武装 LED(输出)
5 (GP5) 启动/解除警报器(输出)
6 (GP6) 警报 LED(输出)
7 (GP7) 报警铃(输出)

激活自己

激活解除激活是报警系统中用于开启(激活系统)和关闭(解除激活系统)监控的术语。我们系统的第 1 区将与系统的激活和解除激活部分连接,因为它将连接到我们进出门的传感器;这个区域将是一个入口出口用途的特殊区域。

当我们设置闹钟时,我们需要一些时间来离开房子。系统知道我们已离开房产的方式是通过监控出口区域,看我们是否在规定的时间内打开并关闭了前门。

同样,当我们返回时,我们将打开前门,但我们不希望报警立刻响起——我们需要在规定的时间内有机会解除激活系统。我们将通过基于 Web 的控制面板或使用某种开关在输入 GP0 上来激活和解除激活系统。

主配置文件

我们的系统将使用一个主配置文件,该文件会告诉系统所有设备如何设置和连接。这个配置文件将被 Web 控制面板和主报警控制脚本同时使用,使得两个子系统能够相互“通信”。让我们创建一个带有初始设置的文件。

设置文件将存储在与我们将在第九章中创建控制脚本的相同位置,整合所有内容,也就是在/etc/pi-alarm文件夹中。因此,接下来我们创建这个文件夹,并赋予其执行权限,以便我们的脚本可以运行:

$ cd /etc
$ sudo mkdir pi-alarm
$ sudo chmod 777 pi-alarm

现在我们将在这个文件夹中创建系统将使用的主配置文件:

$ cd pi-alarm
$ sudo nano alarm.cfg

提示

如前所述,你不必在 Raspberry Pi 上使用 Nano 创建文件——你可以在桌面计算机上创建文件,然后通过 SCP 将其传输到你的 Pi。

# ALARM MASTER CONFIG FILE #

#Number of zones in the system
NUM_ZONES=8

#Display labels for each zone
ZONE_LABEL_1="Zone 1 - Entry/Exit"
ZONE_LABEL_2="Zone 2"
ZONE_LABEL_3="Zone 3"
ZONE_LABEL_4="Zone 4"
ZONE_LABEL_5="Zone 5"
ZONE_LABEL_6="Zone 6"
ZONE_LABEL_7="Zone 7"
ZONE_LABEL_8="Zone 8"

#Zones that are enabled
#Set to 0 to Disable or 1 to Enable
ZONE_ENABLE_1=1
ZONE_ENABLE_2=1
ZONE_ENABLE_3=1
ZONE_ENABLE_4=1
ZONE_ENABLE_5=1
ZONE_ENABLE_6=1
ZONE_ENABLE_7=1
ZONE_ENABLE_8=1

SYSTEM_ARMED=0

#Zone status
#Set to 1 if zone is triggered
ZONE_STATUS_1=0
ZONE_STATUS_2=0
ZONE_STATUS_3=0
ZONE_STATUS_4=0
ZONE_STATUS_5=0
ZONE_STATUS_6=0
ZONE_STATUS_7=0
ZONE_STATUS_8=0

alarm.cfg 文件

创建网页

我们的基于 Web 的控制面板将是一个单一的 PHP 驱动的 HTML5 网页,并且是移动优化的。HTML5 是最新的网页标记标准,得到大多数现代智能手机和浏览器的支持。我们还将创建一个层叠样式表CSS),使我们的页面在移动设备上看起来更加合理。

为了创建网页文件,我建议你在桌面计算机上使用像 Notepad++这样的优秀工具,而不是直接在 Raspberry Pi 上创建。或者,如果你是经验丰富的 Web 开发者,你可能已经有了自己的 IDE。

控制面板 HTML 模板

我们做的第一件事是创建一个 HTML 文件,用于在将 HTML 放入 PHP 文件之前测试布局,以便与系统交互。这可以让我们在不被 PHP 脚本干扰的情况下,先调整页面的外观。

注意

这不是一篇关于 Web 开发的教程——关于这个主题有大量的书籍可供参考——但我希望代码足够清晰,你能理解代码的运作方式。我将给你的代码是完全可用的,因此你可以直接使用我给你的代码,不必做其他操作。希望这也能让你的控制面板看起来还不错!

以下标记为你提供了一个基本的控制面板,显示我们 8 个区域的状态,一个主开关用于武装和解除武装,以及用于启用或禁用任何区域的开关。

代码的<head>部分包含一些<meta>标签,帮助移动设备识别该网站是移动友好的。在主<body>标记中,我们为每个区域创建了一个包含区域名称和开关的部分。每个区域都有自己的容器,以便我们能够高亮显示需要关注的特定区域,例如,如果该区域被触发。

你可以在chapter 7中的代码文件夹里找到完整的 HTML5 标记文件alarm-panel.html,它包含了我们控制面板的 HTML5 代码。

给它一些样式

目前,这个页面看起来并不好(事实上,它看起来很糟糕,像是 1990 年代的产物);它对移动设备并不友好,很可能无法通过香肠测试。因此,我们将应用一些样式,使它看起来不至于那么糟糕。虽然前面的标记中提到了一个 CSS 文件——但我们还没有创建那个文件——所以这就是我们当前页面的样子(如我所说:它看起来糟糕):

给它一些样式

没有样式的 Web 控制面板

以下 CSS3 标记专门为我们的控制面板设计,它使得控制面板看起来相当漂亮,同时也能在触摸屏移动设备上使用。虽然 CSS 代码很长,看起来有些让人不知所措,但你不需要对它做任何处理,也不需要理解它——你唯一需要知道的是,它是为现代浏览器和智能手机设计的,所以不要指望它能在 Internet Explorer 7,甚至可能在 IE9 中正常工作!

本质上,它包含以下样式:

  • 为我们的移动布局准备浏览器

  • 我们的文本和区域区域

  • 创建酷炫的开关,替代单调的复选框

  • 在需要时让某个区域闪烁

/* Clear browser margin and padding defaults */
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, textarea, p {
margin:0;padding:0;-webkit-text-size-adjust:none;
}

body {
  background: #ffffff;
  color: #4A5651;
  font-family: "Trebuchet MS", Helvetica, sans-serif;
  font-size:10px;  
  height: 100%;
  padding:0;
  margin:0 auto;
  max-width:320px;
  min-width:240px;
  text-align: left;
  width:100%;
  -webkit-box-shadow: 0px 20px 40px 0px rgba(0,0,0,0.50);
  -moz-box-shadow: 0px 20px 40px 0px rgba(0,0,0,0.50);
  box-shadow: 0px 20px 40px 0px rgba(0,0,0,0.50);
}

p, .zoneLabel {
  font-size:16px;
  margin:5px;
  line-height:1.4;
  color:#4A5651;
}

#header h1 {
  font-size:20px;
  line-height:40px;
  margin:0;
  padding:0 0 0 15px;
  text-align:center;
  text-overflow: ellipsis;
  font-weight:bold;
}

.zoneControl, .masterControl{
  border-bottom:1px solid #dddddd;
  margin-top:5px;
  margin-bottom:0px;
  padding:5px;
  display:block;
  width:100%;
}

.zoneLabel {
  font-weight:bold;
  text-overflow:ellipsis;
}

input[type="submit"] {
  border: none;
  background-color: #0b70cc;
  color: white;
  height: 32px;
  display: block;
  padding: 4px 7px;
  float: left;
  border-radius: 8px;
  position: relative;
  bottom: 1px;
  margin-left: 4px;
  text-align: center;
}
input[type="submit"]:hover {background-color: #b2ceec;color: #0b70cc;border: none;border: 1px solid #b2ceec;}

/* Flashing animation */
@-webkit-keyframes flash{0%, 50%, 100% {opacity: 1;} 25%, 75% {opacity: 0;}}
@keyframes flash {0%, 50%, 100% {opacity: 1;} 25%, 75% {opacity: 0;}}
.flash {
  -webkit-animation-name:
  flash;animation-name:
  flash;color:#f00000;
}
.animated { 
  -webkit-animation-duration: 1s; 
  animation-duration: 1s; 
  -webkit-animation-fill-mode: both; 
  animation-fill-mode: both; 
  animation-iteration-count:infinite; 
  -webkit-animation-iteration-count:infinite; 
}

/*
  ON/OFF SWITCH STYLES
  The rather cool On/Off switch styling was generated on
  https://proto.io/freebies/onoff/
*/
.onoffswitch {
  position: relative;
  width: 90px;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}

.onoffswitch-checkbox {
  display: none;
}

.onoffswitch-label {
  display: block;
  overflow: hidden;
  cursor: pointer;
  border: 2px solid #FFFFFF;
  border-radius: 20px;
}

.onoffswitch-inner {
  display: block;
  width: 200%;
  margin-left: -100%;
  transition: margin 0.3s ease-in 0s;
}

  .onoffswitch-inner:before, .onoffswitch-inner:after {
    display: block;
    float: left;
    width: 50%;
    height: 30px;
    padding: 0;
    line-height: 30px;
    font-size: 14px;
    color: white;
    font-family: Trebuchet, Arial, sans-serif;
    font-weight: bold;
    box-sizing: border-box;
  }

  .onoffswitch-inner:before {
    content: "ON";
    padding-left: 10px;
    background-color: #34C290;
    color: #FFFFFF;
  }

  .onoffswitch-inner:after {
    content: "OFF";
    padding-right: 10px;
    background-color: #EEEEEE;
    color: #999999;
    text-align: right;
  }

.onoffswitch-switch {
  display: block;
  width: 18px;
  margin: 6px;
  background: #FFFFFF;
  position: absolute;
  top: 0;
  bottom: 0;
  right: 56px;
  border: 2px solid #FFFFFF;
  border-radius: 20px;
  transition: all 0.3s ease-in 0s;
}

.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
  margin-left: 0;
}
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
  right: 0px;
}

.masterswitch {
  position: relative;
  width: 90px;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}

.masterswitch-checkbox {
  display: none;
}

.masterswitch-label {
  display: block;
  overflow: hidden;
  cursor: pointer;
  border: 2px solid #FFFFFF;
  border-radius: 20px;
}

.masterswitch-inner {
  display: block;
  width: 200%;
  margin-left: -100%;
  transition: margin 0.3s ease-in 0s;
}

.masterswitch-inner:before, .masterswitch-inner:after {
  display: block;
  float: left;
  width: 50%;
  height: 30px;
  padding: 0;
  line-height: 30px;
  font-size: 12px;
  color: white;
  font-family: Trebuchet, Arial, sans-serif;
  font-weight: bold;
  box-sizing: border-box;
}

.masterswitch-inner:before {
  content: "ARMED";
  padding-left: 10px;
  background-color: #F00000;
  color: #FFFFFF;
}

.masterswitch-inner:after {
  content: "OFF";
  padding-right: 10px;
  background-color: #EEEEEE;
  color: #999999;
  text-align: right;
}

.masterswitch-switch {
  display: block;
  width: 18px;
  margin: 6px;
  background: #FFFFFF;
  position: absolute;
  top: 0;
  bottom: 0;
  right: 56px;
  border: 2px solid #FFFFFF;
  border-radius: 20px;
  transition: all 0.3s ease-in 0s;
}

.masterswitch-checkbox:checked + .masterswitch-label .masterswitch-inner {
  margin-left: 0;
}

.masterswitch-checkbox:checked + .masterswitch-label .masterswitch-switch {
  right: 0px;
}
/* END OF SWITCH STYLES */

Web 控制面板样式表 – alarm-panel.css

应用样式表后,效果如下(我认为看起来更漂亮了,你应该会同意):

给它一些样式

带样式的 Web 控制面板

使其动态化

现在我们已经定义了控制面板页面的布局代码,我们可以将其插入到 PHP 页面中,这样 PHP 脚本就可以根据我们家庭安防系统的状态动态修改页面。

PHP 脚本将帮助我们实现以下基本功能:

  • 更新配置文件,包含各区域开关的位置

  • 武装和解除武装系统

  • 告诉我们在入侵被检测时,哪个区域被触发

再次强调,我不打算详细讲解 PHP 代码的工作原理,但希望代码中的注释能帮助你理解正在进行的操作,同时也能帮助你在需要时修改它的行为。

首先获得一些帮助

除非更改一些 PHP 配置,否则在代码中出现小错误时尝试找出问题可能会非常头疼,因为基本上你面对的是……什么都没有!

在创建和构建我们的 PHP 页面之前,我们将更改 PHP 配置文件中的一些设置,以确保我们知道是否存在任何问题:

  1. 使用Nano打开配置文件:

    $ sudo nano /etc/php5/cgi/php.ini
    
    
  2. 文件有点大而且笨重,但是通过它,找到这些设置,并按以下方式进行更改:

    error_reporting = E_ALL
    display_errors = On
    
  3. 保存文件并退出 Nano。

  4. 最后,重新启动 Lighty:

    $ sudo /etc/init.d/lighttpd restart
    
    

主 PHP 代码

就是这样……但是现在别运行它——还有更多事情要做……

你可以在chapter 7的代码文件夹内找到完整的主 PHP 代码。在我们的 Web 服务器内容文件夹中,现在应该有以下文件:

pi@raspberrypi ~ $ ls -1 /var/www
alarm-panel.css
alarm-panel.html
index.lighttpd.html
index.php
phpinfo.php

我是另一个人

现在,在我们实际能够成功打开这个 PHP 网页之前,我们需要意识到,默认情况下 Web 服务器实际上作为一个名为www-data的不同用户运行。这意味着它通常没有执行某些操作的权限;特别是那些涉及文件系统的操作。

如果你已经通过前面的 PHP 脚本工作过,你会发现它实际上执行了一些 Linux 命令来读取和更新我们的alarm.cfg文件。

与我们必须在许多命令前面加上sudo因为我们不是 root 用户一样,对其他用户也是如此,包括www-data。因此,为了给 Web 服务器赋予执行某些命令的权限,我们需要将其添加为一个sudoer,使用visudo实用程序。

运行实用程序打开 sudoer 配置文件:

$ sudo visudo

在文件底部添加以下行:

www-data ALL=(ALL) NOPASSWD:/bin/cat,/etc/pi-alarm/update-alarm-setting.sh

然后保存文件并退出。

最后一件事是创建一个小的Bash 脚本,用于处理更新我们的alarm.cfg文件中的设置任务。我们之所以需要这样做是因为我们将使用 Linux 的sed命令来更新文件。我们调用sed命令的方式意味着它需要创建一个临时文件。除非我们在配置 Web 服务器时进行一些工作以处理其文件位置上下文,否则它将无法正常工作。因此,创建一个被 PHP 脚本调用的简单的 Bash 脚本会更容易。这种方式下,Bash 环境处理临时文件上下文。

因此,我们将创建以下 Bash 脚本,并保存在我们的/etc/pi-alarm文件夹中:

#!/bin/bash
#/etc/pi-alarm/update-alarm-setting.sh
############################################
# Provides access to the sed command from  #
# PHP as it needs write access to a temp   #
# folder.                                  #
# $1 - Setting Name                        #
# $2 - Setting Value                       #
############################################

sed -i "s/^\($1\s*= *\).*/\1$2/" /etc/pi-alarm/alarm.cfg

update-alarm-setting.sh

然后我们需要赋予脚本执行权限:

$ sudo chmod 777 /etc/pi-alarm/update-alarm-setting.sh

此时,我们在/etc/pi-alarm文件夹中应该看到以下内容:

pi@raspberrypi ~ $ ls -1 /etc/pi-alarm
alarm.cfg
update-alarm-setting.sh

好了,经过这一切,我想现在我们可以在浏览器中启动控制面板页面了,位于

http://<my-pi-ip>.

index.php被配置为 Lighty 的默认页面,因此你不需要将其添加到 URL 的末尾;只需要 IP 地址就可以了。

通过更改开关位置,然后点击更新系统按钮,你应该会发现设置值在alarm.cfg中得到相应更新。现在你可以看到,这个文件将成为我们在第九章中开发的 Web 控制台和安全系统脚本之间交换状态的方式,整合一切

我是别人

最终操作控制面板

远程访问我们的控制面板

虽然我们可以设置系统在检测到入侵时接收电子邮件警报,但能在任何地方访问我们的基于 Web 的控制面板将非常有用,这样我们就可以在不在现场时,也许通过它来布防和撤防系统,或关闭某些区域。

但是,为了实现这一点,我们需要做一些准备工作:

设置动态 DNS 账户

我们大多数家里没有固定的 IP 地址来连接互联网;它可能会时常变化,尤其是在我们重启或拔掉路由器时,互联网服务提供商会在我们重新连接时分配一个新的 IP 地址。因此,我们不能依赖 IP 地址来访问家庭网络,特别是在外出时。为了解决这个问题,我们需要设置一个动态 DNS账户,这样我们就可以为我们的家庭网络设置一个域名(例如,myhomenetwork.com)。

它通过在你的网络内运行一个服务来工作,比如在路由器或笔记本电脑上,更新托管你域名的动态 DNS 服务,使用你当前的 IP 地址。然后,当你在浏览器中使用你的域名时,它将把你带到你家庭网络中的 Web 服务器。

市面上流行的动态 DNS 提供商包括 No-IP(www.noip.com)和 DynDNS(www.dyn.com)。你还可以通过 OpenDNS 获得一个免费的 DnsOMatic 账户来管理你的服务(www.dnsomatic.com)。

设置动态 DNS 账户

我的 Netgear NAS 设备有一个 DnsOMatic 更新服务插件

设置动态 DNS 账户

我的 Netgear 路由器提供更新动态 DNS 服务的选项

树莓派动态 DNS 客户端

由于你基于树莓派的家庭安全系统很可能始终开机,你可能更愿意在其上安装ddclient更新服务:

$ sudo apt-get install ddclient

安装完成后,你可以使用以下配置文件为你的特定服务和账户详细信息进行设置:

$ sudo nano /etc/ddclient.conf

在树莓派上设置静态 IP

为了让我们的家庭网络始终知道在哪里找到你的树莓派,我们需要为其设置静态 IP 地址,假设它目前每次启动时从路由器的 DHCP 服务器获取 IP 地址。

  1. 为此,我们需要编辑 Raspberry Pi 上的网络设置。在 Nano 中,打开以下配置文件:

    $ sudo nano /etc/network/interfaces
    
    
  2. 您可能会发现以太网端口配置类似于以下内容:

    auto eth0
    allow-hotplug eth0
    iface eth0 inet manual
    
    
  3. 将此配置更改为您网络中未使用的静态 IP 地址。在我的例子中,我将其设置为192.168.0.99。网关设置为我的互联网路由器的 IP 地址:

    auto eth0
    allow-hotplug eth0
    iface eth0 inet static
            address 192.168.0.99
            netmask 255.255.255.0
            gateway 192.168.0.1
    
  4. 现在,我们需要重启网络服务—请注意,您将会断开当前的终端会话。您需要使用新的 IP 地址重新连接:

    $ sudo /etc/init.d/networking restart
    
    

如果您遇到任何问题,只需使用sudo reboot重启 Pi,重新启动后所有问题应该都会解决。

端口转发

这个问题的最后一步是确保我们的互联网路由器能够将给定端口的传入流量定向到我们的 Raspberry Pi 的 Web 服务器。为了举例说明,我假设我们将保持 Web 服务器的默认端口 80。

提示

关于安全性的说明

由于我们的 Web 服务器现在可以从外部访问,我们需要注意确保系统的安全性。实现这一点的两种主要方法是将 Web 服务器的端口更改为 80 以外的随机数(例如,8799),并通过应用基本身份验证为您的网站添加密码保护。这两者都可以在lighttpd配置文件中完成。

大多数路由器都允许您将端口转发设置为其防火墙配置的一部分。基本上,设置这一项意味着来自互联网上某一给定 TCP 端口的任何传入流量将被允许通过路由器,并且会被定向到指定 IP 地址的设备。在我的 Netgear 路由器中,它的设置如下所示:

端口转发

在 Netgear 路由器上设置端口转发

现在,当您在浏览器中输入您的个人域名时,即使您不在家,您也应该能够访问您的警报控制面板。

提示

您可能还希望考虑打开端口 22,这样您就可以从外部网络使用 PuTTy 和 SSH 直接访问 Raspberry Pi。

总结

我们现在已经开始构建控制家庭安全系统的软件,首先确定了主配置文件的格式。我们还安装了一个 Web 服务器,并使用 PHP、HTML5 和 CSS3 构建了一个基本的单页控制面板,该面板可以在我们的手机上良好访问,允许我们配置系统并查看状态。

此外,我们已经学习了如何配置家庭网络和 Raspberry Pi,以便我们在不在家时也能访问控制面板。

在第九章,整合所有内容,我们将把所有电子元件组合在一起,并编写运行家庭安全系统的主要脚本。但在此之前,在下一章中,我们将探讨一些其他的组件,比如将一些其他传感器添加到我们的家庭安全系统中,这些传感器不一定与入侵检测相关。我们还将了解如何通过 Web 浏览器远程管理整个 Raspberry Pi 系统,除了访问我们的家庭安全控制面板。

第八章:一些杂项

前面的章节为我们提供了设计并组装整个家庭安全系统的基础和要素,我们将在下一章完成这一工作。我希望我已经以一种相对结构化和逻辑的方式指导你,帮助你为此做好准备。

不过,在此之前,我先放上一章名为杂项的事物,因为它确实是这样的。这一章包含了一些可选但有用的额外功能,我们应该考虑将其纳入系统,但它们并不足以单独占据一个完整的章节。我想你可以把它们看作是前几章的附注。

因此,我们将讨论以下几个话题:

  • 如何在没有基于 Web 的面板的情况下武装和解除系统

  • 从我们的 GPIO 输出安全驱动感性负载

  • 向我们的系统添加一个逃生水传感器输入

  • 向我们的系统添加一个温度传感器输入

  • 如何将一氧化碳探测器添加到我们的系统中

  • 使用 Webmin 远程管理我们的树莓派

武装和解除武装系统

我们在基于 Web 的控制面板上加入了一个开关,方便你通过智能手机来武装和解除武装系统。然而,这可能不是最方便的方式,特别是在你急匆匆出门时,或者你回到家时手机电池已没电。所以,我们需要找到一种额外的方式,在物业的出入口处武装和解除系统。

在上一章的区域列表表格中,你会注意到我将树莓派 GPIO 的输入 GP0 分配为我们的武装/解除武装开关输入。这个输入将与我们的控制面板开关配合使用。

这个输入可以是一个简单的切换开关,或者更安全一些,比如钥匙开关电子键盘。无论哪种方式,它将在系统武装时与树莓派的 GP0(GPIO17)接地连接。

武装和解除武装系统

我们的武装/解除武装开关的电路图

如果你有开关或其他类似的设备将在户外并暴露于环境中,你需要确保它们适合户外使用,以免损坏并影响系统的完整性。

武装和解除武装系统

适用于户外使用的 IP67 等级钥匙开关(型号 Lorlin WRL-5-E-S-2-B)

通过使用独立的安全键盘,你可以为每个用户分配一个密码来武装和解除系统。例如,CDVI ECO 100 是一款低成本的键盘,支持最多 100 个用户。当输入正确的密码时,它会通过关闭内部开关来武装系统;再次输入密码时,键盘通过打开开关来解除武装系统。

武装和解除武装系统

CDVI ECO 100 可编程键盘

驱动感性负载

我在第六章中讨论了驱动大负载的问题,向我们的安全系统添加摄像头,但现在可能是一个更好的时机,来进一步展开并讨论如何驱动感性负载,例如铃声和白炽灯泡。在之前的电路示例中,我使用了 TIP120 达林顿三极管来驱动一个不具感性的 LED 阵列。对于感性负载,您需要增加一些二极管保护,以防止继电器和铃声开关时产生的电磁干扰对电路造成影响。

这是我们带有 1N4007 整流二极管保护的数字负载驱动器的修改版电路:

驱动感性负载

带二极管保护的数字负载驱动器

超越入侵检测

家庭安全不仅仅是为了防止入侵,也是在防范其他风险,如洪水、火灾、一氧化碳泄漏等。因此,将我们的家庭安全系统扩展到检测这些其他风险也是有意义的。

您可以选择设置系统,让某些类型的警报仅通过电子邮件发送到您的手机,而不是触发所有外部的铃声、灯光和警报。这可以通过调整下一章中的脚本来实现,以便它们按照您想要的方式运行。

一个简单的水位探测器

没有什么比外出几天后回家发现厨房被水淹了更糟的了,因为水槽下方发生了漏水。我们的简单电路将检测到水的存在,并在我们的家庭安全系统中触发一个输入,从而可以提醒您。您也可以购买现成的套件和模块来完成这个功能,但下面的电路便宜且使用了我们的光耦合器,因为我们实际的探测器需要不同的电压。

一个简单的水位探测器

一个简单的水位探测器电路,已与 GPIO 输入隔离

工作原理

当水接触到探头时,电流就会流过水,并且流过三极管 Q1 基极的 R3/R4 电位分压器。当基极电流足够大,达到饱和状态时,三极管会完全导通,导致光耦合器内的 LED 亮起。这将拉低我们系统的输入引脚,并通过光耦合器内的光敏晶体管将其接地。

您可以使用微调电位器 R4 来校准传感器,通过调节其灵敏度。任何常见的 NPN 双极性晶体管都应该可以在这里工作,但显然它们的工作参数各不相同,所以需要选择一个合适的。

一个简单的温度传感器

如果我们希望在环境温度达到某个阈值时收到警报,那么我们可以使用常见的 LM34/LM35 温度传感器来构建一个电路。这是一个简单的设备,只有三个引脚:电源、地和输出,提供与温度成比例的电压。LM34 和 LM35 之间的区别在于,LM34 输出 10mV/°F,而 LM35 输出 10mV/°C。还有一个 LM335 变种,输出为 100mV/°K。

一个简单的温度传感器

引脚图来自德州仪器 LM35DZ 数据手册

你可能已经注意到,这实际上是一个模拟设备——那么我们该如何将它与我们完全数字化的系统进行连接呢?一种方法是将模拟到数字接口集成到我们的输入控制板上,读取从中传入的数据,以便我们知道确切的温度,但这可能超出了本书的范围。因此,我们将实现一个电路,当温度超过预设阈值时,会发出警报,这在我们的家庭安防系统中可能已经足够了。

注意

如果你有兴趣构建一个模拟到数字的模块来扩展你的家庭安防,那么可以看看 NXP 的 PCF8591 芯片,它是一个基于 I2C 的模拟到数字转换器。这将连接到我们已经在使用的 I2C 总线,因此它实际上只是一个附加模块。

bit.ly/NXPPCF8591T

对于我们的温度探测电路,我们将使用一个配置为比较器的运算放大器,当预设温度达到时触发我们的光耦合器输入。因此,对于火灾探测,我们可能希望在环境温度超过 50°C 时进行检测。

一个简单的温度传感器

用于驱动我们数字输入的温度阈值传感器

工作原理

参考电压由可变电阻 R4 设置,它在 12V 和地之间形成一个电压分压器。这实际上意味着,运算放大器比较器的正输入端上的参考电压可以在 0 到 12V 之间。假设我们希望检测到 50°C 时触发,我们将需要运算放大器在负输入为 500mV(10mV/°C)时触发。

在我们的电路中,运算放大器的输出在正常状态下是高电平,这使得光耦合器保持开启。然而,当达到阈值时,运算放大器的输出被拉低,切断晶体管 Q1,进而关闭光耦合器。这通过电阻 R2 将我们的报警输入拉高。

一氧化碳探测器

完全可以构建烟雾和一氧化碳探测器,并将其连接到我们的家庭安防系统,方式与之前的传感器类似,尽管它们稍微复杂一些,因为可能需要特殊处理。SparkFun MQ-7 一氧化碳 (CO) 探测器(实际上是由 Winsen Electronics 制造)可以像我们的温度传感器一样实现,当达到特定阈值时触发报警输入。

一氧化碳探测器

SparkFun 提供的 Winsen MQ-7 一氧化碳气体探测器。

注意

根据 ASHRAE(www.ashrae.org)的标准,一氧化碳(CO)的最大安全连续暴露量为 9ppm(百万分之一)。你绝对不应该长时间暴露在高于这个浓度的 CO 中,35ppm 是正常 8 小时工作日的最大值。

MQ-7 探测器的灵敏度范围在 10 到 500ppm 之间,因此在我看来,我希望它一旦检测到任何东西就发出警报,因此我们应该将比较器的参考电压设置为灵敏度曲线低端的值,以符合数据表中提供的灵敏度曲线,如下所示:

一氧化碳探测器

来自 Winsen MQ-7 制造商数据表中的灵敏度曲线。

注意

警告

我加入这一部分关于一氧化碳探测的内容,更多的是出于兴趣。它是一种有害物质,虽然出于兴趣自己制作探测器是可以的,但请仅限于此。将其作为我们家庭安防系统的一部分,能够在我们外出时提醒我们,这是有用的,但这 不应 作为一个商用探测器的替代品,商用探测器会放在锅炉旁,具备所有的认证、标准等,并且在我们家里时发出非常响亮的警报。

我们的树莓派远程管理

在上一章中,我们学习了如何设置系统和家庭网络,以便我们可以在任何地方远程访问报警控制面板。现在,我将向你展示如何扩展这个设置,以便能够管理和监控整个树莓派系统。

获取 Webmin

Webmin 是一个非常优秀且成熟的基于 Web 的界面,用于管理 Unix/Linux 系统。你可以在 www.webmin.com 上找到关于 Webmin 的所有信息。像本书中所有的内容一样,我假设你在树莓派上使用的是 Raspbian 发行版来安装 Webmin。

安装 Webmin 有几种方式:可以手动下载并解压,或者更新我们的软件源,以便我们可以使用 apt-get。我将选择后者,这样所有依赖项都会自动安装,未来更新也能更容易管理。虽然有几个步骤,但其实非常直接:

更新软件源

  1. 我们需要做的第一件事是更新我们的存储库源,将 Webmin 存储库添加进去:

    $ sudo nano /etc/apt/sources.list
    
    
  2. 将以下两行添加到文件末尾:

    deb http://download.webmin.com/download/repository sarge contrib
    deb http://webmin.mirror.somersettechsolutions.co.uk/repository sarge contrib
    
    
  3. 保存并退出 Nano。

导入签名密钥

  1. 接下来,我们需要下载并导入存储库的签名密钥:

    $ cd ~
    $ sudo wget http://www.webmin.com/jcameron-key.asc
    $ sudo apt-key add jcameron-key.asc
    
    
  2. 现在我们拥有了所需的一切,可以更新包管理器并安装 Webmin。这可能需要一些时间,所以你可能想去泡一杯茶或咖啡:

    $ sudo apt-get update
    $ sudo apt-get install webmin
    
    
  3. 安装完成后,你应该在命令行窗口看到以下消息:导入签名密钥

    Webmin 安装

本地访问 Webmin

默认情况下,Webmin 运行在 10000 端口,并使用安全的 HTTPS 协议;因此,要访问它,你需要在浏览器中输入以下 URL:

https://<my-ip>:10000

其中 <my-ip> 是你树莓派的 IP 地址。

在上一章中,我们在系统上设置了静态 IP 地址;在我的案例中,我将地址设置为 192.168.0.99。所以,要访问我系统上的 Webmin,我将使用:

https://192.168.0.99:10000

注意

HTTPS 隐私错误

在某些浏览器中,例如 Google Chrome,你可能会看到一个隐私错误,当你尝试访问 Webmin 网页时。这是因为 HTTPS 连接背后的 SSL 证书没有被一个已知的认证机构签署。这没问题——只需告诉浏览器你想接受此证书并继续操作(在 Chrome 中,你需要先点击 高级 链接才能看到该选项)。

本地访问 Webmin

你可以使用 rootpi 用户账户登录 Webmin,或者任何具有 sudo 权限的账户:

本地访问 Webmin

Webmin 登录

登录后,你将看到系统的主信息页面。可以好好探索一下,因为这里有很多有用的内容可以查看和操作。

本地访问 Webmin

Webmin 系统信息视图

Webmin 自带了许多模块,并不是所有模块都会被安装;因此,你可能想要探索一下面板中的 未使用模块 部分,看看是否有任何你希望添加到 Webmin 的模块。

远程访问 Webmin

就像我们在上一章中为报警控制面板设置远程访问一样,你也可以用 Webmin 来做到这一点——只需在路由器上为端口 10000 设置端口转发。然后你可以通过 https://<my-public-ip>:10000 在任何地方访问 Webmin。

总结

这章算是对多个主题的混合和总结,在我们建立家庭安全系统框架之前。我希望你喜欢这些与前几章相关的补充内容,并且它能给你一些灵感,帮助你理解家庭安全系统可以做到什么程度。

我们首先查看了如何在不访问基于 Web 的控制面板的情况下,通过添加一个机械或数字开关到臂/解臂输入端,来对系统进行武装和解除武装。

然后我们研究了将模拟类型传感器添加到我们的系统中,利用设置为电压比较器的运算放大器,当达到某个阈值时可以提醒我们。这些比较器电路背后的思想可以应用于不同类型的传感器,用于检测模拟传感器输出端何时达到某一电压阈值。

最后,我们学习了如何在树莓派上安装 Webmin,以便我们能够监控和配置 Linux 操作系统的多个方面。

下一章是我们所有人期待的时刻;我们将把前几章中的所有元素和概念结合起来,构建我们完整的系统,包含我们想要实现的各个元素。整个过程的明星将是我们的 Bash 脚本,它将把所有这些元素串联起来,并提供整个系统的控制逻辑。

第九章. 将所有内容整合

在过去的八章中,我们探讨了你希望在住宅中安装的全功能家庭安全系统的元素和概念。它以模块化的方式呈现,这样你可以选择系统中想要的功能,从而使其变得紧凑基础,或大型复杂,完全根据你的需求来定制。

从根本上讲,家庭安全系统的理念是检测特定区域输入是否被外部传感器触发为高或低信号,不管是开关、运动探测器,还是水探测器。归根结底,控制软件关心的不是传感器的类型,而是系统软件的任务就是检查输入的状态并根据情况发出警报。

在本章的最后,我们将把所有概念整合在一起,构建一个安全系统框架,并围绕它编写控制脚本。我们将覆盖以下内容:

  • 定义我们系统的高层次概述,详细说明连接的元素

  • 构建整个模块化安全系统框架控制脚本,详细探讨代码

  • 深入探讨一些详细的 Shell 脚本技巧,用于执行某些任务

  • 学习如何让我们的系统在启动时自动启动

  • 通过创建基于 RAM 的文件系统来防止 SD 卡烧毁

报警系统示意图

为了避免在过程中迷失方向,我建议首先绘制出一张完整的系统示意图,以便我们能够遵循。我在设计和组装任何系统时都会这么做,这样可以以结构化的方式构建系统,并且便于文档化和修改。

对于本章中的家庭安全系统,我设计了以下系统示意图,我们将以此作为框架。整个概念设计为模块化的,因此你可以根据自己的需求设计适合的系统,并根据本章中展示的脚本进行实现。

报警系统示意图

最终的家庭安全系统示意图

系统元素概述

前面的系统示意图包含了我们在前几章中讨论的元素和模块。以下是这些内容的快速回顾:

一个 +12V 电源

这是我们系统的主要电源,我们将从一个外部的主电源适配器中获取,适配器可能是电池备份的。此电源需要平稳且经过调节,以确保其对当前绘制的系统保持稳定。

所有报警接线和传感器将由该电源供电,外设如声响器和铃声也会从 12V 电源获得电力。第五章,添加被动红外运动传感器 讨论了使用 12V 电源为报警电路供电的优点。

一个 +3.3V 电源

此电源是数字端口扩展电路的调节+3.3V 电源;它还通过光耦合器提供逻辑报警区输入。+3.3V 电源可以从+12V 电源(建议)或根据所需电流选择树莓派 GPIO 连接器上的+5V 电源派生,使用电压调节器。

第三章,扩展 Pi 以连接更多事物,向您展示了如何构建+3.3V 调节供电。

光电隔离输入模块

当高触发时,此将隔离+12V 区域输入电源线与端口扩展器和 GPIO 数字输入,并且当高时应只向其呈现最大+3.3V。

这些光电隔离输入模块的电路在第五章中进行了讨论并显示出来,添加被动红外运动传感器

端口扩展器

端口扩展器是我们的主要数字输入/输出系统,将接收报警区输入并通过 I2C 总线将其传输到树莓派,或允许树莓派开关输出。

我们在第三章中构建了基于 MCP23017 的端口扩展器电路,扩展 Pi 以连接更多事物,并在第四章中为其配置了软件,添加磁接触传感器

武装/解除开关

武器/解除输入覆盖了我们基于 Web 的控制面板上的武器/解除软开关功能,并且是连接到树莓派 GPIO 连接器上的 GP0 的开关(键盘、数字键盘或其他)。

记住将任何开关电路适当连接到 GPIO 引脚,以避免损坏您的树莓派。这在第二章中进行了讨论,使用 GPIO 连接 Pi 的事物

报警输出

在我们的系统中,我们有几个输出设备由我们的树莓派通过输出驱动电路控制。我们有一个出入口蜂鸣器的输出,一个武装状态 LED,一个报警铃声和一个报警 LED 指示灯。

这些由我们的树莓派 GPIO 连接器通过驱动电路开关控制,允许我们使用 GPIO 引脚驱动高电流和感性负载。这些驱动电路基于 TIP120 达林顿晶体管,在第六章中讨论过,向我们的安全系统添加摄像头和第八章中讨论过,各种各样的事物

设计控制脚本

在我们开始编写控制我们的报警系统的脚本之前,最好先概述系统的高级流程。以下流程图帮助我们理解我们的系统应如何工作,以及我们的脚本需要做出的各种逻辑决策。

流程图可能看起来有些复杂,因为它的线条方向各异,但实际上它是非常线性且朝下的。参考流程图,它展示了控制脚本将要执行的以下任务:

  • 安静地坐着,直到通过硬件钥匙开关或基于 Web 的面板软开关启动系统。

  • 当系统首次启用时,它将在真正启动系统之前响起出口蜂鸣器一段预定的时间。这为您提供了离开房产或再次解除系统的机会,系统才开始监控输入。

  • 一旦系统被启用,启用指示灯将亮起,系统将等待查看是否有任何报警区的输入被触发。它还将等待查看是否在您返回房产时解除报警。我们还可以选择在入口区添加一个进入计时器,以延迟触发报警。

  • 如果报警最终被触发,主报警铃声和出口蜂鸣器将被打开。主铃声应仅持续一段时间,具体时间取决于您所在社区的环境限制,因此,它将在预定时间后关闭,但内部蜂鸣器将保持开启。

  • 一旦触发,系统将等待您解除警报,然后再进行重置。设计控制脚本

    控制脚本流程图

构建控制脚本

现在我们已经按照预期设计好了系统,接下来可以开始编写我们的 Bash 控制脚本。和之前一样,我们将把脚本放在/etc/pi-alarm文件夹中,您可能还记得在第七章,构建基于 Web 的控制面板中,也有提到该文件夹是我们的 Web 控制面板写入配置状态文件alarm.cfg的地方。我们在脚本中也将引用该文件。

在这个脚本中,我们将使用bc工具(Bash 命令行计算器)将十六进制值转换为二进制。这个工具默认并未安装,因此您需要安装该软件包:

$ sudo apt-get update
$ sudo apt-get install bc

提示

我们的脚本文件相当长,所以和之前一样,您可能会想坐在沙发上用笔记本电脑写它,使用类似 Notepad++的工具。不过请记住,如果您使用的是 PC,确保将行尾(EOL)格式转换为 Unix 格式,否则当您将脚本复制到 Pi 时,Bash 脚本将无法运行。Notepad++会为您自动完成这项转换。

探索脚本代码

现在,我将引导您了解我所编写的控制脚本代码的各个部分,它将作为我们系统的框架。我之所以称其为“框架”,是因为尽管它为您提供了一个完全可用的控制脚本,但它仍然可以根据您的特定需求进行修改和扩展。

以下代码清单都属于一个名为alarm-control.sh的单一 bash 脚本,您可以从 Packt Publishing 网站下载完整的带注释的版本。

声明

我们首先设置追踪系统状态所需的各种控制变量

#!/bin/bash
#/etc/pi-alarm/alarm-control.sh

ALM_BELL_DURATION=600    #duration in seconds the alarm bell should sound for
ALM_EXIT_DELAY=30    #entry/exit zone delay in seconds
ALM_KEY_ARMED=0    #status of the arm/disarm key switch
ALM_SYS_ARMED=0    #armed status of the system

ALM_ZONE_INPUT_READ=""      #this will store the value of the zone inputs read
ALM_ZONE_INPUT_STAT="00000000"    #binary representation of the inputs (b7-b0)
ALM_ZONE_INPUT_PREV=""      #previous zone input status
ALM_ZONE_TRIGGER=0    #this will be set to 1 if one or more zones is triggered
ALM_ZONES_STAT=(0 0 0 0 0 0 0 0)    #dynamic array of normalised zone status (z1 to z8 order) - 1 is triggered

STAT_RET_VAL=""    #return value from functions

因为我们可能会遇到这样一种情况,输入的 HIGH 或 LOW 可能代表一个触发区域,具体取决于其配置和接线方式,所以我在变量ALM_ZONES_STAT中引入了一组标准化的状态标志,这将是脚本所关注的最终状态。我们稍后会查看处理这个的函数。

更新配置设置

在第七章,构建基于 Web 的控制面板中,我们介绍了配置文件alarm.cfg,该文件存储系统状态和配置,以便 Web 控制面板使用。这个文件不仅需要被主控制脚本读取,以便获取通过控制面板进行的设置,还需要通过主控制脚本更新状态值,以便它们可以反馈到控制面板,基本上是在两个子系统之间交换数据。

因此,我们将包含一个帮助函数,该函数包含与网页 PHP 脚本调用相同的代码,用来从控制面板更新此文件:

#This helper function will update the alarm config
#file with the specified value (alarm.cfg) so that
#the Web panel can know the latest status
function almUpdateConfigSetting()
{
  #$1 - Setting Name
  #$2 - Setting Value
  sudo sed -i "s/^\($1\s*= *\).*/\1$2/" /etc/pi-alarm/alarm.cfg
}

设置 GPIO

我们现在需要根据之前的系统图设置树莓派的 GPIO 引脚。以下命令最早在第二章,用 GPIO 连接树莓派中讨论过:

# GPIO SET UP ###################################
#Set up the Raspberry Pi GPIO pins
#Refer to Chapter 2 for info
#D0 (GPIO17) Arm/Disarm Key Input
sudo echo 17 > /sys/class/gpio/export
sudo echo in > /sys/class/gpio/gpio17/direction

#D4 (GPIO23) Armed LED Output
sudo echo 23 > /sys/class/gpio/export
sudo echo out > /sys/class/gpio/gpio23/direction
sudo echo 0 > /sys/class/gpio/gpio23/value

#D5 (GPIO24) Exit Buzzer Output
sudo echo 24 > /sys/class/gpio/export
sudo echo out > /sys/class/gpio/gpio24/direction
sudo echo 0 > /sys/class/gpio/gpio24/value

#D6 (GPIO25) Alarm LED Output
sudo echo 25 > /sys/class/gpio/export
sudo echo out > /sys/class/gpio/gpio25/direction
sudo echo 0 > /sys/class/gpio/gpio25/value

#D7 (GPIO4)  Alarm Bell Output
sudo echo 4 > /sys/class/gpio/export
sudo echo out > /sys/class/gpio/gpio4/direction
sudo echo 0 > /sys/class/gpio/gpio4/value

注意

请注意,GPIO 引脚只能导出一次,除非它已被取消导出。因此,如果你重新运行脚本,试图再次导出引脚,你可能会看到错误echo: 写入错误: 设备或资源忙碌。你可以安全地忽略这个错误。

我们还会添加一些帮助函数,这些函数将使我们能够轻松地打开或关闭各种输出,从而简化主代码。我是一个很喜欢实现函数的人,不管它们多么简单,因为它们使代码保持模块化、可重用,并且在大多数情况下更容易阅读:

#This helper function will switch a specified GPIO output on or off
function almSetGPIOValue()
{
  #$1 - GPIO pin number
  #$2 - Value
  sudo echo $2 > /sys/class/gpio/gpio$1/value
}
#Helper functions to switch on and off the outputs
function almSetArmedLED()
{
  #$1 - 0 or 1 (Off or On)
  almSetGPIOValue 23 $1
  echo "[ALM] Armed LED set to $1"
}
function almSetExitBuzzer()
{
  #$1 - 0 or 1 (Off or On)
  almSetGPIOValue 24 $1
  echo "[ALM] Exit Buzzer set to $1"
}
function almSetAlarmLED()
{
  #$1 - 0 or 1 (Off or On)
  almSetGPIOValue 25 $1
  echo "[ALM] Alarm Trigger LED set to $1"
}
function almSetAlarmBell()
{
  #$1 - 0 or 1 (Off or On)
  almSetGPIOValue 4 $1
  echo "[ALM] Alarm Bell set to $1"
}

并且,我们将添加一个帮助函数,它将从树莓派的 D0(GPIO17)和 Web 控制台读取 ARM 开关状态,以查看 ARM 软开关是否已经设置:

#this function returns whether the system is armed via
#either the web console or key switch
function almGetArmedSwitchStatus()
{
  STAT_RET_VAL="0"
  #read arm key switch input from 
  local L_VAL=$(sudo cat /sys/class/gpio/gpio17/value)
  if [ $L_VAL -eq 1 ]; then
    #system has been armed with key switch
    echo "[ALM] System ARMED with key switch"
    ALM_KEY_ARMED=1
    almUpdateConfigSetting "SYSTEM_ARMED" "1" #set system armed console flag
    STAT_RET_VAL="1"
  else
    #read system armed value from web console config file
    if [ $SYSTEM_ARMED == 1 ]; then
      echo "[ALM] System ARMED with web console"
      STAT_RET_VAL="1"
    fi
  fi
}

设置 I2C 端口扩展器

接下来的几行代码设置 I2C 端口扩展器,将端口 A 和端口 B 上的所有引脚设置为输入。在我们的系统中,我们只使用端口 A,但这也允许我们在需要扩展系统时再增加 8 个输入。我们在第四章,添加磁性接触传感器中讨论过这个问题:

# PORT EXPANDER SET UP ##########################
#Refer to Chapter 4 for more information about the I2C bus

#We will set up I/O BUS A as all inputs
sudo i2cset -y 1 0x20 0x00 0xFF

#Whilst we're not using BUS B in our system,
#we can set that up as all inputs too
sudo i2cset -y 1 0x20 0x01 0xFF

注意

如果你没有连接 I2C 端口扩展器,那么在你尝试运行这些命令时,会看到以下错误:错误:写入失败

解码区域输入状态

下一个功能是关键的功能—对我们的系统至关重要。它将从 I2C 端口扩展器读取端口 A 的值。该值将以十六进制返回,因此我们需要将其转换为二进制值,用 0 或 1 标记每个输入位。我们将使用之前安装的bc工具来完成此操作。

一旦我们获得了每个输入位的状态,我们就通过确定 0 或 1 表示正触发来规范化状态。结果输出是数组ALM_ZONES_STAT,它包含每个区域的状态——其中 1 表示正被触发的区域。

#This function will read the port inputs and set the
#status of each zone
function almReadZoneInputs()
{
  #preserve previous zone status
  ALM_ZONE_INPUT_PREV=$ALM_ZONE_INPUT_STAT
  #read the 8-bit hex value of port a
  ALM_ZONE_INPUT_READ=$(sudo i2cget -y 1 0x20 0x12)

  if [[ $ALM_ZONE_INPUT_READ = *"Error"* ]]; then
    #An error occurred reading the I2C bus - set default value
    ALM_ZONE_INPUT_READ="0x00"
  fi

  #remove the 0x at the start of the value to get the hex value
  local L_HEX=${ALM_ZONE_INPUT_READ:2}
  #convert the hex value to binary
  local L_BIN=$(echo "obase=2; ibase=16; $L_HEX" | bc )
  #zero pad the binary to represent all 8 bits (b7-b0)
  ALM_ZONE_INPUT_STAT=$(printf "%08d" $L_BIN)

  echo "[ALM] Zone I/O Status: $ALM_ZONE_INPUT_STAT ($ALM_ZONE_INPUT_READ)"

  #check each zone input to see if it's in a triggered state
  #a triggered state may be either 1 or 0 depending on the input's configuration
  #you'll need to set the logic here accordingly for each input
  #the ALM_ZONES_STAT array contains the definitive trigger value for each input

  #zone 1 test (bit 0)
  local L_FLG=${ALM_ZONE_INPUT_STAT:7:1}
  if [ $L_FLG -eq 0 ]; then ALM_ZONES_STAT[0]=0; else ALM_ZONES_STAT[0]=1; fi

  #zone 2 test (bit 1)
  local L_FLG=${ALM_ZONE_INPUT_STAT:6:1}
  if [ $L_FLG -eq 0 ]; then ALM_ZONES_STAT[1]=0; else ALM_ZONES_STAT[1]=1; fi
  #zone 3 test (bit 2)
  local L_FLG=${ALM_ZONE_INPUT_STAT:5:1}
  if [ $L_FLG -eq 0 ]; then ALM_ZONES_STAT[2]=0; else ALM_ZONES_STAT[2]=1; fi

  #zone 4 test (bit 3)
  local L_FLG=${ALM_ZONE_INPUT_STAT:4:1}
  if [ $L_FLG -eq 0 ]; then ALM_ZONES_STAT[3]=0; else ALM_ZONES_STAT[3]=1; fi

  #zone 5 test (bit 4)
  local L_FLG=${ALM_ZONE_INPUT_STAT:3:1}
  if [ $L_FLG -eq 0 ]; then ALM_ZONES_STAT[4]=0; else ALM_ZONES_STAT[4]=1; fi

  #zone 6 test (bit 5)
  local L_FLG=${ALM_ZONE_INPUT_STAT:2:1}
  if [ $L_FLG -eq 0 ]; then ALM_ZONES_STAT[5]=0; else ALM_ZONES_STAT[5]=1; fi

  #zone 7 test (bit 6)
  local L_FLG=${ALM_ZONE_INPUT_STAT:1:1}
  if [ $L_FLG -eq 0 ]; then ALM_ZONES_STAT[6]=0; else ALM_ZONES_STAT[6]=1; fi

  #zone 8 test (bit 7)
  local L_FLG=${ALM_ZONE_INPUT_STAT:0:1}
  if [ $L_FLG -eq 0 ]; then ALM_ZONES_STAT[7]=0; else ALM_ZONES_STAT[7]=1; fi

  echo "[ALM] Zone Trigger Status: $ALM_ZONES_STAT[*]"
}

初始化

现在我们已经声明了模块级变量和辅助函数,我们将开始我们的主程序。首先,我们将初始化系统,清除SYSTEM_ARMED状态,并从配置文件中读取初始设置:

# initialise system #########
echo "[ALM] Initialising system..."
almUpdateConfigSetting "SYSTEM_ARMED" "0" #clear system armed console flag
sleep 1
sudo cat /etc/pi-alarm/alarm.cfg
sleep 1
echo "[ALM] Initialising done"
#############################

系统监控循环

脚本随后进入一个永无止境的循环,这将成为主要控制系统,监控武装/解除武装状态,并在武装时,监控区域输入状态并做出相应反应:

# loop continuously###########
while true
do

  # wait for system to be armed ###############
  echo "[ALM] Alarm now in STAND-BY state - waiting to be armed"
  almSetArmedLED 0 #switch off armed LED
  STAT_RET_VAL="0"
  while [[ $STAT_RET_VAL = "0" ]]; do  
    sleep 1
    #read the control panel status file
    . /etc/pi-alarm/alarm.cfg
    almGetArmedSwitchStatus #result is returned in STAT_RET_VAL
    echo -n "*" # indicate standby mode
  done
  #############################################

武装系统

当系统进入 ARMED 状态时,它将首先启动退出蜂鸣器,然后等待预定的时间。这将给你时间离开房产或解除系统武装:

  # perform exit delay ########################
  echo "[ALM] Alarm now in EXIT DELAY state"
  almSetExitBuzzer 1 #switch on exit buzzer
  COUNTER=$ALM_EXIT_DELAY
  while [[ $STAT_RET_VAL = "1" && $COUNTER -gt 0 ]]; do
    sleep 1
    #read the control panel status file
    . /etc/pi-alarm/alarm.cfg
    almGetArmedSwitchStatus #result is returned in STAT_RET_VAL
    COUNTER-=1
    echo -n "X$COUNTER " # indicate exit mode
  done
  almSetExitBuzzer 0 #switch off exit buzzer
  #############################################

  # system now armed - monitor inputs #########
  ALM_SYS_ARMED=1
  echo "[ALM] Alarm now in ARMED state"  
  almSetArmedLED 1 #switch on armed LED

  #read the control panel status file
  . /etc/pi-alarm/alarm.cfg  
  almReadZoneInputs  # > ALM_ZONES_STAT[x]

监控区域

一旦武装,系统将持续监控区域输入,直到系统被解除武装或某个区域输入被触发。当区域被触发时,系统将检查ZONE_ENABLE_n配置,看看该区域是否被禁用(此操作在基于网页的控制面板中完成)。如果区域未被禁用,则视为报警系统被触发。

ZONE_STATUS_n设置也在此更新,以便基于网页的控制面板显示出哪些区域已被触发:

  #check each zone input to set if it's enable
  #and has been triggered  
  #NUM_ZONES setting is stored in alarm.cfg

  while [[ $ALM_SYS_ARMED -eq 1 ]]; do
    echo -n "A" #indicate armed mode

    ALM_ZONE_TRIGGER=0  
    for (( i=$NUM_ZONES; i>0; i-- )); do  
      if [[ $ALM_ZONES_STAT[$i-1] -eq 1 ]]; then
        #zone has been triggered
        echo "[ALM] Zone $i TRIGGERED"
        E_VAR="ZONE_ENABLE_$i"
        E_VAL=`echo "$E_VAR"` #get zone enabled status loaded from alarm.cfg

        if [[ $E_VAL -eq 1 ]]; then
          #zone is enabled
          ALM_ZONE_TRIGGER=1 #set alarm triggered flag
          echo "[ALM] Zone $i ENABLED - alarm will be triggered"
          almUpdateConfigSetting "ZONE_STATUS_$i" "1" 

          ## YOU CAN INSERT CODE HERE TO TAKE CAMERA IMAGE IF YOU WANT##
          ## REFER BACK TO CHAPTER 6 ##

        fi
      fi
    done

    . /etc/pi-alarm/alarm.cfg
    almGetArmedSwitchStatus #result is returned in STAT_RET_VAL

入口延迟

当报警区域被触发时,系统将首先检查是否触发的是进出口区。如果是,系统将在发出主报警声之前延迟,给你机会解除系统武装。此时只会响起入口蜂鸣器:

    if [[ $ALM_ZONE_TRIGGER -eq 1 ]]; then
      # alarm has been triggered    
      almSetAlarmLED 1
      echo "[ALM] A zone has been triggered"

      #####################################
      # ZONE 1 is the ENTRY zone - if that's triggered then delay
      if [[ $ALM_ZONES_STAT[0] -eq 1 ]]; then
        # perform entry delay ###########
        echo "[ALM] Alarm now in ENTRY state"
        setExitBuzzer 1 #switch on entry/exit buzzer

        COUNTER=$ALM_EXIT_DELAY  
        STAT_RET_VAL="0"
        while [[ $STAT_RET_VAL = "1" && $COUNTER -gt 0 ]]; do
          echo -n "E$COUNTER " #indicate entry mode
          sleep 1
          #read the control panel status file
          . /etc/pi-alarm/alarm.cfg
          almGetArmedSwitchStatus #result is returned in STAT_RET_VAL    
          COUNTER-=1
        done
      fi
      #####################################

响起主报警

如果此时系统还没有解除武装,我们需要响起主铃。由于环境噪声限制,我们对铃声的持续时间有所限制;我们不希望报警响上几个小时,直到我们回家才停止,这样会烦扰邻居。此时,如果你希望收到发送到手机的警报邮件,可以参考第六章,向我们的安全系统添加摄像头,添加代码:

      #####################################
      # STAY in TRIGGERED mode until system has been disarmed
      if [[ $STAT_RET_VAL = "1" ]]; then
        #alarm has not been disabled
        almSetAlarmBell 1 #switch on alarm bell
        echo "[ALM] Alarm now in TRIGGERED state"

        ## YOU CAN INSERT CODE HERE TO SEND YOU AN EMAIL IF YOU WANT##
        ## REFER BACK TO CHAPTER 6 ##

        COUNTER=0
        STAT_RET_VAL="0"
        while [[ $STAT_RET_VAL = "1" ]]; do
          echo -n "T$COUNTER " #indicate triggered mode
          sleep 1
          #read the control panel status file
          . /etc/pi-alarm/alarm.cfg
          almGetArmedSwitchStatus #result is returned in STAT_RET_VAL    

          COUNTER+=1
          if [[ $COUNTER -gt $ALM_BELL_DURATION ]]; then
            almSetAlarmBell 0 #switch off alarm bell            
            echo "[ALM] Bell has been switched OFF"
          fi          
        done
      fi
      #####################################

解除武装并重置系统

当我们解除系统武装时,需要重置其状态并完成监控循环,这样我们就可以重新开始并等待重新武装:

      # alarm has been disarmed ##########
      echo "[ALM] Alarm has been DISARMED"
      ALM_SYS_ARMED=0
      almSetAlarmBell 0 #switch off alarm bell
      almSetExitBuzzer 0 #switch off exit buzzer
      almSetAlarmLED 0
      almSetArmedLED 0 #switch off armed LED

      #####################################
    fi

  done
  #############################################

done
#############################################

我们完成了(差不多)…

到这里了:树莓派上整个报警控制脚本的框架。如果你希望在脚本中实现其他功能,可能包括以下内容:

  • 当触发时,从某区域的摄像头发送照片或视频片段

  • 当触发报警时,发送包含状态详情的电子邮件提醒

  • 写入一个定期的日志文件,记录历史状态信息

  • 将额外的环境传感器添加到 B 端口

    提示

    每个脚本块都来自单个脚本文件alarm-control.sh,所以你应该能够将所有描述的部分组合到一个文件中,以便拥有一个完全功能的脚本。

一如既往,在运行脚本之前,我们需要赋予它执行权限:

$ sudo chmod 777 /etc/pi-alarm/alarm-control.sh

将脚本复制到树莓派后,我们应该能在/etc/pi-alarm文件夹中看到以下内容:

pi@raspberrypi ~ $ ls -1 /etc/pi-alarm
alarm.cfg
alarm-control.sh
update-alarm-setting.sh

自动启动系统

现在,显然我们不希望每次树莓派启动时都手动启动报警控制脚本,例如在停电后——首先,我们甚至可能不在现场。因此,我们需要设置操作系统,使其在启动时自动启动alarm-control.sh脚本。

为此,我们需要使用 Nano 编辑rc.local文件:

$ sudo nano /etc/rc.local

在包含exit 0的行之前,插入以下一行:

sudo /etc/pi-alarm/alarm-control.sh &

注意

行末的&符号很重要,因为它会使脚本在一个不同的进程中运行,否则rc.local脚本将永远无法退出。

你的rc.local文件现在应该像这样:

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

sudo /etc/pi-alarm/alarm-control.sh &
exit 0

操作系统在系统启动后运行rc.local脚本,因此你可以将任何想要在此时自动发生的操作放入该脚本。

保持 SD 卡的完整性

我想分享的最后一个话题是如何保护树莓派的 SD 卡。SD 卡有有限的写入周期,持续写入会最终损坏它。如果我们要频繁写入日志文件并拍摄大量图像,我们就需要保护 SD 卡,以保持系统的完整性和可靠性;使用系统 RAM 来代替可以帮助我们实现这一点。

创建一个基于 RAM 的文件系统

树莓派拥有大量的快速系统 RAM(最新型号有 1Gb),它不容易受到写入烧毁问题的影响。因此,我将向你展示如何分配一部分内存,用来创建一个临时磁盘,这样我们可以将不需要保存在 SD 卡上的文件写入其中。此类文件包括那些相当大的摄像头图像文件,它们会被通过电子邮件发送出去——因此不需要永久存储。你还应该考虑那些定期写入的日志文件,这些日志文件会定期从系统中传送出去。

注意

请记住,这是一个基于 RAM 的文件系统,所以当树莓派关闭或重启时,内容会丢失。因此,不要将任何需要在重启后保留的数据存储在这里。

让我们创建一个名为 setup-ramfs.sh 的 Bash 脚本文件,并将其复制到我们的 /etc/pi-alarm 文件夹中:

#!/bin/bash
#/etc/pi-alarm/setup-ramfs.sh

RAM_DISK="/ramfs"
RAM_DISK_SIZE=64M

# Create RAM Disk ##########################
if [ ! -z "$RAM_DISK" ]; then
  echo "[INIT] Creating RAM Disk... $RAM_DISK"
  mkdir -p $RAM_DISK
  chmod 777 $RAM_DISK
  mount -t tmpfs -o size=$RAM_DISK_SIZE tmpts $RAM_DISK/
  echo "[INIT] RAM Disk created at $RAM_DISK"  
fi
############################################

setup-ramfs.sh RAM 磁盘创建脚本

运行前面的脚本将会在 /ramfs 创建一个 RAM 磁盘文件夹——你可以像对待任何其他文件夹一样对待它;唯一不同的是,它存在于系统内存中,而不是 SD 卡上:

$ cd /ramfs
$ ls

你可以通过在 alarm-control.sh 脚本中包含以下行,在初始化过程中调用此脚本:

. /etc/pi-alarm/setup-ramfs.sh

结论

树莓派是一款强大的小型计算机,是构建低成本但高度可用的嵌入式系统的理想平台。其 GPIO 接口的设计使得通过简单的低成本电子元件和一些配置,就能轻松地将模块与之连接,从而构建出功能强大且灵活的系统。专用的相机接口和网络接口的加入,提供了构建互联网连接家庭安全系统所需的所有功能。

本书中我涉及了很多话题,虽然我本可以继续深入,但我希望我所呈现的内容是有条理且方法论的,并且能为你提供继续这段旅程的工具和技巧,使你能够根据自己的需求打造完美的家庭安全系统。

构建系统的技巧

作为一个需要日常处理多种技术和学科的系统工程师,我只想给你留下以下几点思考,供你在选择基于我们在本书中所构建的系统进行扩展时参考,当然,我希望你会选择这样做:

  • 首先创建你提议系统的高层次图——有点像我在本章早些时候展示的那种图。

  • 以模块化的方式定义所有内容,这样你可以将系统分成小块进行构建和测试,这使得尽早发现问题变得更容易。

  • 使用更小模块来构建系统,使得重新使用和替换电路和代码变得更加容易,而且不要害怕混合和搭配不同的技术,使用最适合单个模块的技术。

  • 不要试图重新发明轮子——使用已有的经过验证的代码和电路资源。这可以让你更快速地让系统工作,并最小化你与难题作斗争的次数。我称之为“借力”。

总结

好的,我们已经走到了使用强大的树莓派迷你计算机构建一个功能完备且可扩展的家庭安全系统的旅程的终点。在本章的最后,我们将前几章的所有元素和概念结合起来,从硬件和软件角度共同构建了一个家庭安全框架。

具体来说,本章指导我们构建了一个模块化的家庭安全系统框架,实施了任何商业化系统中都会有的功能,还有一些市面上看不到的功能。我们详细分析了完整的控制脚本,探索了其各个部分,并理解它们如何融入我们的系统中。

我们还学习了如何在树莓派启动时自动启动我们的家庭安全系统脚本,以及数据是如何通过配置文件在树莓派和基于网页的控制面板之间实时共享的。最后,我们探讨了如何通过创建一个非常实用的基于 RAM 的临时文件系统来防止 SD 卡烧毁。

posted @ 2025-07-05 15:47  绝不原创的飞龙  阅读(32)  评论(0)    收藏  举报