面向孩子们的树莓派项目-全-

面向孩子们的树莓派项目(全)

原文:zh.annas-archive.org/md5/ce321bbb133ccdc7a8c4419a7a66335d

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

欢迎阅读树莓派儿童项目,它将教你如何利用树莓派计算机和 Python 代码的力量,创建 12 个精彩的项目。你可以将这里学到的新技能和技术应用到你自己的项目中。本书面向初学者,帮助他们使用树莓派并进行创客制作,同时也适合那些有经验的人,帮助他们为自己的下一个伟大作品找到灵感。

这本书到底讲了什么?

多年来,我从树莓派这台小巧而多功能的$35 计算机中获得了很多乐趣。在我作为一名大学校园计算机科学教师的工作中,我有幸亲眼见证了基于树莓派的学习所带来的积极且改变生活的影响。使用树莓派可以培养编程技能和思维能力,同时建立自信。树莓派作为教育工具取得了巨大的成功,其成功的关键在于树莓派是有趣的

编程不再只是一个接一个地输入指令行。相反,你可以通过黑客改造 Minecraft、制作 MP3 音乐播放器或搭建间谍摄像头来学习编程。当使用树莓派时,你不仅仅是在构建和编程一个距离传感器——你是在创造一个朋友或敌人追踪器

正是这种乐趣与学习相结合,以及树莓派的多功能性,使它成为一个极好的教育工具。

在我的空闲时间,我会研究、折腾并创建许多自己的树莓派项目,然后在我的网站上分享,* www.tecoed.co.uk *。我写这本书是因为我想分享我对制作的热情。最重要的是,我想写一本书,让你通过有趣且实用的黑客项目来培养相关技能。

书中内容

在本书的每一章中,你将通过构建有趣、富有启发性和实用的黑客项目,学习 Python 编程技能,这些项目可以在你家中使用。每一章都分为简单的逐步指导,并附有大量的图示来帮助你完成项目。我还会鼓励你通过进一步开发自己的黑客项目来测试和提升你的技能。

本书不要求有任何树莓派编程或经验。

我希望你在构建这些项目时能像我在写这本书时那样享受乐趣。以下是每一章内容的简要概述:

第一章:开始使用树莓派

本章提供了你开始使用树莓派所需的所有信息。它涵盖了硬件设置、操作系统安装、从另一台计算机访问树莓派以及其他有用的技能。如果你已经熟悉树莓派和 Python 编程语言,你可以直接进入其中一个黑客项目。

第二章:Python 编程

在这一章,你将熟悉 Python 编程语言的基础知识。这个入门章节将为书中的项目做准备。接下来的每一章都包含一个独立的黑客项目,提供所需的所有指令和代码。你可以随时参考这一章,学习如何使用 Python。

第三章:热熔胶夜灯

这是一个简单但令人兴奋的黑客项目。你将使用一把热熔胶枪(可能你在学校也用过),并用热胶填充你选择的硅胶模具。接着,你将加入一个 LED,并让它凝固。当你接好 LED 时,你将制作出属于自己的定制胶水灯。然后你将把它和一个光传感器结合起来,制作一个在环境变暗时会变亮的灯。

第四章:Pi 摄像头:自拍神器

在这一章,你将掌握 Pi 摄像头,学习如何拍摄照片、应用 Instagram 风格的滤镜并编辑你的照片。接着,你将制作属于自己的自拍神器,自动为你拍照,还将制作一个定时拍摄相机。最后,你将学会如何捕捉视频并与朋友和家人分享。

第五章:Pi Spy 第一部分:黑客入侵摄像头进行秘密监控

通过这个项目,你将破解一个旧摄像头,并将它与树莓派结合,流媒体播放视频到你的手机或平板设备上。这是一个非常适合用来监视家人、观察宠物或监控你秘密藏匿糖果的项目。

第六章:操控 Minecraft

在这一章,你将掌控 Minecraft 世界,并通过代码来操控它。你将用草地制作一个蹦床,创建一个躲避致命沙块掉落的游戏,甚至利用 Minecraft 控制你在第三章中制作的胶水灯。在这一章的最终黑客项目中,你将制作一个 Pi 摄像头遥控器,让你通过击打 Minecraft 中的一块西瓜方块来录制视频!

第七章:无线电入侵

在这个项目中,你将控制你家中的收音机,广播你自己的信息、音乐,甚至是八卦给当地的听众。你将破解无线电波,编程一个流畅的界面,让你只需点击一个按钮就能控制收音机。

第八章:自动短信机

这个项目教你如何安全地黑客入侵你的手机,并通过你的树莓派发送消息。你将结合这些技能,制作一个个人短信提醒助手,在特定的时间发送短信提醒你做某件事情——比如遛狗、倒垃圾或见朋友!

第九章:Pi Spy 第二部分:Wi-Fi 和蓝牙追踪

在这个项目中,你将了解如何追踪连接到你家互联网的设备或启用了蓝牙的设备。你将访问连接设备的品牌和型号,并找出它们的 IP 和 MAC 地址。最终目标是创建一个物理状态板,当某个人带着设备进入你的房子时,它会亮起。

第十章:魔法音乐盒

如果你喜欢音乐,你一定会喜欢这个项目。只需几个按钮、一些电线、一个扬声器和一个小盒子,你就能打造自己的 MP3 播放器。你将学习如何为每个按钮设置操作,使你能够更换歌曲或调节音量。你将把你的音乐播放器嵌入一个小盒子里,你可以自定义和装饰它。

第十一章:自然盒子:运动感应相机

这个黑客项目将 Pi 摄像头与红外传感器配对,自动拍摄野生动物的照片。连接好各个部件,编写代码,将所有设备安全地放入盒子中,你就可以将你的自然盒子部署在院子里或周围的区域。当有东西在附近移动时,传感器会提醒 Pi,Pi 将拍摄照片。你甚至可以将这个黑客项目设置为将照片上传到 Dropbox,这样你就可以远程查看这些照片,而不打扰害羞的野生动物访客。

第十二章:智能插座与智能家居黑客

在这个项目中,你将使用智能插座和你自己的代码,安全地破解你家中的电源,使你能够通过手机或平板电脑远程控制开关灯、烧水壶或打开电视。这个项目非常适合用来恶作剧。

第十三章:镜子,镜子:社交媒体状态镜

如何在检查社交媒体更新的同时完成其他事情?你可以通过制作一个社交媒体镜子来实现。这个实用的黑客项目将你的镜子变成一组闪烁的灯光,提醒你何时收到特定用户的消息,何时获得转发,或者何时在你的动态中出现特定的关键词。镜子甚至会通过扬声器传递信息,这样你就不必停下手头的事情去阅读它们。

第十四章:带有 Sense HAT 的计算机游戏

在这里,你将使用被送往国际空间站的同样先进技术,来扩展经典的石头、剪刀、布游戏,创建石头、剪刀、布、蜥蜴、斯波克游戏。你将通过使用内置的操纵杆、LED 矩阵和 Raspberry Pi Sense HAT 上的代码,与 Raspberry Pi 进行对战。

本书所需材料

这是你完成本书中每个项目所需的所有组件清单。如果你不想一次性购买所有东西,每一章也会列出仅完成该项目所需的材料。对于没有指定数量的部分,我建议你购买一包或一套,这样你手头就有一些备用。

以下是你完成这些项目所需的与 Raspberry Pi 相关的组件:

  • 1 个 Raspberry Pi(我建议购买最新型号,目前为第 4 代,尽管程序代码已在多个型号上进行过测试。)

  • 1 个 Raspberry Pi Zero W(推荐用于第七章)

  • 1 个 Pi Camera v1 或 v2

  • 鳄鱼夹

  • 1 个 USB 便携电池

  • 1 个 USB 网络摄像头

  • 1 个 USB 麦克风

  • 各种跳线(公对公、公对母、母对母)

  • 各种 LED 灯

  • 1 个 面包板(400 引脚)

  • 1 个 光敏电阻(LDR)

  • 各种 220 欧姆到 330 欧姆之间的电阻

  • 1K 欧姆电阻

  • NPN 三极管

  • 1 个 被动红外传感器(PIR)

  • 1 个 0.1 微法拉电容

  • 按钮开关

  • 1 个 Energenie 智能插座

  • 1 个 Pi-mote

  • Android 手机或平板

  • 支持 FM 的收音机(数字或模拟),最好有两个扬声器或内置扬声器

  • 蓝牙扬声器或音频插孔扬声器

  • 一套电池驱动的 LED 灯串

  • 小型扬声器

  • 1 个 Raspberry Pi Sense HAT

这些是你需要的其他工具:

  • 一把热熔胶枪

  • 硅胶模具(如新奇冰块托盘)

  • 剪刀

  • 胶带

  • 小型 Philips 螺丝刀

  • 热熔胶棒

  • 焊接铁或导电漆

  • 镜子

  • 电钻

  • Dropbox 账户

  • Twitter 账户

  • 小型透明塑料盒

  • 小型木盒

  • 小块纸板

  • 图片或照片

第一章:开始使用树莓派

当我开始我的第一个树莓派俱乐部时,几个人以为这是一个烹饪俱乐部。他们来时以为会做蛋糕、馅饼和其他甜点。回想起来,我想从某种意义上说,他们是对的。树莓派使你能够制作东西。你可能不会做甜点,但就像烘焙一样,你跟随食谱从几个不同的组件中构建出某种东西,并加入自己的创意。或者,你也可以完全偏离轨道,做自己想做的事情。树莓派最终是一个用于发现、创造和学习的工具。

就像一本食谱书,这本书包含了 12 个有趣的项目的说明。然后,你可以将所有章节中的元素和技能结合起来,创造自己的黑客项目。每一章都是独立的,意味着它涵盖了完成项目所需的所有技能、内容和技术。

本章介绍了如何开始使用你的树莓派的基本内容,包括如何设置它、将其连接到互联网以及如何将文件传输到它。 本章还是你进行树莓派常见任务的参考。如果你已经熟悉树莓派和 Python 编程语言,可以跳过到第三章并开始进行热胶夜灯项目。

什么是树莓派?

树莓派是一台大小约为信用卡的微型计算机。但许多人第一次看到它时会说:“那不是计算机!它没有屏幕。”

像所有台式计算机一样,你可以将屏幕或显示器连接到你的树莓派。但是,计算机并不需要屏幕;它只是一个进行计算的设备,意味着它接受输入,处理输入,然后通常输出某些东西。即使是电视的遥控器也是一台计算机,尽管你可能没有把它当作计算机来看待。当你使用遥控器时,你按下频道号,这是输入。频道改变了,这是输出。中间控制频道变化的部分叫做“过程”。

自 2012 年 2 月发布以来,树莓派已成为英国最畅销的计算机。它由 Ebon Upton 创造,目的是通过一个价格低廉、易于获取且有趣的设备来促进计算机科学的教学。树莓派系列随后扩展到了多个不同版本的计算机,以满足各种需求和能力。

树莓派的硬件

树莓派的硬件由设备的物理部分组成——也就是你可以触摸或拿起来的部分。换句话说,硬件就是树莓派的主板。树莓派之所以能如此小巧且廉价,是因为它是一个系统单芯片SoC),或者是一个包含计算机所有主要组件的单一板子,类似于智能手机中使用的那些。我们将在本节中讲解树莓派主板的特点,从六种树莓派型号的区别开始。

树莓派 4

本书中的项目可以在大多数 Raspberry Pi 型号上完成。最新的型号 Raspberry Pi 4(图 1-1)具有最高的规格,且价格与早期型号相同。它支持 2.4 GHz 和 5GHz Wi-Fi,为下载和更新提供了快速的 Wi-Fi 连接。

Image

图 1-1 Raspberry Pi 4

你可以以大约 35 美元购买 Raspberry Pi 4。它可以运行多种程序,包括 Open Office、Java 和 Minecraft Pi Edition。该板卡具有双 HDMI(高清)端口,可以将现有的电视或显示器连接为显示器或双屏显示。图形芯片(GPU)还支持 4k 视频播放和显示。

其他型号

虽然我推荐在本书中使用 Pi 4,但其他 Raspberry Pi 型号也可以完成大多数项目的工作。我不会讨论每个 Raspberry Pi 型号的硬件规格,但下面的表格将对它们进行比较。

型号 处理器 核心 RAM USB 端口 蓝牙 Wi-Fi
4 1.5 GHz 4 1GB、2GB 或 4GB 4 是(2.4 & 5 GHz)
3B+ 1.4 GHz 4 1GB 4 是(2.4 & 5 GHz)
3A+ 1.4 GHz 4 512MB 1 是(2.4 & 5 GHz)
3 1.2 GHz 4 1GB 4
2 900 MHz 4 1GB 4
Pi Zero W 1 GHz 1 512MB 1(micro USB)
Pi Zero 1 GHz 1 512MB 1(micro USB)

Raspberry Pi 的关键组件是其大脑,称为处理器,它负责处理所有接收到的指令。处理器速度越快,它每秒能完成的指令越多。例如,4 型号的处理器速度为 1.5 GHz(处理器速度以千兆赫兹为单位)。千兆前缀表示 10 亿,而赫兹表示每秒发生的次数。因此,4 型号的处理器每秒能处理 1,500,000,000 条指令。这可是真正的处理能力!

核心一词指的是处理器的处理单元数。可以想象一下拥有两个甚至四个大脑!4 型号的处理器有四个核心,这意味着它有四个处理器;每个处理器每秒可以处理最多 1,500,000,000 条指令。这意味着它每秒最多可以处理 6,000,000,000 条指令!这就是为什么你可能会发现 Raspberry Pi 非常热,尤其是在接近处理器的位置。

所有这些处理能力都是有用的,但 Pi 需要一个地方来存储所有这些指令,这就是随机存取存储器RAM)的作用。RAM 是用于存储数据的高速内存。你的 Pi 拥有的 RAM 越多,它就能存储和读取更多的指令,直到内存填满为止。一旦 RAM 满了,你的 Pi 就开始变慢,因为必须删除冗余指令并将新的指令写入 RAM。因此,Pi 的 RAM 越多,它的表现就越快。

你可以使用 USB C 电缆为 Pi 4 提供电源,这种电缆与许多手机充电器使用的是相同的。其他型号使用 micro USB。你还可以使用 USB 端口连接键盘和鼠标。

从树莓派 3 型号开始,所有树莓派型号都内建了蓝牙和 Wi-Fi 芯片,提供无线连接功能,同时还有一个以太网端口供有线连接使用。对于媒体项目,树莓派提供了标准 HDMI 端口、3.5 毫米音频端口,可以输出声音和视频;还有一个 CSI 端口,你可以连接树莓派摄像头;以及一个显示端口,可以连接官方树莓派显示屏。

GPIO 引脚

每个树莓派型号从 B+ 型号开始,板上都有 40 个金色 GPIO 引脚,如图 1-2 所示。

图像

图 1-2 树莓派 GPIO 引脚布局

每个引脚有不同的功能,例如提供电源、读取传感器数据等,允许你创建电路。例如,如果你将 LED 的一端接到引脚 18,另一端接到地线引脚,你就可以通过一个简短的程序来开关 LED。地线引脚电压为零,通常用作测量电压的参考点。

令人困惑的是,GPIO 引脚有两种标准来进行标识和编号。一种标准叫做BOARD 编号 系统。使用这种系统,你通过树莓派上每个引脚的物理位置来标识它们。编号从左上角的引脚开始,它是编号一。右上角的引脚是编号二,其余的引脚按顺序编号,从这一行开始。树莓派基金会标准,这是更常见的引脚编号系统,使用Broadcom SoC 通道BCM)编号系统。BCM 编号系统通过 SoC 芯片内使用的通道编号来标识每个引脚。例如,板上的物理引脚 40 是通道 21,所以你称它为 GPIO 21。参见图 1-2 以查看这些 GPIO 编号。在本书的早期章节中,我将同时使用物理 BCM 编号来帮助你理解两者。标准做法是使用 BCM 编号系统。

设置你的树莓派

设置树莓派时,你需要以下物品:

  • USB 键盘

  • USB 鼠标

  • 至少 8GB 的 micro SD 卡

  • 官方树莓派电源适配器,或者为 Pi 4 型号提供 15.3 W 5.1V 3A 电源的 USB C 充电器,或者为其他所有型号提供 2.5A 电源适配器(如手机充电器)

  • 显示器(计算机显示器或电视)

你还需要一台带有互联网连接的笔记本或台式计算机,用于下载将运行并操作你的树莓派的所需软件。

要使用硬件,你需要软件。软件是一组指令,告诉树莓派在接收到某些输入时该做什么。当你按下键盘上的一个键、点击屏幕上的图标或移动鼠标时,树莓派会执行相应的操作。软件还控制着使你能够使用树莓派的各种进程。

你需要的最重要的软件是操作系统,它使你能够运行所有其他的软件。常见的操作系统包括 Windows、iOS、Android 和 Linux。虽然树莓派可以运行多种不同的操作系统,但你将使用专为树莓派打造的Raspbian操作系统。这个软件是免费的开源软件,意味着你可以使用、定制并与他人分享它。

下载操作系统

在你的联网笔记本或台式电脑上,打开浏览器并访问www.raspberrypi.org/downloads/。树莓派官网的这个页面列出了可用的操作系统。

点击Raspbian进入下载页面。你会看到带桌面和推荐软件的 Raspbian Buster选项(参见图 1-3)。这个选项包含了所有推荐的软件。点击下载 ZIP,开始将 ZIP 文件下载到你的计算机。下载完成后,找到并打开 ZIP 文件,查看操作系统镜像文件。通过复制粘贴或拖放,将该文件提取到新位置。

Image

图 1-3 从树莓派官网下载安装 Raspbian 操作系统。

安装操作系统

你需要将操作系统镜像写入你的 micro SD 卡(参见图 1-4),因此你可能需要一个 micro SD 适配器,以便将其插入你的计算机。

Image

图 1-4 一款 micro SD 卡转换器

将操作系统写入 SD 卡的最简单方法是下载一款免费的软件——Etcher

下载 Etcher

访问etcher.io/,点击下载按钮(它应该能自动检测你计算机的操作系统,如果没有检测到,可以点击下拉箭头并在列表中选择适当的系统)。

当 Etcher 下载完成后,双击文件打开它。它会询问你是否要安装该程序。点击我同意来安装它。安装程序文件到你的计算机上需要几分钟时间。

将操作系统写入 SD 卡

现在你准备好将操作系统写入 SD 卡了。将你的 micro SD 卡插入转换器中。然后在你的互联网连接电脑上找到 SD 卡槽并插入。等待 SD 卡加载并且电脑识别它。通常情况下,电脑会发出声音,提示它已经识别到 SD 卡。打开 Etcher 程序,点击第一个镜像按钮,然后选择你刚刚下载的Raspbian.img文件的位置。

点击选择驱动器,然后从驱动器列表中选择你的 micro SD 卡。最后,点击写入!按钮,将镜像写入 SD 卡(图 1-5)。这个过程需要几分钟时间,等候时可以继续阅读。

如果你遇到问题或卡住了,可以参考树莓派官网提供的更详细的安装指南,网址为www.raspberrypi.org/documentation/installation/installing-images/. 这里还有一本免费的详细用户指南:www.raspberrypi.org/magpi-issues/Beginners_Guide_v1.pdf

Image

图 1-5 将操作系统写入 SD 卡

设置你的树莓派设备

通过任何一个 USB 端口将你的 USB 键盘和鼠标连接到树莓派上。然后使用 HDMI 线将你的显示器连接到树莓派。

一旦操作系统完成写入 micro SD 卡,从电脑中弹出卡片并从转换器中取出。翻转你的树莓派,将卡片插入 SD 卡槽,直到卡片稳固为止。连接你的鼠标、键盘和 HDMI 显示器。然后将电源适配器插入树莓派的电源端口,并将另一端连接到主电源插座。打开电源并开启显示器。

启动你的树莓派

几秒钟后,你将看到屏幕上的活动。通常,你会看到一条彩色的彩虹图案,意味着图形正在加载。接着,显示器会显示树莓派的标志,这意味着树莓派正在启动并加载操作系统。

操作系统的程序代码会在屏幕上运行几行,然后才会显示出如图 1-6 所示的主桌面。开机时间取决于你使用的树莓派型号(最快的是型号 4,最慢的是 Pi Zero 和原始型号)。

Image

图 1-6 Raspbian 操作系统桌面

配置你的树莓派

Raspbian 操作系统有许多不同的设置和配置,你可以用它们来定制你的树莓派。例如,你可以增加为图形芯片分配的内存,以使 Minecraft 等游戏运行得更流畅。你还可以重新命名你的树莓派、更改密码以及进行其他修改。

当你第一次启动 Pi 时,系统会自动要求你配置一些功能。你首先看到的屏幕是一个设置窗口,如图 1-7 所示。点击下一步继续。

Image

图 1-7 初始配置开始

现在,你将看到设置位置的选项。这一步至关重要,它能确保你能够在所在国家/地区连接互联网,设置正确的键盘布局,并确保 Pi 的时间准确。选择你的国家、语言和时区,如图 1-8 所示,然后点击下一步。(如果你在美国,记得选择使用美国键盘。)

Image

图 1-8 设置所需的本地化

接下来的选项窗口会提示你更改密码,如图 1-9 所示。默认密码是raspberry,直到你更改它。这意味着任何人都可以登录并访问你的 Pi。如果你想更改密码,现在可以进行更改,但请确保记住,它不再是默认密码,这一点在后续章节中我会使用。

Image

图 1-9 更改默认密码

现在,配置向导会搜索所有可见的 Wi-Fi 网络并列出它们。你可以从列表中选择你的网络连接。接下来,系统会提示你输入 Wi-Fi 密码,如图 1-10 所示。点击下一步以连接。如果你决定跳过此设置,需要更改 Wi-Fi 连接设置,或想连接到其他 Wi-Fi 网络,你将需要运行配置工具,相关内容见“上线”部分,参见第 18 页。

Image

图 1-10 配置 Wi-Fi

接下来,系统会提示你调整屏幕大小;只有在图片周围有黑色边框并且未填满整个屏幕时,你才需要执行此操作。点击下一步按钮。

最后的窗口会自动检查并下载任何操作系统更新,然后安装它们,如图 1-11 所示。这可能需要一些时间。

Image

图 1-11 更新软件

桌面包括树莓派的 Logo、垃圾桶图标以及屏幕左上角的主任务栏。

树莓派操作系统快速浏览

任务栏上的第一个图标是树莓派 Logo,点击它可以打开操作系统的主菜单和子菜单(如图 1-12 所示)。所有你加载到树莓派上的程序都会按类别进行分组,以便于查找。例如,你可以在“游戏”菜单中找到 Minecraft。如果你想创建一个文档——比如说,一封信——你可以在“办公”菜单中找到相关软件。要打开菜单中的程序,只需点击它。如果程序图标在桌面上,双击即可打开。

Image

图 1-12 使用屏幕顶部的任务栏访问包含软件的菜单。

图 1-13 显示了任务栏中的其余图标。

Image

图 1-13 Raspbian 操作系统任务栏

第二个图标 ❶ 打开互联网浏览器 Chromium。你需要连接到 Wi-Fi 网络才能访问互联网。我将在“上网”部分讲解这个过程,详见 第 18 页。

接下来是文件夹图标 ❷,你将使用它来查看和管理文件及文件夹。你可以使用该工具创建、删除、重命名、复制和移动文件。

第四个图标 ❸ 打开 终端,这是一个工具,允许你通过代码行(称为 命令)而不是鼠标点击来导航 Raspberry Pi,安装和删除软件以及执行其他任务。我将在下一节中详细介绍终端。

在任务栏的右侧,你会看到 图 1-14 中显示的图标。

Image

图 1-14 任务栏右侧的图标

第一个图标 ❶ 打开和关闭蓝牙,并连接到支持蓝牙的设备。接下来的图标是 Wi-Fi 图标 ❷,它显示你是否已连接到网络以及当前的信号强度。你还可以在这里查看可用的 Wi-Fi 网络,连接或断开连接。

第三个图标 ❸ 打开音量管理对话框,允许你选择输出声音的设备。右键点击该图标以选择扬声器、HDMI 显示器或蓝牙设备。

你可能想要显示 Pi 的处理器正在执行的工作量,这样你就能了解你正在消耗多少处理能力。ARM 处理器是 Raspberry Pi 的大脑,负责处理所有的数字运算和程序指令,它每秒能处理多达 1,500,000,000 条指令,具体取决于你使用的是哪个型号。右键点击任务栏,然后选择 添加/移除面板项 选项。如果你点击“添加”按钮,你应该能看到 CPU 使用监视器图标。选择添加它。该图标显示一个百分比:20% 表示当前任务占用了 Raspberry Pi 约 20% 的处理能力。有时它会飙升至 100%,通常意味着你打开了很多程序或运行了图形密集型软件。如果你的 Pi 无响应,可以检查这个工具。如果它显示为 100%,请等待直到数字下降再进行下一个操作。

第四个图标 ❹ 显示当前的时间和日期(这样你就能检查自己花了多少时间在“黑客攻击”上!)。当你插入 USB 闪存驱动器或存储棒时,最后一个图标会出现。存储棒对于在 Raspberry Pi 和其他计算机之间传输和备份文件非常有用。你还可以使用这个图标安全地弹出你的大容量存储设备。

配置你的 Raspberry Pi

你可以使用树莓派配置工具访问和更改之前的设置。要打开该工具,请点击树莓派图标并加载主菜单。点击首选项选项。向下滚动至首选项列表底部的树莓派配置选项(图 1-15)并点击它。配置工具应该会打开。

Image

图 1-15 点击配置工具以更改你的首选项。

在接下来的子章节中,我将概述你可能想要使用此配置工具更改的设置。

系统标签

该工具的“系统”标签(图 1-16)包含一个按钮,用于更改密码。回想一下,当你启动树莓派时,你无需登录或输入密码,因为操作系统自带一个默认帐户。该帐户的用户名是pi,密码是raspberry。要更改密码,请点击更改密码,并输入你的新密码。在“更改密码”下方是主机名字段,你可以在这里重命名你的树莓派。如果你有多个 SD 卡,而其中一个是用于特定项目的,这很有用。只要记住你选择的名称,因为在后续章节中,你必须使用这些名称,而不是默认的名称。

Image

图 1-16 使用“系统”标签更改你的密码或重命名你的设备。

“系统”标签还提供了将树莓派启动到桌面或命令行界面CLI)的选项。如果你使用过 Windows,桌面应该很熟悉;它包括背景图像、图标和菜单,你可以点击它们来打开程序。你还可以使用鼠标点击、选择和滚动。CLI 是一个纯文本界面,你通过键盘输入文本命令来控制操作系统和树莓派。关于 CLI 的更多细节,你可以参见第 20 页的“使用命令行”章节。

通过取消选择“自动登录”框,你可以要求用户登录,从而为你的树莓派增加一个额外的安全层。你还可以调整树莓派的屏幕输出分辨率,使屏幕上的文本易于阅读,图标大小适合你的显示器。

接口

接口是计算机、硬件、软件和人类之间共享数据的一种方式。例如,图形用户界面GUI)使你能够通过按钮和菜单与树莓派进行交互。键盘接口使你能够控制设备或输入数据。

树莓派配置工具的“接口”标签(图 1-17)允许你启用或禁用各种软件和硬件接口。

Image

图 1-17 在配置工具中启用或禁用接口。

如果您使用的是 Pi 摄像头,如在自拍相机、Minecraft 黑客和自然盒子项目中所用,您需要在此选项卡中启用它才能正常工作。我们会在需要时进行设置,所以现在不用担心。

性能

在性能选项卡上,您可以使用 GPU 内存字段(图 1-18)来增加或减少可用于处理图形的内存量。如果您正在处理图像、游戏或视频,您需要增加 GPU 内存。然而,如果您的项目使用命令行或不需要屏幕,您可以减少 GPU 内存,这样可以释放更多内存给处理器使用。

图片

图 1-18 在性能选项卡上增加或减少 GPU 内存。

现在,保持设置不变。

本地化

本地化选项卡(图 1-19)包含您所在国家和地区的设置。配置这些设置可以确保 Raspberry Pi 能够访问在线软件仓库并连接互联网。在此选项卡上,您还可以更改时区,调整键盘布局以符合您的偏好,并选择位置和首选语言。

图片

图 1-19 在本地化选项卡上选择您的国家和地区。

完成后,点击确定关闭配置工具。

上网

要充分发挥 Raspberry Pi 的潜力,您需要连接互联网。内置的网页浏览器使您可以像使用其他任何联网设备一样浏览网站和内容。然而,与平板电脑或手机不同,当您使用 Raspberry Pi 上网时,您可以通过终端更新操作系统和安装软件(我很快会告诉您如何做)。您还可以从其他设备远程访问 Raspberry Pi,并在 Pi 和设备之间传输文件。这也意味着您可以断开 Pi 与显示器、键盘和鼠标的连接并远程使用它。远程使用 Pi 对于创建 Pi 间谍、魔术音乐盒、自然盒子和社交媒体镜像项目时会非常有用。

连接到互联网时连接显示器

当您首次启动 Raspberry Pi 时,它会尝试连接到您的 Wi-Fi。如果您更改位置或时区,您需要重新配置 Wi-Fi。在 Raspberry Pi 配置工具中,点击本地化选项卡,然后点击设置 WiFi 国家。弹出一个窗口,您可以从列表中选择当前国家。图 1-20 展示了这个整个过程。选择好国家后,点击确定并关闭配置工具。

图片

图 1-20 点击设置 WiFi 国家以选择您当前的国家。

现在你可以开始扫描可用的 Wi-Fi 网络了。定位任务栏右侧的 Wi-Fi 符号(图 1-21),点击它查看可用网络列表。找到你的网络,点击它,并在有密码的情况下输入 Wi-Fi 网络密码。完成后,树莓派将连接到 Wi-Fi,你就可以上网了。现在,每次启动树莓派时,它将默认尝试连接到此 Wi-Fi 网络。

Image

图 1-21 连接到你的 Wi-Fi 网络。

通过 SD 卡连接互联网

当你无法访问显示器时(这被称为 无头模式),你可以通过 SD 卡连接互联网。你可以提前编程你的 SD 卡,当你将其插入树莓派并启动时,它会自动连接到 Wi-Fi。这对于你在户外设置的项目也很有用,例如本书后面提到的 MP3 播放器或自然盒子。

要编程 SD 卡,关闭树莓派电源,拔掉电源线,然后弹出 micro SD 卡。将 SD 卡插入转换器,然后插入电脑或笔记本的 SD 卡读卡器中。在文件浏览器中打开它(图 1-22)。

在 SD 卡的主目录中,通过右键点击鼠标并创建一个新的文本文件来创建一个名为 wpa_supplicant.conf 的新文件;然后按回车键。下载并安装 Notepad++文本编辑器,访问 notepad-plus-plus.org/。在 Notepad++中打开该文件,添加以下代码行:

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
network={
  ssid="YOUR_NETWORK_NAME"
  psk="YOUR_PASSWORD"
  key_mgmt=WPA-PSK
}

这个文件存储在 SD 卡的启动分区中,这意味着当树莓派启动时,它会从中加载并读取此文件,从而连接到互联网。将 YOUR_NETWORK_NAME 替换为你要连接的 Wi-Fi 网络名称,将 YOUR_PASSWORD 替换为该网络的密码。保存文件。弹出 SD 卡并将其放入树莓派中。下次树莓派启动时,它将使用提供的凭据连接到 Wi-Fi。

Image

图 1-22 在电脑的文件浏览器中打开 micro SD 卡。

如果你更喜欢使用有线连接来访问互联网,你可以使用标准以太网线连接到你的网络。将一端连接到路由器,另一端连接到树莓派的以太网端口。请注意,树莓派 Zero 型号没有以太网端口。

使用命令行

到目前为止,我们已经看过了 Raspbian 操作系统的 GUI 元素——换句话说,就是通过点击屏幕上的按钮来访问的部分。但操作系统也提供了一个 命令行,你可以通过终端访问它。点击任务栏中的终端图标,在新窗口中打开终端,如图 1-23 所示。

命令行让你输入指令来控制你的树莓派。一旦你熟悉了它,你将能够比使用鼠标和图形用户界面(GUI)更快速地完成任务。

Image

图 1-23 终端

注意,终端中已经包含了这段文本:pi@raspberrypi:- $。这就是提示符;它包含了你当前登录的用户名(pi)以及你所在的文件夹信息,你稍后会看到。当用户名后面没有斜杠(/)和文件夹名称时,你就在主目录。

为了说明使用 GUI 和使用终端的区别,假设你要创建一个名为sounds的新文件夹。使用 GUI 时,你需要执行以下步骤:

  1. 将鼠标移至文件夹图标。

  2. 点击它。

  3. 右键点击窗口。

  4. 向下滚动至新文件夹图标。

  5. 点击它。

  6. 将文件夹命名为sounds

  7. 按 ENTER 键。

使用终端,你只需输入以下命令(不包括提示文本)来创建新文件夹:

pi@raspberrypi:- $ mkdir sounds

命令mkdir是“make a directory”(创建目录)的缩写。这个过程比使用 GUI 快得多。缺点是你需要学习并记住这些命令,但随着时间的推移,你会变得熟悉它们。以下表格包含了一些常用且有用的命令行指令,供你尝试。

指令 作用
cd foldername 进入某个文件夹(将 foldername 替换为你想进入的文件夹名称)
cd ~ 进入主目录
cd 从任何文件夹返回主文件夹
cd /home/pi 进入名为pi的文件夹
ls 列出当前文件夹中的所有文件
mkdir filename 创建新文件夹(将 filename 替换为你的文件夹名称)
ifconfig 查找你的 IP 地址
sudo shutdown 关闭树莓派
sudo reboot 重启树莓派
top 列出当前正在运行的所有程序
sudo idle3 以 sudo 用户身份打开 Python 3 编辑器
free -m 显示你有多少空闲存储空间(以兆字节为单位)
sudo -s 保持终端为 sudo 用户
alsamixer 打开音量控制

注意,一些命令前面会加上sudo。例如,关闭树莓派的命令是sudo shutdown。术语sudo指的是超级用户执行(super user do),即树莓派的管理员。在这种情况下,就是你。作为超级用户,你可以获得额外的权限和权限,允许你执行某些任务。并不是每个人都有权限关闭你的树莓派!

为了熟悉终端,尝试我在以下子节中提供的一些简单任务。

设置时间

如果你的树莓派长时间未开机,时钟显示可能不正确。原因是树莓派没有内部电源来保持时钟运转。如果时间不准确,你可能无法访问互联网或运行程序。

解决这个问题的一种方法是确保树莓派连接到互联网。这样系统会自动将时钟更新为当前时间。如果你处于离线状态,则需要手动设置时间。打开终端并输入以下命令来设置时钟。

pi@raspberrypi:- $ sudo date -s "Jul 5 08:10"

请确保将示例中的时间和日期替换为正确的月份、日期和时间。为了更精确地设置时间,你可以输入如下命令:

pi@raspberrypi:- $ sudo date --set '2020-04-26 18:26:00'

用当前的年份、月份和日期替换示例中的时间和日期,然后输入正确的小时、分钟和秒。片刻后,时钟将会更新。

访问配置设置

你已经学会了如何使用配置工具设置自定义树莓派。但你也可以直接从命令行访问配置工具。输入以下命令来打开图 1-24 中显示的窗口:

pi@raspberrypi:- $ sudo raspi-config

Image

图 1-24 从命令行访问配置工具

你可以使用键盘来控制这个工具;使用箭头键进行选择,按 ENTER 进入该选项。完成配置设置后,导航至 <Finish>,按 ENTER 重启你的树莓派。

更新和升级

随着时间的推移,你的软件和操作系统将需要升级。大多数软件更新都存储在一个外部在线仓库中,每当你做出更改时,它都会更新。为了升级你的树莓派并保持系统最新,你需要连接到互联网。然后,你可以使用两个简单的命令。在终端窗口中输入以下命令以下载操作系统和已安装软件的任何更新:

pi@raspberrypi:- $ sudo apt update

然后输入此命令将大部分软件升级到最新版本:

pi@raspberrypi:- $ sudo apt upgrade

安装软件包

很多软件也存储在一个中央的在线仓库中,你可以在树莓派连接到互联网时访问该仓库。你可以通过在终端窗口输入一个简单的命令,后面跟上你想安装的软件的名称,直接下载软件。例如,要安装 GIMP(一个免费的图像编辑软件包,用于你的树莓派),输入以下命令:

pi@raspberrypi:- $ sudo apt install gimp

你也可以通过输入purge命令来删除不需要的软件:

pi@raspberrypi:- $ sudo apt purge gimp

当你不知道软件的完整名称时,可以输入以下命令来搜索可用的软件:

pi@raspberrypi:- $ sudo apt search keyword

用软件名称的一部分替换关键字。以后当需要时,我会在后续章节中介绍其他安装软件的方法。

远程访问你的树莓派

本书中的一些项目需要你移除屏幕或显示器,并断开 Raspberry Pi 的鼠标和键盘连接。例如,要在第十章中构建魔法音乐盒 MP3 播放器,你需要将 Raspberry Pi 放入一个木箱中。在这种情况下,即使 Raspberry Pi 断开了所有连接,你仍然需要访问其文件。最简单的方法是通过家用网络(你在连接互联网时也连接的网络)从另一台计算机或设备远程连接到 Raspberry Pi。我将向你展示几种方法来实现这一点。首先,你需要你的用户名、密码和 Raspberry Pi 的 IP 地址。

请记住,如果你更改了任何这些信息,你需要使用更新后的信息。

通过 SSH 访问 Raspberry Pi

你可以通过 安全外壳协议SSH)从另一台设备远程访问 Raspberry Pi 的命令行。这允许你在另一台连接到 Raspberry Pi 的设备上打开终端会话或窗口,从而远程发送命令(如命令行指令表中的命令)到 Raspberry Pi。出于安全原因,默认情况下 SSH 是关闭的,以防止他人黑入你的 Raspberry Pi。要启用 SSH,请从桌面菜单中打开 Raspberry Pi 配置工具。

在接口选项卡上(图 1-25),将 SSH 字段设置为 启用。如果你使用的是终端配置工具,请点击 OK 或选择 完成。我强烈建议你在使用 SSH 时更改默认密码。你可以在配置工具中完成这项操作。

Image

图 1-25 在配置工具的接口选项卡中启用 SSH。

接下来,你需要 Raspberry Pi 的 IP 地址。IP 地址是一组数字,用来标识你的 Raspberry Pi 在无线网络上的位置。要查找这个地址,请打开终端并输入以下命令:

pi@raspberrypi:- $ hostname –I

终端中会显示一个数字(图 1-26)。它看起来应该像 192.134.244.03。请记下这个数字;你稍后需要用它来远程访问你的 Raspberry Pi。

Image

图 1-26 记录终端中显示的 IP 地址。

接下来,你需要使用访问 Raspberry Pi 的设备,这可以是手机、平板电脑或计算机。要连接到 Raspberry Pi,你需要安装一个 SSH 客户端。SSH 客户端是允许你通过其他设备使用 SSH 连接到 Raspberry Pi 的软件或程序。你可以从 www.chiark.greenend.org.uk/~sgtatham/putty/latest.html 下载一个常用的客户端 PuTTY,或者如果你愿意,也可以在应用商店搜索 SSH 客户端应用。

下载并安装 SSH 客户端后(参见图 1-27),打开它并在主机名(或 IP 地址)下输入 raspberrypi.local 或 Raspberry Pi 的 IP 地址。如果你使用 Windows 操作系统进行访问,需要从 support.apple.com/downloads/bonjour_for_windows 安装 Bonjour。如果你使用的是 Linux 或 macOS,那么无需额外操作!

系统会提示你输入 SSH 密码,即你的登录密码(默认为 raspberry,除非你已更改)。然后输入 Raspberry Pi 登录信息以建立与 Raspberry Pi 的连接。应该会出现一个终端窗口,你可以通过它来控制你的 Raspberry Pi。

有时,raspberrypi.local 主机名可能无法正常工作。很可能是因为你使用的 IP 地址自上次使用网络以来发生了变化。使用前面提到的 hostname –I 命令查找新的 IP 地址,然后输入新的地址。

Image

图 1-27 配置你的 SSH 客户端

使用 RealVNC 通过 VNC 访问 Raspberry Pi

通过 SSH 方法远程访问 Raspberry Pi 可以使用命令行进行控制。如果你希望访问 Raspberry Pi 的完整图形桌面(GUI),并在 Pi 以外的设备上使用,你可以使用内置的 虚拟网络连接 (VNC) 服务器。VNC 允许你通过网络访问 Raspberry Pi 的桌面。

VNC 的一个额外好处是它可以免费安装和使用。它需要两部分:第一部分是已经安装在 Raspberry Pi 上的 VNC 服务器,你只需要启用它(参见图 1-28):打开 首选项 中的 接口 标签,点击启用 VNC 的选项。

第二部分涉及在你希望查看 Pi 桌面的远程设备上安装 VNC 查看器。打开远程设备上的浏览器,访问 RealVNC 网站 www.realvnc.com。点击 产品,然后点击 爱好者和制造者。你应该会看到一个 Raspberry Pi 的 VNC Connect 页面。向下滚动并点击 下载(在下载 VNC Viewer 部分)。

Image

图 1-28 启用 VNC 客户端。

这里你应该可以看到支持的各种操作系统(参见图 1-29)。点击你的操作系统图标,然后点击 下载 VNC Viewer 以下载安装程序。找到安装程序(通常在 下载 文件夹中),双击它以运行。然后按照屏幕上的设置说明进行操作,接受默认选项。

Image

图 1-29 选择并安装所需的 RealVNC 查看器。

安装完成后,找到 VNC Viewer 图标并点击它以打开程序。你应该会看到如图 1-30 所示的窗口。

注意

记住,您可以通过打开终端,输入命令 hostname –I,然后按 ENTER* 来查找 Raspberry Pi 的 IP 地址。您还可以通过点击 Pi 上的 VNC 图标来查找 IP 地址,有时该地址会显示在连接性标题下方。

在顶部的框中输入您的 Raspberry Pi 的 IP 地址。

然后,在您用来访问 Raspberry Pi 的设备上找到相同的 VNC 图标。点击该图标,VNC 服务器应该会加载。

Image

图 1-30 输入您的 Raspberry Pi 的 IP 地址

第一次连接,或者如果您连接到新的 Pi,您可能会看到 图 1-31 中的身份验证屏幕。

Image

图 1-31 身份验证

如果是,请点击继续以访问您的 Pi。然后,系统会要求您输入 Raspberry Pi 的用户名和密码。

现在您应该已连接!如果您想断开连接,只需关闭您的 Pi 或关闭 VNC 查看器窗口。有时,您可能会看到计算机拒绝连接的消息。这可能是由多种原因造成的。请检查您是否输入了正确的 IP 地址、用户名和密码。还要检查您是否在 Raspberry Pi 配置菜单中启用了 VNC。请参见图 1-28 进行操作,然后重新启动您的 Pi,以确保更改已生效。最后,考虑到某些网络会阻止 VNC,这在学校、图书馆和企业中很常见。

通过远程桌面访问 Raspberry Pi

还有第二种方式可以从另一台设备访问您的 Pi 的图形界面:通过远程桌面。这与使用 VNC 连接类似,但如果您使用的是 Microsoft Windows 系统,您无需在计算机、笔记本电脑或 Surface 上安装任何软件。使用远程桌面还意味着,随着 Microsoft 发布更新,您的设备将保持最新,而如果使用 VNC,您将需要下载并手动更新服务器和查看器。

返回您的 Raspberry Pi,打开首选项中的接口选项卡,并启用 VNC。

注意

在撰写本文时,对于 Apple 设备,此替代方法没有免费的软件。如果需要免费选项,请返回 第 27 页的“通过 RealVNC 使用 VNC 访问 Raspberry Pi”部分。

启用 VNC 后,您需要在 Pi 上安装一些远程桌面软件。打开终端窗口并输入以下命令进行安装:

pi@raspberrypi:- $ sudo apt-get install xrdp

当软件在你的 Pi 上安装时,返回到你的笔记本电脑或其他连接互联网的设备,并安装远程桌面应用程序。如果你使用的是 Windows 设备,可以通过在开始菜单中搜索来找到名为Remote Desktop Connection的内置远程桌面应用。如果你使用的是 Android 设备,可以从 Google 商店下载远程桌面应用,只需在商店中搜索Microsoft Remote Desktop。如果你使用的是 Apple 设备,可以在 Apple 应用商店中搜索Apple Remote Desktop(该应用收费)。

一旦你的 Raspberry Pi 完成安装远程桌面软件,打开你其他设备上的远程桌面应用。一个窗口,像图 1-32 中所示的那样,应该会出现。

图片

图 1-32 使用远程桌面应用连接到你的 Raspberry Pi。

Computer:旁边的相关文本框中输入你的 Raspberry Pi 的 IP 地址或主机名,然后点击Connect或等效的按钮。系统会提示你输入 Raspberry Pi 的用户名和密码。输入完成后,应用程序将建立连接,并打开一个独立的窗口,允许你查看 Raspberry Pi 的桌面(图 1-33)。

图片

图 1-33 远程查看你的 Raspberry Pi 桌面

你可以像在设备上操作一样与桌面进行交互。记住,你所做的任何更改都会保存到你的 Raspberry Pi 上。

文件传输

有时你需要在计算机和 Raspberry Pi 之间传输文件。也许你想备份保存在 SD 卡上的代码,或在设备之间传输 MP3 文件,或者向 Raspberry Pi 添加新文件。为此,你需要在你想连接到 Raspberry Pi 的设备上下载文件传输程序。

如果你使用的是 Windows,访问winscp.net/eng/download.php/并下载 WinSCP 软件的最新版本。如果你使用的是 macOS,可以在 Apple 应用商店中搜索File Transfer App

好消息是,你不需要在 Raspberry Pi 上安装任何额外的软件来传输文件。一旦你为其他计算机或设备下载并安装了文件传输程序,打开它并输入你的 Raspberry Pi 主机名或 IP 地址(图 1-34)。你需要确保已启用 SSH,如图 1-25 所示。

图片

图 1-34 在文件传输程序中输入你的 Raspberry Pi 主机名或 IP 地址。

你可能会被提示输入 Raspberry Pi 的用户名和密码。输入完成后,你的设备应该会连接成功,屏幕上会显示文件管理器(图 1-35)。

文件管理器将显示保存于两个设备上的文件。点击并拖动文件,从文件管理器的一侧传输到另一侧。

Image

图 1-35 使用文件管理器在你的设备之间传输文件。

总结

现在你应该对你的树莓派及其一些功能有了基本了解。请记住,每一章的黑客技巧都是独立的,涵盖了你所需的所有技能、理论和代码。如果需要回顾或解释,可以参考本章内容。

让我们开始黑客入门吧!

第二章:PYTHON 编程

本书中的大多数项目使用Python 编程语言编写,该语言已预装在 Raspbian 操作系统中。Python 是一种相对简单的语言,因为其结构使得它极其用户友好。但它也有适合更高级程序员的复杂特性,并且能够高效地完成任务。

在构建本书中的项目时,我会引导你为每个程序编写 Python 代码。通过这些项目,你将学习语言的基础知识。在我们开始之前,我们将先了解一些编程基础,如打印语句、做出选择和避免 Python 中的常见错误。

探索 Python

为了说明 Python 的高效性,假设你正在编写经典的第一个程序,以在屏幕上显示Hello World

在 Java 编程语言中,你将写出以下代码:

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello World"); 
    }
}

在 Python 中,你只需一行代码就可以实现相同的输出:

print ("Hello World")

Python 代码是在集成开发环境IDE)中编写的。IDE 是你用来编写 Python 代码的软件,就像你可能使用文字处理软件来编写文档,或使用网页浏览器来加载和查看网站一样。

前言 Thonny 和 IDLE

Raspbian 操作系统预装了一个名为Thonny的 Python IDE(图 2-1)。这个默认的 Python 编辑器包括广泛的错误检测和代码高亮功能。如果你因某些原因需要手动安装 Thonny,请打开终端并输入以下命令:

pi@raspberrypi:- $ sudo apt-get install python3-thonny

Thonny IDE 有一个区域供你编写完整的程序代码,以及一个 Shell。Shell 区域是你可以编写单行代码并测试它们的地方,而无需保存完整的程序代码。你可以在thonny.org/了解更多关于 Thonny 的功能。

Image

图 2-1 Thonny,默认的 Python 集成开发环境

Python 还有其他可用的编辑器,包括 IDLE(发音为idol),这是编写 Python 代码的经典集成开发环境。当你将 Python 下载到除 Raspbian 以外的其他设备上,例如你常用的笔记本时,通常会提供这个编辑器。

每个项目的程序代码可以在任何 Python 编辑器中编写,但本书中始终使用 IDLE,代码的颜色也与其相匹配。如果你想使用 IDLE,打开终端窗口并输入以下代码:

pi@raspberrypi:- $ sudo apt install idle3

你可以在www.python.org/downloads/找到更多信息和下载详情。

加载 IDLE 时,将会打开两个窗口(图 2-2)。左侧窗口是Shell 窗口,你可以在这个窗口中测试单行代码:编写代码并按 ENTER 键,代码会立即运行。右侧的Untitled窗口允许你编写并保存多行代码或完整程序。然后,你可以随时返回它们。

Image

图 2-2 IDLE 是编写 Python 代码的经典 IDE。

一旦你写完程序,就需要运行或 执行 它,测试它是否能正确工作。对于两个已安装的 IDE,你都可以按 F5 键来保存并执行程序。大多数程序员在开发程序时会边写边测试,以便及早发现并去除错误。

编写你的第一个程序

让我们编写你的第一个程序,它将 打印 一条简单的消息到屏幕上。从主菜单中选择“编程”选项,选择 Thonny 或 Python 3(IDLE),然后点击 文件新建。你也可以从 IDLE 点击 文件新建文件。在新窗口的顶部,精确输入这一行(虽然你可以根据需要修改引号中的文本,输入你自己的消息):

print ("I can code")

要运行程序,按键盘上的 F5 键;系统会提示你保存程序。点击 确定,程序将保存并运行。文本 I can code 将出现在 Shell 窗口中。

返回到上一个窗口,并在你之前的代码下方输入这一行:

print ("Look a second line!")

再次按 F5 键保存并运行程序,现在它应该像这样:

print ("I can code")
print ("Look a second line!")

现在你已经写了第一个程序,让我们复习一些 Python 基础知识。

字符串

字符串 是一种表示文本的数据类型。你可以通过将字符输入到单引号或双引号内来创建一个字符串:只要两端使用相同类型的引号,两种方法都可以。

在引号内,你通常输入文本,比如 "欢迎来到树莓派儿童项目"。你可以打印字符串(在屏幕上显示),保存它,甚至修改它。你还可以用 Python 代码搜索字符串中的特定词或字符,测量字符串的长度,甚至替换字符串的一部分。

字符串不仅仅用于字母。它们也可以包含数字和符号。例如,字符串 "C3P0" 包含字母和数字。然而,字符串中的数字没有实际的值;它们仅仅表示数字 3 和数字 0 的符号。即使是单独的数字,如 "465",也没有值,这意味着你不能像在代码中将它加到另一个数字上一样使用它作为数字。

试试这个:输入以下程序,它看起来应该是用来加两个数字的:

Total = 500 + "500"
print (Total)

当你运行这个程序时,你会遇到一个错误。它无法正常工作,因为程序试图将一个数字和一个字符串相加,而字符串没有值。要解决这个错误,你需要去掉 "500" 中的引号,这样 500 就从字符串变成了数字。现在两个数字都有值了,你可以将它们相加。现在试着运行这个程序。

变量

你编写的一些程序需要用户输入他们自己的数据。想象一个程序,要求玩家输入他们的名字。因为这个数据会因用户而异,你可以将其存储在一个变量中。可以将变量视为计算机内存中的盒子,里面存储着信息。你给每个盒子一个标签,这样你就可以再次找到它。

在 Python 中,首先通过给变量一个标签来定义变量,这就是声明变量。例如,假设我们使用NameOfMyPet作为变量标签。接下来,使用等号(=)表示该变量包含一个字符串或一个值。这个过程的术语是赋值,即将某物赋给变量。最后,声明变量应该包含的内容。例如,我的宠物名字是 Iron Cat,所以我可以这样声明一个变量:NameOfMyPet = "Iron Cat"。字符串"Iron Cat"现在存储在计算机的内存中,我可以随时通过调用变量NameOfMyPet来获取它。要打印变量的内容,使用代码print(NameOfMyPet)

NameOfMyPet = "Iron Cat"  
print(NameOfMyPet)

你可以通过编辑字符串来更改变量的内容。例如,你可能会把宠物的名字改为 Tony Bark。当你更改后运行程序时,变量的原始内容将被新的宠物名字覆盖。

注意

你可以使用小写风格的变量标签,例如 nameofmypet。但是,这样的变量名不容易阅读和理解。Python 程序员使用一种被称为驼峰命名法的技巧:大写字母表示每个新单词的首字母(例如,NameOfMyPet)。

你还可以使用变量来存储用户对问题的回答,例如“你的名字是什么?”。

要创建一个存储用户输入的变量,你首先声明该变量。然后使用等号(=),在等号的另一侧加上你想要回答的问题或提示,括号内写上内容。删除你在 IDLE 或 Thonny 文件中的内容,并输入以下代码:

name = input("What is your name? ")
print ("Welcome ", name)

程序将用户输入的名字存储在一个名为name的变量中。当你运行程序时,它会从name变量中获取名字,将其与单词Welcome结合,然后在底部打印出来。例如,如果你的名字是 Sarah,它将打印Welcome Sarah。你可能会注意到在What is your name?Welcome后面有一个刻意的空格。这是为了确保单词之间有空格分隔,否则它会打印出类似WelcomeSarah.这样的内容。

循环

在本书的一些项目中,你需要让程序不断地重复一组指令。例如,在 第三章 中,程序需要不断检查房间里的光线强度,以确定何时触发热熔胶夜灯的开关,这意味着代码需要不断运行。在 第六章 中,蹦床程序持续检查你是否站在 Minecraft 中的草块上。如果你站在草块上,就进入蹦床模式,你会被弹起到空中!

这种编程技巧通常称为 循环。循环有两种类型:while 循环和 for 循环。

while 循环

while 循环会在特定条件满足时继续重复执行代码。以下示例展示了如何使用此循环:

❶ count = 5
❷ while count > 0:
      question = input("Do you like cheese? (yes or no) ")
    ❸ print ("")
    ❹ count = count - 1	

你首先将 count 变量的值设置为 5 ❶。只要 count 中的值大于 0 ❷,程序会询问问题 "你喜欢奶酪吗?(是或否)"。然后它打印一个空行 ❸,以保持布局整齐。每次程序循环——即每次 while 循环后面的缩进代码 ❷ 运行——它会将 count 的值减 1,在代码 count = count – 1 ❹ 中执行。

在第一次循环运行之后,新的 count 值被设置为 4。因为 4 仍然大于 0,循环再次运行,并且在循环结束时减去 1。程序将继续循环,直到 count 的值达到 0,循环才会停止,程序结束。所以这个程序会运行五次,然后停止。注意,❸ 和 ❹ 处的行有缩进。缩进表示这些行是循环的一部分,只有在循环运行时才会执行。

当你使用 while True 语句,如这里所示时,程序将不断运行,直到用户停止或关闭程序:

while True:
    question = input("Do you like cheese? (yes or no) ")
    print ("")

试试看,你将永远被问到“你喜欢奶酪吗?”(当然,直到你退出程序为止)。

for 循环

for 循环会重复执行代码行指定的次数。在这个例子中,range 的值设置为 5,这意味着你将被问到是否喜欢奶酪五次,然后循环停止:

for x in range(5):
    question = input("Do you like cheese? (yes or no) ")
    print ("")

注意,for 循环完成了与我们之前创建的第一个 while 循环相同的任务。但它使用的代码更少,使得程序更加简洁高效。

条件语句

Python 中的 条件语句 让程序可以做出决策:当 这个 为真时,这个 会发生,但当 那个 为真时,那个 会发生。可以将条件语句看作按钮:按下一个按钮,它会发出声音,但按下另一个按钮,它会闪烁灯光。

条件语句让你的代码能够根据不同的输入或输出作出不同的响应。你可以通过使用 ifelifelse 语句来创建条件语句。

if 语句是条件语句的开始,用来检查第一个条件是否满足。例如,如果 按下按钮 A,就播放声音 A。

如果第一个条件没有满足,你可以使用elif语句来检查其他条件,elifelse if的缩写。这个语句会检查下一个选项,如果前一个选项没有被选择的话。在这个例子中,如果按钮 A 没有被按下,它可能会检查按钮 B 是否被按下。你可以根据需要添加任意多个elif语句,检查按钮 C、D,等等。

条件语句的最后部分是else语句。它告诉程序在其他条件不满足时应该做什么;在这个例子中,如果没有按下任何按钮,则执行else语句中的代码。

请考虑以下程序,看看条件语句是如何工作的:

while True:
 ❶ question = input("Do you like cheese? (yes or no) ")
    print ("")
 ❷ if question == "yes":
       print ("Good choice")
 ❸ elif question == "no":
       print ("shame, I have lots spare")
 ❹ else:
        print ("Did you answer correctly?")
        print ("")

你开始时会询问用户是否喜欢奶酪❶。用户输入一个回答。然后你使用if语句❷来检查用户的回答是否是yes。如果是,程序将打印出信息Good choice

如果用户没有输入yes,程序将跳到elif语句❸,检查用户是否输入了no。如果是,将打印出一个备用信息。

最后,你使用else语句❹来回应任何不是yesno的回答。

进入程序并运行它。你可能想尝试编写你自己的示例。

函数

函数是一种存储执行单一任务的多行代码的有用方式,就像一组指令。然后,当你想在代码的其他地方执行这个任务时,你只需输入函数名,而无需再次键入所有代码。

要创建一个函数,你输入def,然后是函数的名称、一对空括号和一个冒号(冒号非常重要!)。然后按 ENTER 键移到下一行,输入函数将要执行的指令。注意,当你按 ENTER 键时,光标会出现缩进位置,而不是在行的开始。像循环一样,属于函数的代码行需要缩进,这样 Python 才知道它们属于该函数。

要在程序的任何位置运行函数,你写出函数的名称,后跟空括号。这称为调用函数,如下例所示:

def PocketMoney():
    money = good_behavior * rate
    tax = (good_behavior  / 100) * 20
    print (money)
    print (tax)
good_behavior = int(input("Please enter the number of days you were
good "))
rate = float(input("Please enter your pocket money rate " ))

你开始时定义一个名为PocketMoney()的函数。函数定义后的缩进行计算用户获得的零花钱金额以及他们根据输入的“税费”信息所需支付的金额。

如果现在运行这个程序,它不会做任何事情,因为你还没有调用这个函数;你只是定义了它。包括函数调用的完整程序如下所示:

def PocketMoney():
    money = good_behavior * rate
    tax = (good_behavior  / 100) * 20
    print (money)
    print (tax)
good_behavior = int(input("Please enter the number of days you were
good "))
rate = float(input("Please enter your pocket money rate " ))
PocketMoney()

现在程序会要求用户输入他们表现良好的天数以及每天获得的零花钱数额。然后它会计算并打印用户的最终零花钱金额,减去税费!

从终端运行 Python 代码

有时你需要从 IDLE 或 Thonny 之外的地方运行程序。例如,你可能从另一台计算机远程运行程序,或者你可能没有将显示器连接到树莓派(如第十一章中的自然盒子项目)。在这种情况下,你可以通过终端运行程序。这样做可以释放处理器、内存和图形,专注于 Python 程序的运行。

要从终端运行程序,首先打开终端,使用它导航到包含你想要运行的程序的文件夹。然后输入以下命令:

pi@raspberrypi:- $ sudo python3 name_of_the_program.py

用你的 Python 程序的名字替换 name_of_the_program.py,它就会运行!

注意

当我展示你应该在终端中运行的代码时,我会在前面加上提示符,像这样: pi@raspberrypi:- $. 但是你只需要输入提示符后面的命令,不需要输入实际的提示符。

常见编程错误

程序员常说,编程是 20%的编码和 80%的调试,调试指的是修正代码中的错误。bug这个词可以追溯到计算机刚开始非常庞大,几乎占满整个房间的时代。故事是这样的:1947 年 9 月 9 日,一位著名的程序员 Grace Hopper 发现计算机停止工作。当她检查计算机时,发现一只飞蛾卡在了某些活动部件之间。Hopper 随后把这只飞蛾粘在了她的日志本上,并写下了“发现 bug”的字样。

错误、bug 和失误会导致程序运行不正常、卡顿或甚至无法启动。采用一种有条理的逐行调试方法可以使这一过程变得更加容易。但要做好准备,可能需要花费大量时间来找出一个小小的错误。希望你不会找到一只飞蛾。

许多常见的错误都是语法问题。语法是规定你必须如何编写代码的一套规则。例如,条件语句中的代码行必须始终以冒号结尾。不同的编程语言使用不同的语法。当遇到错误时,首先检查你是否使用了正确的语法。两种常见的语法问题是字母大小写的错误使用和缩进问题。

大写字母

最常见的语法错误之一就是错误使用大小写字母。编程语言并不总是遵循标准的英语语法规则。有些代码行以小写字母开头,有时一个单词中间可能会出现一个随机的大写字母。Python 对大写字母的使用非常敏感!不要尝试修正看起来像是错误但实际上并不是的地方;你必须完全按照书中所示的方式输入代码。

例如,考虑以下两行代码:

Print ("Hello there")

print ("hello there")

只有其中一个是有效的。你认为是哪一个?试着运行它们,看看自己是否正确。

缩进

另一个常见的错误原因是缩进——代码行开头的空格数。通常来说,在 Python 中,缩进的空格数并不重要,只要保持一致。一个好的做法是每次缩进留四个空格或一个制表符。你可以任选其一,但请确保一致,且在程序中只使用一种方法。不要在同一程序中同时使用两种方法。作为正确缩进的示例,请参见下章中的代码片段:

while True:
 ❶ print(ldr.value)
 ❷ time.sleep(2)
 ❸ if ldr.value <= 0.4:
    ❹ print("light on")
    ❺ led.on()
 ❻ else:
    ❼ led.off()

❶、❷、❸ 和 ❻ 处的行是对齐的。❹、❺ 和 ❼ 处的行也是对齐的。此程序代码的缩进正确,因此可以运行。

记住,当你使用条件语句和循环,或者定义函数时,需要对代码行进行缩进。更一般地说,任何以冒号结尾的行,后面的行都必须缩进。如果你的缩进没有对齐,程序将失败并产生错误消息,如图 2-3 所示。

Image

图 2-3 如果你的代码包含语法错误,它会产生如下的错误消息。

例如,下面的程序有缩进错误:

while True:
     print(ldr.value)
     time.sleep(2)
 ❶ if ldr.value <= 0.4:
     print("light on")
            led.on()
        else:
               led.off()

你能找出哪些行缩进不正确并会导致错误吗?

当你希望某个事件发生在循环外部或条件判断之后时,代码必须去缩进,即与循环或条件语句开始前的代码对齐。例如,如果你在末尾添加了一个 print 语句,但它与 if 语句❶对齐,那么 if 语句也会执行这个 print 命令。

要了解更多关于缩进的信息,请参阅 Python 官方网站上的https://www.python.org/dev/peps/pep-0008/#indentation/

使用注释

当你完成一个程序并且稍后回过头来看时,你可能会发现某些编码部分令人困惑,尽管是你自己写的。为了帮助自己和他人理解代码的作用,你可以使用注释。注释是为读者提供关于代码某部分的说明。

Python 提供了两种添加注释的方法。第一种是使用井号 (#)。通常,你会在注释前加一个井号,如下所示:

print ("hello") # this is a comment

创建注释的第二种方法是使用三个单引号标记打开注释,再用三个单引号标记关闭注释,如下所示:

print ("hello") '''this is also a comment'''

记住,注释不是命令;当你运行程序时,它们不会执行。它们的唯一作用是提供对代码的描述和提醒。那么,应该使用哪种注释方法呢?如果注释是单行的,使用 # 选项。如果注释跨越多行,使用 ''' 方法。

总结

通过这本简短的 Python 介绍,你现在已经准备好开始本书的活动了。每一章都会带你完成一个完整的项目。你可以按任何顺序完成这些项目,尽管它们的难度会逐渐增加。你还可以从每一章中提取代码、技术和元素,发明你自己定制的小技巧。如需更多指导,可以查看树莓派基金会的网站和帮助论坛。

开始一个项目吧!

第三章:热熔胶夜灯

在这个项目中,你将使用热熔胶枪、硅胶冰块或烘焙模具以及 LED,制作一个小型定制灯光。然后,你将编写程序让灯光闪烁或渐变开关。你还将通过添加一个光传感器,将这个项目扩展为一个夜灯,使其在黑暗中自动打开,在天亮时自动关闭。

热熔胶是一种塑料粘合剂,在加热时呈液态,因此非常适合填充任何形状并迅速干固成型。硅胶模具具有耐热性,可以防止胶水粘附在模具上,方便在胶水干固后轻松取出。图 3-1 展示了一个完成的灯光。

Image

图 3-1 R2-D2 热熔胶枪灯

所需材料

这是你将需要的项目清单:

  • 树莓派

  • 热熔胶枪

  • 热熔胶棒

  • 硅胶模具

  • 发光二极管(LED)

  • 母对母连接线

  • 面包板

  • 光敏电阻(LDR)

  • 电阻器(220 到 330 欧姆之间)

  • 0.1 微法电容器

选择任何你喜欢的模具形状!最近,我制作了一个绿色的 R2-D2 LED 和一个红色的死星。我还找到了几个复仇者联盟的模具,于是我做了一个绿色的浩克拳头(会闪烁),一个蓝色的美国队长盾牌和一个红色的钢铁侠面罩。

只需确保模具是硅胶材质,这样它才具有耐热性。此外,你应该知道光敏电阻也叫做光依赖电阻LDRs)或光电池

警告

热熔胶枪会变得非常热。在胶枪或胶水冷却之前,切勿触摸其喷嘴或胶水。同时,要小心滴落的胶水:不要让它滴到鞋子、衣物或地板上。建议在制作表面下放置报纸以保护其表面。

自定义夜灯的构建

自定义夜灯的制作分为两个阶段。首先,你需要实际制作灯光。其次,你将编写程序来为灯光设置指令。让我们开始吧。

按照以下步骤构建灯光:

  1. 准备热熔胶枪: 将胶棒插入热熔胶枪,插上电源,等待几分钟让它加热。当它准备好使用时,通常会有少量胶水从喷嘴滴出。

  2. 准备模具: 将硅胶模具放在一个稳固的表面上。你可能需要在模具下方放些纸张或防尘罩来保护表面。确保模具无尘且干燥。

  3. 准备 LED: 拿起 LED 并查看两根导线,也叫做引脚。注意,其中一根比另一根略长,如图 3-2 所示。较长的引脚是正极;较短的引脚是负极。这个细节在你将 LED 连接到模具时非常重要。如果你将引脚接错,电路将无法闭合,电流无法流过 LED,LED 将无法亮起。

Image

图 3-2 LED 引脚

因为你需要在胶水固化时接触到 LED 的腿部,所以在加热胶水之前,检查一下你希望 LED 在模具中的位置。你希望 LED 的腿部直接伸出,还是稍微弯曲向下或向外?LED 的放置位置取决于模具的形状。你希望 LED(包括腿部)大约位于模具的中间。太深的话,LED 会突出;如果放得不够深,它就无法牢固地固定在胶水中。大致估计一下 LED 的位置。

  1. 添加电阻: 将电阻的一端绕在 LED 的较长正极腿上。电阻可以防止 LED 过热并烧坏。

  2. 往模具中添加胶水: 当你知道 LED 的位置时,将其从模具中取出。然后,使用预热的热胶枪,开始慢慢地将胶水挤入模具。当模具充满约 80%时,轻轻地将 LED 插入胶水中,用手握住其腿部。等胶水定型后,你可能需要再加一点胶水,直到模具完全填满。握住 LED,但不要让热胶粘到你的手指上。图 3-3 展示了 LED 的良好位置。

Image

图 3-3 将热胶水填充到模具中

  1. 定位并固定 LED: 当模具充满胶水时,放下胶枪。继续固定 LED,确保它在胶水干燥时保持在你希望的位置。

    几分钟后,胶水开始变成白色。此时,你可以松开 LED,让胶水固化约 15 分钟。然后轻轻触摸胶水。如果不再粘手,仔细地将模具从胶水中剥离。如果胶水固化的形状不容易脱出,你可能需要再等几分钟,让它完全固化。

  2. 连接 LED: 当胶水完全固化并冷却后,取两个母对母的跳线,将每根跳线连接到 LED 的两条腿上。正极腿上绕着电阻,所以将跳线连接到电阻的末端。将来自正极的较长腿的跳线连接到 GPIO 引脚 18,这是 Pi 的物理引脚 12。将较短的负极腿连接到 Pi 的任意地面引脚:你可以选择物理引脚 9、14、20、30、34 或 39。图 3-4 展示了 LED 的接线图,正极腿是直腿。

Image

图 3-4 LED 的接线图

编写夜灯代码

现在是时候添加软件了。以下是步骤:

  1. 插入并启动你的树莓派。

  2. 通过打开终端并输入 sudo idle3,或者点击 Pi 图标选择开始编程Python 3,来加载 Python。

  3. 在 IDLE 窗口中,选择文件新建文件,如图 3-5 所示。

Image

图 3-5 在 IDLE 中打开新文件

  1. 输入清单 3-1 中的简单代码,使 LED 闪烁。

       from gpiozero import LED
       from time import sleep
    ❶ led = LED(18)
    ❷ while True:
        ❸ led.on()
        ❹ sleep(1)
           led.off()
           sleep(1)
    

    LISTING 3-1 闪烁 LED

让我们看看这段代码是如何工作的。首先,你从 gpiozero 库中导入 LED 类,该库包含一些命令,帮助你控制 LED。你还导入了 sleep() 函数。一个 函数 由执行特定任务的代码组成,但它由一个单一的词(或两个)表示,作为函数的名字。当你在代码中调用这个函数名时,Python 会运行该函数中的指令,免去了你再次输入所有这些代码行的麻烦。你可以随意为函数命名,虽然最好使用一个描述该函数功能的词。例如,在前面的章节中,当我们使用 print("hello") 代码时,print 这个词就是一个函数。IDLE 编辑器会将所有函数名显示为浅紫色,方便你识别。print() 函数包含几行代码,会在 IDLE 窗口中显示括号内的文本。在接下来的章节中,你将经常使用 print() 函数。

sleep() 函数在指令之间添加延迟。这意味着你可以以不同的速度闪烁 LED。如果你使用较低的延迟值,LED 会更快闪烁。然后,你告诉树莓派 LED 连接到哪个引脚,这里是引脚 18 ❶。

你创建了一个 循环,这个循环会无限次重复其下方缩进的指令,除非你停止程序 ❷。最后,你把指令添加到循环中:打开 LED ❸,等待 1 秒 ❹,关闭 LED,然后等待 1 秒。LED 会一直闪烁。

运行你的程序

要运行程序并让 LED 闪烁,按下键盘上的 F5。系统会提示你保存文件。为你的程序命名并保存:此时你的 LED 模式就会启动!要结束程序,请通过点击 X 关闭 Python 窗口。

修改:使 LED 渐变

你可以修改程序,使 LED 渐亮渐暗,也叫做 脉冲,而不是简单的开关闪烁。打开一个新的 Python 文件,并添加 Listing 3-2 中的代码。这个程序会逐渐让 LED 变亮,然后再逐渐变暗。

from gpiozero import PWMLED
from signal import pause
led = PWMLED(18)
led.pulse()
pause()

LISTING 3-2 使 LED 渐变

在这里,你导入了 PWMLED 类,以便能够脉冲控制 LED,设置你用于 LED 的 GPIO 引脚编号,然后设置脉冲。你还添加了一个暂停,这可以确保程序持续运行并减少 CPU 负载,使其运行更快。通常,程序运行一次后,GPIO 引脚会被重置。pause() 指令确保 GPIO 程序的信号不会停止:程序继续运行,直到你退出程序,LED 会一直闪烁。

保存这个代码并运行它,看看变化!

构建 LED 夜灯

让我们提升夜灯项目。你将添加一个光敏电阻来制作一个简单的夜灯,为你的房间增添氛围,如图 3-6 所示。光敏电阻是一种传感器,用来测量房间中的光线强度并返回一个值。这个值可以触发灯光的开关,取决于房间的光线强度。

光线读数是模拟的,这意味着它们可以是任何值,而不仅仅是开或关。想象一下太阳的升起或落下:它不会突然出现在早晨,而是光线逐渐增加。与此不同,计算机是数字的,意味着它们只理解开或关的值。计算机使用数百万个可以打开或关闭的微小开关(就像开关一样)。

然而,如果你有一个调光开关,你可以调整光线的亮度级别。你将在这里使用类似的技巧。

当光线照射到光敏电阻时,它会产生一个小的电荷。你将把这个电荷存储在一个电容器中,电容器是一种专门用来存储电荷的小型设备。然后,你可以使用电容器中存储的电荷量来指示检测到的光线强度。

如果电容器充满电,读数将为 1,表示房间完全亮起,夜灯不需要开启。读数为 0.4 表示房间亮度约为 40%,这时房间足够黑暗,树莓派可以开启夜灯。

图片

图 3-6 铁人夜灯

连接你的夜灯

你需要将 LED 灯连接到 GPIO 引脚 18。图 3-7 显示了接线图供参考。

按照以下步骤连接光源:

  1. 添加新组件: 将光敏电阻的引脚插入面包板,确保引脚之间至少留一行空隙。将电容器添加到面包板上,将其中一只引脚放置在光敏电阻右侧引脚的同一行,如图 3-7 所示。

图片

图 3-7 将电容器和光敏电阻添加到面包板

  1. 添加接线: 将接线 1 连接到光敏电阻的左引脚所在的行。将接线 2 连接到光敏电阻的右引脚和电容器的左引脚所在的行。将接线 3 连接到电容器的左引脚所在的行。这些接线在图 3-7 中有显示。

  2. 连接到你的树莓派: 将接线 1 连接到第一个物理引脚,即 3V3 引脚,它提供电源。将接线 2 连接到 GPIO 引脚 4:这是左侧的第四个物理引脚。将接线 3 连接到地面引脚 GND。我建议你使用最近的地面引脚,即第 6 号引脚。

编写夜灯代码

要编写夜灯代码,创建一个新的 Python 文件,然后在清单 3-3 中添加程序。

❶ from gpiozero import LightSensor, LED
❷ import time
❸ ldr = LightSensor(4)
❹ led = LED(18)
❺ while True:
       print (ldr.value)
       time.sleep(2)
    ❻  if ldr.value <= 0.4:
           print ("light on")
           led.on()
    ❼ else:
           led.off()

清单 3-3 编写夜灯代码

你从gpiozero库中导入了LightSensorLED类,分别用于控制光敏电阻和 LED ❶。然后你导入了time模块,以便在每次读取光线时添加短暂的暂停 ❷。首先,你将数值设置为 2。这将使你能够通过将手放在光敏电阻上来测试程序。当你将程序用作夜灯时,可以增加延时。因为日落可能需要几分钟,频繁地每秒读取一次将毫无意义;达到所需光线水平可能需要 45 分钟,而你的树莓派则需要进行超过 2700 次读取,这会消耗处理时间和电力。

在❸处,你告诉程序光敏电阻连接到 GPIO 4 端口。然后,你告诉程序 LED 连接到 GPIO 18 引脚 ❹。

你创建了一个while True循环,使程序持续读取光线并检查数值,以确保不会错过日落 ❺。你将光线读取值打印到屏幕上,并添加了短暂的 2 秒暂停。你可能对光线值的读取感兴趣,将其显示在屏幕上能让你更容易测试程序是否正常运行。

然后,你使用条件语句检查光线读取值是否小于或等于 0.4(记住 0 表示无光,1 表示完全阳光) ❻。条件语句是一条指令,它告诉程序只有在某些条件为真时才执行特定的命令。这个if语句告诉程序,如果值小于或等于 0.4,说明天色变暗了,程序应该打印一个可选的警告信息并开启夜灯。你可以调整光线等级以匹配你的环境。例如,如果你住在城市里,可能需要将数值设置得更高,以考虑到路灯的影响。

else 语句是另一个条件语句,它告诉计算机,如果读取值大于 0.4,应该关闭 LED ❼。

运行你的程序

要运行程序并测试你的光敏电阻,按下键盘上的F5。这会提示你保存文件并给它一个易于识别的名称,然后开始运行。通过在传感器上覆盖一块布或用手指挡住光线来测试你的夜灯:这应该会触发夜灯的开启。

你可以通过减少或增加程序第 8 行的数值来调整灵敏度。例如,尝试将该行更改为以下内容:

 if ldr.value <= 0.2:

这一行只会在非常黑暗时触发光线。要结束程序,点击 Python 窗口右上角的X按钮。

你房间里的光线和你使用的 LED 类型将决定适合你的最佳数值。可以通过实验光敏电阻的数值来找到最适合你环境的数值。

总结

现在你有了一个工作的、定制的夜灯。这里有一些改善你灯光的建议:

  • 创建更多的 LED 生物并将它们添加到你的收藏中。

  • 使用不同颜色的 LED 灯来制作不同颜色的光。

  • 在胶水未干之前加入闪粉,使其闪闪发光。

  • 使用小型硬币电池,通过将电池放置在 LED 腿之间,使功能具有可携带性。

  • 反转光线值,使 LED 灯成为警报,当光线值高于 0.80 时闪烁。

第四章:Pi 摄像头:自拍快照

照片已经成为我们日常生活的必需品,这要归功于摄像头被广泛应用于每部手机。2016 年,拍摄的照片数量超过了自相机发明以来拍摄的所有照片总和。在本章中,你将学习如何使用专为树莓派硬件设计的 Raspberry Pi 摄像头模块拍照。

你将设置和配置 Pi 摄像头以拍摄照片。接下来,你将学习一些更高级的功能,比如添加滤镜、定制照片质量以及添加触发按钮。然后你将学习如何使用 Pi 摄像头录制视频。最后,你将创建一个延时视频,用来捕捉日出或日落、植物生长、蜡烛燃烧,或者任何你喜欢的场景。

你需要的物品

以下是你在这次简单的 Pi 摄像头入门中需要的物品:

  • 树莓派

  • Pi 摄像头

  • Pi 摄像头排线

  • 2 根母对公跳线

  • 一个按钮(可选)

  • 鳄鱼夹或烙铁和焊锡(用于将线连接到按钮)

版本与规格

有几种型号的 Pi 摄像头可供选择,包括专门设计用于夜视的版本。所有版本的树莓派都支持 Pi 摄像头硬件以及用于将树莓派连接到摄像头的摄像头排线。排线有多种长度可选,这样你可以将摄像头放置在准确的位置,拍摄所需的照片或视频。以下表格包含了 Pi 摄像头的规格。

版本 图像分辨率 传感器分辨率
摄像头模块 v1 500 万像素 2592 × 1944 像素
摄像头模块 v2 800 万像素 3280 × 2464 像素
Pi NoIR 摄像头模块 v2 用于夜间摄影

官方的 Pi Zero 外壳配有较短的排线和内置在外壳盖子顶部的摄像头外壳,提供一个简洁、小巧、便于携带的摄像头。

对于这个项目,你需要使用常规的摄像头模块 v1 或 v2。

使用 Pi 摄像头拍照

你首先将组装摄像头,然后你将学习如何拍照和存储照片。接下来,我将展示如何通过调整分辨率、调整图像大小、使用滤镜以及添加文本来定制你的摄影。然后,你将通过添加按钮来使摄像头更易于使用,以便拍摄照片。

硬件和软件的设置

让我们从连接并启用 Pi 摄像头硬件开始,然后写一个小的测试程序来检查它是否正常工作。在开始之前,确保没有电源连接到你的 Pi,并且你的 Pi 已经断开电源。接着按照以下步骤操作:

  1. 连接相机: 将带状电缆连接到 Pi Camera(图 4-1)。根据您购买 Pi Camera 的地方,可能已经为您完成了此步骤。如果没有,轻轻拉下相机背面的塑料卡扣,并将带状电缆插入,确保蓝色带子朝向您。轻轻推上卡扣以固定电缆。

    找到 Pi 上的 Pi Camera 插槽,位置位于 HDMI 插槽和音频插孔之间。注意,它也有一个类似于 Pi Camera 上的卡扣。轻轻拉起卡扣将其打开。将带状电缆的另一端插入插槽,确保蓝色带子朝向音频插孔,并远离 HDMI 插槽。轻轻按下卡扣以固定带状电缆。在操作相机电缆时要非常小心。

Image

图 4-1 将 Pi Camera 安装到 Raspberry Pi 上

  1. 安装 Pi Camera 软件: 启动您的 Pi,打开终端窗口并输入以下命令:

    pi@raspberrypi:- $ sudo apt update
    

    一旦该命令运行完成,输入以下命令:

    pi@raspberrypi:- $ sudo apt install python3-picamera
    

    Pi Camera 软件随操作系统镜像预安装,并包括一个与相机接口的 Python 包。在将来,您可能需要在发布新版本时重新安装或更新该包,您可以使用以下命令进行更新:

    sudo apt update
    sudo apt upgrade
    
  2. 启用 Pi Camera: 默认情况下,相机是设置为关闭的。在配置开启之前,您无法使用它。要启用相机,请返回终端窗口并输入以下命令以打开配置工具(图 4-2):

    pi@raspberrypi:- $ sudo raspi-config
    

    Image

    图 4-2 启用 Pi Camera

    在配置工具中,选择 接口选项。然后选择启用连接到 Pi Camera 的选项(图 4-3)。使用箭头键选择 退出 并按 ENTER。系统会提示您在启用 Pi Camera 之前重新启动 Raspberry Pi。选择 以重新启动。现在,您可以测试相机是否已正确连接并正常工作。

Image

图 4-3 选择 Pi Camera 选项

测试 Pi Camera

要测试相机,您将运行预览功能。打开一个新的 Python 文件并输入 Listing 4-1 中的程序。此简单代码将触发相机在您的显示器上显示相机捕获的图像 10 秒钟。通过使用这段代码,您可以测试相机是否正常工作。

❶ from picamera import PiCamera
❷ from time import sleep
❸ camera = PiCamera()
❹ camera.start_preview()
❺ sleep(10)
❻ camera.stop_preview()

列表 4-1 测试相机

首先,您需要从 picamera 库中导入 PiCamera 类 ❶。这为您提供了控制相机的代码。接着,您需要导入 sleep``(``) 函数 ❷,以便您可以添加 10 秒的延迟,使得图像在屏幕上显示 10 秒钟。

然后,你创建一个名为camera的变量,用来存储PiCamera``(``)指令❸。这样你就可以调用 Pi Camera 并控制它,而无需每次都写出PiCamera``(``)变量是代表树莓派内存中某个位置的占位符,存储着内容。例如,如果你创建一个name变量,如name = "Dan Aldred",每当你在代码中使用name时,Python 会用name的内容替换,并插入Dan Aldred,而不是name

接下来,你通过告诉相机启动预览❹并添加 10 秒延时❺来触发预览。最后一步会停止预览❻,关闭相机。

要运行程序,按下键盘上的F5;系统会提示你保存文件。为程序命名并保存,然后它会开始执行。

如果你能在屏幕上看到预览,说明你的硬件和软件都正常工作。如果看不到预览,说明相机无法正常工作:检查连接到相机的电缆是否正确连接,并确保它连接到了你的树莓派。然后检查相机在配置工具中是否启用。

程序将在 10 秒后停止。但如果你想提前结束程序,可以通过点击窗口右上角的X关闭 Python 窗口。

拍摄自拍照

一旦你的相机开始工作,你可以创建一个程序拍摄第一张照片,一张自拍照(图 4-4)。

Image

图 4-4 拍摄自拍照!

打开一个新的文本编辑器窗口,将文件保存为selfie.py,然后输入清单 4-2 中的程序。这个程序会启动一个 5 秒钟的预览,给你时间站到相机前,整理发型,并练习姿势,然后相机会自动拍摄照片。

   from picamera import PiCamera
   from time import sleep
   camera = PiCamera()
   camera.start_preview()
   sleep(5)
❶ camera.capture('/home/pi/Desktop/selfie.jpg')
   camera.stop_preview()

清单 4-2 拍摄自拍照

再次,你导入PiCamera类和sleep``(``)函数,然后使用camera变量存储PiCamera``()命令。接着,你启动预览并添加一个 5 秒钟的延时(如果你喜欢,可以设置更长的时间)。你触发相机拍摄图像❶并将其保存到桌面,方便查找。文件被保存并命名为selfie.jpg

预览停止,程序结束。保存并运行程序,拍摄一张自拍照,然后返回桌面并打开图片,查看你的照片。

更改图片保存的位置

如果你一次拍摄了多个自拍,它们很快会把你的桌面占得满满的。为了保持自拍照的整洁,最好创建一个新文件夹,方便存储和管理所有的图片。

要创建一个新文件夹,打开终端并输入以下命令:

pi@raspberrypi:- $ mkdir my_photos

mkdir 命令是 创建目录(make a directory)的缩写,它会在你的主目录中创建一个名为 my_photos 的新文件夹。现在,返回到你的 selfie.py 文件,并编辑第 camera.capture('/home/pi/Desktop/selfie.jpg') ❶ 行,将 Desktop 替换为你新文件夹的名称。除非你使用了不同的文件夹名称,否则新行应如下所示:camera.capture``(``'/home/pi/``my_photos``/selfie.jpg')

将每张图像保存为新文件

请注意,每次你拍摄新照片并保存图像时,程序会覆盖之前的图像文件。如果你想保留所有照片,这个功能就不是很实用。之所以会这样,是因为你的程序在保存每张图像时使用了相同的文件名。你可以通过在图像文件名中添加日期戳来解决这个问题。

日期戳 包含表示图像拍摄时间的日期和时间信息。该信息是从树莓派的时钟中提取的,因为时间是不断前进的,所以每个文件名都是唯一的。修改 selfie.py 文件以匹配 Listing 4-3 中的程序。

  from picamera import PiCamera
  from time import sleep
❶ import datetime
  camera = PiCamera()
  while True:
   ❷ current_time = datetime.datetime.now()
      camera.start_preview()
      sleep(1)
   ❸ camera.capture('/home/pi/my_photos/'+ str(current_time) +
     'photo.jpg')
     camera.stop_preview()
     sleep(10)

LISTING 4-3 保存所有图像

这段代码大部分与 Listing 4-2 中的自拍程序相似。你需要修改的代码在 ❶、❷ 和 ❸ 处。

你导入 datetime 模块 ❶ 以从时钟中获取当前的日期和时间。你创建一个循环,使 Pi 摄像头每 10 秒拍摄一张照片。这样你就有足够的时间查看预览,然后为下一张照片摆好姿势。然后你创建一个名为 current_time 的变量 ❷,并使用 datetime.datetime.now``(``) 来获取当前日期和时间,并将结果存储在该变量中。

代码的最后一部分将日期戳值作为图像的文件名 ❸。你使用 Listing 4-2 中的 camera.capture 代码,并跟上文件位置来存储图像。但这次,你添加了存储在 current_time 变量中的值。由于日期戳是数字格式,你需要将其转换为字符串,然后才能用作文件名。现在这段代码将以唯一的日期戳保存所有图像名称!

自定义你的图像

现在你已经设置好了 Pi 摄像头的硬件和软件,准备好了解如何自定义图像了。让我们先来复习一些图像术语。

图像由称为 像素 的图像元素组成。这些小点会按一定的模式开关,从而形成整体图像。图像质量 由图像包含的像素数量与其大小的关系决定:例如,考虑在一个 1 × 1 英寸的小方块上有 1000 个像素和在一张大号账单纸上有 1000 个像素之间的区别。即使像素数量相同,账单纸上的图像质量也会较低。

图像的大小和像素数量被称为其分辨率。例如,分辨率为 100 × 100 的图像包含 100 行,每行包含 100 个像素,总共有 10,000 个像素。标准的真正高清电视分辨率是 1080 × 1080,这意味着屏幕包含 1,166,400 个像素。如果你在这个电视上观看的节目仅包含一百万个像素,那么图像就比电视屏幕的最大潜力少了 166,400 个像素,图像质量会下降。但如果你在平板设备上观看同样的节目,你不会注意到图像质量的下降,因为平板更小,像素的分布不那么密集。

接下来,你将学习如何通过更改分辨率来控制照片的质量。在本节稍后,你将进一步通过调整图像大小、使用滤镜和添加文本来定制图像。

更改分辨率

你可以通过使用代码camera.resolution = (500 x 500)来调整 Pi Camera 的分辨率,该代码将图像设置为 500 像素的高度和宽度。这很有用,因为 Pi Camera v2 的分辨率为 3280 × 2464 像素,这意味着你拍摄的每一张图像包含 8,081,920 个像素。调整后的 500 × 500 图像将仅包含 250,000 个像素,因此文件将更小,消耗的存储空间也更少。

记住,不同的分辨率适用于不同的屏幕尺寸。调整分辨率可以确保你在 Raspberry Pi 上节省存储空间。你不一定总是需要包含 8,081,920 个像素的图像!

打开你的selfie.py文件,并将其编辑成像 Listing 4-4 中的程序,尝试不同的相机分辨率,并找到适合你屏幕的分辨率。

   from picamera import PiCamera
   from time import sleep
   import datetime
   camera = PiCamera()
   current_time = datetime.datetime.now()
❶ camera.resolution = (1024, 768)
   camera.start_preview()
   sleep(2)
   camera.capture('/home/pi/my_photos'+ str(current_time) + 'photo.jpg')
   camera.stop_preview()

LISTING 4-4 更改图像分辨率

程序将相机分辨率设置为 1024 × 768 ❶,拍摄一张照片,然后将文件命名并保存为拍照时的当前日期和时间。此分辨率是 17 英寸屏幕的标准分辨率设置;每张图像将仅包含 786,432 个像素,相比最大分辨率 8,081,920 个像素,这张图像的像素数量约为 Pi Camera v2 的九分之一。通过调整❶行的数值,你可以尝试不同的分辨率设置。

调整图像大小

调整图像大小是可选的,但这是一项很好的技能。如果你不适当地调整图像的大小,使其包含的像素数量过多,像素将开始相互拥挤并重叠,从而扭曲图像。较低的分辨率适用于小屏幕,较高的分辨率适用于大屏幕。4K 屏幕需要 8,294,400 个像素才能填满屏幕并保持清晰。如果你只有这些像素的一半,像素会分散,图像质量会降低。为了保持相似的图像质量,你需要减小图像大小并在较小的屏幕上显示。

一种变通方法是在拍照后调整图像大小。调整分辨率和图像大小是非常有用的技能,特别是对于需要长时间拍摄图像的项目,如第十一章中的自然盒子项目。

打开你的程序代码,来自清单 4-4,并修改它使其看起来像清单 4-5。该程序将图像大小调整到适合分辨率。

  from picamera import PiCamera
  from time import sleep
  import datetime
  camera = PiCamera()
  current_time = datetime.datetime.now()
❶ # camera.resolution = (1024, 768)
  camera.start_preview()
  sleep(2)
  camera.capture('/home/pi/my_photos/' + str(current_time) + '.jpg',
  resize=(600, 600))
  camera.stop_preview()

清单 4-5 改变图像大小

你将camera.resolution这一行在❶处变成注释,这样该行代码就不会执行。捕获图像后,你需要添加代码来调整图像的大小:resize=(600, 600)

尝试调整括号内的值以适应你的屏幕大小。通常,屏幕越小,调整的值越低;屏幕越大,值越大。尝试不同的值,找到图像质量和存储(内存)要求之间的平衡。记住,图像质量越高,存储所需的空间越大;质量越低,所需的空间越少。

使用滤镜

滤镜可以瞬间改变图片的风格。你可能已经在相机应用程序中使用过经典的褐色、美女拍摄和复古滤镜。Pi Camera 也有一系列易于使用的滤镜(图 4-5)。要应用滤镜,你需要使用代码camera.image_effect,然后添加滤镜名称。

Image

图 4-5 应用滤镜:浮雕、水彩和负片

我们从浮雕滤镜开始,它可以让图像看起来像是被凸起的,类似于硬币上的设计。打开清单 4-5 中的程序代码,并在清单 4-6 的❶位置添加这一行。

   from picamera import PiCamera
   from time import sleep
   import datetime
   camera = PiCamera()
   current_time = datetime.datetime.now()
   camera.resolution = (1024, 768)
   camera.start_preview()
❶ camera.image_effect = 'emboss'
   sleep(2)
❷ camera.capture('/home/pi/my_photos/' + str(current_time) + '.jpg',
   resize=(600, 600))
   camera.stop_preview()

清单 4-6 添加滤镜

这个程序将每个文件保存为photo.jpg,因此每次运行时,之前的图像文件将被新的图像覆盖。如果你想保留每个过滤后的图像副本,可以更改保存图像时使用的文件名,见❷。

你也可以使用其他滤镜。通过将❶处的'``emboss``'替换为下面列出的关键字来尝试每一个滤镜:

  • '``watercolor'

  • '``cartoon'

  • '``negative'

  • '``sketch'

  • '``denoise``'

  • '``oilpaint``'

  • '``hatch'

  • '``pastel'

  • '``film'

  • '``blur'

  • '``colorswap``'

  • '``washedout``'

  • '``posterise``'

向图像添加文本

让我们来看看如何在图像上叠加文本,以添加消息、标题或仅仅是提醒。你还可以将此功能与日期戳结合使用,这样拍摄照片的日期和时间就会包含在最终图像中。

返回清单 4-5 中的代码,并在清单 4-7 中添加加粗的那一行。

   from picamera import PiCamera
   from time import sleep
   import datetime
   camera = PiCamera()
   current_time = datetime.datetime.now()
   camera.resolution = (1024, 768)
   camera.start_preview()
❶ camera.annotate_text = 'THIS IS A TEXT OVERLAY!'
   sleep(2)
   camera.capture('/home/pi/my_photos/' + str(current_time) + '.jpg',
   resize=(600, 600))
   camera.stop_preview()

清单 4-7 添加文本叠加

用于添加文本的代码行是camera.annotate_text;你只需将❶处的 THIS IS A TEXT OVERLAY 替换为你自己的信息,然后保存并运行程序(图 4-6)。

图片

图 4-6 向图像添加文字覆盖

使用按钮触发相机

你已经写了一个程序来拍照,但每次想拍照时都需要运行该程序。如果你想拍 100 张照片,就需要运行程序 100 次。让我们通过连接一个简单的按钮来使相机更像相机,当按钮被按下时,它会触发相机拍照。

连接按钮

你将使用一个按键和两根跳线将其连接。你可以根据按钮的类型以不同方式连接按钮:

  • 使用带鳄鱼夹的电线,并将夹子连接到按钮的引脚上。

  • 用胶带将电线和引脚固定在一起,确保它们接触。

  • 将电线滑到按钮的引脚上。

  • 将电线焊接到按钮的引脚上。

焊接是一种更为持久但更复杂的选项。这种技术要求你将焊锡(通常是由铅和锡组成的金属合金)加热到液态。然后使用液态焊锡将电线与按钮脚连接。当你去除热源时,焊锡再次硬化,将电线固定在一起。焊接需要特别的设备,包括电烙铁和一个安全的焊接表面。如果你以前没有焊接过,查看树莓派基金会提供的优秀指南:projects.raspberrypi.org/en/projects/getting-started-with-soldering

如果你选择焊接方法,仔细将跳线的每个公端焊接到按钮的一根脚上。一旦焊锡冷却并固化,将一根线的母端连接到 GPIO 引脚 2,另一根线的母端连接到一个接地引脚(图 4-7)。连接到引脚 2 的电线不重要。

图片

图 4-7 连接按钮

注意

如果你没有按钮,你仍然可以通过将一根电线的一端连接到所需的 GPIO 引脚,并将另一端的两根电线接触在一起,来创建一个触发器。这会形成一个像按钮按下那样的电路,触发树莓派相机拍照。

按钮编程

要编程按钮,返回到你的 Python 编辑器并创建一个新文件。然后输入清单 4-8 中的程序,并将此文件保存为selfie_snapper.py。该程序使用gpiozero设置按钮,并在每次按下按钮(或电线接触时)时触发树莓派相机拍照。这是一个可以轻松与树莓派硬件和 GPIO 引脚交互的代码库。你可以在这里了解更多信息:gpiozero.readthedocs.io/en/stable/index.html

❶ from gpiozero import Button
   from picamera import PiCamera
   import datetime
❷ from signal import pause
❸ button = Button(2)
   camera = PiCamera()
❹ def capture():
       current_time = datetime.datetime.now()
       camera.capture("/home/pi/my_photos/%s.jpg" % current_time)
       print ("Picture taken")
❺ button.when_pressed = capture
❻ pause()

LISTING 4-8 编写触发器

你从gpiozero库中导入Button类❶,该库提供了控制按钮的命令。然后,你导入通常使用的P``i``C``amera类和datetime模块,用于拍照并创建唯一的文件名。❷处的代码用于保持程序循环,并检查按钮是否被按下。

接下来,你告诉程序按钮连接到哪个 GPIO 引脚❸,即 GPIO 引脚 2。

然后你开始程序的主要部分,这一部分从capture``(``)函数开始。在这里,你正在创建自己的函数!当没有现成的函数能够完成你想要的操作时,你需要创建一个新的自定义函数。capture``(``)函数将负责拍摄、命名和保存图像❹。

要创建一个函数,你使用def命令,它表示定义,然后给函数命名。在这种情况下,你将函数命名为capture。然后,你需要添加括号和冒号。不要忘记冒号;它告诉 Python 接下来的几行缩进代码是函数的一部分,缺少冒号,函数将无法工作。

在接下来的几行中,你列出了函数的指令。你需要将这些指令缩进四个空格,以便 Python 知道它们属于该函数。在这个例子中,指令获取当前的日期和时间,保存这些数据,拍照并将其保存到你的文件夹中。由于这个读取返回日期和时间,你可以在第二天拍照而不会覆盖前一天的照片。文件名将与日期和时间数据结合,确保每次拍摄的图片都是唯一的。

但你必须将数据转换为字符串,以便与文件名一起使用,因为日期数据有一个值,不能与.jpg扩展名结合。为此,你使用代码/%s.jpg" % current_time%符号包含current_time的值,s将数据转换为字符串。

函数的最后一部分打印一条简短的消息,告诉你何时拍摄了照片。因为这是capture(``)函数中的最后一条指令,所以你不需要缩进下一行,这样 Python 就知道它不是capture()的一部分。

你已经创建了函数,但要拍照,你需要在代码中调用该函数。你使用来自gpiozero库的简单代码button.when_pressed = capture❺将函数分配给按钮,这样按钮就能拍照了。

最后,你使用pause(``)命令,确保程序能够再次运行❻,并且你可以拍摄另一张照片。

保存你的程序并按F5运行。现在,每次你按下按钮,它都会拍摄一张新照片,并使用唯一的文件名保存图像。做得好!你已经建好了自己的相机。

使用 Pi 相机拍摄视频

使用 Pi 相机拍摄视频的代码与拍照的代码类似,你可以应用之前学到的相同的过滤器和设置。

创建视频

要创建视频,打开一个新的 Python 文件,命名为 video.py,并将其保存到你的 my_photos 文件夹中。输入 Listing 4-9 中的程序来录制一个短视频。

❶ import picamera
❷ camera = picamera.PiCamera()
❸ camera.resolution = (640, 480)
❹ camera.start_recording("/home/pi/my_photos/video_test.h264")
❺ camera.wait_recording(10)
❻ camera.stop_recording()

LISTING 4-9 视频录制代码

导入 picamera 库的所有模块 ❶,并将 Pi Camera 分配给 camera 变量 ❷。

可选地,你可以设置视频的分辨率 ❸。请记住,高分辨率可以提高视频的整体质量,但也会生成更大的文件,要求更多的存储空间。

然后你需要添加代码以开始视频录制 ❹,并提供你想要保存视频文件的文件夹位置。你需要包括文件名(此处为video_test)和保存视频的文件格式;我这里使用的格式是 .h264,这是一个适合高清晰度视频的格式。

接下来,你需要添加你希望视频录制的时长 ❺。录制时间以秒为单位,因此要录制 1 分钟,你需要将 10 改为 60;要录制 5 分钟,则将值改为 300 秒。

在 ❻,你停止视频录制,这会停止相机并保存视频文件。

运行程序并录制一些视频!从短时间开始测试程序,然后调整 ❺ 行中的秒数来更改录制时长。按 F5 保存你录制的视频。

播放视频

一旦你录制了视频,打开终端并输入以下命令导航到保存视频的文件夹:

pi@raspberrypi:- $  cd my_photo

按下 ENTER 进入文件夹。进入媒体文件夹后,你可以通过输入以下命令查看该文件夹中的所有文件:

pi@raspberrypi:- $  ls

你应该能看到你的 video_test 文件。要播放视频,输入以下命令:

pi@raspberrypi:- $  omxplayer video_test.h264

该命令打开 OMXPlayer 并显示你的视频。OMXPlayer 利用树莓派的硬件播放许多流行的音频和视频文件格式。请记住,视频的大小和质量取决于你设置和录制时的分辨率。

在计算机或其他设备上播放视频

你可能想与朋友和家人分享你的精彩视频,甚至上传到社交媒体账户。但是,以当前格式,视频文件无法在其他设备上播放(这些设备可能没有安装 OMXPlayer),除非它们具备特定的视频 编解码器,这是一种能够读取多种视频文件格式的程序。因此,你将把视频文件转换为更常见的 .mp4 格式,这种格式可以在大多数平板电脑、手机和智能电视上播放。返回终端窗口并输入以下命令来安装转换器:

pi@raspberrypi:- $  sudo apt install ffmpeg

然后输入此命令将视频文件转换为 .mp4 格式:

pi@raspberrypi:- $  ffmpeg -i video_test.h264 -codec copy video_test.mp4

一旦转换完成,将文件复制到内存棒上,你就可以播放、编辑并分享你的视频了!

注意

VLC 媒体播放器是一个免费的播放器,适用于大多数设备和操作系统。在你的 Raspberry Pi 上,你可以在 StartSound and Video 下找到 VLC 媒体播放器。如果你在其他设备上查看视频,可以从 www.videolan.org/ 下载并访问 VLC 媒体播放器,无需转换即可播放视频。

创建延时摄影视频

延时摄影视频 是由几百张单独的照片制作而成,当它们合成在一起时,便产生了一种动画效果。例如,你可以拍摄蜡烛燃烧的延时摄影视频,通过每隔几分钟拍一张照片,视频就能在几秒钟内展示蜡烛燃烧的过程。

拍得越多,视频看起来越流畅。但如果拍得太多,你会失去视频的整体效果。

这个项目有两个主要阶段:拍摄照片和拼接延时摄影视频。让我们一步步进行设置。

当你运行延时摄影程序时,它可能会捕获几百张照片。因此,在编写程序之前,你需要创建一个新的文件夹来存储这些图像,以确保它们放在同一个地方,不会和之前拍摄的其他照片混在一起。返回终端并输入以下命令返回 home 文件夹:

pi@raspberrypi:- $ cd

然后使用此命令创建一个名为 my_timelapse 的文件夹:

pi@raspberrypi:- $ mkdir my_timelapse

如果你重命名了这个文件夹,记得更改文件位置并替换为你的文件夹名称。

编写延时摄影视频捕捉代码

picamera 库提供了捕获延时摄影视频所需的所有代码。当你执行程序时,它将持续运行,直到你停止它。你需要告诉程序多长时间拍一张照片,程序就会一直拍照。打开一个新的 Python 文件,将其保存为 time_lapse.py,并将程序添加到 Listing 4-10 中。

   from time import sleep
   from picamera import PiCamera
   camera = PiCamera()
❶ sleep(2)
❷ for filename in camera.capture_continuous("home/pi/my_timelapse/
   img{counter:04d}.jpg"):
❸     print ("Captured %s" % filename)
❹     sleep(10) # wait 5 minutes

LISTING 4-10 延时摄影代码

到现在为止,你应该已经熟悉了前三行代码,这些代码导入了你需要使用的库,并保存了 PiCamera``(``) 命令。

然后,你添加了一个小的延时 ❶ 以便相机在开始拍照前准备好。

接下来,你使用 for 循环 ❷ 来拍照、重命名、打印信息,然后等待 10 秒。这个循环让你避免重复编写相同的代码;如果没有它,你需要为拍摄 1000 张照片重复添加相同的代码 1000 次。请记住,循环直到你停止程序或程序满足某个特定条件或点时才会停止。

在循环之后,你使用标准的 camera.capture``(``) 代码来拍照 ❷。但这次你还添加了 _continuous,确保相机会一直拍照,直到你停止程序。

该行的下一部分,/home/pi/my_timelapse/,告诉 Pi 存储照片的位置。然后,它提供了如何命名文件的指令,以避免文件被覆盖。让我们逐步分析这段代码是如何工作的。

首先,你添加文件名,即img。然后,你使用counter(``)函数从 0 开始计数。每拍一张照片,计数器的值就会加到文件名img上。计数器的最大位数已设置;在此程序中,设置为04d,即最多四位数字。

当程序执行时,拍摄的第一张照片会被保存为img0000,下一张保存为img0001,然后是img0002,依此类推。由于你最多可以使用四位数字,因此在它们开始互相覆盖之前,你最多可以拍摄 9999 张照片。这个数字应该足够了!根据你拍摄的内容,你可能希望拍摄更少(或更多)照片。如果是这样,你只需将04d的值调整为03d(三位数字)、02d(两位数字)等。

一旦程序保存了图像文件,它会将文件名❸打印到屏幕上,这样你就可以检查程序是否正确保存了文件。你还可以一目了然地看到程序已拍摄了多少张照片。

程序的最后一行设置了每张照片之间的时间延迟。延迟 10 秒❹可以让你在部署更长的延时之前测试程序是否正常工作。相机将每隔 10 秒拍摄一张照片,直到你终止程序。在你的实际延时视频中,你可能需要使用更长的延迟时间。

现在,让我们运行程序的测试版本并创建一个延时视频。保存程序并按F5运行它。大约一分钟后,停止程序:记住,这只是为了测试并创建一个延时序列(图 4-8)。

Image

图 4-8 运行程序并收集图像

确定延迟时间

你设置的时间延迟应该根据你拍摄的内容来决定。例如,考虑到蜡烛需要三个小时才能烧完。你可以设置延迟为 60 秒,这样每分钟拍一张照片,最终会得到 180 张照片。你甚至可以将延迟设置为 30 秒,拍摄 360 张照片。根据你希望视频的长度和流畅程度,两种设置都能产生很好的延时视频效果。

现在,让我们考虑拍摄一朵花的生长过程。这可能需要五天或更长时间。使用相同的设置将得到 14,400 张照片,以每秒 30 帧的速度,最终会生成一段 8 分钟的视频片段。这太长了。设置 1200 秒的延迟会更合适,这样每 20 分钟拍一张照片。你的最终视频将包含 360 张照片,视频时长为 12 秒。

每当你设置相机拍摄延时图像时,确保考虑图像的数量并选择一个合适的延迟。

合成视频

要制作延时摄影视频,你需要将照片拼接在一起,从而创造出时间流逝的假象。你将使用 Libav 软件,它可以为你转换、处理和流式传输各种多媒体格式。以下是步骤:

  1. 安装 Libav: 打开终端,输入以下命令来下载所需的 Libav 工具:

    pi@raspberrypi:- $ sudo apt -y install libav-tools
    
  2. 导航到延时摄影文件夹: 程序安装完成后,进入存储所有图像的my_timelapse文件夹,可以通过输入以下命令:

    pi@raspberrypi:- $ cd my_timlapse
    
  3. 创建延时摄影: 在终端中输入此代码来运行 Libav 软件并创建视频(确保准确输入以下命令):

    pi@raspberrypi:- $ avconv -r 10 -i img%04d.jpg -r 10 -vcodec libx264 -crf 20 -g 15 timelapse.mp4
    

    这个过程会定位每个.h264图像并将其与前一个.h264文件合并,然后 Libav 将完成的文件转换为.mp4格式并保存为名为timelapse.mp4的文件。如果你有几百张图像,转换可能会需要一些时间。这也是为什么在进行五天拍摄之前进行测试的另一个原因!

  4. 播放延时摄影: 现在你的延时摄影测试视频已经是.mp4格式,你可以将其复制到 U 盘上,或者传输到笔记本电脑或其他设备上,就像播放任何视频文件一样!注意图像显示的速度以及它的流畅度。你可以使用这个测试来决定未来希望延迟的时间。

制作你自己的延时摄影影片

现在,你已经准备好设置你的延时摄影相机了。计算你需要拍摄的照片数量和时间段——无论是一小时、一整天还是一周。将树莓派相机放置好位置后,启动程序。根据我的经验,值得在旁边留下一个提示,让其他人知道树莓派在执行任务,这样他们就不会随便关掉它。完成所需的时间后,停止程序,进入图像文件夹,将图像拼接起来,享受你的视频。别忘了分享你的延时摄影作品,让别人也能欣赏。

总结

本章介绍了使用树莓派相机的一些基础知识,比如添加滤镜、更改设置以及拍照、录像和制作延时摄影视频。你可以将树莓派相机应用到许多项目中。比如制作定格动画,或者设置相机观察你的宠物,看它在你外出时做了什么?在第十一章中,你将把树莓派相机与运动传感器结合,远程拍摄野生动物的照片,并将其上传到 Dropbox。

第五章:Pi 间谍第一部分:为秘密监控黑客摄像头

在本章中,您将使用 Raspberry Pi 和经典的 USB 摄像头为您进行间谍监控。您将学习如何将摄像头与 Pi 配合使用。然后,您将把摄像头的实时画面传输到远程设备,比如您的手机,制作一个廉价的家庭监控系统。通过摄像头,您可以监控宠物、花园或您的兄弟姐妹。您甚至可以把它藏在一个不起眼的地方,找出是谁在偷吃家里的巧克力!

您将需要的设备

以下是您在此项目中需要的物品:

  • Raspberry Pi

  • USB 便携电池

  • USB 摄像头

如今,大多数计算机或计算机显示器都配有内置摄像头,因此原始的 USB 型摄像头通常可以在网上低价购买。您甚至可能会在抽屉的角落找到一个。如果您购买摄像头用于此项目,建议选择来自 elinux.org/RPi_USB_Webcams 的摄像头,它已被证明可以与 Pi 配合使用。

您将使用 USB 电池供电来为间谍摄像头提供电力,并使其便于携带——因此购买一个品牌电池是值得的。虽然它们略贵一些,但质量更高,使用寿命也比预算款更长。您可以将摄像头隐藏在棚子里或家里的一个空麦片盒中,无需担心插入电源插座。这样,电线就不会暴露出来,暴露了秘密。

设置您的摄像头

首先将摄像头连接到您的 Pi,并测试它们的兼容性。启动您的 Raspberry Pi,连接显示器、键盘和鼠标。完成设置后,您可以将这些设备移除。只需将摄像头线的 USB 端插入其中一个备用 USB 端口,如图 5-1 所示。

现在通过以下步骤检查摄像头是否正常工作。

图片

图 5-1 安装摄像头

  1. 识别摄像头: 您可以通过打开终端并输入 ls 命令来检查 Raspberry Pi 是否识别到摄像头。此命令会列出所有当前连接到 USB 端口的设备,输出类似于这样:

    pi@raspberrypi:- $ lsusb
    Bus 001 Device 007: ID 1908:2311 GEMBIRD
    Bus 001 Device 006: ID 093a:2510 Pixart Imaging, Inc. Optical
    Mouse
    Bus 001 Device 005: ID 0e6f:0149 Logic3
    Bus 001 Device 004: ID 05e3:0610 Genesys Logic, Inc. 4-port hub
    Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
    SMSC9512/9514 Fast Ethernet Adapter 
    Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
    SMC9514 Hub
    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    

    我的摄像头是 Logic3,您可以在输出的第三行看到它。您可以查看您的摄像头品牌名称,或者移除所有其他连接的 USB 设备(除了键盘),看看哪些设备仍然列出。如果您的摄像头未列在输出中,请尝试重新启动 Raspberry Pi 并重新运行代码。如果摄像头仍未显示在输出中,可能是您的型号不兼容,您需要更换一个。前面提供的网站列出了与 Pi 兼容和不兼容的摄像头。

  2. 安装摄像头软件: 现在,你将安装一个名为fswebcam的程序,它将在继续主项目程序之前测试摄像头是否正常工作。没有什么比编写程序后发现硬件不兼容更糟糕的了!返回终端窗口,输入以下命令下载并安装所需的软件:

    pi@raspberrypi:- $ sudo apt-get install fswebcam
    
  3. 拍摄测试图像: 安装完程序后,你可以通过拍摄图像来测试摄像头。在终端中输入以下命令,从摄像头拍照并将其保存为名为test的图像文件:

    pi@raspberrypi:- $ fswebcam test.jpg
    

    要查看图像,打开你的 Pi home文件夹,找到test.jpg文件,并双击它。如果已经拍照,说明摄像头工作正常!图 5-2 显示了图像成功捕获后终端窗口的输出。如果没有拍照,你可能需要使用不同的网络摄像头。另外,检查摄像头是否已连接,并确保代码正确无误。

图片

图 5-2 从网络摄像头捕获图像

从网络摄像头流式传输视频

现在你知道网络摄像头已经正确安装并工作,你可以编写一个简单的程序,将摄像头捕获的图像流传输到树莓派。你将使用一个名为PyGame的代码包,它是一个为编写视频游戏而设计的 Python 模块集。然而,它也包含了计算机图形、视频和声音库,专为与 Python 一起使用而设计。你将借用一些 PyGame 的函数和工具,以免自己编写这些功能。

PyGame 在树莓派操作系统中预先安装,因此你不需要下载它。打开一个新的 Python 文件,并将其保存为pi_spy.py。然后输入示例 5-1 中的代码,这是代码的第一部分。

❶ import sys
❷ import pygame
   import pygame.camera
❸ pygame.init()
   pygame.camera.init()
   # set the size of the window
❹ screen = pygame.display.set_mode((320,240),0)

示例 5-1 Pi spy 代码的第一部分

和大多数程序一样,你首先需要导入所需的模块和 PyGame 库。首先,导入系统模块❶,然后导入pygame。从pygame中导入pygame.camera模块❷。

接下来,初始化pygame和摄像头命令❸。这为pygame的运行做好准备。代码的最后一行设置了pygame窗口的大小❹。PyGame 将在一个单独的窗口中运行程序,这一行代码在运行程序时会创建该窗口。你可以通过调整当前设置为 320 × 240 的宽度和高度来更改窗口的大小。图 5-3 显示窗口只占据了屏幕的一小部分。一些网络摄像头支持更高的分辨率。你可以在elinux.org/RPi_USB_Webcams找到几个选项。

图片

图 5-3 网络摄像头窗口

接下来,你将使用 PyGame 找到连接的网络摄像头并启动它。为此,将示例 5-2 中的代码添加到你的程序中。

   # locate the camera and turn it on
❶ cam_list = pygame.camera.list_cameras()
❷ print (cam_list)
❸ webcam = pygame.camera.Camera(cam_list[0],(32,24))
❹ webcam.start()

LISTING 5-2 程序的第二部分:启动网络摄像头

首先,你需要让 PyGame 通过请求列出可用的摄像头来找到网络摄像头。你创建一个变量来保存摄像头搜索的结果❶,这应该返回你的单个网络摄像头。结果将存储在一个包含一个项的列表中,因为这里只有一个网络摄像头。

然后,你打印结果列表❷,以确保你的摄像头已经被找到。如果你只连接了一个网络摄像头,它应该列在位置 0。记住,列表编号总是从 0 开始,而不是 1。

然后,你设置网络摄像头并将详细信息存储在名为webcam的变量中,以便稍后可以更方便地再次调用它❸。摄像头的名称从你之前通过传递列表名[0]获得的cam_list中提取,这告诉 Python 从列表中位置 0 的第一个项目获取值。

你还指定了网络摄像头图像的尺寸。你可以看到这些尺寸在括号中,而不是方括号,值之间用逗号分隔:这是一种被称为元组的数据类型,它类似于列表,但其中的值是不可更改的。元组通常被称为不可变的,这意味着在程序运行时,元组内部的数据不能被更改。代码❸会改变显示在流窗口中的图像质量。如果你将尺寸设置为 32 × 24,视频流会看起来更像像素化的画面,但 PyGame 窗口保持原始的 320 × 240 大小。要更改它,你必须返回代码,编辑尺寸,然后重新运行程序。

代码的最后一行告诉摄像头开始工作❹,这会启动摄像头。

现在进入程序的最后部分。将 Listing 5-3 中的代码添加到你的程序中。

❶ while True:
       # stream the images and scale to a set size
    ❷ stream = webcam.get_image()
    ❸ stream = pygame.transform.scale(stream,(320,240))
    ❹ screen.blit(stream,(0,0))
       # update the display
    ❺ pygame.display.update()

LISTING 5-3 Pi 间谍的最终代码:视频流

你开始时创建一个while循环,以使程序继续运行后续的指令❶。该循环告诉 PyGame 不断从网络摄像头获取图像并在屏幕上显示。

然后创建一个变量来存储从附加的网络摄像头获取的图像❷。下一行代码❸将每张图像缩放以适应之前输入的屏幕大小。在此情况下,它将图像缩放为 320 × 240,以匹配在程序开始时screen = pygame.display.set_mode((320,240),0)变量中设置的尺寸。如果你更改了窗口大小或缩放比例,请确保这些尺寸匹配。

要在屏幕上绘制视频图像,你可以使用 PyGame 中的blit()函数❹。你的显示屏由数百万个微小的点组成,这些点被称为像素,它们可以打开或关闭。当你在屏幕上显示一张图像时,软件会管理哪些像素是开着的,哪些是关着的,以及它们的颜色。像素还构成了你的图像。来自网络摄像头的视频由许多静态图像组成,这些图像一个接一个地播放,类似于翻页书或动画。Blitting操作会将某一图像的所有像素复制到屏幕上的像素,显示该图像。然后,当程序循环时,会从摄像头捕获下一张图像并保存。使用blit()函数更新像素,生成视频。

代码的最后一行❺更新了 PyGame 窗口,显示了网络摄像头的流视频,供你观看。程序然后再次循环,收集一张新图像,进行 blitting,然后显示在你的屏幕上。

保存你的程序并运行!记住,按下F5将保存并执行程序。一个小的 PyGame 窗口将弹出,你会看到来自网络摄像头的实时视频流。

将实时视频流传输到设备

到目前为止,你的 Pi 的视频流来自与你的 Pi 同一位置,这没问题,如果你坐在桌前并能观看 PyGame 窗口。但假设你想在不同的地点进行监控。比如,你想在窗户旁安装一个摄像头,监控家门口的情况,以便看到访客接近?或者你可能想把摄像头藏在厨房,看看你离开时宠物们在做什么。你甚至可以把摄像头用作简单的卧室监视器。接下来的程序将教你如何为此类项目制作一个便携式间谍摄像头。

确保你的摄像头仍然插在树莓派的 USB 端口上。接着,你需要下载并配置一款叫做motion的实用软件,它能让你将摄像头的图像流传输到某个设备上,比如你的手机或平板电脑。为此,你需要确保树莓派观看设备都已连接到你的家庭网络。按照以下步骤操作:

  1. 安装软件: 要下载并安装motion程序,请打开终端并输入以下命令:

    pi@raspberrypi:- $ sudo apt install motion
    
  2. 找到你的 IP 地址: 要从其他设备访问实时视频流,你需要知道你的树莓派 IP 地址。IP 地址是用来标识网络上每个设备的地址。就是这个地址使得你的树莓派、游戏机和智能电视能够同时在线,而不会意外收到错误的数据。在终端中查找你的树莓派 IP 地址,输入以下命令:

    pi@raspberrypi:- $ ip addr show
    

    或者输入以下命令:

    pi@raspberrypi:- $ hostname -I
    

    这个命令会列出与网络连接相关的许多数据。查找以wlan0开头的行。在第二行,你会找到 IP 地址,前面有inet,并像这样显示:192.168.1.751。这是你的树莓派的个人 IP 地址。把它写下来,因为稍后你会用到它。

  3. 创建守护进程以运行程序: 在计算机术语中,守护进程 是一种在后台运行的程序,自动执行任务。您作为用户,无需直接控制它。网络摄像头流媒体不需要任何用户交互,可以作为后台进程运行;因此,您需要通过向 motion 代码文件中添加一行来将其设置为守护进程。在终端窗口中输入以下命令:

    pi@raspberrypi:- $ sudo nano /etc/default/motion
    

    该命令会打开一个名为 motion 的文本文件,您可以在其中添加指令。motion 软件会识别 motion 文件。在文件的底部,添加以下代码行:

    pi@raspberrypi:- $ start_motion_daemon=yes
    

    这段代码指示守护进程启动网络摄像头服务器,并将其作为后台进程运行,如图 5-4 所示。按 CTRL-X 退出,系统会提示时,按 Y 保存文件。

Image

图 5-4 网络摄像头服务器窗口

  1. 更改配置文件中的设置: 在开始流媒体之前,您需要在运动配置文件 motion.conf 中进行一些更改。在这里,您可以添加或修改代码来更改程序的行为。这个文件很大,包含很多设置,所以我们需要逐步讲解,以免让您迷失。请在终端中输入以下命令以打开 motion.conf

    pi@raspberrypi:- $ sudo nano /etc/motion/motion.conf
    

    配置这些设置:

    启动守护进程: 要启动守护进程,请找到文件开头附近的 daemon off 项,并将其更改为 daemon on。

    调整画质: 将流媒体的图像质量调整为 1 到 50 之间,其中 50 为最高质量。更高的质量会呈现更清晰的图像,但会对树莓派和网络带来更大负担。如果该值过高,树莓派可能无法处理,从而导致崩溃。我建议初始设置为 20。之后,如果您想要更清晰的图像,可以再做调整。找到 Live Stream Server 部分,找到 stream_quality 行,并将其设置为 20,确保它显示如下:

    stream_quality 20
    

    调整帧率: 调整帧率会影响每秒显示的帧数。帧数越高,视频播放越流畅。帧数越低,图像会显得更加卡顿。然而,有时设置得太高会消耗过多带宽并导致连接变慢,尤其是在其他人也在使用您的网络时。将帧捕获设置为大约 25,这将使您获得接近实时流的效果,延迟约为 0.2 秒。找到设置项 stream_maxrate 并将其设置为 50。同样,您可以稍后根据您的网络能力调整这些设置。

    更改显示的视频大小(可选): 如果您在智能手机或平板电脑上查看视频,您可能需要将视频窗口大小调整得更小。在这种情况下,找到配置文件中的 widthheight 项,并调整数字以适应您的设备。您可能需要尝试几个不同的数字,直到找到合适的大小。

    最终设置:stream_localhost 行设置为关闭,以便将摄像头图像流传输到你的移动设备,而不仅仅是树莓派(本地主机)。完成这些更改后,按 CTRL-X。系统会提示你保存配置文件的更改。按 Y 选择 。如果以后想要再次更改图 5-5 中显示的一些设置,只需使用 sudo nano /etc/motion/motion.conf 命令打开文件。然后进行所需的调整,退出并保存文件。

Image

图 5-5 设置窗口

  1. 启动运动检测: 要启动网络服务器并捕获视频流,请输入以下命令:

    pi@raspberrypi:- $ sudo service motion start
    
  2. 访问视频流: 要访问视频,打开你要流式传输到的设备上的浏览器窗口。在地址栏中,输入树莓派的 IP 地址(即你之前记录下来的地址);在地址的末尾,添加端口号 :8081。完整的地址应该类似于 192.168.1.56:8081。按下 ENTER 键后,浏览器会查找你树莓派的 IP 地址并与之连接。树莓派和运动程序将响应并开始将来自摄像头的实时视频流传输到你的设备,正如图 5-6 所示。

Image

图 5-6 向浏览器传输实时视频

现在,你需要找到一个地方来隐藏你的树莓派和摄像头开始间谍活动;只要确保摄像头有清晰的视野!你可以移除显示器、鼠标和键盘,将树莓派间谍摄像头放置在一个合适的位置。记得确保树莓派处于 Wi-Fi 信号范围内,这样图像才能传输到你的移动设备上。

停止和重新启动运动检测

一旦你完成了网页流的使用,你可以回到你的树莓派并重新连接显示器、鼠标和键盘。然后打开终端并输入以下命令来停止程序并结束流:

pi@raspberrypi:- $ sudo service motion stop

如果你无法访问显示器和键盘,只需切断树莓派的电源以停止流。

有时你的运动软件可能会卡住,视频流会停止。如果发生这种情况,图像将变为静态或冻结。此时,通过以下命令重新启动软件:

pi@raspberrypi:- $ sudo service motion restart

每当你调整配置文件设置时,也应重新启动软件。

如果你正在秘密使用你的摄像头,请记得在访问你的 Pi 并重新启动程序之前,确保周围没有人。否则,你会暴露隐藏摄像头的位置。

总结

现在你拥有了一个紧凑的间谍摄像头,可以自由使用它。你将在第十一章中再次使用树莓派的图像能力,设置一个自然盒子,用来拍摄任何触发其传感器的物体的隐秘照片。现在,尝试调整设置,看看什么分辨率、帧率和大小最适合你的系统。

第六章:操控 MINECRAFT

在本章中,你将编写 Python 程序,进入 Minecraft 环境,进行一些酷炫的操作。你将向 Minecraft 世界发送信息,并把草地变成蹦床。然后,你将编写第一个 Minecraft 游戏,沙子掉落,玩家需要躲避掉落的沙块。利用你在第三章中制作的胶枪 LED 灯,你将加入实际的硬件,并从 Minecraft 中控制它。结合 Minecraft 的强大功能和 Pi 摄像头,你将创建一个隐藏的摄像头,通过金块、砖块和西瓜塔来控制它!

你将需要的物品

在本章的大多数项目中,你将使用 Python 和 Minecraft 编程,因此你只需要 Pi 和计算机,其他物品并不多。以下是你需要的几个额外物品:

  • Raspberry Pi

  • 你的胶枪 LED 灯或带有 220-330 欧姆电阻的新 LED

  • 跳线

  • Pi 摄像头

在 Raspberry Pi 上运行 MINECRAFT

你可能已经熟悉 Minecraft,但如果你不熟悉,这里有个描述:Minecraft 是一款游戏,你可以在其中开采、制造和创造各种奇特的物品,所有的操作都发生在一个由各种地形、栖息地和材料构成的 3D 世界中。它常被描述为 LEGO 的计算机游戏版。这个世界是自由漫游的:你可以作为主角 Steve 或 Alex,随意旅行、观看你想看的事物,基本上做你想做的任何事情。

Raspberry Pi 操作系统预装了 Minecraft Pi 版。这是一个简化版的游戏,让你通过多种编程语言,包括 Python(我们将在这里使用它),来修改 Minecraft 世界。

启动 Minecraft

启动你的 Raspberry Pi,点击屏幕左上角的 Pi 图标。会弹出一个下拉菜单。向下滚动并点击 Games 标签,出现打开 Minecraft 的选项。点击该选项加载 Minecraft Pi 版,如图 6-1 所示。加载时间取决于你使用的 Pi 型号,可能从几秒钟到一分钟不等。在 Pi 4 型号上,Minecraft 加载最快。

图片

图 6-1 加载 Minecraft Pi 版

在 Pi 上使用 Minecraft 和在计算机上使用是一样的。Pi 使用经典的键盘控制布局来移动玩家角色。但 Pi 版有额外功能:它允许你 飞行,意味着你可以迅速到达其他地方。你可以通过按下空格键两次来实现飞行,使用箭头键控制方向,使用鼠标来控制玩家的视角(和以前一样)。

你在这些黑客操作中将使用的另一个控制键是 TAB 键。它可以将鼠标从 Minecraft 控制中释放,这样你就可以点击其他地方。这意味着你可以选择 Python 编程界面,编写新的代码或选择你要运行的程序。然后,你可以启动 Minecraft,并在世界的中间双击鼠标左键或右键:鼠标的移动将再次控制玩家的视角。

这个表格显示了 Minecraft 主要控制的按键。

动作
W 向前移动
A 向左移动
S 向后移动
D 向右移动
E 加载物品栏
空格键 跳跃
双击空格键 飞行/下落
SHIFT 向下漂浮
ESC 暂停/游戏菜单
TAB 释放鼠标光标

找到自己:使用 X、Y 和 Z 坐标

尽管 Pi 版 Minecraft 的世界没有完整版那么大,但仍然很容易迷路。为了定位玩家在世界中的位置,游戏使用 x、y 和 z 坐标,如图 6-2 所示。

Image

图 6-2 x、y 和 z 坐标位于窗口的左上角。

这三个值是游戏的核心部分。它们确保你在程序中创建的事件发生在玩家的位置。你将在程序中使用这些坐标。

坐标值显示在屏幕的左上角。所有的值都是从游戏世界的中心开始测量的。因此,如果你处于 x、y 和 z 值为 0.0、0.0、0.0 的位置,你就处于世界的正中心。负数或减号的 x 值表示玩家位于世界中心的左侧;负数的 y 值表示玩家位于中心的下方;负数的 z 值表示玩家位于中心的后方。例如,0.0,-45.7,0.0 意味着玩家位于世界中心下方 45.7 个方块的位置。但世界是随机生成的,所以世界的大小会有所不同。此表列出了这些坐标的最大值和最小值。

坐标 表示 最小值 最大值
x 左右移动 -255 +255
y 上下移动 -128 +128
z 前后移动 -255 +255

破解 Minecraft

让我们开始破解 Minecraft 吧!Raspberry Pi 操作系统允许你通过 Python 代码直接连接到 Minecraft。你可以开发并编写程序,打开并启动 Minecraft 游戏,运行代码,然后返回到 Minecraft 世界中,查看效果。

在运行黑客程序时,除非你使用的是 Raspberry Pi 4,否则不要在全屏模式下运行 Minecraft;应保持窗口的默认大小。全屏模式会阻止你查看代码或任何程序错误,并且对显卡的要求非常高,会导致游戏卡顿。如果你使用的是 Pi 4,可以放心使用全屏模式!另外,由于 Minecraft 程序中的一个 bug,全屏模式还会阻止你访问背包中的最后一行物品。

向聊天室发送消息

我们将从一个简单的程序开始,它会向 Minecraft 世界发送一条消息,如图 6-3 所示。这个黑客程序只有三行代码,它将教你 Python 和 Minecraft 之间的交互方式。

Image

图 6-3 在 Minecraft 中显示聊天消息

编写聊天消息程序

要开始你的第一个黑客程序,打开 Python 并创建一个新文件。然后在清单 6-1 中输入程序代码。记住,你可以从* www.nostarch.com/raspiforkids/*下载程序,进行对比或直接运行下载的 first_program.py 文件。

from mcpi import minecraft
mc = minecraft.Minecraft.create()
mc.postToChat("Welcome to my first Minecraft hack")

清单 6-1 Minecraft 消息程序

首先,导入 mcpi 库,它允许你使用 Python 控制 Minecraft。这个库告诉程序在 Minecraft 中打开并运行。为了在 Minecraft 世界中触发事件,使用库中的 minecraft.Minecraft.create() 函数从 Python 到 Minecraft 创建连接。将该函数存储在变量 mc 中,这样你每次就不需要重新输入。然后,在新创建的 mc 变量后,使用 postToChat() 函数在 Minecraft 游戏中显示消息。你可以随意更改引号内的消息内容,显示任何你想要的文本。

运行你的程序

要运行程序,保存文件后打开 Minecraft。启动游戏并等待世界加载。一旦加载完成,按 TAB 键释放鼠标,使你能够找到你的黑客程序。接下来,选择你的 Python 程序(如果你关掉了它,就重新打开),然后按 F5 执行它。程序会自动在 Minecraft 中运行。通过点击任意鼠标按钮返回 Minecraft 游戏,你的自定义消息应该会出现。恭喜!你已经制作了你的第一个 Minecraft 黑客程序!接下来,让我们做一些更有冒险性的事情。

蹦床草地

这个蹦床项目带你一步步创建你的第一个真正的互动程序。每当你踏上草地上的一个方块时,你会像在蹦床上一样弹跳起来,如图 6-4 所示。

Image

图 6-4 将所有草地变成蹦床

编写蹦床草地程序

打开 Python 并开始一个新文件,启动黑客任务。输入清单 6-2 中的程序代码。记住,你可以从www.nostarch.com/raspiforkids/下载程序,使用它与你的程序进行对比,或者直接运行下载的trampoline.py文件。

from mcpi import minecraft
mc = minecraft.Minecraft.create()

❶ while True:
      ❷ p = mc.player.getTilePos()
      ❸ b = mc.getBlock(p.x,p.y-1,p.z)      
      ❹ if b == 2: # grass
          ❺ mc.player.setPos(p.x, p.y+20, p.z)

清单 6-2 跳床草程序

再次从mcpi库导入minecraft模块,以便你能使用 Python 控制 Minecraft。将minecraft.Minecraft.create()连接保存到一个名为mc的变量中。

然后创建一个while循环❶,如你在第二章中记得的那样,确保程序一直运行下去。同时,记得在while语句后缩进四个空格,因为它属于循环的一部分。

在循环中,你可以找到并返回玩家在 Minecraft 环境中的当前位置❷,以及玩家站立的方块类型❸。这很重要,因为只有草地是有弹性的,而其他地面没有。这一位置将返回一个 ID 号,并存储在名为b的变量中。你从方块的 y 位置中减去 1,这个 y 值是上下坐标。原因是你想影响玩家下方的方块,而不是玩家本身。

使用条件语句检查你站在什么样的地面上❹。每种方块都有一个 ID 值,草方块的 ID 值是2。所以你检查存储在变量b中的方块 ID 值是否等于2,只有当它的值为2时,才运行代码让草地变得有弹性。

当条件判断发现玩家确实站在草地上时,使用setPos()将玩家送到空中到新的 y 位置❺。你想让玩家向上移动,而不是横向移动,所以保持相同的 x 和 z 位置,只需将 y 值加上 20。玩家应该会飞跃到空中。

运行你的程序

如之前一样,保存你的程序并确保 Minecraft 已打开。恢复游戏并等待世界加载完毕。按 TAB 键将鼠标从游戏中释放,然后选择你的 Python 程序。按F5运行程序,然后点击 Minecraft 窗口回到游戏中。四处走动,注意那片草地!

如果你遇到任何错误,检查是否按清单 6-2 中的缩进格式编写代码。还要确保在while Trueif语句后都加上了冒号。

更进一步

你可以修改程序,让玩家飞得更高、更低,甚至进入地下——一种反重力蹦床——通过改变 mc.player.setPos() 中的 p.y 数值。尝试一些不同的数字来查看效果。作为另一个挑战,尝试改变触发方块的 ID,让水代替沙块触发蹦床。查看方块 ID,请访问 www.raspberrypi-spy.co.uk/2014/09/raspberry-pi-minecraft-block-id-number-reference/

沙块掉落游戏

现在,你将制作你的第一个游戏,叫做 沙块掉落。在这个简单的游戏中,沙块从天空中掉下来,玩家需要躲避它们。在 Minecraft 环境中,沙块会自动受到重力的影响:如果你将沙块放在玩家上方,它会向下掉落,如果玩家没有及时移动,沙块就会砸到玩家。如果其中一个沙块击中了玩家,游戏就结束了。

你将编写程序,让沙块随着你在世界中移动(见图 6-5),所以别以为你可以逃跑。沙块正追着你呢!

图片

图 6-5 沙块掉落 游戏

编写沙块掉落游戏

通过打开 Python 并开始一个新文件来开始破解。将程序代码输入清单 6-3。记得你可以从 www.nostarch.com/raspiforkids/ 下载程序,并用它来与自己的程序进行比较,或者直接运行下载的 sand_drop.py 文件。

from mcpi import minecraft
mc = minecraft.Minecraft.create()
from time import sleep

❶ sleep(5)
pos = mc.player.getTilePos()

❷ while mc.getBlock(pos.x, pos.y, pos.z) != 13:
     mc.setBlock(pos.x, pos.y + 25, pos.z, 13)
     sleep(1)
     pos = mc.player.getTilePos()
❸ mc.postToChat("Got you!")

清单 6-3 沙块掉落 游戏程序

和以前的破解一样,首先导入 minecraft 库,并将事件触发器存储在 mc 变量中,这样就可以简化操作,避免每次都输入 minecraft.Minecraft.create()

同时从 time 模块导入 sleep() 函数,该函数在每个沙块掉落之间添加了一个小延迟。这样可以给你一些时间来躲避每个沙块。在游戏开始和沙块开始掉落之前,添加一个短暂的时间延迟 ❶,这样你可以有时间从 Python 窗口返回到游戏。沙块会随着你移动,所以你需要获取玩家的 x、y 和 z 坐标数据。使用 while 循环检查是否有沙块击中你 ❷。这个循环通过检查玩家当前位置的方块 ID 来判断,如果方块 ID 不是 13——沙块,那么你还没有被击中。

如果你没有被击中,下一行代码会将沙块放置到指定位置并掉落。注意,沙块的 y 坐标是 +25,这意味着沙块在掉落前会在你上方 25 个方块的位置被放置。然后,你需要等待 1 秒钟才运行下一行代码,这时会读取玩家新的 x、y 和 z 坐标,以防你已经移动。如果沙块击中了你,Minecraft 世界中会发布一条消息,游戏结束 ❸。

运行你的程序

如前所述,将程序保存为sand_drop.py,确保 Minecraft 已经打开,并开始或继续游戏。按下 TAB 键释放鼠标,然后选择并执行你的 Python 程序。返回 Minecraft 游戏,准备好躲避掉落的沙子。

进一步探讨

如果你想测试你的反应能力,尝试改变方块的高度,使游戏更加具有挑战性。如果方块在开始掉落时距离玩家较近,你将有更少的时间来反应。你也可以减少每次方块掉落之间的延迟或sleep()时间。

Minecraft 中不同的方块有不同的物理特性。通过将方块从沙子换成其他类型的方块来测试这个世界。这会改变方块掉落的方式:它可能掉得更慢,也可能更快,或者可能落在不同的位置,影响你的游戏方式。可以尝试下表中的方块作为开始。

方块类型 方块 ID
9
熔岩 10
沙砾 13

所有方块 ID 的列表可以在 www.raspberrypi-spy.co.uk/2014/09/raspberry-pi-minecraft-block-id-number-reference/ 上找到。

MINECRAFT 控制的 LED

在这个项目中,你将编写一个程序,让你使用 Minecraft 控制你在第三章中制作的热熔胶枪 LED 灯。每当你在 Minecraft 世界中触碰到水时,现实世界中的灯就会亮起。当你离开水域时,灯会熄灭。

连接 Minecraft LED

使用你在第三章中制作的定制热熔胶枪灯,或者使用新的 LED,将一根公对母的跳线连接到 LED 的每根引脚。将较长的正极引脚连接到 GPIO 引脚 18:这是 Pi 上的物理引脚 12。将另一根跳线连接到任意一个地线引脚:可选的物理引脚有 9、14、20、30、34 或 39。图 6-6 显示了接线图。

Image

FIGURE 6-6 连接 LED

编写 Minecraft LED 控制程序

打开一个新的 Python 文件并输入 Listing 6-4 中的代码。

❶ from gpiozero import LED
   import time

   from mcpi.minecraft import Minecraft
❷ mc = Minecraft.create()

❸ led = LED(18)

❹ while True:
       x,y,z = mc.player.getPos()
    ❺ block_id = mc.getBlock(x, y, z)

    ❻ if block_id == 9: # 9 = water
           led.on()
       else:
           led.off()

LISTING 6-4 Minecraft LED 程序

首先从gpiozero库导入LED类 ❶,正如在第三章中所做的那样。同时导入time模块,以便在需要时添加延迟,并导入minecraft库。再次设置变量mc来存储minecraft库的代码 ❷。

告诉 Pi LED 连接在 18 号引脚上 ❸,并创建一个while循环 ❹,使程序持续运行接下来的几行缩进的代码。然后获取玩家的 x、y 和 z 坐标数据。使用getBlock()函数来找出你所站立的方块的 ID,并将这个 ID 存储在名为block_id的变量中 ❺。回想一下,你可以在 www.raspberrypi-spy.co.uk/2014/09/raspberry-pi-minecraft-block-id-number-reference/ 上查看方块 ID。

检查方块的 ID 是否等于 9,这是水的 ID ❻。如果是,LED 灯将点亮。如果你不站在水上,LED 灯将熄灭。

运行你的程序

保存你的程序,打开 Minecraft,按 TAB 键释放鼠标。选择你的程序并通过按 F5 执行它。返回 Minecraft 游戏并找到一些水!当角色站在水中时,LED 应该亮起,如 图 6-7 所示。

Image

图 6-7 当角色接触到水时,LED 点亮。

摄像头监控 Minecraft 塔楼

在这个破解中,你将设置一个监控摄像头,并通过 Minecraft 控制它。对于周围的其他人来说,看起来你只是简单地在玩 Minecraft。但实际上,你将使用游戏中的三个塔楼来控制摄像头。塔楼 1 由金块构成,触发摄像头拍照。塔楼 2 由砖块构成,开始录制视频。塔楼 3 由西瓜构成—没错,是西瓜—结束视频录制。要触发每个塔楼,你将用剑击打它。然后你可以查看照片并观看视频,或者拍摄另一个视频或照片。

在开始之前,按照 第四章 的方法设置你的 Pi 摄像头。

建造三个触发塔楼

让我们来制作秘密触发塔楼。加载一个新的 Minecraft 世界,找到一个合适的平坦空间来建造三个塔楼。选择背包中的金块,建造一个由三到四个方块组成的小塔楼。

对其他两个塔楼重复相同的过程,只不过使用砖块和西瓜块。把砖块塔楼和西瓜塔楼建得很靠近,因为当你击打砖块塔楼时,视频不会停止录制,直到你击打西瓜塔楼!图 6-8 显示了塔楼的放置位置。

Image

图 6-8 摄像头控制塔楼

你实际上可以使用任何你想要的方块,只要你修改程序代码,包含相关的方块名称。但确保每个塔楼只能由一种方块类型构成。

你需要一种方法来触发塔楼,因此通过按 E 并选择剑,给玩家装备背包中的剑。当你点击左键时,你会移除方块,点击右键则会击打方块。击打方块并不会摧毁它,而是返回方块数据,你将在这个破解中使用这些数据来控制 Pi 摄像头的功能。在你开始编写程序代码之前,可以先尝试击打这些塔楼。

编写摄像头塔楼的代码

打开一个新的 Python 文件,并输入 清单 6-5 中的代码。这段代码设置了摄像头和玩家的指令消息。稍后你将添加其余的代码。

❶ from picamera import PiCamera
   camera = PiCamera()
   camera.resolution = (600, 600) # size of photo
❷ camera.framerate = 100

   from mcpi.minecraft import Minecraft
❸ import mcpi.block as block
   mc = Minecraft.create()

   import time
   import datetime
❹ mc.postToChat("Minecraft Camera Controller")
   time.sleep(4)
   mc.postToChat("Gold = Take a picture")
   mc.postToChat("Brick = Start filming")
   mc.postToChat("Melon = Stop filming")

清单 6-5 设置摄像头和玩家指令

首先导入 PiCamera ❶ 类来控制摄像头。然后创建一个名为 camera 的变量来存储 PiCamera() 命令,这样你每次就不用重复输入它了。

将相机图像的分辨率设置为 100 ❷。这是 帧率:较高的 framerate 值会产生流畅的视频,较低的 framerate 值则使视频显得更加生硬——但这是一种权衡。帧率设为 100 是合适的,因为如果设置得太高,树莓派会很快耗尽内存,导致无法顺利拍照。如果你想改变视频质量,可以稍后调整 framerate 值。

然后,导入常见的 Minecraft 库,以及 block 库 ❸,它允许你通过块的名称而不是 ID 号码来识别和引用块。例如,你可以通过名称指定金块、草块、沙块和砖块。最后,导入 time 模块和 datetime 库,以便为文件名添加时间戳。

添加一行代码,在 Minecraft 中显示程序的标题 ❹。添加一个短暂的延迟,给玩家足够的时间读取信息。然后,再发送一条消息,告知玩家每个块触发的动作;例如,金块会拍照。现在,让我们添加触发器。

击中金塔

现在,你将添加程序的主部分,如 列表 6-6 所示。该代码检查击中的塔块,并根据适当的相机命令做出响应。你将从金塔开始,稍后再添加其他塔。

确保你按照示例缩进代码行:while 循环内的代码需要缩进四个空格。接下来,紧随其后的 for 循环内的代码需要再缩进四个空格,最后,if 语句内的代码需要再缩进四个空格。

while True:
  current_time = datetime.datetime.now()

  for hitBlock in mc.events.pollBlockHits():
    # print (hitBlock)
    if mc.getBlock(hitBlock.pos.x, hitBlock.pos.y, hitBlock.pos.z)
    == block.GOLD_BLOCK.id:
      print ("PICTURE")
      mc.postToChat("Smile!")
      time.sleep(1)
      camera.capture('/home/pi/Desktop/'+ str(current_time) +
      'photo.jpg')

列表 6-6 编写金塔程序

首先创建一个 while True 循环,这意味着程序会不断检查你是否击中了某个塔。如果某个塔被击中,记录当前时间并将其存储在名为 current_time 的变量中。然后,在屏幕上打印一行,显示你击中的块的详细信息。这包括玩家的 x、y 和 z 坐标,以及塔的 ID 和块的名称。

接下来,你需要确定击中了哪种类型的塔,并采取相应的相机动作。你可以通过 if 语句检查你刚刚击中的块是否为金块。如果是,屏幕上会打印出 PICTURE 字样。同时,Minecraft 聊天信息中会显示 Smile! 提醒角色微笑。等待一秒钟让角色摆好姿势,然后触发相机拍照。

将拍摄的图片存储在桌面上,并赋予它当前时间作为文件名,这个时间是你之前记录并存储在 current_time 变量中的。现在,金塔的操作已经完成。将你的程序保存为 camera_tower.py,但保持程序打开,以便添加更多代码。

击中砖塔

将示例 6-7 中的代码添加到你的 camera_tower.py 程序中。这段代码检查玩家是否击中了砖块。如果是,摄像头将开始录制视频。

❷ elif mc.getBlock(hitBlock.pos.x, hitBlock.pos.y, hitBlock.pos.z)
   == block.BRICK_BLOCK.id:
     # print ("brick")
  ❶ mc.postToChat("Video Recording!")
     camera.start_preview()
     time.sleep(1)
  ❸ camera.start_recording('/home/pi/my_video.h264')

示例 6-7 编程砖块塔

添加一个 elif 语句来检查砖块是否被击中 ❶。这就像你添加的 if 语句,用来检查金塔是否被击中。但 elif 语句表示 else if,只有在塔没有由金块构成时才会执行。

向 Minecraft 屏幕发布一条消息,告知玩家视频即将开始录制 ❷。在屏幕上启动预览,以便你可以看到正在拍摄的内容,等待一秒钟,然后触发摄像头开始拍摄。

最后,将文件保存到你的 /home/pi 文件夹,并命名为 my_video.h264 ❸。视频将继续录制,直到西瓜塔被击中,或者你通过点击 IDLE 窗口右上角的关闭按钮停止程序。

击中西瓜塔或什么都没击中

程序的最后一部分响应玩家击中西瓜塔、击中其他任何方块或什么都不做的情况。将示例 6-8 中的代码添加到你的 camera_tower.py 文件的末尾。

    elif mc.getBlock(hitBlock.pos.x, hitBlock.pos.y, hitBlock.pos.z)
    == block.MELON.id:
      # print ("Melon")
      mc.postToChat("Stop the video!")
      camera.stop_preview()
      time.sleep(1)
      camera.stop_recording()
    else:
      pass

示例 6-8 编程西瓜塔

添加另一个 elif 语句来检查被击中的方块是否为西瓜块。如果是,向 Minecraft 发布一条聊天消息,告知玩家视频录制即将停止,并停止预览。等待 1 秒钟,然后停止录制。

然后添加最后一个 else 语句,以响应玩家做的其他事情,比如没有击中三座塔中的任何一座。如果玩家没有击中三座触发塔中的任何一座,使用 pass 语句。pass 语句告诉程序不执行任何操作,除了返回到示例 6-6 中的 while 循环开始部分。然后程序会检查下一个方块的交互。

运行你的程序

保存你的程序并运行。如果遇到任何错误,请务必仔细检查缩进级别是否正确。如果你无法让程序运行,可以从下载部分加载 camera_tower.py 文件,地址是 www.nostarch.com/raspiforkids/,并与程序进行比较(或者直接使用我的!)。同时,确保你在代码中使用了正确的大小写字母。还要记得确保 Minecraft 已打开并运行。

拍完一些照片和视频后,点击窗口右上角的中间图标最小化 Minecraft 窗口。你应该能在 Pi 的桌面上看到照片文件。只需点击并打开即可查看每张照片。要查看视频,你需要从终端执行它。点击终端图标。

在终端中输入这一行,但如果你给文件起了不同的名字,请将 my_video 替换为你的文件名:

pi@raspberrypi:- $ omxplayer my_video.h264

这一行运行的是 OMXPlayer,一个专为树莓派制作的视频播放器。按下 ENTER 键,视频应该开始播放。要重新播放,按上箭头键。别忘了与朋友分享你的照片和视频。

总结

作为额外的挑战,试着将塔楼改为其他方块,比如羊毛、沙子,甚至黑曜石。你还可以建造更多的塔楼,为你的程序增加更多功能。也许可以加入一个方块,用来为你的照片添加滤镜。

如果你喜欢这些 Minecraft 的技巧并想要更多,务必查看 Craig Richardson 的《Learn to Program with Minecraft》(No Starch Press,2015)和 Al Sweigart 的《Coding with Minecraft》(No Starch Press,2018)。

第七章:无线电入侵

树莓派总是让我感到惊讶。只需一根跳线,你就能将树莓派转变为一个无线电发射器。没错:你可以用它向真正的收音机广播信息。

在本章的项目中,你将创建一个简单的音效板,用来触发和控制广播到收音机的信号。你将录制一些声音或短语,比如“大家听着”或“我想要一块饼干”,或者创建一个紧急广播信息。然后,你将为每个声音文件分配一个按钮,点击该按钮即可将信息广播到调频到正确频率的收音机。通过劫持无线电波,你将能够恶搞毫无戒心的听众、分享重要八卦,或者创建一个个人广播系统。

无线电波无处不在。当你观看电视时,节目很可能是通过无线电波传输给你的。你家里的 Wi-Fi 路由器会将数据包作为无线电波广播出去。事实上,现在许多汽车使用无线电波编码的数据来解锁车门或启动引擎。那么,如何黑入树莓派并将其变成一个无线电发射器呢?

好吧,GPIO 引脚 4 可以生成扩频时钟信号,扩展带宽,从而使信号在最多 40 米的范围内扩散。你将使用名为 PiFM 的软件来控制该引脚,并使其发送你的 FM 无线电波。你可以在 www.icrobotics.co.uk/wiki/index.php/Turning_the_Raspberry_Pi_Into_an_FM_Transmitter 上了解更多关于科学原理和项目的信息。

法律问题

在开始之前,请注意,这个项目仅用于教育和学习目的,不得用于商业用途。标准的 4 英寸跳线支持大约 30–40 米的合法广播范围。你需要自行了解你所在国家的相关法律和要求,也有责任遵守这些法律的规定。此项目不得在任何机场或军事基地附近使用。

你将需要的物品

这是你需要准备的项目物品:

  • 树莓派 Zero 或原版 Pi 型号(替代型号:树莓派 Model B+ V1.2 2014 或树莓派 Model A+ V1.1 2014)

  • 跳线

  • USB 麦克风(推荐)或其他麦克风

  • 支持 FM 的收音机(数字或模拟,最好带有两个扬声器,外部或内置均可)

准备树莓派

在构建主项目之前,你需要安装 PiFM 软件,设置收音机,然后检查其是否正常工作。这是一个容易设置的项目。

为了设置硬件,将一根跳线连接到 GPIO 引脚 4——树莓派左侧的第四个物理引脚,如 图 7-1 所示。就这样,这就是你设置的所有硬件,用来广播你的信号。

图片

图 7-1 将跳线连接到树莓派

按照以下步骤设置项目:

  1. 创建新文件夹: 你将创建一个文件夹来存储程序软件和音频文件。打开终端并输入以下命令:

    pi@raspberrypi:- $ mkdir Radio
    

    这个mkdir命令会创建一个名为Radio的文件夹。通过在终端中输入以下命令进入Radio文件夹:

    pi@raspberrypi:- $ cd Radio
    

    命令行的提示符应该现在显示你在Radio文件夹中,类似于下面这样:

    pi@raspberrypi:- $ ~/Radio
    
  2. 下载 Python 库: 通过在终端中输入以下命令,下载所需的 PiFM 库:

    pi@raspberrypi:- $ wget http://www.tecoed.co.uk/uploads/1/4/2/4/14249012/pifm.tar.gz
    

    这一行命令将程序文件下载到你的Radio文件夹。文件是压缩的,你需要解压才能访问文件。要解压,输入以下命令:

    pi@raspberrypi:- $ tar xvzf pifm.tar.gz
    

    然后按 ENTER 键将程序文件提取到Radio文件夹中。

运行测试程序

在加载你自己的声音之前,你需要检查收音机是否正常工作:

  1. 调谐收音机: 插入收音机并打开它。确保它处于 FM 模式,并调至 100.0 MHz。你应该会听到收音机的嗡嗡声,因为在该频率上没有其他内容广播。

  2. 运行程序: 返回终端窗口并输入以下代码:

    pi@raspberrypi:- $ sudo ./pifm sound.wav 100.0
    

    按下 ENTER 键;你应该会听到一个熟悉的旋律通过收音机广播。如果你有便携式收音机,可以通过在家里走动或甚至走到户外来测试广播范围。

  3. 更改频率: 你可以通过更改命令末尾的值来更改广播频率。例如,要在 105.0 频率上广播,输入以下命令:

    pi@raspberrypi:- $ sudo ./pifm sound.wav 105.0
    
  4. 立体声广播: 下载的程序文件夹包括一个音频文件,用来测试你的收音机的立体声设置。你需要两个扬声器:一个左扬声器和一个右扬声器。像之前一样输入相同的命令行,但这次使用left_right.wav文件,如下所示:

    pi@raspberrypi:- $ sudo ./pifm left_right.wav 100.0
    

音乐旋律将再次在你的收音机上播放,但这次,如果你的收音机有两个独立的扬声器,声音将通过两个扬声器播放,形成立体声效果。如果你没有听到声音,请检查以下内容:

  • 你正在使用正确的树莓派型号。(树莓派 Zero,原始 Pi 型号,树莓派 Model B+ V1.2 2014,或者树莓派 Model A+ V1.1 2014)

  • 你已经将跳线连接到 GPIO 4 引脚。

  • 收音机已经调到正确的频率。

  • 命令行中的频率与你收音机上的频率匹配。

  • 在终端窗口中,你已经进入了Radio文件夹,并从该文件夹运行了程序代码。

有时候,如果一个文件在另一个文件执行之前没有停止播放,文件就不会播放,或者你可能只会听到来自收音机的单一音调。重启你的树莓派并重新运行程序。

流式播放 MP3 音乐文件

到目前为止,你只播放了 WAV 文件,但你的广播设备也可以播放 MP3 文件,这样你就可以播放常规音乐了。WAV 文件的音质比 MP3 文件更高,但它占用更多的存储空间,这使得 WAV 文件不适合用于流媒体播放和下载音乐。MP3 文件仍然保持高质量,但占用的存储空间要小得多,因此你可以在设备上存储更多内容,并且无需担心缓冲或延迟时间。大多数音乐文件使用 MP3 作为标准格式,这意味着你可以用你的树莓派创建一种便携式扬声器。按照这些步骤试试看:

  1. 下载 MP3 文件: 下载你最喜欢的歌曲并将它们传输到你的树莓派(参见 第 31 页的“文件传输”部分);确保它是一个以 .mp3 结尾的 MP3 文件。将此文件保存到 Radio 文件夹中。另外,我建议将文件重命名为一个简短的名字,以减少在输入命令播放歌曲时发生错误的可能性。

  2. 安装 FFmpeg: 声音不是数字化的,因此信号需要从模拟转换为一串零和一,以便在计算机上播放。采样 过程捕获音频,以便可以进行转换。采样率越高,每秒的捕获次数越多,音质也会越高。大多数 MP3 文件的采样率与 PiFM 程序不兼容,因此你需要安装 FFmpeg 程序,它会在广播时实时调整 MP3 文件的采样率。在终端中输入以下命令:

    pi@raspberrypi:- $ sudo apt install ffmpeg
    

    此命令安装 FFmpeg 程序。

  3. 播放 MP3 文件: 安装 FFmpeg 后,使用以下命令进入 Radio 文件夹:

    pi@raspberrypi:- $ cd Radio
    

    然后输入以下命令,将 name_of_your_file.mp3 替换为你的 MP3 文件名(现在你明白为什么我鼓励你更改文件名了!):

    pi@raspberrypi:- $ ffmpeg -i name_of_your_file.mp3 -f s16le -ar
    22.05k -ac 1 -|sudo ./pifm –
    

    按下 ENTER 执行代码行。然后打开你的收音机并调到你听到 MP3 播放的频道。恭喜,你的歌曲已经在广播了!要停止播放,按 Q 退出,或者按 CTRL-X。

录制和广播你自己的文件

现在你将创建自己的音频文件并播放它们。创建和编辑音频文件需要大量的处理能力,因此最好使用另一台设备,如笔记本电脑或台式电脑。这些设备大多数都内置了麦克风,你可以使用它们,或者如果你更喜欢,也可以连接一个 USB 麦克风。

设置你的麦克风和扬声器

要编辑音频文件,你需要安装一个免费的开源音频编辑器叫做 Audacity。如果你使用的是 Windows 或 macOS 电脑,请访问网站 www.audacityteam.org/,点击链接下载软件。然后在你的设备上安装它。如果你使用的是 Linux,请通过常规的终端方法下载软件。

将麦克风插入其中一个 USB 端口。首先,你将录制一段声音以测试麦克风和扬声器;在录制最终的广播声音之前,你需要更改一些设置。准备好后,打开 Audacity,点击录音按钮,如图 7-2 所示,开始录音。

图片

图 7-2 使用 Audacity 控制

对着麦克风说话。记住,你只是测试程序能否识别麦克风并录制声音,所以说什么内容并不重要。录音完成后,点击停止按钮停止录音。点击播放按钮播放你的录音。别忘了确保扬声器已经打开并调高音量。如果你能听到声音,说明你的设置正常。

在树莓派上制作并播放你的录音

为了正确广播你的新音频文件,你需要在录音前调整采样率。采样率显示在程序的左下角,在项目采样率(Hz)下拉列表中,如图 7-3 所示。点击下拉箭头,从列表中选择11025

图片

图 7-3 将项目采样率更改为 11025 Hz

现在,通过点击录音按钮,就像之前一样进行录音。

一旦你对录音效果满意,你需要将其导出为 PiFM 程序所需的格式,并进行广播。从菜单中点击文件导出。在弹出的列表中,选择WAV (Microsoft),并选择签名 16 位 PCM(如适用)。程序将提示你添加元数据到文件中。元数据是关于音轨的信息,如其名称、时长以及制作年份等。你可以通过点击确定跳过此选项。图 7-4 显示了两个下拉菜单。

图片

图 7-4 导出音频文件

接下来,你需要将你的音频传输到树莓派并保存在Radio文件夹中。使用“传输文件”一节中讨论的任一传输方法,或者你也可以将文件复制到 USB 闪存驱动器中,然后插入并将文件复制到树莓派。确保将文件保存到Radio文件夹。

广播文件

要播放你的新音频文件,打开终端并输入 cd Radio 进入Radio 文件夹。然后,通过输入以下命令列出文件夹中的内容:

pi@raspberrypi:- $ ls

你应该能看到你新录制的 WAV 文件。输入以下命令,将 myfile 替换为你新录音文件的名称:

pi@raspberrypi:- $ sudo ./pifm myfile.wav 100.0

打开你的收音机,将其调到 100.0 频率,然后按下 ENTER。你的录音应该会播放出来!

有时你可能会听到一个单一的音调,而不是你的录音。如果在广播完成前被中断,就会出现这种情况。只需重新启动你的树莓派,并再次运行命令即可。

编写声音板代码

现在你已经知道如何录制音频文件并通过收音机广播它们,你将创建一个简单的 图形用户界面 (GUI) 声音板来触发和控制广播。GUI 给程序提供了一个用户友好的界面,便于使用。GUI 是大多数计算设备、电视、手机和游戏主机的标配,因为 GUI 使用窗口、图标、菜单和指针来帮助用户控制设备。你将创建一个包含按钮的 GUI 来触发声音,避免每次都必须在终端输入代码。

本项目使用了 guizero,一个可以非常简便地创建 GUI 的 Python 库。如果你想了解更多关于 guizero 的信息或添加更多功能,可以访问这个网站: lawsie.github.io/guizero/。通过 GUI,你将为每个自制的 MP3 声音文件分配一个按钮,如 图 7-5 所示。当你点击按钮时,相应的消息将广播到所有调谐到同一频率的收音机。

图片

图 7-5 最终的声音板 GUI

创建 GUI 功能

通过打开终端并输入以下命令,下载并安装 guizero Python 库:

pi@raspberrypi:- $ sudo pip3 install guizero

随着 guizero 库的进一步开发,其创建者将添加更多的功能和特性。你可以使用以下命令将你的版本升级到最新版本:

pi@raspberrypi:- $ sudo pip3 install guizero --upgrade

在开始创建你的声音板之前,你需要录制并创建至少三个包含你想要广播的短语的个人声音文件。按照 “录制并广播自己的文件” 部分中的说明在 第 120 页 中录制声音文件。如果你不想录制自己的文件,可以从本书资源中下载示例文件,网址是 www.nostarch.com/raspiforkids/。资源中还有一张图像可以用作 GUI 窗口。你必须将所有声音文件、代码和图像保存在你在章节开始时创建的 Radio 文件夹中。

你将在 IDLE 中创建构建声音板的程序。打开 Python IDLE 编辑器,然后点击 文件新建文件 打开一个新脚本。将你的新 Python 文件保存到 Radio 文件夹中,命名为 radio_gui.py,并输入 代码清单 7-1 中的代码。

❶ import os
   import time 
   from guizero import App, Text, PushButton, info, Picture

❷ def message1():
     print ("Hello")
     os.system("sudo ./pifm hello.wav 100.0")
     time.sleep(1)

❸ def message2():
     print ("Bring me food")
   os.system("sudo ./pifm bringmefood.wav 100.0")
   time.sleep(1)

❹ def message3():
   print ("Let's take a selfie")
     os.system("sudo ./pifm selfie.wav 100.0")
     time.sleep(1)

❺ def close_message():
     info("Goodbye", "See you soon")
     app.destroy()

代码清单 7-1 构建声音板

程序通过导入操作系统模块 os ❶ 开始。该模块允许你在 Python 代码和程序中运行终端命令。因此,你可以为每个按钮分配一个终端命令,以避免每次播放声音时都手动输入命令。

接下来,你导入time模块和guizero库,然后导入AppTextPushButtoninfoPicture小部件。我不会讨论这些每一个小部件,但它们共同使你能够控制 GUI 应用程序,创建按钮触发声音,显示信息弹出窗口,以及在声音板中添加图像。

然后你创建了四个独立的函数,每个函数包含触发一个声音文件并将其广播到收音机的指令。

第一个函数是message1() ❷,它播放与包含的 WAV 文件名匹配的音频文件。它还会在屏幕上打印Hello,让你知道文件已被触发。

你会认出代码行中包含的sudo ./pifm,它来自本章的前面部分。注意这次它以os.system()开头,这告诉 Python 像从终端执行命令一样运行这行代码,而不是从 Python 编辑器运行。os.system()函数是必需的,因为你正在使用 Python 代码编写 GUI 程序,而 PiFM 使用 Linux 命令。os.system()函数使你能够在 Python 程序中触发 Linux 命令。将代码中的hello.wav替换为你的第一个声音文件的名称,如果需要,调整广播频率。然后为你的其他音频消息添加第二个 ❸ 和第三个 ❹ 函数,记得将文件名改为匹配你的音频 WAV 文件的名称。

第四个函数关闭 GUI 窗口 ❺。此函数会打印一条简短的告别消息,然后销毁应用程序!这听起来不像那么严重:app.destroy()函数只是简单地关闭 GUI。

创建窗口和按钮

现在,你可以添加代码部分来创建实际的 GUI 窗口和按钮。将 Listing 7-2 中的行添加到你的radio_gui.py Python 程序中。

 # sets the size of the app window
❶ app = App(title = "My Radio", width=270, height=350, layout="grid")

   # adds the title
❷ Title = Text(app, text="CLICK A MESSAGE TO BROADCAST", size=11,
   font="consolas", grid=[0,0], align='top')

   # adds the image
❸ radio = Picture(app, image="sound.gif", grid=[2,0], align='top')

LISTING 7-2 构建 GUI 界面

第一行代码设置了 GUI 窗口的标题以及窗口的宽度和高度(以像素为单位)❶。你还将窗口的布局设置为网格布局,这意味着你可以使用坐标来绘制并在窗口内放置按钮。

接下来,你为窗口添加标题,告诉用户 GUI 的功能 ❷。你设置了字体的大小和类型:你可以更改这些以个性化你的 GUI,但请记住,你可能需要调整 GUI 窗口的宽度和高度以适应较大的字体。

你将标题设置到网格位置 0 和 0,即网格的第一行,然后将其对齐到窗口的中心,使其看起来整齐有序。

然后,你将从书本资源中获取的图像添加到窗口 ❸。图像必须是.gif格式,并且位于Radio文件夹中。同样,你设置图像的网格位置,这次是第二行,并将其对齐到顶部,将其放置在 GUI 窗口的中央。

注意

如果使用不同的图像,你需要调整中的窗口尺寸,以确保图像适配窗口。

在构建了功能和图形用户界面(GUI)之后,您可以添加程序的最后一部分,创建按钮并将每个功能分配给一个按钮。当您运行程序时,点击按钮将触发相应的功能执行,并广播音频消息。将清单 7-3 中的代码添加到您的程序中。

   # sound1
❶ button1 = PushButton(app, command = message1, text="Hello",
   grid=[3,0], align='top')

   # sound2
❷ button2 = PushButton(app, command = message2, text="Bring me food",
   grid=[4,0], align='top')
 # sound3
❸ button3 = PushButton(app, command = message3, text="Let's take a
   selfie", grid=[5,0], align='top')

   # quit the program
❹ quit_button = PushButton(app, command = close_message, text="QUIT
   PROGRAM", grid=[6,0], align='top')

❺ app.display()

清单 7-3 创建按钮并分配功能

您使用PushButton()函数创建一个按钮 ❶。对于每个按钮,您需要包含guizero类中的PushButton,该按钮会选择在点击时运行的功能。接下来,您为按钮添加标签,使用文本告知用户该按钮的功能。在第一个按钮上,标签Hello表示该按钮将广播Hello声音。然后,设置按钮在 GUI 窗口中的网格位置。该按钮位于网格的第 3 行,并且对齐方式为顶部,这将使它位于窗口的中央。

使用相同的格式编写其他三个按钮 ❷ ❸ ❹。记住,如果您使用了不同的图像或不同的文本长度或大小,您需要更改网格坐标和对齐方式,以适应内容并填充到 GUI 窗口中。

最后一行是创建 GUI 的代码 ❺。这段代码将您创建的所有元素整合在一起并显示出来。输入代码后,保存您的程序,确保将其保存到Radio文件夹中。

运行程序

要运行您的程序,请按照以下步骤操作:

  1. 打开收音机并调至广播频率,在本程序中为 100.0 MHz。

  2. 按下键盘上的F5键运行程序。您的 GUI 应该加载,并且您应该能看到主图像和四个按钮。

  3. 点击其中一个按钮以广播声音文件。按钮将在播放声音文件时保持按下状态,防止您刷屏(反复按按钮)并阻塞广播。

总结

您可以通过改进此项目来增加更多功能。尝试以下增强功能作为起点:

  • 添加更多按钮。

  • 创建一个简单的音乐播放器,允许您选择一首歌曲并将其广播到收音机。

  • 创建一个万圣节恐吓机器,通过录制和分享吓人的消息或恐怖声音与听众互动。

第八章:自动短信发送机

在本章中,你将设置你的 Raspberry Pi,以便向已注册的手机发送短信。一旦你完成了这一部分,你将创建一个简单的自动短信提醒服务:你将编写一个时间和简短的消息,当指定的时间到达时,消息将被发送到你输入的号码。例如,你可以发送一条消息提醒自己遛狗或接人,或者你也可以每天向别人发送一条消息,提醒他们欠你 5 美元!

你将需要的物品

以下是本项目所需的物品:

  • Raspberry Pi

  • 一部可以发送和接收短信的基本手机

关于短信的简要介绍

1992 年 12 月 3 日,第一条短信服务(SMS)消息,通常被称为文本消息,从计算机发送出去。内容是圣诞快乐。这种廉价、快速、简单的服务成为了手机设备之间非常流行的通信方式。

创建一个简单的文本发送器

你将创建一个简化的文本发送器,将你输入的任何消息发送到指定的号码。第一步是设置你自己的账户,注册Twilio,一个基于云的通信公司。Twilio 让你编写程序代码,可以拨打和接听电话,发送短信,收集通话时长统计等。

由于 Twilio 是一个基于云的服务,它使用应用程序编程接口(API)。API 是一组工具,让你通过代码与基于 Web 的应用程序进行交互。你可以使用 API 与 Twilio 的 Web 服务进行交互,例如发送短信或查看通话记录。Twilio 网站让你创建一个免费试用账户,这对于本项目来说完全足够。

注意

如果你觉得这样更方便,可以在另一台计算机上设置 Twilio 凭据,然后在 Pi 上重新登录 Twilio

注册 Twilio 账户

在你的 Pi 上,前往 Twilio 网站:www.twilio.com/。点击页面中间的红色注册并开始构建按钮,如图 8-1 所示。

Image

图 8-1 从 Twilio 着陆页开始

通过填写表格注册一个账户,该表格可能与写作时有所不同。输入你的名字、姓氏、电子邮件地址以及你希望设置的密码。密码必须至少包含 14 个字符。完成这些步骤后,点击红色的开始免费试用按钮。图 8-2 展示了所有这些选项。

Image

图 8-2 注册 Twilio 账户

你应该已经收到一封验证电子邮件,里面有一个超链接,发送到你用来注册的地址。点击邮件中的链接,验证你的电子邮件地址,并向网站确认你不是机器人。

接下来,你需要进一步确认你不是机器人。(证明你是人类是一个两步过程!)添加你的手机号码并点击验证按钮。你需要输入一个有效的号码,因为 Twilio 会将验证码发送到你输入的号码。你将需要这个验证码才能继续。

你的手机应该会收到一条带有验证码的短信。在 Twilio 页面上,输入验证码并点击提交,如图 8-3 所示。

图片

图 8-3 做得好,人类。

你现在已被验证为人类;可以放心了!

你将会被问到一系列关于编码的问题,这些问题可能自本书写作以来有所变化,所以我会大致引导你通过这些问题。如果被问到是否会编程,回答“是”。接着你将看到一个语言列表,选择 Python。

现在你将被问到类似你今天的目标是什么? 选择最接近在项目中使用 Twilio的选项。你也会被问到你想要先做什么,选择涉及发送或接收短信的选项。

一旦完成所有这些步骤,你应该会看到你的仪表板,界面应该像图 8-4 所示。

图片

图 8-4 Twilio 仪表板

设置 Twilio 电话号码

设置账户的最后一步是获取一个 Twilio 电话号码,用来从你的 Pi 发送短信。点击获取试用号码按钮。(如果你在仪表板中看不到“获取试用号码”按钮,点击左侧菜单中高亮部分所示的三点符号,然后点击可编程 SMS。接着点击开始使用,然后选择获取号码。)

Twilio 会为你推荐一个合适的电话号码。请再次确认该号码注册在你所在的国家,并且已启用短信功能,如图 8-5 所示。

图片

图 8-5 Twilio 会为你选择一个号码;确保它是基于你所在的国家并且能够接收和发送短信。

如果号码未启用短信功能,你需要通过点击搜索其他号码来获取另一个号码,可能会收取少量费用。确认信息无误后,记下该电话号码以备后用,然后点击选择此号码

你现在已经设置好 Twilio 账户,并获得了一个可以在 Python 程序中使用的新电话号码。你现在可以开始创建项目并编写发送短信到电话号码的程序。

创建一个项目

现在是时候设置你的项目并命名了。此时,Twilio 还会显示你项目的授权代码和凭证。在“项目名称”下,输入SMS_Phone,或者任何其他包含 SMS 的名称。仪表板现在会显示标题为 SMS_Phone Dashboard,如图 8-6 所示。

当你创建 Twilio 账户时,你还创建了一个账户 SID 和授权令牌,这些将在稍后用于控制与你验证过的手机之间的通信。你会看到出于安全原因,授权令牌没有显示。要访问令牌,请按旁边的复制按钮。现在,你可以将令牌粘贴到你的程序代码中。

Image

图 8-6 你将在这里找到项目凭证。

安装 Twilio

要安装 Twilio,打开终端窗口并输入以下内容:

pi@raspberrypi:- $ sudo pip3 install twilio

相关的 Python 包将被下载并安装到你的树莓派上。然后重启树莓派,当它加载完成后,打开位于“编程”菜单中的 IDLE 3。

编写代码

将列表 8-1 中的代码复制到文件中,并将其保存为SMS.py

❶ from twilio.rest import Client

   # Find these values at https://twilio.com/user/account
❷ account_sid = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
❸ auth_token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

❹ message_text = input("Please enter your message ")

❺ client = Client(account_sid, auth_token)
   # Replace +999 with your own phone number and +000 with your Twilio
   number

❻ message = client.messages.create(to = "+999", from_="+000",
   body = message_text) 

❼ print ("Your message is being sent")
❽ print ("check your phone")

列表 8-1 简单的短信发送代码

你首先通过从twilio.rest导入Client()函数 ❶。表征状态转移(REST)是一种在计算机设备(你的树莓派)和基于网页的服务(Twilio 的云服务)之间传输信息的方法。你导入Client()是为了能够使用 Twilio 的 REST 服务。

接下来,你需要添加你的账户安全标识符(SID) ❷和授权令牌 ❸。你已经在 Twilio 网站的仪表板上找到了这两个值,所以只需将它们复制并粘贴到代码中,替换掉 X 占位符。如果你选择手动输入这些代码,确保使用正确的字母大小写。

然后,你创建一个名为message_text ❹的变量,并给它一个提示用户输入的短语:input("请输入你的消息 ")。这行代码提示用户(在这个例子中,用户是你)输入他们想要发送的 SMS 消息;你的程序可以发送任何你想要的文本,而不是标准的预设消息。

为了使用客户端发送 SMS 消息,你需要将account_sidauth_token放入名为client ❺的变量中。通过这样做,你可以创建一行代码,将消息、Twilio 凭证和相关电话号码结合起来,然后发送 SMS 消息。

你创建一个名为message的变量,并添加命令client.messages.create()。当调用时,message会从你的client变量中收集详细信息,并创建一个对象来引用 Python Twilio 类,从而创建 SMS 消息 ❻。

在同一个message变量中,你需要在to =代码之后添加你在 Twilio 注册并验证的手机号码。你需要将代码中当前的+999 替换为你发送消息的手机号码(但确保保留加号)。

from_= 后,你输入你设置的 Twilio 支持的手机号码,替代代码中当前的 +000。接下来,你添加 message_text 变量,该变量包含短信的正文内容。这就完成了发送消息的代码行。

然后你添加一行简单的确认信息,用来打印消息是否已发送 ❼,再添加一行提醒用户检查消息已发送到哪个手机的行,消息的接收者 ❽。

让我们来试试吧!保存程序并运行,确保注册的手机已开机。当程序运行时,系统会提示你输入一条短消息。输入你的消息并按 ENTER;你应该会看到消息出现在你的手机上,如 图 8-7 所示。

Image

FIGURE 8-7 在手机上接收 Twilio 消息

创建自动短信提醒机

现在你已经设置了一个基本的短信程序,你可以将其与其他 Python 功能结合,创建一个自动短信提醒系统,如 图 8-8 所示。你将输入提醒消息,然后输入需要发送提醒的时间。

Image

FIGURE 8-8 自动文本提醒

程序检查树莓派上的当前时间。当时间达到指定时间时,它会将提醒消息发送到你的手机。因为时间需要准确,我建议你将树莓派连接到互联网。这样,每次树莓派启动时,时间都会更新,提醒程序也会在正确的时间做出响应。

设置和验证格式

在 IDLE 3 或你的 Python 代码编辑器中打开一个新文件,并添加代码的第一部分,如 LISTING 8-2 所示。将其保存为 reminder_machine.py

❶ from twilio.rest import Client
   import time
   import datetime
   import sys

   # Find these values at https://twilio.com/user/account
❷ account_sid = "XXXXXXXXXXXXXXXXXXXXXXX"
   auth_token = "XXXXXXXXXXXXXXXXXXXXXXXXX"
   client = Client(account_sid, auth_token)

   # validation for time
❸ def isTimeFormat(input):
    ❹ try:
       ❺ time.strptime(input, '%H:%M')
       ❻ return True
    ❼ except ValueError:
       ❽ return False

LISTING 8-2 提醒机程序的第一部分代码

同样,你需要导入 Twilio 的 Client ❶。但你还需要导入 time 模块来添加短暂的延迟,并导入 datetime 模块以访问树莓派的当前时间。你还需要导入 sys 模块,以便在程序中使用命令行功能。

和之前一样,你需要将 Twilio 账户的凭证添加到变量 account_sidauth_token 中,然后将它们组合成一个名为 client ❷ 的变量。

下面这一部分是新的:你需要设置一个简单的验证,检查输入的时间格式是否正确。用户需要输入 小时:分钟 的格式,例如:11:10。如果用户输入时间格式不正确,比如 1110 或 111:0,程序将会失败,因为这些格式无法匹配树莓派的时间。为了检查正确的格式,你创建一个名为 isTimeFormat() ❸ 的函数,然后使用 try 方法验证输入 ❹。接下来,你使用函数 time.strptime() ❺ 来检查输入的时间是否符合 %H:%M 格式,即 小时:分钟,或者 00:00。

如果输入的时间格式正确,验证检查返回True值❻。如果发生错误❼或输入的值不符合正确的格式,将返回False值❽。你稍后将使用这些值来触发程序继续进入下一阶段,或提示用户重新输入正确格式的时间。

编写提醒消息

现在你准备添加第二段代码,这段代码介绍了程序的功能,并请求用户输入他们的提醒消息和时间。将列表 8-3 添加到reminder_machine.py文件的底部。

❶ print ("Welcome to the reminder machine")
❷ print ("")
❸ time.sleep(1)
❹ reminder = input("Please enter your message ")
   print ("")

   # validation check for time format
❺ check = False

❻ while check == False:
    ❼ reminder_time = input("Please enter the reminder time.
      For example 09:45 ")
    ❽ check = isTimeFormat(reminder_time)
      print ("")
    ❾ print (check)
      print ("")

列表 8-3 你的 reminder_machine.py 程序的第二部分

你首先使用一个简单的print()语句,通知用户提醒机器正在运行并准备就绪❶。然后,在每条语句之间添加一个空行,使它们更容易阅读❷,并暂停一秒钟,让用户有时间阅读欢迎消息和说明❸。

接下来,你创建一个变量,提示用户输入提醒消息❹并存储该消息。

然后,你检查time值是否是正确的格式,00:00。默认将其设置为False❺。你创建一个while语句,它只在isTimeFormat()验证检查值为False时运行❻。因为你在上一行将其设置为False,所以while循环会不断地执行。

while循环内部,你提示用户输入希望发送提醒的时间,并将其存储在变量reminder_time中❼。

为了检查用户输入的时间是否符合正确格式,你调用你在列表 8-2 中创建的isTimeFormat()函数,并输入存储在reminder_time变量中的值❽。

根据时间是否按正确格式输入,函数将返回TrueFalse。如果返回值为False,则时间输入格式不正确,while循环将再次运行,提示用户按正确的格式输入时间。

当返回值为True时,时间格式输入正确,程序继续执行到第❾行,并打印验证值。这是为了你的测试目的:程序运行正常后,你可以通过在❾行左侧添加#来注释掉这一行,使其看起来像这样:# print (check)

匹配时间并发送消息

程序的最后部分,如列表 8-4 所示,比较树莓派的当前时间与提醒时间,并且在两者匹配时,发送消息。

❶ while True:
    ❷ current_time = datetime.datetime.now().time()
       # print (current_time)

    ❸ current_time = str(current_time) # swap the time to a string

    ❹ current_time = current_time[0:5]

    ❺ if reminder_time == current_time:
            ❻ print ("Reminder Time")
            ❼ message = client.messages.create(to = "+999999",
               from_="+00000000", body=reminder)
            ❽ time.sleep(60)
            ❾ sys.exit()
    else:
            ❿ time.sleep(1)

列表 8-4 reminder_machine.py 的最后部分:发送消息!

首先,你创建一个while True循环,以便让程序的这一部分持续运行,检查当前时间并与提醒时间进行比较❶。

接下来,你将从树莓派操作系统存储当前的日期和时间到一个名为current_time的变量中❷。你将打印出这个值用于测试,但一旦程序正常运行,记得将其注释掉。该值的格式为小时:分钟:秒:毫秒,它包含了比你需要的更多数据。所以,在使用日期时间值之前,你将其转换为字符串❸,然后将其切割为仅包含前五个字符❹,以匹配 00:00 的格式。

现在你可以将reminder_time与当前时间进行比较:下一行检查例如 11:10 是否等于 11:10❺,如果两个值匹配,程序会打印出一行,指示现在是提醒时间❻!

为了发送消息,你需要创建一个名为message的变量,并添加tofrom的电话号码。然后,你将你输入的提醒信息存储在reminder变量中,作为消息的正文❼。client.messages.create()函数会创建并将消息发送到你的手机。

从代码行❷到❿被放置在一个while循环中,这意味着程序会持续检查条件,并在条件满足时发送消息。如果你将程序代码保持这样,它会在与你设定时间匹配的那一分钟发送尽可能多的消息。为了避免这种连续发送消息的情况,你需要在下一行代码运行❽之前添加一个 60 秒的延迟。这样,时间就会前进到例如 11:11,并且不再与循环再次运行时的时间匹配。

你通过从终端调用系统退出命令❾来添加退出程序的代码。在程序的最后一行,你添加了 1 秒的延迟,然后整个循环将重新开始❿。

这样程序就完成了。现在你可以保存并执行它。请注意缩进级别。如果你遇到运行问题,首先检查各行是否按照正确的级别缩进。

输入你的提醒信息和提醒时间。然后让你的树莓派继续运行,系统将在指定的时间将你的文本发送给你!

完结

你可以用你的自动短信机器做各种事情,尤其是现在你已经设置了 Twilio 账户。查看 Twilio 文档,了解你可以做些什么(www.twilio.com/docs/quickstart)。作为一个小提示,为什么不试试侦探工作:使用 Twilio 追踪发送到和从连接到 Twilio 的手机的消息和电话。你可以在 www.tecoed.co.uk/spooking-a-mobile.html 找到这个黑客技术的代码。

第九章:PI SPY PART 2:WI-FI 和蓝牙跟踪

你可以利用你新的 Raspberry Pi 技能,通过定位设备的信号来窃听屋内每个人的动态。几乎每个人都有智能手机、平板电脑或其他设备,这些设备会在周围传输大量未被察觉的数据。你将利用这些数据来检测谁在周围。

你将使用 Raspberry Pi 通过互联网路由器和蓝牙来检测设备。你会通过互联网路由器或蓝牙将数据从 Raspberry Pi 发送到每个设备,并检索包括设备名称、设备类型、是否已连接以及设备所属者是否在附近等信息。这假设每个设备由一个人拥有,而不是例如在家庭成员之间共享。否则,要知道谁在附近会变得更难!

然后,你将创建一个 LED 视觉显示板指示器,或称为状态板,当用户在家时,它会亮起,而当用户不在时,它会熄灭(如图 9-1 所示)。

Image

图 9-1 完成的状态板

你将需要的东西

  • Raspberry Pi Zero W 或 Raspberry Pi 4(推荐)

  • 10 根母对母跳线

  • 5 个 LED 灯——或者与想要跟踪的设备数量相同的 LED 灯数量

  • 5 个电阻(220 到 330 欧姆)

  • 纸板

  • 图片(可选)

  • 胶带(可选)

  • 按钮

你可以使用家人照片,或者为了掩盖状态板的用途,使用随机图片来代表每个家庭成员,正如我在图 9-1 中所示。

创建你的状态板

我们从创建状态板开始,它将显示谁在附近。

如果你有,Raspberry Pi Zero W 是最适合安装到状态板背后的尺寸,因为它是最小的 Pi。如果你使用的是更大的 Pi,如型号 4,你可能需要将它放置在状态板旁边的坚固表面上。然而,如果你使用 Zero W,你可能会发现使用型号 4 来做这个项目更容易,然后将 SD 卡转移到 Pi Zero W 上。

LED 连接线

取一个 LED,找到较长的(正极)引脚。将一个电阻的引脚绕在这个 LED 的引脚上,使其紧密固定,如图 9-2 所示。然后,将一根母对母跳线的一端连接到 LED 的短引脚(负极)。将另一根母对母跳线的一端连接到电阻的引脚。对于所有五个 LED,重复这个步骤。

Image

图 9-2 LED 连接线

将 LED 灯添加到面板

在你用于状态板的纸板上,轻轻标记出你想要放置每个 LED 的位置。如果你将树莓派固定在板上,确保为树莓派和所有的电线留出足够的空间。小心地在每个已标记的位置上打一个小孔,以固定每个 LED。你可以使用笔或螺丝刀来完成此操作。将每个 LED 推入孔中,并用胶带将其固定,以保持整洁,同时减少电线相互接触造成短路的可能性。你状态板的背面应类似于图 9-3。每个数字对应 LED 连接到的 GPIO 引脚。(请参见下一页的 GPIO 引脚编号表。)例如,左侧的 LED 1 连接到 GPIO 4。

Image

图 9-3 将 LED 连接到状态板

现在你可以为电路板的前面添加个人风格。也许可以添加一些照片来代表你家中的成员,并根据你的风格和口味装饰它。

连接 LED 到树莓派

为了为每个 LED 供电,你需要将较长的正极引脚(上面绕着电阻)与跳线连接到一个 GPIO 引脚。(任何一个都可以,但我建议使用下表中的引脚。)然后,将较短的负极引脚与跳线连接到标记为GND的接地 GPIO 引脚。图 9-4 展示了这些连接。GND 引脚位于物理引脚 6、9、14、20、25、30、34 和 39。你可以使用其中的任何一个。

Image

图 9-4 连接 LED 到树莓派

现在,为你的其他所有 LED 进行同样的操作,将它们连接到树莓派板上(见图 9-5)。下表显示了每个 GPIO 引脚应使用的情况。

项目 GPIO
LED 1 4
LED 2 18
LED 3 24
LED 4 7
LED 5 26
关闭按钮 16

最后一组电线是用于关闭按钮的,按下时将关闭树莓派的电源。将一根跳线连接到 GPIO 16,另一根连接到 GND 引脚。如果需要,你可以将 LED 连接到任何引脚,除了 3V 和 5V 引脚(1、2、4 和 17),具体取决于你的 LED 和树莓派在板上的位置。例如,如果你的树莓派位于板子的中间,你可能会想为位于右侧的 LED 使用 GPIO 2、3、4、14、15 或 18,为位于左侧的 LED 使用 13、19、26、16、20 或 21。LED 的另一端连接到一个接地引脚,以完成电路。如果你使用不同的 GPIO 编号或添加更多的 LED,请注意每个 GPIO 引脚编号及其对应的 LED,因为在用 Python 编程时你需要它们。

Image

图 9-5 连接所有 LED 和按钮

安装树莓派

大多数树莓派配有四个小螺丝孔,分别位于板子的四个角落。这些螺丝孔非常适合将树莓派安装到状态板上。你可以使用小螺丝将树莓派固定,或者使用粘性胶点来固定它。如果你希望以后能够拆卸树莓派并将其用于其他用途,建议使用螺丝。

使用 IP 地址跟踪设备

首先,你需要设置树莓派,通过其 IP 地址跟踪你家庭成员的设备。稍微考虑一下,使用互联网的设备数量是多么庞大:手机、智能电视、游戏机、智能手表、冰箱、汽车、平板电脑和报警系统,仅举几例。在这么多设备连接的情况下,正确的数据如何发送到正确的设备呢?比如你正在流式播放一个视频,数据如何知道应该发送到你的平板,而不是家里其他人正在查看电子邮件或听音乐的设备上?

每个连接到互联网的设备都会分配一个互联网协议(IP)地址。从外部来看,这些地址的作用类似于邮政编码:它们表示一组在特定网络内的设备——你的网络就是你共享的互联网连接。从这个网络内部,每个设备都会分配一个唯一的 IP 地址,以便路由器知道将信息发送到哪里。

IP 地址由四组数字组成:例如,192.168.1.24。根据最新统计,大约 340 万亿万亿万亿个 IPv6 地址正在使用中!^(1)

你将使用你网络中设备的单独 IP 地址来检查每个设备是否在附近。由于你的树莓派连接到家庭网络,它也会有自己的 IP 地址。让我们来查找它是什么。

查找你的 IP 地址

打开终端,输入以下命令,这将为你提供一些关于网络的信息:

pi@raspberrypi:- $ hostname –I

这会显示你树莓派的 IP 地址。在图 9-6 中,我的 IP 地址是 192.168.1.171。

Image

图 9-6 获取树莓派的 IP 地址

查找其他设备的 IP 地址

有两种方法可以找到你网络中其他设备的 IP 地址。第一种是使用类似 Fing 的应用程序,通过你的手机或平板进行扫描,第二种是通过树莓派上名为 Nmap 的程序进行扫描。

使用 Fing

Fing 会扫描你的路由器和网络,并返回所有 IP 地址,同时列出所有连接设备的名称和制造商。记下这些信息,方便稍后在程序代码中使用。如果你想选择这种更简单的方式,可以在手机或其他设备上下载 Fing,并按照* www.fing.io *上的说明进行操作。

使用 Nmap

另外,你也可以在树莓派上写一个简单的 Python 程序来扫描并返回地址。这有一个优势:你可以将数字直接复制粘贴到主程序中,而不用担心打错。为此,你将使用一个叫做 Nmap 的程序。

安装 Nmap

Nmap (网络映射器) 是一个用于发现计算机网络中设备和服务的扫描器。Nmap 向路由器发送一个特定的数据包,然后分析响应以构建关于连接设备的信息表。从这个表中,程序构建出你的网络地图。要安装 Nmap,打开终端窗口并输入以下内容:

pi@raspberrypi:- $ sudo apt install nmap

Nmap 命令通过终端执行,因此要使用 Python 和 Nmap 的功能,你需要安装一个 Python 封装程序。封装程序是一个程序,它允许你在终端中编写 Python 代码来控制某些软件。在终端中,输入以下命令:

pi@raspberrypi:- $ sudo pip3 install python-nmap
编写程序以查找 IP 地址

现在你已经安装了 Nmap,可以编写一个 Python 程序来搜索连接设备的 IP 地址。打开 IDLE,创建一个新的 Python 文件。添加 Listing 9-1 中的代码并保存。在运行之前,确保你的手机或其他设备已连接到路由器,因为你将使用自己的设备作为最终代码的测试器。

❶ import nmap
❷ nm = nmap.PortScanner()
❸ data = nm.scan(hosts='192.168.1.0/24', arguments= '-sP')
   print (data['scan'])
   print ("")
❹ for host in nm.all_hosts():
    ❺ print ('Host : %s (%s)' % (host, nm[host].hostname()))

LISTING 9-1 Nmap 扫描程序

首先导入 nmap 库 ❶,然后创建变量 nm 来保存 nmap 库中的 PortScanner() 函数 ❷。正如你可能猜到的,这个函数用于扫描端口!

接下来,添加代码以扫描路由器 ❸。大多数路由器都有一个通用的 IP 地址,这个地址在所有路由器中是标准的。这行代码扫描从 192.168.1.0 到 192.168.1.24 范围内的所有 IP 地址,然后检索每个 IP 地址持有的关于连接设备的数据。

然而,你可能需要检查特定路由器的 IP 范围,并更改该范围以反映这一点。例如,你的路由器的 IP 地址范围可能从 192.168.0.1 开始,或者你可能已经配置了自己的自定义范围。如果不确定,请参考服务提供商的指导和文档。

现在使用 for 循环遍历所有找到的主机(设备) ❹。这将为你提供每个当前连接到路由器或之前连接的设备的 IP 地址及其名称(许多路由器会保存所有曾连接过的设备列表)。

你获取的列表提供了关于设备的详细信息,所以你会打印出来 ❺ 以帮助你识别每个设备。一旦输入并保存了程序,按 F5 运行,你将在 Python 编辑器窗口中看到连接设备及其 IP 地址的列表,就像 Listing 9-2 中的那样。

Python 3.5.3
[GCC 6.3.0 20170124] on linux
Type "copyright", "credits" or "license()" for more information.
>>>
=== RESTART: /home/pi/find_ipadresses.py ===
{'192.168.1.1': {'addresses': {'ipv4': '192.168.1.1'}, 'hostnames':
[{'name': 'TestBox.cc', 'type': 'PTR'}], 'vendor': {}, 'status':
{'reason': 'syn-ack', 'state': 'up'}}, '192.164.1.xxx':
{'addresses': {'ipv4': '192.168.1.xxx'}, 'hostnames': [{'name': '',
'type': ''}], 'vendor': {}, 'status': {'reason': 'conn-refused',
'state': 'up'}}}

LISTING 9-2 已找到的 IP 地址

将你的详细信息复制并粘贴到文本文件或新的 Python 文件中。如果你粘贴到 Python 文件中,请记得在每行前加上#,将其注释掉——这样程序运行时就不会出现错误。现在,尝试确定每个设备属于谁,并在设备名称旁边记录额外的信息。你可能能够通过设备名称来识别,或者你可以快速检查家里谁在,并使用排除法。例如,如果家里有两个 iPhone 用户,而只有一部手机出现,检查型号。如果某个人不在家,你就知道显示出来的手机不是他们的。有些用户会通过使用自己的真实姓名作为设备名称来简化识别。你还应该记录下哪个设备是你自己的。

记住,在理想情况下,设备将连接到路由器,并返回当前的 IP 地址。但 IP 地址在路由器关闭或重置时可能会发生变化,因此家中关闭的设备可能会显示其以前的 IP 地址。最好等到设备重新上线后再收集地址。

编写状态板代码

现在你已经拥有了 IP 地址、设备和用户的列表,你可以创建一个程序,将这些信息与状态板中的电子元件结合起来,监控谁在你家中。如果你使用的是 Pi Zero W,你需要通过 SSH 连接(参见“通过 SSH 访问 Raspberry Pi”章节,第 25 页)或使用远程桌面应用程序访问你的 Pi。否则,你可以在 Raspberry Pi 上编写并测试本节代码,然后将其 SD 卡转移到 Pi Zero W 中。

在 Python 代码中,通常会将 LED 等常见元素归为一组。然而,如果你按每个人或设备来分组代码,我们的代码会更有意义;这样,你可以轻松添加 LED 并检查更多的人。这就是你在这里要做的。

这个程序使用了gpiozero库和PingServer()类,它会发送 ping 请求。那么,ping 是什么呢?你可以将ping看作是发送到路由器的请求。该请求由数据包组成,表示为一串零和一,目的是请求路由器提供关于所有连接设备的信息。路由器随后将这些信息发送回你的 Pi 和 Python 程序。在这个项目中,数据会被返回到你的 Raspberry Pi。你将使用这些信息来找出谁在附近。

设置并识别第一个设备

打开你的 Python 编辑器,添加 LISTING 9-3 中显示的代码。

然后将其保存为wifi_spy.py

❶ from gpiozero import PingServer, LED, Button
❷ from subprocess import check_call()
❸ from signal import pause

❹ def shutdown():
    check_call()(['sudo', 'poweroff'])

   # first device apple phone
❺ frank = PingServer('192.168.1.1') # hub
❻ led = LED(4)

LISTING 9-3 设置和查找第一个设备

首先,从gpiozero库导入所需的函数❶。你需要PingServer()来从路由器获取数据,LED类来控制 LED,Button模块来添加关闭板的按钮功能。

你还需要从subprocess库中导入check_call()❷。这使你可以使用终端命令来控制 Raspberry Pi,并将用来从终端关闭 Pi。

最后,从signal库中导入pause,以便让程序持续运行❸。

接下来,创建一个shutdown()函数,包含关闭 Pi 的代码❹。该函数使用check_call()命令运行sudo poweroff,用于关闭 Pi。

现在你可以开始检测人员了!我已经添加了一个注释来帮助你跟踪设备和状态板上的每个 LED 所对应的人;你应该修改这个注释,来反映你家中第一个设备的信息。

创建一个变量,用于存储你检查的第一个 IP 地址的详细信息❺。你应该根据设备的使用者命名这个变量,这个信息你可以在“查找其他设备的 IP 地址”章节的第 147 页中找到。该变量包含 ping 路由器和设备 IP 地址的命令。在这里,我填写的是 IP 地址,你应当将之前通过 Fing 或 Nmap 获得的第一个 IP 地址放在括号内,并记得保留引号。

最后一行标识了该人员 LED 使用的 GPIO 引脚❻。每个个人应该有自己独立的 LED,因此这里将 Frank 分配到连接 GPIO 引脚 4 的 LED。

查找其他设备

现在,你将使用相同的代码结构,设置你想要检查的其他设备 IP 地址的变量,并为每个设备分配一个 LED。将列表 9-4 中的代码行添加到程序的底部。

   # second device oneplus
   scott = PingServer('192.168.1.22') # oneplus
   led_scott = LED(18)

   # third device laptop 1
❶ liz = PingServer('192.168.1.72') # laptop 1
   led_liz = LED(24)

   # fourth device raspberry pi
   jenny = PingServer('192.168.1.165') # pi
   led_jenny = LED(7)

   # fifth device laptop 2
   jade = PingServer('192.168.1.209') # laptop 2
   led_jade = LED(26)

列表 9-4 为每个设备分配 LED

如同之前,你创建一个变量来保存每个 ping 的结果,指示设备所有者的姓名。记得将每个变量名替换成你家中的名字,并将每个 IP 地址替换为之前收集的相应 IP 地址❶。

如果你找不到足够的设备,仍然添加每个变量对应的代码,并使用已经使用过的 IP 地址。这将同时点亮多个 LED,但这意味着你可以测试电路和程序代码是否正常工作。一旦一切都能正常运行,你可以回过头来删除或注释掉那些代码行。

添加延迟、响应数据,并添加按钮

最后,添加程序代码的结尾部分,如列表 9-5 所示。此部分将在每个设备的 ping 之间添加延迟。

你在每次 ping 之间设置延迟,因为频繁 ping 路由器会产生大量流量并拖慢网络速度。你可能会被发现!而且,通常情况下,人们不会每秒钟都进出家门,所以你可以每分钟检查一次。

   # check every 60 seconds
❶ led.source_delay = 55
   led_scott.source_delay = 56
   led_liz.source_delay = 57
   led_jenny.source_delay = 58
   led_jade.source_delay = 59

   # power on LEDs
❷ led.source = frank.values
   led_scott.source = scott.values
   led_liz.source = liz.values
   led_jenny.source = jenny.values
   led_jade.source = jade.values

   # GPIO 16 for shutdown
❸ shutdown_btn = Button(16, hold_time=2)
❹ shutdown_btn.when_held = shutdown

❺ pause()

列表 9-5 在 ping 之间添加延迟

将我在这里使用的名称替换为你为变量使用的名称,然后列出我所做的延迟秒数 ❶。如果你发现网络仍然很慢,或者你希望检查频率较低,可以增加延迟的秒数。例如,你可以将延迟时间增加到 300 秒,因为用户可能会在线较长时间。请注意,某些智能手机在屏幕锁定时可能会显示与网络断开连接。

接下来,程序需要响应 ping 数据。如果 ping 中发送的 IP 地址在路由器上,并且确认是活跃的,那么对应的 LED 会亮起 ❷。如果找不到该 IP 地址,则说明设备没有找到,LED 会熄灭。

最后,你设置了按钮来启动关机序列 ❸。你将其设置为 GPIO 引脚 16(如果你使用了不同的引脚,确保在这里填写相应的 GPIO 引脚编号)。

hold_time中的值指的是按钮必须按住多久才能触发关机序列 ❹。这个程序将时间设置为 2 秒,以防止误按按钮导致程序关闭。(你必须至少按住按钮 2 秒钟。)when_held检查按钮是否被按下,如果是,它会运行关机功能,关闭树莓派并关闭状态板。

最后,使用pause() ❺使程序永远循环。这就完成了主程序结构和状态板硬件的设置!

运行程序

让我们试试看!要检查程序代码是否正常工作,按下F5。这将提示你保存程序,然后执行。根据周围的人和连接的设备,你应该能看到一些 LED 灯亮起。

确保你的设备已开机并连接到路由器。为了测试代码是否响应,关闭你的设备或设置为飞行模式。你应该会看到自己的 LED 灯熄灭!记住,代码每分钟运行一次,所以可能不会立即熄灭。

故障排除

如果状态板似乎无法正常工作,检查以下常见问题:

  1. 是否有设备连接到路由器?

  2. 你的 LED 灯有故障吗?

  3. 你是否正确连接了 LED 灯?(检查正负极脚是否正确连接。)

  4. LED 引脚编号是否与代码中使用的引脚编号匹配?

  5. 你是否为家中每个用户使用了正确的 IP 地址?

  6. 是否将正确的 LED 分配给了正确的用户?

设置代码自动运行

项目的最后一步是配置程序以便自动运行,这样您的状态板在 Raspberry Pi 启动时会做出响应。一旦您的程序正常工作,状态板也能正常运行,您可以移除显示器并将 Raspberry Pi 连接到状态板上。然后,要启动状态板,只需插入 Raspberry Pi 的电源。要关闭它,按下关机按钮或将两根电线触碰在一起,您的 Raspberry Pi 将开始关闭过程。

使用 Cron 自动启动程序

要使状态板在启动时自动启动,您需要安排 Python 程序在启动时执行。这使用了cron,一个基于时间的简单作业调度工具,其名称源自希腊语“时间”(chronos)。该工具使您能够创建规则,在特定时间自动触发任务。例如,您可以设置规则,每小时下载文件,每天下午 4 点运行更新,或每周同一时间发送电子邮件。

您在一个crontabcron 表的缩写)文件中创建这些规则,这是一个包含任务指令的简单文本文件,最重要的是,包含触发任务的时间。

在将此任务添加到 crontab 文件之前,您需要知道 wifi_spy.py 程序在 Pi 系统中的保存位置。除非您将文件保存到了某个特定位置,否则该位置可能是 /home/pi/wifi_spy.py。请注意正确的文件路径 wifi_spy.py,然后您可以编辑 crontab。

打开终端并输入以下命令:

pi@raspberrypi:- $ crontab –e

这将打开 cron 控制台(图 9-7)。

Image

图 9-7 crontab

终端应该给您三种编辑 cron 文件的方法。选择选项 2 以打开 nano 文本编辑器——您将使用此编辑器,因为它最简单。

这应该会在 nano 中打开 cron 文件,您应该会看到其中已有的一些代码,看起来类似于清单 9-6。

# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected)
#
# For example, you can run a backup of all your user accounts
# at 5 am every week with:
# 0 5 * * 1 tar –zcf /var/backups/home.tgz /home/
#
# For more information, see the manual pages of crontab(5) and
cron(8)
#
# m h  dom mon dow    command

清单 9-6 crontab 文件中的启动代码

向下滚动到文本底部,找到黑色区域,在最后添加以下代码行:

@reboot python3 /home/pi/wifi_spy.py &

该命令很容易理解:它声明在 Raspberry Pi 重启时,以超级用户模式运行 Python,打开home/**pi 文件夹,并执行名为 wifi_spy.py 的程序。确保如果需要,您将文件名替换为您的文件名,并且使用正确的文件夹路径。

代码行末尾的 & 语法告诉 cron 在后台运行您的程序,这样您仍然可以用 Raspberry Pi 做其他事情。

一旦您检查过添加到 crontab 文件中的代码并确认它正确无误,请按 CTRL-X 保存并退出文件。现在,每次启动或重启 Raspberry Pi 时,crontab 文件都会运行并执行状态板程序。

如果你想停止程序自动运行,可以通过终端再次打开 crontab 文件,使用 crontab –e,然后注释掉或删除你添加的那行代码。接着保存文件并重启。

重启到命令行

由于状态板设计为 无头模式(没有屏幕或显示器),你不需要让 Raspberry Pi 启动到桌面环境,因为这会消耗更多的时间、内存和处理能力。相反,你可以配置 Pi 启动到命令行界面。打开终端窗口并输入以下命令:

pi@raspberrypi:- $ sudo raspi-config

这将显示终端中的配置工具,如 图 9-8 所示。选择第三个选项,3 启动选项,然后按回车。接着选择 B1 桌面 / CLI,再选择 B1 控制台 选项。

图片

图 9-8 启动到命令行选项

设置完成后,使用下方向键选择 并按回车。系统会提示你保存配置文件并重启。选择 Yes,你的 Pi 将会重启。重启后,状态板程序应该会加载。

现在,为了显示周围的人员信息,插入电源,你的状态板会告诉你。你可能需要等待一两分钟,直到 Pi 连接到路由器。移除 Raspberry Pi 上的 HDMI 电缆,因为你现在真正处于无头模式!

使用蓝牙跟踪设备

在这一部分,你将学习如何通过蓝牙而不是 Wi-Fi 跟踪周围的设备。类似于你使用 IP 地址来跟踪设备的方式,你可以使用 Raspberry Pi 扫描蓝牙数据传输并获取设备的地址。然后,可以使用这个地址触发状态板上的相关 LED 灯。

什么是蓝牙?

蓝牙 是行业中对 2.402 至 2.480 GHz 范围内的无线电波的标准名称,用于让设备在短距离(大约 100-200 米)内无线通信。像所有技术一样,蓝牙也在不断发展,最新的蓝牙 4 技术可以实现最远 200 米的通信。蓝牙功耗低,非常适合在移动设备上使用。

每个蓝牙设备都有自己独特的地址,格式为 D2:72:F6:87:D2:8A。因为每个地址都是设备独有的,所以如果你获得了地址并知道每个设备的拥有者,就可以追踪到人员!从安全角度考虑,如果你打算分享程序代码或向他人展示如何使用,建议你删除或编辑这些地址。

打开 Raspberry Pi 蓝牙

Raspberry Pi 和 Pi Zero W 都自带蓝牙硬件。如果你使用的是旧款 Pi,仍然可以使用这个方法,但你需要购买一个蓝牙 USB 加密狗并通过 USB 端口连接。

要在 Pi 上打开 Bluetooth,找到桌面右上角的标准 Bluetooth 图标,点击该图标,然后选择打开 Bluetooth

好消息是,为了找到附近的 Bluetooth 设备,你不需要与它们连接——否则就有点暴露了。如果你手头有自己的设备,打开并启用 Bluetooth。然后返回到你的 Pi,点击 Bluetooth 图标,选择使设备可发现选项。Bluetooth 图标会开始闪烁绿色。再次点击 Bluetooth 图标,选择添加设备选项。一个新窗口应该会打开,Bluetooth 硬件将开始扫描并寻找设备。最终,你将得到一份所有启用了 Bluetooth 的附近设备的列表!

为了收集设备的 Bluetooth 地址,你将编写一个简单的 Python 脚本,就像你搜索 IP 地址时做的一样。该程序会扫描地址,将每个地址添加到列表中,然后将列表打印到 Python 控制台。首先,你需要安装一些库。

要安装所需的 Python 库,打开终端窗口并输入以下命令:

pi@raspberrypi:- $ sudo apt install bluez
pi@raspberrypi:- $ sudo apt install bluetooth libbluetooth-dev
pi@raspberrypi:- $ sudo pip3 install pybluez

当这三个程序下载并安装完成后,重启你的 Raspberry Pi。

那么,让我们看看你家里有哪些支持 Bluetooth 的设备。打开一个新的 Python 文件,复制 Listing 9-7 中的程序。保存为bluetooth_finder.py

import bluetooth
from time import sleep
# find devices
print ("Searching for devices...")
nearby_devices = bluetooth.discover_devices(lookup_names = True)
sleep(10)
print ("found %d devices" % len(nearby_devices))
for addr, name in nearby_devices:
    print ("  %s - %s" % (addr, name))

LISTING 9-7 使用 Bluetooth 发现设备

这段代码控制 Bluetooth 硬件来搜索附近传输 Bluetooth 信号的设备。然后,它会创建一个组织良好的地址列表,并将其打印出来。

输出应该类似于 Listing 9-8 中的内容。

Python 3.5.3 (default, Jan 19 2017, 14:11:04)
 [GCC 6.3.0 20170124] on linux
Type "copyright", "credits" or "license()" for more information.
>>>
 RESTART: /home/pi/bluetooth_finder.py
Searching for device...
found 0 devices
>>>
 RESTART: /home/pi/bluetooth_finder.py
Searching for device...
found 1 devices
  D0:57:78:87:F6:8A - DANLAPTOP

LISTING 9-8 扫描输出

一旦你复制了程序,可以用它复制并粘贴地址,或者将其写下来用于你的状态板。记得检查每个地址对应的是哪个设备,并进行一些测试,以确保你为每个人都找到了正确的地址。

编写状态板代码

现在你可以定位 Bluetooth 设备,并且你已经拥有了附近设备的地址,可以将这些地址用作 ID 代码,触发状态板上的 LED。代码会分几部分给出,以便我能逐步解释每一部分的工作原理。首先,让我们设置一些基本内容。

导入并设置 LED

让我们导入所需的包并设置 LED。将 Listing 9-9 中的程序代码复制到一个新的 Python 文件中,并命名为bluetooth_status_board.py

❶ import Bluetooth
   from bluetooth import *

   from gpiozero import LED, Button
   from subprocess import check_call()
   from signal import pause
   from time import sleep

❷ def shutdown():
   ❸ check_call()(['sudo', 'poweroff'])

   # define the LEDs
❹ led1 = LED(4)
   led2 = LED(18)
   led3 = LED(24)
   led4 = LED(7)
   led5 = LED(26)
   button = Button(16)

LISTING 9-9 Bluetooth 状态板代码的第一部分

通过导入Bluetooth库启动程序,然后依次导入控制状态 LED 和关机按钮❶的LEDButton类。接着导入subprocess库,以便你能像 Wi-Fi 版本中那样从终端使用check_call()。使用sleep()函数为程序添加延迟或暂停。

接着,创建shutdown()函数 ❷,并让它使用check_call()来调用终端命令sudo shutdown,这将指示树莓派关机 ❸。

然后将状态板上的每个 LED 分配给一个 GPIO 引脚 ❹。你还将按钮分配给引脚 16。

添加搜索代码

现在你将添加代码来搜索你收集的蓝牙地址!你将使用 Listing 9-7 中的搜索代码来查找已启用蓝牙的设备,然后如果找到对应的蓝牙地址,将点亮状态板上的 LED。将 Listing 9-10 中的代码添加到你当前的bluetooth_status_board.py文件底部。

while True:
        # find devices
        print ("Searching for devices...")
     ❶ nearby_devices = bluetooth.discover_devices(lookup_names =
        True)
        sleep(5)
        # search for particular devices
        # person 1
     ❷ liz = bluetooth.lookup_name("C5:ED:FB:F5:BB:D7", timeout=5)
     ❸ if (liz != None):
        ❹ print ("Test 1")
        ❺ led1.on()
     ❻ else:
            led1.off()
            pass

        # person 2
        sarah = bluetooth.lookup_name("C5:ED:FB:F5:BB:D7", timeout=5)
        if (sarah != None):
            print ("Test 2")
            led2.on()
        else:
            led2.off()
            pass

LISTING 9-10 搜索你的前两个地址

首先,添加代码以检测附近的蓝牙设备并创建一个已找到的地址列表 ❶。这次你不需要打印这些数据,所以可以注释掉打印语句。

然后搜索你之前找到的特定地址!与 IP 地址一样,将特定的蓝牙地址分配给一个变量(使用蓝牙地址所属人的名字作为变量名),查找蓝牙地址 ❷,并添加一个 5 秒的延迟,以允许设备发送和接收所需的数据。

下一行 ❸ 检查是否已发现与 LED 1(此处为liz)关联的目标地址,意味着该设备已出现在房间内。如果已找到该地址,则变量的值不为None

第❹行用于测试,可以从最终程序中注释掉。如果已找到蓝牙地址,状态板上的第一个 LED 灯将使用led1.on() ❺点亮。

如果在搜索过程中没有找到蓝牙地址,LED 灯将关闭,程序将继续进行下一个检查 ❻。

添加更多目标

要添加更多目标,请复制 Listing 9-11,并将其粘贴到你的程序中,改变蓝牙地址为你找到的另一个地址,同时记得更改变量名和 LED 编号。保持这些代码行的缩进级别不变。

        # person 3
        frank = bluetooth.lookup_name("C5:ED:FB:F5:BB:D7", timeout=5)
        if (frank != None):
            print ("Test 3")
            led3.on()
        else:
            led3.off()
            pass

LISTING 9-11 添加更多目标

如果设备较少,你可以在代码中使用更少的目标,或者可以多次使用你自己的蓝牙地址来测试电路和 LED 是否正常工作。

关闭程序

代码的结尾部分,显示在 Listing 9-12,设置了关闭按钮。

        #shutdown button
     ❶ if button.is_pressed:
         ❷ shutdown()
        else:
            pass

LISTING 9-12 设置关闭按钮

添加if button.is_pressed:代码来检查按钮是否被按下 ❶。如果按下,shutdown()函数将执行 ❷。这将关闭状态板,且树莓派将关闭。

这完成了使用蓝牙地址来触发状态板的代码。现在保存程序代码并测试它是否正常工作。你还可能希望通过使用“使用 Cron 自动启动程序”部分中的指示来编辑 crontab 文件,页面 155,让该程序在启动时运行,而不是 IP 地址查找器。为了确保准确性,请分别运行 IP 查找器或蓝牙查找器,而不是同时运行两者。

做得好!你已经使用了 Wi-Fi 和 IP 地址来追踪移动设备,或者通过蓝牙获取附近设备的信息。或者你可能两者都使用了!然后你创建了一个视觉状态板,只有当某个特定设备被定位时,LED 灯才会亮起,这意味着该设备的所有者可能就在附近。一旦你添加了设备所有者的照片或写下他们的名字,你就能轻松地看到谁在附近。

总结

一旦你的状态板开始运行,你可能想为这个项目添加更多功能。为了激发你的创造力,你可以尝试以下一些内容:

  1. 为每个目标分配不同的颜色。

  2. 添加更多 LED。

  3. 让 LED 闪烁。

  4. 构建一个更大的状态板。

  5. 在设备被检测到时播放声音。

第十章:魔法音乐盒

在这一章中,你将创建一个个性化的 MP3 播放器,我们称之为魔法音乐盒。你将使用树莓派的 PyGame 库来构建一个可以播放预加载音乐的 MP3 播放器,并通过扬声器播放,然后添加四个互动按钮:一个用来跳过歌曲,两个用来调节音量,还有一个电源开关来关闭播放器。

你将把所有硬件组合成一个精美的盒子,打造你的 MP3 音乐系统(图 10-1)。程序代码使用了 PyGame,这是你在第五章中使用的用于创建游戏的 Python 库。PyGame 允许你通过 Python 代码来添加和控制声音、图像和视频。在这个项目中,你将使用 PyGame 来控制 MP3 文件,启动和停止它们,并调整音乐的音量。

Image

图 10-1 完成的 MP3 魔法音乐盒及其扬声器

所需物品

以下是你完成项目所需的一些物品:

  • 树莓派

  • 4 个按钮

  • 8 组母对公跳线

  • 蓝牙扬声器或音频插孔扬声器

  • 焊接工具和电烙铁或导电漆

  • 小盒子(午餐盒、木盒、纸板盒或其他)

  • 电钻或螺丝刀(用来打孔)

  • USB 电池(可选)

选择扬声器类型

根据你使用的树莓派型号,你首先需要决定使用哪种方法来输出音频。低成本的选择是使用一个小型扬声器,连接到耳机插孔,并将扬声器隐藏在音乐盒内部。你可以在网上零售商或者本地商店寻找带有 3.5mm 音频插孔的便携式扬声器;所有 3.5mm 型号的扬声器都可以使用。

使用小型扬声器的缺点是它们会产生低质量的声音,只有树莓派 2、3、4 和 A+型号才有音频端口来支持它们。

另一种稍微贵一点的选择是使用蓝牙扬声器。这个扬声器通常具有高质量的声音,并且便携,所以你可以将扬声器放置在任何你想要的位置。树莓派 3、4 和 Zero W 型号具有内建的蓝牙功能。Pi Zero W 的另一个优势是它非常小巧,使得它成为最容易嵌入音乐盒的型号。如果你使用的是较老的树莓派型号,且有空余的 USB 端口,你可以通过购买一个支持蓝牙 4 和音频的标准 USB 蓝牙适配器,并将其连接到一个端口来增加蓝牙功能。

下表展示了适合各种 Pi 型号的扬声器选项。

型号 耳机插孔 蓝牙 优点 缺点
A+ 小型板;可通过 USB 端口支持蓝牙适配器 性能较慢
Pi 2 多个 USB 端口 板子较大
Pi 3B+ 良好的规格;比 Pi 4 便宜 板子较大
Pi 4 最快的处理器 比其他型号贵
Pi Zero W 最小的板;内建 Wi-Fi;可以远程上传 MP3 文件;价格便宜 性能较慢

构建魔法音乐盒

一旦你为电路板选择了扬声器,你就可以开始制作音乐盒了。你可以使用任何材料作为音乐盒的外壳——例如木质铅笔盒、塑料午餐盒或旧的麦片盒。要注意,你需要在材料上打孔;因此,如果你没有工具钻孔(例如木材),可以选择纸板外壳。

在开始项目之前,你应该花时间安排按钮、扬声器和树莓派在盒子内的位置,以找出合适的设置。你可以自定义设置,但要用常识。例如,在大多数音乐播放器中,音量控制按钮是并排放置的。

按钮接线

来制作按钮吧!以下是步骤:

  1. 连接电线: 选择一个按键和两根公对母的跳线,并找到公端。使用一些焊锡或导电漆,将公端连接到按钮的每个引脚上(图 10-2)。我建议你使用一根黑色电线连接其中一个引脚,以表示它是地线(GND)。按照这个方法连接其余三个按钮。

    这些按钮将构成你将连接到树莓派的 MP3 播放器的主要控制。

    Image

    图 10-2 连接每个按钮的电线

  2. 接线按钮: 对于每个按钮,将黑色电线(地线)连接到树莓派的任一 GND 引脚,然后将另一根电线连接到下表所示的 GPIO 引脚。

    引脚 按钮
    GPIO 7 下一首歌(play_skip
    GPIO 25 音量增大
    GPIO 8 音量减小
    GPIO 17 关机

    你可以使用位于物理引脚 6、9、14、20、25、30、34 和 39 的任意一个 GND 引脚(图 10-3)。有些引脚可能更容易让电线接触到,或者在盒子内部布线时能保持更整洁的布局。

    Image

    图 10-3 将每个按钮接线到树莓派

  3. 打孔: 如果你使用的是木质或塑料盒子,你需要钻孔以安装按钮。孔的大小取决于按钮的尺寸(图 10-4)。首先打一个小孔,试着安装按钮,然后再调整孔的大小。如果你使用的是木盒,可以请成人帮忙钻孔。如果你使用的是纸板盒,可以用锋利的铅笔或钢笔戳孔。决定按钮的放置位置后,钻或戳四个足够大的孔,以容纳每个按钮。Image

    图 10-4 在盒子上打孔

  4. 安装按钮: 你这样做的方式取决于你使用的按钮和盒子的类型。许多按钮的顶部有一个小圆形边缘,放置在盒子孔的外侧,两个灵活的夹子固定按钮的主体。这种类型的按钮通常被称为街机按钮。对于其他类型的按钮,你可以使用胶水或双面胶带将按钮固定在适当位置,但只有在你确认程序和硬件工作正常后再进行此操作

    在将树莓派固定到盒子内部之前,先玩一下树莓派的布局,以确保电线和按钮能够适合。 图 10-5 展示了一个布局示例,你也可以把电池隐藏在盒子里。注意,你仍然可以访问树莓派,因为最好在固定树莓派和其他部件之前编写和测试程序。这样,如果需要调整,你仍然可以访问按钮和电线。

  5. 装饰盒子: 装饰并个性化盒子的外观。你可以添加颜色、贴纸、音乐播放器的使用说明或任何你喜欢的东西。

Image

图 10-5 准备内部硬件布局

连接扬声器

要听 MP3 文件的声音,你需要设置音频输出。根据你使用的是耳机插孔还是蓝牙扬声器,设置方法不同。启动树莓派并连接显示器、键盘和鼠标。

使用耳机插孔连接扬声器

如果你使用的是音频插孔,通过标准的 3.5 毫米插孔电缆将扬声器连接到树莓派的黑色耳机插孔,该插孔位于 HDMI 端口旁边。插入树莓派并启动它;加载 Pi 桌面。找到并点击桌面顶部的音频图标 。从音频输出下拉菜单中选择模拟。现在,所有音频将通过你的扬声器播放。

使用蓝牙扬声器

如果你正在使用蓝牙扬声器,请通过点击屏幕右上角的蓝牙图标来开启树莓派上的蓝牙软件。然后打开你的蓝牙扬声器。你需要在扬声器上启用设置,使其可以被发现。再次点击蓝牙图标,从下拉菜单中选择添加设备。树莓派将尝试查找所有支持蓝牙的设备,包括你的扬声器。

当你的树莓派找到扬声器时,它将出现在弹出窗口中。从列表中选择它,然后点击配对按钮,以建立树莓派与蓝牙扬声器之间的连接。

一旦连接确认,点击音频图标 ,从下拉菜单中选择你的蓝牙扬声器。两个设备将再次尝试配对并建立连接。扬声器设置完成后,你的树莓派将始终自动找到并连接到该扬声器。

编写魔法音乐盒代码

让我们编写代码来播放音乐并设置按钮功能。你将使用 PyGame 的音频混音工具来控制 MP3 文件的播放和音量。按钮功能通过gpiozero库来控制,该库提供了简单的代码来触发事件。

创建新文件夹

首先,你需要创建一个文件夹,用来存储程序代码和你想要播放的 MP3 音乐文件。在终端中,输入以下命令:

pi@raspberrypi:- $ mkdir MP3

然后通过输入以下命令切换到MP3文件夹:

pi@raspberrypi:- $ cd MP3

将所有 MP3 音乐文件传输并保存到MP3文件夹中。然后打开一个新的 Python 文件,并将其保存到MP3文件夹中,命名为music_box.py

导入模块和库

现在,你需要导入程序中将要使用的所有模块和库。输入 Listing 10-1 中的代码开始。

❶ import glob, time, pygame

   from gpiozero import Button
❷ from subprocess import check_call
   from signal import pause
   from pygame.locals import *

❸ pygame.init()
❹ pygame.display.set_mode((100, 100))

❺ global the_song
   global level
   global songs_found
   global number_of_songs

LISTING 10-1 设置导入和全局变量

导入globtimepygame❶。你已经熟悉了time模块。你将使用glob模块来搜索 MP3 文件名,并且将使用pygame库来控制 MP3 文件的播放和音量。

然后从gpiozero库中导入Button类,它允许你在每个按钮按下时触发事件。接下来,导入check_call()函数❷,你将用它来调用shutdown()命令以关闭音乐播放器。

PyGame 文件运行在一个独立的窗口中,尽管你不会使用该窗口,但你仍然需要通过pygame.init()❸来初始化它,以便使用 PyGame。然后你需要定义该窗口的大小。因为你不会与它交互,所以可以将其设置为非常小——比如 100 × 100 像素❹。

接下来,创建四个变量❺:the_song保存当前播放的 MP3 文件名,level保存音量级别,songs_found保存 MP3 歌曲的文件名,number_of_songs保存歌曲的总数。这些变量是全局变量。全局变量是在函数外部声明的,这意味着 Python 程序可以提取存储在全局变量中的数据,并在程序的其他地方使用。

将按钮分配到 GPIO 引脚

接下来,添加 Listing 10-2 中的代码,设置shutdown()函数,并将每个按钮分配到一个 GPIO 引脚。

❶ def shutdown():
   ❷ check_call(['sudo', 'poweroff'])

   ### Buttons for control ###
❸ play_skip = Button(7)      # begin the music green
   volume_up = Button(25)    # volume up blue
   volume_down = Button(8)     # volume down yellow
   shutdown_btn = Button(17, hold_time=2) # shutdown

❹ the_song = 0 # as first song is in position 1
❺ playing_songs = True

LISTING 10-2 定义按钮和引脚

定义shutdown()❶函数,使用check_call()函数调用将关闭 Pi 的 Python 程序❷。这个函数让你在没有显示屏的情况下也能关闭音乐盒!

接着,创建每个按钮的变量,分别是play_skipvolume_upvolume_downshutdown_btn。然后将每个变量分配给连接到该按钮的 GPIO 引脚编号❸。如果你使用了与本示例中不同的引脚编号,确保更改括号中的值。

请注意,在将 shutdown_btn 分配给其 GPIO 编号后,你指定了按住时间,即在触发 shutdown() 函数之前,按钮需要按住的秒数❷。(在此示例中,按住时间为 2 秒。)添加按住时间可以减少误操作,当你不小心按下按钮时,MP3 播放器不会被意外关机。

接下来,创建两个变量。the_song 变量❹ 包含当前播放歌曲在歌曲列表中的位置。列表从第一个位置的歌曲(位置 0)开始。playing_songs 变量指定是否有歌曲在播放❺。程序使用此变量来搜索并播放 MP3 文件。将其设置为 True

构建歌曲播放列表

程序的下一部分使用你之前导入的 glob() 函数查找你保存在 MP3 文件夹中的 MP3 音乐文件。此代码,见 Listing 10-3,构建了一个包含所有歌曲标题的列表,程序使用这个列表来加载并播放每首歌曲。记住,文件必须保存在与 Python 程序文件相同的文件夹中。

❶ def find_mp3_files():
       global songs_found
       global number_of_songs

  ❷ mp3_files_playlist = glob.glob('*.mp3')
  ❸ songs_found = mp3_files_playlist
  ❹ print ("I have found the following song", songs_found)
  ❺ number_of_songs = len(songs_found)
  ❻ print ("there are", number_of_songs, "songs")

   #### set volume ###
❼ level = 0.10
❽ pygame.mixer.music.set_volume(level)

LISTING 10-3 查找歌曲

第一行定义了 find_mp3_files() 函数,该函数包含查找每个 MP3 文件并存储文件名的代码❶。因为你稍后需要在程序中使用此函数收集的信息,所以从之前导入了两个全局变量。这些变量允许你将歌曲详细信息和歌曲数量的数据传递到下一个函数。

然后创建另一个变量 mp3_files_playlist,它使用 glob() 函数搜索所有以 .mp3 扩展名结尾的文件❷ 并将它们存储在该变量中。

接下来,将 MP3 文件名的列表复制到一个新变量 songs_found ❸ 中。这样,原始列表会保存在 mp3_files_playlist 变量中,并且你可以编辑新列表而不影响旧列表。如果你将新音乐添加到 MP3 文件夹,每次运行程序时,它都会被识别并添加到歌曲列表中。

打印所有 MP3 文件的名称❹ 让你拥有歌曲标题列表。仅当文件名与歌曲名称相同才有效。例如,如果歌曲名为“Life On Mars”,你需要将 MP3 文件保存为 life_on_mars.mp3。一旦你将 MP3 播放器与屏幕断开并以无头模式运行,你将看不到 MP3 文件的打印输出,但执行此步骤对测试很有帮助。

因为你希望 MP3 播放器播放列表中的所有歌曲,所以你需要计算歌曲的数量❺ 并查看打印出来的数字❻。每次你添加或删除播放列表中的歌曲时,都必须执行此步骤,因为这会改变列表的长度。

你还需要为音量控制按钮设置初始音量级别。该值在 0 到 1 之间,0 表示无声,1 表示最大音量。音量范围取决于你使用的扬声器类型。暂时将初始音量设置为 0.10,即最大音量的十分之一❼;如果音量太大或太小,你可以稍后调整。代码的最后一行指示pygame设置音量❽。

创建播放歌曲的代码

清单 10-4 创建了主函数,控制神奇音乐盒的按钮。

   ### Main code for playing and changing songs ###
❶ def play_mp3_songs():
      global the_song
      global level
      global songs_found
      global number_of_songs   
   ❷ if playing_songs == True:   
      ❸ while the_song < number_of_songs:
            ### Play a song ###
         ❹ pygame.mixer.music.load(songs_found[the_song])
         ❺ pygame.mixer.music.play()
            print ("Playing Song")
         ❻ the_song = the_song + 1
            print ("the song number is", str(the_song))

         ❼ while pygame.mixer.music.get_busy():
            ❽ pygame.time.Clock().tick(10)  # waits for song

清单 10-4 神奇音乐盒播放代码

需要注意缩进级别,因为接下来的大部分代码都在play_mp3_songs()函数内。记住,如果你遇到困难,可以从www.nostarch.com/raspiforkids/下载代码。

定义一个新函数play_mp3_songs()❶,该函数包含主程序代码。接下来,添加你之前创建的四个全局变量:the_song用于控制正在播放的 MP3 文件,level用于调节音量,songs_found用于访问 MP3 文件名列表,number_of_songs

创建一个while循环❷,让神奇音乐盒继续播放歌曲,直到播放列表中的最后一首歌。

然后,确保当播放器播放到最后一首曲目时停止播放❸。如果正在播放的是 20 首歌中的第 4 首,播放列表会继续播放,因为 4 小于歌曲总数。一旦播放列表播放到第 20 首,它将从头开始循环。稍后,你将使用程序的这一部分跳过歌曲并控制音量。

代码加载即将播放的 MP3 文件❹。因为你之前将the_song设置为 0,所以 PyGame 会加载 MP3 文件列表中索引为 0 的第一首歌曲。加载后,程序使用代码pygame.mixer.music.play()❺来播放该文件。打印当前播放的歌曲名称,确保它与正在播放的歌曲一致。

接下来,将the_song的值增加 1❻。这告诉程序每次循环时选择列表中的下一首歌。

总结一下,程序选择第一首歌并开始播放。当当前歌曲播放结束后,程序会选择下一首歌播放(或者如果你按下盒子上的物理“下一首歌”按钮)。在此之前,程序会继续播放当前歌曲,并检查 PyGame 音乐混音器是否正在忙碌❼。你使用 PyGame 时钟❽来阻止 PyGame 播放下一首歌,直到当前歌曲播放完毕。这是使用wait()sleep()函数的替代方法,因为每首歌的时长不同。

编写“下一首歌”按钮的程序

现在,添加 Listing 10-5 中的代码,这让你能够跳到下一首歌曲。这段代码从前一段代码继续,顶部行与 Listing 10-4 最后一行的缩进相同。以此缩进为指导来缩进其余的代码。

                ### Change the song ### 
             ❶ if play_skip.is_pressed:
                ❷ time.sleep(0.5)
                   print ("End of Songs")
                ❸ pygame.mixer.music.stop()
                ❹ break

LISTING 10-5 跳过歌曲

代码检查是否有人按下了下一首歌曲按钮❶,这个按钮之前命名为play_skip。你还添加了一个短暂的延迟❷,以便 PyGame 可以注册按钮按下事件。否则,程序可能会崩溃。

如果有人按下了按钮,PyGame 使用pygame.mixer.music.stop()❸函数来停止播放音乐。

一旦你停止了当前的歌曲,你希望程序返回到开始的地方,在那里while the_song < number_of_songs条件检查是否还有歌曲待播放。为此,你可以跳出循环,这样 PyGame 混音器就不再忙碌❹。如果还有歌曲,PyGame 将播放下一首。

增加音量

添加 Listing 10-6 中的音量控制按钮代码。你从增加音量的按钮开始,这个按钮之前命名为volume_up

                ### Change volume up ###
             ❶ if volume_up.is_pressed:
                ❷ if level < 1:
                   ❸ level = level + 0.10
                      print (level)
                   ❹ pygame.mixer.music.set_volume(level)
                ❺ else:
                      pass
                      print ("top volume")

LISTING 10-6 增加音量

如同在 Listing 10-5 中所示,首先检查按钮(volume_up)是否被按下❶。如果按下了,程序需要检查当前音量是否已达到最大值。它通过检查当前音量是否小于或等于 1❷来实现,1 是最大音量。如果音量小于 1,你可以让程序通过增加 0.10 来提高音量❸。

如果程序刚开始,音量为 0.10。按一次volume_up会增加 0.10,将音量提高到 0.20❹。

你为条件“不满足‘小于一’”时编写了响应代码,这个条件是在音量达到最大时。使用else语句❺,然后输入pass,以保持音量处于最大水平。

降低音量

添加 Listing 10-7 中的代码,使用volume_down来降低音量。

                ### Change volume down ###
             ❶ if volume_down.is_pressed:
                ❷ if level > 0:
                   ❸ level = level - 0.10
                      print (level)
                      pygame.mixer.music.set_volume(level)
                ❹ else:
                       pass
                        print ("bottom volume")

LISTING 10-7 降低音量

这段代码类似于增加音量的代码。你检查是否按下了volume_down按钮❶。如果按下了,检查当前音量水平,如果音量水平大于 0❷,则从当前音量减去 0.10❸。

再次使用else语句❹来pass按钮按下事件,如果音量为 0,因为在这种情况下,无法将其调低。

关闭魔法音乐盒

Listing 10-8 中的代码允许你关闭音乐盒。当你按下电源按钮或播放完所有歌曲时,音乐盒将停止播放。

             ❶ shutdown_btn.when_held = shutdown

 ❷ else:
    ❸ print ("end of playlist")

LISTING 10-8 关闭音乐盒

添加电源按钮的代码❶。然后使用else语句❷,当所有歌曲播放完毕时,也会关闭音乐盒,并打印end of playlist❸。

结束程序

你已经定义了执行所有工作的三个函数。现在通过使用清单 10-9 来调用这些函数,完成程序的编写。

   ### Main Program ###
❶ find_mp3_files()
❷ button.wait_for_press()
❸ play_mp3_songs()

清单 10-9 调用魔法音乐盒的三个函数

find_mp3_files() ❶ 函数在程序运行时会定位所有的 MP3 文件。所以,如果你添加了新的 MP3 文件,函数将会找到它们并将它们添加到播放列表中。

button.wait_for_press() ❷ 函数为按钮添加了用户交互功能,当你按下按钮时,魔法盒会做出相应反应。

play_mp3_songs() ❸ 函数运行主程序循环,检查是否有 MP3 文件可以播放,然后播放每一首。它还会检查是否有按钮被按下,并作出响应。

运行你的程序

是时候测试你的程序了!如果你使用的是蓝牙扬声器,请检查它是否与树莓派配对。如果你使用的是通过音频插孔连接的扬声器,请确保它已正确连接。然后保存程序并按F5运行它。

程序应该定位到文件夹中的所有 MP3 文件并建立歌曲列表。然后,它会播放列表中的第一首歌。按下下一首按钮切换歌曲。接着,试试音量按钮。按下关机按钮停止音乐播放并关闭你的树莓派。

如果你的 MP3 播放器无法正常工作,检查以下错误:

  • 音量是否过低或静音?

  • 音频设置是蓝牙还是耳机插孔?

  • 蓝牙扬声器是否已配对?

  • MP3 文件是否保存在与 Python 代码相同的文件夹中?

  • 声音文件是否以.mp3扩展名结尾?

  • 按钮是否连接到程序代码中使用的 GPIO 引脚号?

自动启动 MP3 播放器

作为最后的点睛之笔,让我们设置你的魔法音乐盒在每次连接电源时自动启动并播放音乐。根据你使用的是耳机插孔还是蓝牙扬声器,你可以通过两种方式来实现。

耳机插孔选项

如果你使用耳机插孔,你需要使用 cron,这是你在第九章中使用的基于时间的任务调度工具。cron 程序使你能够在特定的时间自动运行某个程序。

使用 cron 时,你需要创建指令来说明你希望在哪个事件发生时运行,以及何时运行这些事件。你在crontab文件中执行此操作,该文件包含要运行的程序或事件的指令,最重要的是,何时运行这些事件。

打开终端并输入以下命令以打开 cron 控制台:

pi@raspberrypi:- $ sudo crontab –e

控制台会提供三种编辑 cron 文件的方法。选择选项 2 并按 ENTER 键使用 nano 文本编辑器打开 crontab 文件。滚动到 crontab 文件的底部,找到空白处。然后添加以下代码行:

@reboot sudo python3 /home/pi/MP3/music_box.py &

该命令表示每次树莓派重新启动时,它应以超级用户模式运行 Python(这允许运行任何程序,就像管理员模式一样),打开/home/pi/MP3文件夹,并执行名为music_box.py的程序。

如果你为代码文件命名为其他名称,替换music_box.py为你给它命名的文件名。还要通过打开存储music_box.py程序的文件夹并查看文件路径,来检查文件夹路径是否正确。

代码行末的&符号告诉你的程序在后台运行,这样你就可以同时使用树莓派进行其他任务。

一旦你检查过命令详情并确信它们正确,按 CTRL-X 保存并退出 crontab 文件。现在,每次你打开或重启树莓派时,crontab 文件都会运行,启动魔法音乐盒。

如果你想停止程序自动运行,可以再次从终端打开 crontab 文件,输入以下命令:

pi@raspberrypi:- $ crontab –e

然后删除你之前添加的代码行。保存文件并重启。

由于 MP3 播放器设计为无头模式运行,你不需要让树莓派启动到桌面并显示背景屏幕和图标,也不需要使用鼠标。启动到桌面会因为这些不必要的项目而变得更慢,同时占用更多的内存和处理能力。你看不到桌面,所以加载它是没有意义的。相反,你将配置树莓派从命令行启动。打开终端窗口并输入以下命令:

pi@raspberrypi:- $ sudo raspi-config

接下来,选择第三个选项,启动选项,按 ENTER 键。然后选择B1 桌面 / CLI选项,选择B2 控制台自动登录选项,选择,并按 ENTER 键 (图 10-6)。你需要重新启动你的树莓派。

Image

图 10-6 启动到命令行并自动登录

选择完成选项,系统会提示你保存配置文件并重启。选择,树莓派将重新启动。随着树莓派的启动,魔法音乐盒也会加载。

蓝牙选项

如果你使用的是蓝牙音响,仍然可以在启动时自动启动程序,但这稍微有些复杂,因为你需要配置多个设置。

你还需要向.bashrc文件中添加一行文本,以触发 Python 运行你的程序。.bashrc文件是一个每次树莓派启动时都会运行的 shell 脚本。

在你更改这个文件之前,确保蓝牙音响已经与树莓派配对。为了让 Python 程序访问音响,你需要先启动到图形界面(GUI)桌面,而不是命令行模式。但是,当 PyGame 运行时,它会让屏幕变成空白。这没问题,因为魔法音乐盒不需要屏幕。但是,这也让解决程序中的任何问题变得困难,因为你看不到或无法访问程序代码。

为了解决这个问题,打开 Python MP3 播放器程序,并在导入库的代码行后添加time.sleep(30),为程序启动添加一个 30 秒的延迟。这给了你时间编辑或调整设置代码,如果它没有正常工作。只要记得在测试程序时你已经添加了延迟,这样就不会误以为程序没有运行!

如果在添加了 30 秒延迟后,Python 已经加载了 PyGame,并且你仍然需要访问树莓派,你需要通过 SSH 远程访问它。但是在添加自动启动脚本之前,请在配置设置中启用 SSH。有关如何操作的提醒,请参阅第 25 页的“通过 SSH 访问树莓派”。

现在你准备好创建启动脚本了。打开终端窗口,输入以下命令打开.bashrc文件:

pi@raspberrypi:- $ sudo nano .bashrc

滚动到文件底部,输入 python3,后跟 MP3 程序的文件路径——例如,python3 /home/pi/MP3/MP3_Player_BT.py(见图 10-7)。

Image

图 10-7 启动脚本

记得将示例文件路径替换为你程序的文件名和位置。保存文件,然后重启树莓派。它应该会加载你的 MP3 Python 代码。记得等待 30 秒,并确保你的蓝牙音响仍然配对连接。延迟结束后,屏幕会变黑。按下下一首歌按钮播放第一首歌。

如果你听不到音乐,但怀疑代码正常工作,尝试将扬声器或耳机插入耳机插孔。如果能听到音乐,就说明程序正常工作,问题出在蓝牙配对上。

如果遇到其他问题并需要编辑代码,你可以重启树莓派,或打开终端窗口并按 CTRL-X 或 CTRL-Z 暂停代码。然后检查各种设置。如果这不起作用,可以使用 SSH 访问树莓派并打开、编辑或注释掉.bashrc文件;然后重启。继续测试你的音乐盒,直到你确信硬件和程序代码正常运行。

汇总

一旦你完成了程序代码并确认它正常工作,你可以开始将硬件固定到你的盒子里。它可能看起来像图 10-8 所示。

Image

图 10-8 完成的魔术音乐盒

如果你使用的是简单的容器,比如午餐盒,将树莓派和电池放入盒子中并固定好盖子。如果你使用的是木盒,像图 10-8 所示,你可以使用双面胶带、固定膏或小尼龙螺丝将树莓派固定。

然后你可以将电线收纳起来,避免外界看到。你还可以开额外的孔来安置需要的电源或 USB 线。如果程序代码出错,最好的解决办法是取出 SD 卡,并将其插入另一台 Pi 设备。当问题解决后,再将 SD 卡放回原来的 MP3 播放器 Pi 中。

总结

恭喜!你已经建好了自己的独立魔法音乐盒。现在你可以为你的魔法音乐盒添加更多功能,比如以下这些:

  • 更多的 MP3 文件

  • 一个音乐播放器加载时闪烁的 LED 灯

  • 随着音乐播放闪烁的 LED 灯

  • 一个静音按钮

  • 额外的按钮,用于播放上一首歌曲、播放列表的片段,或者一个隐藏的彩蛋歌曲

  • 一个小型 LCD 屏幕,显示当前歌曲的名称

第十一章:自然盒子:运动感应相机

在本章中,你将构建并编写一个自然盒子相机,当它感应到运动时可以拍照。然后,你将把自然盒子放置在野外,拍摄当地的野生动物。你永远不知道——你可能会拍到一只鸟、一只獾,甚至是一只狐狸!

该盒子将包含一个运动传感器。每当动物接近时,传感器会触发内置的树莓派相机拍照。然后,树莓派会将每张图片上传到一个在线 Dropbox 文件夹,供你查看并与朋友分享。你可以让相机从早到晚一直工作,看看有哪些“访客”来到(图 11-1)。

图片

图 11-1 构建一个像这样拍摄动物照片的自然盒子。

你将需要的物品

以下是完成项目所需的几样物品:

  • 树莓派(你可以用任何树莓派型号来构建自然盒子。Pi 2、3 A+和 Zero W 非常适合,因为它们小巧,可以隐藏在较小的空间中。)

  • Pi 相机

  • 被动红外传感器(PIR)

  • 跳线

  • 透明塑料盒,用于容纳硬件

  • Dropbox 账户

  • USB 便携电池

  • 电钻

设置被动红外传感器

一个 PIR 传感器(图 11-2)更常被称为运动探测器。它探测从温暖物体和生物(如人类、动物,甚至是车辆)释放出的红外光,并测量光的变化。你可以编写代码来响应某些变化级别的检测,并触发相应的事件,如打开灯光、发出警报或自动打开门。

当物体经过 PIR 时,它会释放热量,改变周围的温度。PIR 感应到周围红外辐射的差异,并改变内部电压,这意味着它已经检测到某物。在传感器的圆顶内有小镜子,帮助 PIR 从远至 30 英尺的地方探测红外光的变化。

图片

图 11-2 一款 PIR 传感器

连接 PIR 传感器

你只需要三根电线就可以将 PIR 连接到树莓派。将 VCC PIR 引脚(提供电源)连接到树莓派板上方的 5V GPIO 引脚。图 11-3 中使用的是物理引脚 2 的 5V 引脚。将 OUT 引脚连接到树莓派上的 GPIO 引脚 4(物理引脚 7)。然后,将 PIR 上的接地(GND)引脚连接到树莓派上的接地引脚。

图片

图 11-3 连接 PIR 传感器

测试 PIR

让我们编写一个简单的程序来测试 PIR 是否正常工作。你的身体会散发热量,当你四处移动时,你会扰动并移动包围你身体的热量。Listing 11-1 中的代码初始化了 PIR 传感器,然后检查热量是否发生变化。如果 PIR 传感器检测到显著的变化,按照你设定的阈值,程序将指示它已经看见你了。打开你的 Python 编辑器,输入 Listing 11-1 中的代码。将其保存为PIR_test.py

❶ import time
   import RPi.GPIO as GPIO
   GPIO.setmode(GPIO.BCM)

❷ PIR = 4
   GPIO.setup(PIR, GPIO.IN)

   print ("Ready to find you")
   time.sleep(2)

❸ def Motion_Sensing(PIR):
       print ("We see you")

❹ try:
   ❺ GPIO.add_event_detect(PIR, GPIO.RISING, callback=Motion_Sensing)
   ❻ while 1:
          time.sleep(1)
❼ except KeyboardInterrupt:
       print ("Quit")
       GPIO.cleanup()

LISTING 11-1 使用 PIR 检测运动

导入time模块和RPi.GPIO模块,以控制和编写 GPIO 引脚的代码❶。因为有两种 GPIO 引脚编号系统,你需要通过设置模式为BCM来定义你使用的编号系统。

接下来,声明一个变量来定义你用于检测响应的 GPIO 引脚编号。这是你连接到 PIR 传感器的 GPIO 4 引脚❷。通过使用GPIO.IN告诉程序检查 GPIO 4 引脚的输入。

因为这是一个测试程序,打印一行表示传感器已准备好寻找你,然后在从 PIR 传感器获取数据之前提供一个短暂的时间延迟。这样你就有时间准备,开始移动或挥动手臂,从而触发传感器。

然后定义一个响应任何感应到的运动或变化的函数❸。在这个测试程序中,响应是一个简单的语句,表示你已经被看到。

接下来,程序尝试检测运动❹。你使用tryexcept方法来避免当 PIR 传感器工作不正常或读取到异常数据时引发错误。这样可以确保程序在 PIR 发生故障时仍然继续运行。

接下来,你从 PIR 传感器读取数据❺。这段代码检查 GPIO 4 引脚的电压是否上升。如果是,说明有东西触发了 PIR 传感器。所以你使用callback方法运行响应任何感应到的运动或变化的函数❸,并打印We see you

为了防止 PIR 传感器多次读取相同的输入,你添加了一个 1 秒的延迟❻。这样可以为 PIR 传感器重置提供足够的时间,然后再检查温度是否发生变化。记住,你是在寻找运动,而不是存在;因此,摄像头不需要始终保持活动状态。

最后,你需要一种停止程序的方法。你添加了except响应,它检查是否按下了键盘上的任何按键❼。如果检测到按键,它会打印Quit,重置 GPIO 引脚,并停止程序。

保存程序并按F5键运行它。你可以交替进行移动和保持静止的操作,确保 PIR 能够检测到你。如果你愿意,你可以将这变成一个游戏。你能保持如此静止,以至于不会触发 PIR 吗?你能进入一个房间而不被 PIR 探测到吗?

设置 Pi Camera

有了能够检测运动的 PIR 传感器,你就可以设置 Pi Camera 来捕捉任何触发传感器的影像。

连接 Pi Camera

如果你在第四章中完成了 Pi Camera 项目,你可能已经设置好了硬件。使用排线将相机连接到 Pi,如 Figure 11-4 所示,并记得确保 Pi Camera 在配置工具中启用。这需要你重新启动 Raspberry Pi。如果遇到问题或需要更详细的说明,请参阅第 16 页的“接口”部分。

Image

FIGURE 11-4 设置 Pi Camera

创建一个新文件夹来存储图片

为了保持自然盒子项目的有序性,你会希望将图片和程序文件存储在一个地方。通过打开终端并输入以下命令,创建一个名为Nature_Box的新文件夹:

pi@raspberrypi:- $ mkdir Nature_Box

创建一个项目文件夹可以防止你的主文件夹被成百上千的图片弄得乱七八糟。

编写测试代码

在连接了 Pi Camera 之后,你可以开始一个新的 Python 文件,或者修改 Listing 11-1 中的代码,让相机在 PIR 传感器检测到运动时拍照。你可以拍摄鸟类、猫或其他本地野生动物——甚至可能是狐狸——它们经过你的自然盒子设置时。输入 Listing 11-2 中的代码,并将其保存为Cam_PIR.py

❶ from time import sleep
   from picamera import PiCamera
   import RPi.GPIO as GPIO
   GPIO.setmode(GPIO.BCM)

   PIR = 4
   GPIO.setup(PIR, GPIO.IN)

❷ global Image_Number
   Image_Number = 0
   camera = PiCamera()

   print ("Ready to find you")
❸ sleep(2)

   def Motion_Sensing(PIR):
    ❹ global Image_Number

    ❺ camera.resolution = (1024, 768)

    ❻ camera.capture("Nature" + str(Image_Number) + ".jpg")
    ❼ Image_Number = Image_Number + 1

   try:
      GPIO.add_event_detect(PIR, GPIO.RISING, callback=Motion_Sensing)
      while 1:
       ❽ sleep(1)
   except KeyboardInterrupt:
       print ("Quit")
       GPIO.cleanup()

LISTING 11-2 每当 PIR 检测到运动时拍照

编辑程序代码,导入time模块中的sleep()函数,并导入picamera类❶。

然后,为了防止每张新图片覆盖之前的图片,创建一个变量来存储当前的Image_Number❷。将其设置为全局变量,以便稍后在主程序功能中使用这些数据,保存每个文件时使用不同的名称。将Image_Number变量设置为0,这意味着拍摄的第一张图片将命名为Nature0.jpg

然后将PiCamera()函数添加到camera变量中。将time.sleep(2)替换为更简洁的代码sleep(2)❸。这种写法更好,因为你输入的文本更少,从而减少了出错的机会。你可以在其他程序和项目中使用这种简洁的代码。它的作用相同:暂停程序 2 秒,在下一行代码运行之前创建一个小的延迟。

接下来,在Motion_Sensing(PIR)函数内,添加触发 Pi Camera 并拍照的代码。在❹处,你添加了在❷处创建的全局变量。然后你设置了相机的分辨率❺,并添加了捕捉图像的代码❻。

注意,捕获图像的代码❻将字符串"Nature"与当前设置为0Image_Number值❷结合起来。这会创建一个名为Nature0的文件,并加上.jpg文件扩展名,将图像保存为Nature0.jpg

然后,向当前的图片值❼添加1,这样程序将保存下一个文件为Nature1.jpg,接下来的是Nature2.jpg,以此类推,直到程序停止。你还将最后一行代码从time.sleep(1)更改为sleep(1)❽。

将程序保存在 Nature_Box 文件夹中,这样所有图片都会存储在那里,便于稍后访问。按 F5 运行程序。每当有物体触发 PIR 时,Pi 摄像头将拍摄一张照片并保存在文件夹中。你可以将你的自然盒子放在最珍贵的物品或巧克力储藏处附近。如果有人偷了你的东西,你将有盗窃者的照片证据。

如果 PIR 传感器触发过于灵敏,可以调整其灵敏度。在温暖的日子里,树枝的摆动可能会移动红外辐射,PIR 可能会感应到。如果盒子靠近道路,经过的汽车可能会改变周围的空气。

为了避免在这些情况下触发相机,找到 PIR 后面两个小旋钮(见图 11-5)(通常是橙色的)。一个是 延迟时间调整 旋钮,调整每次触发 PIR 后的重置时间。另一个是 距离调整 旋钮,用来增加或减少灵敏度;基本上,它决定了触发 PIR 所需的温度变化量。你可以调整这些旋钮,找到适合你自然盒子和当地环境的完美设置。

Image

图 11-5 调整 PIR 后部的灵敏度

从 Raspberry Pi 获取图片

现在你需要弄清楚如何检索你的图片。如果你将硬件放置在野外,直到你收回自然盒子之前将无法访问图片。根据你的设置或你想监测的野生动物类型,你可能希望将其放置几天。但等待这么长时间查看图片并不是理想的选择。解决方案是将你的自然盒子连接到互联网,这样你就可以使用文件共享网站 Dropbox 和一个简单的 Python 程序实时上传每张图片。你将能通过平板、笔记本电脑或手机远程访问这些照片。

设置 Dropbox 账户

如果你已经拥有 Dropbox 账户,可以跳过此步骤。如果没有,前往 Dropbox 网站 https://www.dropbox.com/,注册一个新账户。点击 创建账户 并填写注册表单(见图 11-6)。另外,你也可以使用 Google 账户信息进行注册。

Image

图 11-6 注册或登录 Dropbox

登录后,前往 Dropbox 开发者页面 (图 11-7),网址是 https://www.dropbox.com/developers/ 创建一个应用。

Image

图 11-7 访问 Dropbox 开发者页面

点击 创建你的应用 选项。Dropbox 会为你提供几个选项。当它提示你选择 API 时,选择 Dropbox API。然后选择 应用文件夹 作为访问类型。为你的应用命名;我将其命名为 Nature Box。同意条款和条件,并点击蓝色的 确认 按钮以访问下一个配置设置页面(图 11-8)。

Image

图 11-8 配置应用程序的设置

保持状态行设置为 开发,将开发用户行设置为 仅限你。这意味着只有你可以编辑该应用。你还可以编辑应用文件夹的名称。

忽略应用密钥和应用密钥行;此项目不需要这些。

选择 生成的访问令牌 选项,生成一个代码,使你的 Raspberry Pi 能够将图像文件发送到 Dropbox 应用。稍后你需要在 Python 程序中使用这个令牌。记住,这个令牌确保你的账户安全,因此不要与他人分享。

配置好 Dropbox 应用后,你现在可以使用它上传自然盒子图像。

安装 Dropbox Python 库

你需要安装一个 Python Dropbox 库,这样你就可以从 Python 程序代码访问你的 Dropbox 应用和文件夹。要安装所需的软件,请打开终端并输入以下命令:

pi@raspberrypi:- $  sudo pip3 install dropbox

然后,为了确保你使用的是最新的更新,输入以下命令:

pi@raspberrypi:- $  sudo pip3 install dropbox --upgrade
创建测试程序

在你将 PIR 和 Pi Camera 连接到 Dropbox 之前,你需要创建一个简单的程序来测试 Dropbox 应用和令牌是否正常工作。但首先,在 Nature_Box 文件夹中保存一张 JPEG 图像以供测试。在列表 11-3 中的程序里,我使用了一张可爱的松鼠图片(图 11-9),并将文件命名为 squirrel.jpg

Image

图 11-9 将图像保存到 Nature_Box 文件夹

打开你的 Python 编辑器,创建一个新文件,并添加以下代码。将其保存为 DB_uploader.py

❶ import dropbox

❷ token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"

❸ dbx = dropbox.Dropbox(token)

❹ with open("/home/pi/Nature_Box/squirrel.jpg", "rb") as file:
    ❺ dbx.files_upload(file.read(), '/squirrel.jpg', mute = True)

❻ print ("uploaded")

列表 11-3 测试你的 Dropbox 应用和令牌

导入 dropbox 模块 ❶。创建一个名为 token 的变量,并将你之前获得的应用令牌用引号括起来 ❷。接下来,创建另一个名为 dbx 的变量来存储 Dropbox 类 ❸,它使你的 Python 程序能够与你的 Dropbox 账户进行通信。

现在,你准备好定位并打开你想要上传的图像文件 ❹。通过使用 with open 打开图像存储的文件夹。接下来,从文件中读取并使用 Dropbox API 上传图像到你的账户 ❺。然后打印一个简短的确认信息 ❻,以便你知道文件上传完成。

运行程序

访问 Dropbox 网站,登录并导航到你的应用程序文件夹。然后返回到你的 Raspberry Pi,运行测试程序。测试将尝试上传你的图像文件(在此示例中是松鼠的图像),因此请检查你的 Dropbox 文件夹以查看上传情况(图 11-10)。

Image

FIGURE 11-10 上传到 Nature_Box 文件夹中的图像

编写最终的自然盒子程序

到目前为止,你已经创建了三个独立的程序:第一个测试 PIR 是否正常工作,第二个在 PIR 检测到运动时触发 Pi Camera,第三个则允许 Raspberry Pi 将图像上传到你关联的 Dropbox 文件夹。现在,让我们将你本章所学的所有内容结合起来,制作最终的自然盒子程序。

设置最终程序

保存并重命名你之前的 DB_uploader.py 文件,或者开始一个新的 Python 文件并将其保存为 NatureBox.py。然后输入 Listing 11-4 中的代码。

❶ import dropbox
   from time import sleep
   from picamera import PiCamera
   import RPi.GPIO as GPIO
   GPIO.setmode(GPIO.BCM)

LISTING 11-4 开始最终的自然盒子程序

导入 dropbox 模块 ❶,该模块允许你通过 Python 代码与 Dropbox 进行交互。还需导入常用的 sleep() 函数以增加小的延迟,然后导入 picamera 库,最后导入 Rpi.GPIO 模块以控制 PIR。接着,你需要将 GPIO 编号系统设置为 BCM

结合相机和传感器

接下来的代码部分,如 Listing 11-5 所示,结合了 PIR 代码和相机代码,所以当检测到运动时,Pi Camera 会被触发。它拍摄一张照片,然后创建并保存图像文件。前几行代码应该对你来说并不陌生,因为它们来自你创建的 Cam_PIR.py 测试程序。

PIR = 4

GPIO.setup(PIR, GPIO.IN)

global Image_Number
Image_Number = 0
camera = PiCamera()

print ("Ready to find you")
sleep(2)

def Motion_Sensing(PIR):
    global Image_Number
    print ("We see you")
    camera.resolution = (1024, 768)
 ❶ Image_Number = Image_Number + 1
    camera.capture("Nature" + str(Image_Number) + ".jpg")

 ❷ pic = ("Nature" + str(Image_Number) + ".jpg")

LISTING 11-5 捕捉并保存图像

给图像文件变量 ❶(你之前设置为 0)加 1,这样第一个保存的图像将命名为 Nature1.jpg

在 Pi Camera 捕捉图像后,它会保存图像文件,但不保存文件名。为了同时保存文件名,创建一个名为 pic ❷ 的新变量,将图像的文件名作为字符串存储。这意味着程序可以在稍后上传图像文件到 Dropbox 时访问它。

创建 try 和 except

接下来,在 Listing 11-6 中,你将使用 tryexcept 方法。try 部分尝试运行负责将图像文件上传到 Dropbox 的代码。如果由于某些原因自然盒子无法访问 Dropbox——比如网站不可用,或自然盒子不再在线——程序将通过 except 部分跳过这一部分并继续运行,而不会停止或产生错误。

   try:
     ❶ token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
     ❷ dbx = dropbox.Dropbox(token)
        print ("ready")
     ❸ with open("/home/pi/Nature_Box/" + pic, "rb") as file:
         ❹ dbx.files_upload(file.read(), '/Nature%s.jpg' 
            %(Image_Number), mute = True)
            print ("uploaded")
            file.close()
 ❺ except:
        pass
        print("failed")

LISTING 11-6 保存图像文件名

添加你的 Dropbox 令牌❶并将其分配给一个名为dbx的变量❷,你用它来验证账户凭据并启用与 Dropbox 的交互。在❸处,通过将图片的文件夹位置和pic变量(它保存最新图片的文件名)结合起来,打开相机刚拍摄的图片。代码rb将图片作为二进制文件打开,并以字节形式读取其内容,而不是字符串。这一点很重要,因为文件包含的是图像,而不是文本。然后将这些数据赋值给一个名为file的变量,就像存储在树莓派上的文件中构成图像的实际数据。

程序从file读取字节并尝试上传数据❹。代码'/Nature%s.jpg' %(Image_Number)将文件名添加到 Dropbox,因为你上传的二进制数据本身不包含文件名。代码mute = True会防止用户收到上传通知。你可以想象,收到成百上千条上传文件的通知会是多么让人分心。

上传后,关闭文件:打开的文件会占用处理器资源,并且可能会减慢你的树莓派,尤其是在文件很多的情况下。最后,添加程序的tryexcept方法的except部分❺。如果程序无法上传图片,它会通过跳过并打印通知来响应,然后进入循环,重置 PIR 并等待传感器再次触发。图像仍然保存在树莓派的Nature_Box文件夹中,只是不会被上传。

运行运动传感器

程序的最后部分,如 Listing 11-7 所示,与你之前创建的PIR_test.py程序完全相同。它指示 PIR 等待输入,并在接收到输入时运行Motion_Sensing()函数。

try:
    GPIO.add_event_detect(PIR, GPIO.RISING, callback=Motion_Sensing)
    while 1:
        sleep(1)
except KeyboardInterrupt:
    print ("Quit")
    GPIO.cleanup()

LISTING 11-7 结束程序

一旦你复制了完整的程序,保存并运行它。通过四处移动触发 PIR 传感器来测试它是否工作。打开你的互联网浏览器并导航到你的 Dropbox 文件夹,查看上传的图片(图 11-11)。在将你的自然盒子投入实际使用之前,你可以通过有趣的方式测试你的程序:和朋友竞争,看谁能不触发传感器进入房间,或者将你的自然盒子放在前门,检查是谁在敲门。在决定开门之前,可以先在 Dropbox 文件夹中寻找证据!

Image

FIGURE 11-11 当你运行程序时,你的照片应该出现在 Dropbox 文件夹中。

如果自然盒子没有正常工作,考虑以下常见错误:

  • 程序中使用的 Dropbox 令牌是否正确?

  • 树莓派是否已连接到互联网?否则,所有图像文件将保留在Nature_Box文件夹中。

  • 代码中的文件夹名称是否与实际文件夹名称匹配?

  • 代码中的文件名是否与实际的文件名匹配?

  • PIR 的灵敏度是否调整得当,以便适当的动作触发它?

上传到 Dropbox 时,无法覆盖相同的文件。因此,每次在首次运行程序后再次运行时,请确保你已将第一批图像文件移动到其他文件夹或将其删除。

自动启动程序

由于你将在户外设置 Nature Box,因此不会连接显示器。每次插入 Raspberry Pi 电源时,程序都需要执行。回想一下,我在第七章、9 章和 10 章中已经介绍过如何自动启动程序。现在,你将为 Nature Box 程序再次设置。

你将使用 cron 来自动运行程序,通过在 crontab 文件中创建指令,指定你想运行的事件及其执行时间。如果你需要复习如何使用 cron,请参考第九章。

打开终端并输入以下命令以打开 cron 控制台:

pi@raspberrypi:- $ sudo crontab –e

cron 控制台为你提供了三种编辑 crontab 文件的方法。选择第二个选项并按回车键,使用 nano 文本编辑器打开 crontab 文件。

滚动到 crontab 文件底部,找到空白区域。然后添加以下代码行以自动运行程序:

@reboot sudo python3 /home/pi/Nature_Box/NatureBox.py &

该命令表示每次 Raspberry Pi 重启时,应该以超级用户模式运行 Python,打开/home/pi/Nature_Box文件夹,并执行你的NatureBox.py程序。

如果你将代码文件命名为其他名称,请将NatureBox.py替换为你的文件名。还要通过打开存储NatureBox.py程序的文件夹并注意文件路径,检查文件夹路径是否正确。

代码行末尾的&告诉你的程序在后台运行,这样你就可以同时使用 Raspberry Pi 做其他任务。

检查命令详情并确保它们正确无误后,按 CTRL-X 保存并退出 crontab 文件。现在,每次打开或重启 Raspberry Pi 时,crontab 文件都会运行,启动 Nature Box。

如果你想停止程序的自动运行,请从终端重新打开 crontab 文件,并删除你添加的代码行,如下所示:

pi@raspberrypi:- $ crontab –e

然后保存文件并重新启动。

Nature Box 设计为无需显示器即可运行,因此你无需让 Raspberry Pi 启动到桌面界面并显示背景墙纸和图标。因为你不会看到桌面,所以不需要加载它。相反,你可以配置 Pi 启动到命令行界面。打开终端窗口并输入以下命令:

pi@raspberrypi:- $ sudo raspi-config

选择第三个选项,启动选项,然后按回车键。接着选择B1 桌面 / CLI选项,再选择B1 控制台选项。点击保存选项,然后重新启动 Raspberry Pi。

设置完成后,选择<完成>选项并按下 ENTER 键:系统会提示你保存配置文件并重启。按来重启你的 Pi。当树莓派启动时,自然盒子会加载。在你把盒子放到野外之前,先进行一个快速测试,确保它正常工作并将图片上传到你的 Dropbox 账户。

整合所有部分

现在你有了一个可用的程序,可以设置好盒子并将其放置在你的花园、当地公园或其他地方。使用塑料盒作为盒子的外壳是理想的选择(图 11-12),因为你可以用胶带密封它,它会保持相当的防水性——不过我不建议你将自然盒子放入池塘中或在雷雨天气下将其放置在外面。

准备自然盒子相当简单:

  1. 使用电钻打一个小孔,以便相机镜头可以穿透。

  2. 使用电钻打一个更大的孔,让 PIR 可以穿透。最好先钻一个小孔,然后逐渐扩大钻头的尺寸,这样就不会裂开塑料。如果你使用的是木盒,应该没有问题。孔只需要大到足够让 PIR 穿透即可。Image

    图 11-12 完成的自然盒子示例

  3. 使用挂钩胶或双面胶带将 Pi Camera 和 PIR固定在你的盒子内。或者,Pi Camera 和一些 PIR 上有小螺丝孔,你也可以用它们进行安装。

    祝你自然狩猎愉快!

总结

干得好,做得非常棒——现在你拥有一个可以感知运动的自然盒子,触发 Pi Camera 拍照。然后,这张图片会被上传到你的 Dropbox 账户,你可以查看并分享。现在试试以下操作:

  • 使用你的设置放在卧室中,看看谁进来了,并希望能看到他们出去。

  • 将它放在饼干罐或巧克力藏匿处附近,以便收集拍摄证据,查看是谁一直偷吃你的东西。

  • 购买 Pi NoIR Camera,它可以在夜间或黑暗中拍摄照片。

第十二章:智能插座用于智能家居黑客

在本章中,你将学习如何通过手机远程控制电子设备。为此,你将结合树莓派和 Energenie 智能插座,后者允许你通过手机上的单次点击来控制流经电子设备的电力。

你将能够使用完成的项目打开卧室的台灯、电热水壶、电视、派对灯或任何其他可以通过简单插入插座打开的设备!图 12-1 显示了 Energenie 智能插座。该插座由 Pi-mote 控制,Pi-mote 是一块小型电路板,连接到你的树莓派上,允许你打开或关闭插座。

Image

图 12-1 连接到树莓派的 Energenie 智能插座和远程控制板(Pi-mote)

注意

该项目的手机控制部分仅适用于安卓设备

所需物品

这是项目所需的物品:

  • 树莓派

  • 手机或平板电脑(安卓系统)

  • Energenie Pi-mote

  • Energenie 智能插座

  • 英国至美国适配器插头

在美国,你可以通过搜索“Energenie Pi-mote control with two remotes”或者使用产品代码 ENER002-2PI,在多个网站上找到 Pi-mote 和插座。

Energenie 直接向 220-Electronics 供应产品 (www.220-electronics.com/pi-mote-remote-control-outlet-starter-kit-with-2-sockets.html),该公司位于美国并且全球配送。

插座也可以在以下零售商处购买:

如果在其他地方找不到 Pi-mote,你可以尝试 eBay。

在英国,你可以直接从 Energenie 购买 Pi-mote,网址为 energenie4u.co.uk/。Pimoroni (shop.pimoroni.com/)和 Amazon 也有销售。

设置 Energenie 远程插座

随着技术的发展,你可以通过手机或平板电脑控制越来越多的电器设备,而无需离开座位。例如,你可以通过网站调节中央供暖,通过应用程序打开烤箱,并在汽车接近时自动打开车库门。

许多此类控制设备使用 继电器,它是一个可以开关电路的开关。电路 是指电流在连接的一组电线中流动,这些电线连接到硬件设备(例如电机、灯泡或蜂鸣器)。Energenie 公司创建了一套安全且易于使用的继电器插座,你可以直接通过树莓派控制它们。通过打开或关闭这些继电器,你可以打开或关闭连接到插座的电器或硬件。

要使用 Energenie 插头,你需要将一个小型控制板 Pi-mote 插入 Raspberry Pi,这样你就可以切换插头来开关电源。该插头的工作范围可达 30 米,并且可以穿透门、墙壁和天花板。这个项目听起来很有趣吗?让我们开始吧。首先,你需要确保插头正常工作:

  1. 测试插头: 将你的 Energenie 插头插入电源插座。打开插座给 Energenie 插头供电。将灯泡插入 Energenie 插头并打开灯泡。按下插头上的绿色按钮以打开插头。你应该会听到一个独特的 咔嗒 声音,这是继电器在关闭电路时发出的声音。当你按下绿色按钮时,继电器闭合并连接插头内部的电路,电流流向灯泡。由于灯泡已经开启,灯泡会亮起。

  2. 连接 Pi-mote: 确保你的 Raspberry Pi 已关闭,并且已断开 Raspberry Pi 的电源。智能插头的电源是否开启都不影响。将 Pi-mote(L 形板)连接到 GPIO 引脚的顶排,使板子的 L 形部分朝内,面向 HDMI 接口,如 图 12-2 所示。将板子牢牢压上,使其与 GPIO 引脚接触并固定好。 Image

    图 12-2 将 Pi-mote 连接到你的 Pi

  3. 安装软件: 在你创建运行插头和 Pi 的程序之前,需要安装所需的 Python 库,这些库将允许你与插头进行交互。打开终端窗口并输入以下两行代码,每输入一行后按 ENTER:

    pi@raspberrypi:- $ sudo apt-get install python3-pip
    pi@raspberrypi:- $ sudo pip3 install energenie
    

    安装完成后,通过输入以下命令重启你的 Raspberry Pi:

    pi@raspberrypi:- $ sudo reboot
    

测试灯泡

你将创建一个简单的程序来测试 Energenie 插头和 Raspberry Pi 是否能够相互通信。

远程控制灯泡

你将使用的简单程序会打开插头,进而打开灯泡。确保你的灯泡仍然插入到你插入电源插座的 Energenie 插头中,并且灯泡开关是开启的。保持插头通电,如果灯泡亮起,表示继电器已闭合。然后按下 Energenie 插头上的绿色按钮将其关闭。打开 Python 编辑器并新建一个文件。输入以下代码并将文件保存为 plug_test.py

❶ from energenie import switch_on
❷ switch_on()

程序首先导入 switch_on 类 ❶,正如你可能推测的那样,这是打开插头的程序功能。

在下一行,调用 switch_on() 函数 ❷,这将触发 Pi 从 Pi-mote 向插头发送消息,打开插头。按 F5 执行代码。你的插头将被打开,灯泡也会亮起。非常酷吧。

要关闭插头,请将 Python 程序中的代码更改为以下内容:

from energenie import switch_off
switch_off()

保存此程序并再次运行;灯泡应该会关闭!此代码还导入了switch_off()函数,并调用该函数来关闭插座。

使灯泡闪烁

作为最终测试,您将结合前面两个程序,并添加短暂的延迟,使灯泡闪烁。技术上来说,您并不是让灯泡闪烁,而是每隔 5 秒钟切换一次插座的开关。在一个新的 Python 文件中,添加 Listing 12-1 中的程序代码,并将其保存为plug_flash.py

❶ import time
❷ from energenie import switch_on, switch_off

❸ while True:
    ❹ switch_on()   # add the socket number between the parentheses
    ❺ time.sleep(5)
    ❻ switch_off()
    ❼ time.sleep(5)

Listing 12-1 切换插座的开关

程序首先导入time模块❶,以便在插座开关之间添加延迟。然后导入switch_on()switch_off()函数❷。

接下来,使用while True循环保持程序的下一行代码持续运行❸。然后使用导入的函数来开启插座❹,暂停 5 秒❺,然后关闭插座❻。

代码的最后一行❼添加了另一个 5 秒的延迟。否则,插座关闭后将没有延迟立刻重新打开:灯泡会亮 5 秒钟,然后关闭,并立即重新亮起。

保存代码并运行程序。您的灯泡应该每 5 秒闪烁一次。

测试程序后,通过按 CTRL-C 停止程序。

使用应用程序控制插座

使用 Python 程序控制家电设备非常酷。但更棒的是,您可以使用一个应用程序,通过点击一个按钮来控制智能插座。Blue Dot是一个超级简单的 Android 应用程序,它让您通过手机或平板设备上的一个大蓝点与 LED、马达和其他组件(包括 Energenie 插座)进行交互,如图 12-3 所示。该应用程序使用蓝牙使您的设备与树莓派进行通信,通信范围大约为 10 米。您可以将您的设备视为灯泡的手持遥控器。

图片

图 12-3 Blue Dot 应用程序

  1. 在树莓派上设置 Blue Dot: 首先,您需要在树莓派上安装所需的 Python 库。打开终端窗口并输入以下命令:

    pi@raspberrypi:- $ sudo apt install python3-dbus
    pi@raspberrypi:- $ sudo pip3 install bluedot
    pi@raspberrypi:- $ sudo pip3 install bluedot --upgrade
    
  2. 安装应用程序: 在安装 Python 库的同时,解锁您的手机或平板设备(请记住,这仅适用于 Android 设备),然后前往 Google Play 商店。在商店中,搜索 Blue Dot 应用程序,它的图标应类似于图 12-4。点击安装按钮,应用程序将下载到您的设备上。图片

    图 12-4 为您的设备下载 Blue Dot 应用程序

  3. 配对你的设备和树莓派: 在你的移动设备上启用蓝牙,通常可以在设置中找到此选项(见图 12-5)。确保蓝牙设置为可发现选项,这样你的树莓派就能找到你的设备。返回到树莓派,找到桌面右上角的蓝牙符号,点击该符号,在菜单中选择开启使设备可发现。几分钟后,你应该会看到你的移动设备出现在列表中,选择它进行连接。根据你的设备,可能需要输入共享 PIN 码。

Image

图 12-5 从设备连接到你的树莓派

另一种连接蓝牙的方法是通过移动设备进行配对。首先搜索附近的设备,然后从列表中选择你的树莓派。根据屏幕提示完成配对。配对过程通常很标准,虽然根据设备的品牌不同,可能会略有差异。

编写智能插头代码

在连接好树莓派和设备后,你就可以开始编写程序,通过移动设备来控制 Energenie 插头了!返回到你的 Python 编辑器,输入程序代码(见列表 12-2),并将其保存为plug_bluedot.py

❶ from energenie import switch_on, switch_off
❷ from bluedot import BlueDot

❸ bd = BlueDot()

❹ while True:
    ❺ bd.wait_for_press()
    ❻ switch_on()
    ❼ bd.wait_for_release()
    ❽ switch_off()

列表 12-2 从移动设备控制插头

程序首先导入switch_on()switch_off()函数❶,然后导入BlueDot()类❷。

BlueDot()类设置为名为bd的变量❸,以便更快速地使用,然后创建一个while循环,使程序代码能够持续运行❹。

然后让树莓派检测你的设备上的蓝点是否被点击❺,如果是,便打开 Energenie 插头,进而点亮灯泡❻。当你松开蓝点❼时,插头会关闭❽,灯光熄灭。

运行程序

检查树莓派和设备之间的蓝牙连接是否仍然有效。如果连接中断,可能需要重新建立连接。运行程序。如果树莓派成功连接到你的设备,控制台窗口会显示一条消息,确认连接成功(见图 12-6)。

Image

图 12-6 连接你的设备到蓝牙

返回到你的设备并打开 Blue Dot 应用,你会看到屏幕上显示一个大蓝点,如图 12-7 所示。用手指按住蓝点,灯泡就会亮起;然后松开蓝点,灯泡会熄灭。

Image

图 12-7 连接到树莓派

改进控制开关的代码

现在你将修改plug_bluedot.py程序,使你可以通过单击蓝点来打开插头,再次点击蓝点来关闭插头。这样,你可以打开插头并保持开启状态,直到你再次点击蓝点。

这个程序更加实用,因为你可以把任何设备连接到插座!例如,如果你将电热水壶连接到 Energenie 插座,你可以通过点击蓝点来打开它。如果你需要关闭电热水壶,只需再次按下蓝点,开关就会关闭。

为了做到这一点,你将使用 Blue Dot 的 D-pad 功能,这类似于游戏控制器上的方向键,你可以按上、下、左、右按钮来控制玩家。在这个项目中,你将使用上按钮来打开插座,使用下按钮来关闭插座。打开你的plug_bluedot.py文件并修改它,使其与 Listing 12-3 中的代码一致。

 from energenie import switch_on, switch_off
   from bluedot import BlueDot
❶ from signal import pause

   bd = BlueDot()

❷ def dpad(pos):
       if pos.top:
           print ("up")
           switch_on()
    ❸ elif pos.bottom:
           print ("down")
           switch_off()

❹ bd.when_pressed = dpad

❺ pause()

LISTING 12-3 使用 Blue Dot 来打开和关闭插座

在新代码中,首先从signal库中导入pause()函数❶。你需要这个函数,因为在程序运行时,它总是等待 D-pad 被点击,这会给处理器带来负担。添加pause()函数可以减轻整体负担。

接下来,创建一个dpad()函数来处理 D-pad 被点击时应该发生的操作❷。首先,告诉程序,如果 D-pad 的上方被点击,应该运行switch_on()函数来打开插座。

第二步,添加一个elif语句❸,以捕捉 D-pad 底部被点击的情况。告诉程序,如果 D-pad 的下方被点击,应该通过使用switch_off()函数来关闭插座。

然后检查蓝点点击❹。当点击蓝点时,这行代码会运行你刚刚创建的dpad()函数。最后,添加pause()函数❺来减少处理器的负担。

保存并执行程序。确保你的 Raspberry Pi 和设备通过蓝牙连接,然后在手机设备上打开 Blue Dot 应用。将灯具仍然插入 Energenie 插座后,点击蓝点的上半部分,就像 D-pad 上的上按钮一样,打开灯具。点击蓝点的下半部分,就像 D-pad 上的下按钮一样,关闭灯具。记住,这次你不需要按住蓝点。

现在你有了一个可以远程控制的智能插座!尝试用其他一些电器来测试它。

总结

一旦你掌握了这个项目的基础,你可以根据需要进行调整。你可以制作一个恶作剧项目,每次有人试图打开灯具时,它就会关闭。或者,如何制作一些更有用的东西,比如一个系统,可以打开电视、收音机,甚至洗碗机?你还可以将这个项目与第三章中的胶枪夜灯结合,创建一个灯具,当房间变得足够暗时会自动打开,并在房间变亮时自动关闭。

第十三章:镜子,镜子:社交媒体状态镜

在这一章中,你将创建一个社交媒体状态镜,这是经典童话《白雪公主》中“镜子,镜子,墙上的镜子”的现代版。想象一下,你正准备外出,想知道朋友们在网上发布了什么。或者,也许你最喜欢的队伍正在比赛,而你希望在早晨准备的时候随时了解比赛动态。

社交媒体状态镜可以帮助解决这两种情况。你只需在 Python 程序中输入一个关键词。然后,每当包含该关键词的推文出现在你的社交媒体时间线上时,一排 LED 灯将闪烁几次,随后镜子会读出这条推文。LED 灯会提醒你有新消息。如果你将个人的 Twitter 账号作为关键词,镜子会读出任何提到你的推文,并告诉你是谁发的推文。

你可以通过选择一套彩色 LED 灯,甚至是动物、汽车或水果形状的 LED 来定制这个项目。或者,你可以选择不将 LED 灯安装在镜子上,正如图 13-1 所示,而是将它们安装在图片、公告板、书架或窗框上。我推荐使用 Raspberry Pi Zero 来做这个项目,因为它小巧而隐蔽,易于隐藏。它还内置了 Wi-Fi,这是你从 Twitter 账户流式传输数据所必需的。

Image

图 13-1 创建你的社交媒体状态镜。

你需要的材料

这里是完成该项目所需的一些物品:

  • Raspberry Pi Zero W(推荐)

  • 一套电池供电的 LED 灯(最大 3.3V)

  • Twitter 账号

  • 扬声器

  • USB 电池

  • 跳线

  • NPN(三极管:负-正-负,型号 2N 2222)

  • 1K 欧姆电阻

  • 焊接铁和焊锡,或鳄鱼夹,或铝箔

  • 镜子(或你想安装灯光的任何物品)

  • 小面包板(可选)

你需要考虑使用哪种类型的扬声器。你可以使用一个简单的便携式扬声器,配有标准音频插孔和电缆。但这样你就不能使用 Raspberry Pi Zero,除非你有一个小型镜子,无法安装较大的 Pi 板。另一个选择是使用支持蓝牙的扬声器,就像第十章中的设置一样。Pi Zero W 支持蓝牙,这意味着你可以直接将音频流式传输到扬声器。

准备设备

这个项目有几个部分,所以在开始编写代码之前,你需要设置扬声器,教 Raspberry Pi 通过扬声器朗读文本,连接状态 LED 灯,并将你的 Pi 连接到 Twitter。

连接扬声器

要将扬声器连接到项目中,你有两个选择。如果你没有使用 Pi Zero,你可以使用内置的音频插孔。通过标准的 3.5 毫米插孔电缆,将扬声器连接到 Raspberry Pi 后面的耳机插孔,该插孔位于 HDMI 端口旁边。插入你的 Raspberry Pi 并启动它;然后返回桌面。找到桌面顶部的音频图标并右键点击它。从音频输出下拉菜单中选择 模拟。现在所有音频将通过你的扬声器播放。

如果你使用的是蓝牙扬声器,通过点击屏幕右上角的蓝牙图标打开 Pi 上的蓝牙软件。然后打开蓝牙扬声器。再次点击蓝牙图标,从下拉菜单中选择 添加设备。Raspberry Pi 将尝试查找所有启用蓝牙的设备,包括你的扬声器。确保在扬声器上启用蓝牙设置,使其可以被发现。

当找到扬声器时,它将在弹出窗口中显示。从列表中选择它并点击 配对,以建立 Raspberry Pi 和蓝牙扬声器之间的连接。

一旦连接确认,点击音频图标并从下拉菜单中选择你的蓝牙扬声器。两个设备将再次尝试配对并建立连接。一旦扬声器设置完成,你的 Raspberry Pi 应该会自动查找并连接到此扬声器。

教授 Pi 朗读文本

要让 Raspberry Pi 朗读你的推文,你需要创建一个文本转语音程序。这个程序将书面文字转换为音频并播放。你可以使用该程序朗读任何类型的文本,包括短信、电子邮件或天气更新。所以,当你准备好时,你可以轻松调整这个项目,让它朗读其他信息给你听!

打开终端窗口并通过输入以下命令安装 espeak

pi@raspberrypi:- $ sudo apt install espeak python3-espeak

然后打开 Python 并开始一个新程序。输入以下代码并将其保存为 espeak_test.py

from espeak import espeak
espeak.synth("Have you met my friend Alexa?!")

确保将引号中的消息替换为你自己的消息。保存程序并运行它。你现在应该拥有一个会说话的 Raspberry Pi——好吧,一个带有文本转语音程序的 Raspberry Pi。进行实验并添加你自己的消息。

准备 LED 灯

现在你将设置 LED 并编写一个程序来控制它们。你的电池供电 LED 应该与电池包一起循环使用,如 图 13-2 所示。LED 将由电池包供电,而你将使用 Raspberry Pi 作为开关,打开和关闭电路,控制 LED 的开关。确保你使用的电池提供的电压不超过 3V,这相当于两节 AA 或 AAA 电池。

Image

图 13-2 电池供电 LED

为了准备电池供电的 LED,使用剪刀剪断接地线。你可以通过追溯电线回到电池包,找到连接到负极端子(即电池平面一侧的连接处)的电线,从而识别接地线,如图 13-3 所示。

Image

图 13-3 找到接地线

留下几英寸的电线仍然连接在电池包上。剥去两端的绝缘层,使电线的一部分裸露出来。

使用一根单独的母对公跳线,将其中一端的母头连接或焊接到剥线的一端。将第二根母对公跳线连接到另一根剥线的末端。电池包的外观应该类似于图 13-4。

Image

图 13-4 将一根跳线连接到每根被剪断的线的末端。

注意

如果你没有焊接工具,可以将鳄鱼夹夹到电线的一端,然后将另一端夹在跳线的末端。或者,你也可以用铝箔包裹电线的两端。

使用 NPN 晶体管

NPN 晶体管充当开关,用于切断电池到 LED 的电流。如果没有晶体管,LED 仍然会从电池包接收到一些电流,这意味着即使 LED 应该关闭时,它们也会微弱地发光。NPN 晶体管有三个引脚:发射极在左侧,基极在中间,集电极在右侧。图 13-5 显示的是晶体管的正面,即其平面侧。

Image

图 13-5 NPN 晶体管

为了设置电路,你需要将电池的接地线连接到发射极,然后将接地线的另一部分(通过剪断接地线获得的)连接到集电极。你将使用树莓派 GPIO 来打开基极,从而翻转晶体管中的开关,实现 LED 的开关控制。

准备电路

将跳线的两端连接到 NPN 晶体管,通过将左侧的跳线连接到 NPN 晶体管的左侧发射极引脚,将右侧的跳线连接到 NPN 晶体管的右侧集电极引脚(图 13-6)。NPN 晶体管通过控制电路中通过的电流来充当开关。通过停止电流流动,NPN 晶体管切断了电路,使得来自电池包的电流无法到达 LED,因此 LED 熄灭,就像你把它们关掉了一样。将 1K 欧姆电阻连接到晶体管的中间基极引脚。需要这个电阻以避免在晶体管工作时损坏树莓派的 GPIO 引脚。

Image

图 13-6 设置电路

使用一根女性到女性的电缆将电阻的另一端连接到 GPIO 3 针脚,这是提供电流以关闭电路的针脚。然后,使用一根男性到女性的电缆将晶体管的左侧发射极引脚连接到树莓派的地面针脚。方便的是,GPIO 3 的右侧紧挨着有一个地面针脚,即物理针脚 6。

测试 LED

打开你的 Python 编辑器并输入清单 13-1 中的测试程序。这个代码与第三章中创建热熔胶夜灯时使用的程序相似。它告诉程序哪一个 GPIO 针脚连接了电线,然后打开该针脚。这通过 GND 针脚创建一个电路,允许电池为 LED 供电。

from gpiozero import LED
from time import sleep
led = LED(3)
while True:
    led.on()
    sleep(1)
    led.off()
    sleep(1)

清单 13-1 创建一个电路来点亮 LED

保存并运行程序,然后打开电池组。电线上的 LED 灯会每秒闪烁一次。如果灯光一直亮着不灭,交换两根电缆:将 GPIO 3 线连接到 GND 针脚,将 GND 线连接到 GPIO 3。

一旦你使 LED 正常工作,你就可以开始创建你的社交媒体状态镜子了。但不要急于将 LED 附着到镜子、照片或架子上。相反,你将通过使用显示器来观察推文流和程序对其做出的反应。

设置你的 Twitter 开发者账户

要通过 Python 从树莓派访问 Twitter,你需要注册一个开发者账户并创建一个应用程序。然后,你可以生成唯一的密钥和令牌,在程序代码中使用它们来授权树莓派和 Twitter 之间的通信。这些密钥和令牌将你识别为用户,从而允许你通过 Python 代码流式传输你的时间线并发布推文。

首先,确保你有一个有效的 Twitter 账户。你可以使用现有账户,但如果没有账户,可以在https://www.twitter.com/上注册。

设置好账户后,访问https://developer.twitter.com/并点击页面右上角的申请按钮(图 13-7)。

Image

图 13-7 Twitter 开发者网站

然后,你将看到一个包含多个 API 的页面:这些是用于与 Twitter 交互的编程代码和功能的集合。

选择标准 API并点击申请开发者账户(图 13-8)。这个账户是免费的,非常适合镜像黑客的要求。你将被要求输入你的 Twitter 用户名和密码以创建账户。如果你已经有了开发者账户并且回来创建你自己的黑客版本,可以点击登录

Image

图 13-8 选择标准 API选项。

接下来,您将开始验证您的开发者账户。第一步要求您输入一个有效的手机号码。这纯粹是一个安全功能,以便 Twitter 可以向您发送确认短信来授权账户。点击添加有效的手机号码(图 13-9)。

Image

图 13-9 添加有效的手机号码。

输入适合您所在地区的详细信息,输入您的电话号码,然后点击下一步(图 13-10)。一个验证代码将通过短信发送到您的手机。

Image

图 13-10 输入您的详细信息。

当您收到短信时,请打开它。然后将确认码输入到图 13-11 中显示的验证手机号窗口,并点击验证

Image

图 13-11 输入您在手机上收到的代码。

完成后,您应该看到一条消息,说明您的电话号码已成功验证(图 13-12)。点击继续

Image

图 13-12 Twitter 验证您的电话号码。

验证的第二步要求您选择请求访问的对象(图 13-13):选择第二个选项,我为个人使用请求访问。然后为您的账户添加一个名称,这将是您的开发者账户的用户名。您可以使用自己的名字——例如,Dan 的 Twitter——或者使用您的 Twitter 账户名。选择您的主要运营国家/地区,这必须是您所在的位置以及运行镜像黑客的地方。然后点击继续

Image

图 13-13 为您的开发者账户命名。

在第三阶段,Twitter 将要求提供有关您项目使用的信息。

在第一个您感兴趣的用例是什么?问题中(图 13-14),选择聊天机器人和自动化

Image

图 13-14 选择您感兴趣的领域。

然后,您将被提示回答更多关于您正在构建的内容目的的问题。以下是您需要回答的四个问题:

  1. 您使用 Twitter API 的目的是什么?

  2. 您打算分析推文吗?

  3. 您希望转发内容吗?

  4. 数据将如何显示?

网页显示了一个简单的指南,其中包括一些建议和示例回答,您可以用它们来创建自己的回答。

回答这些问题时,您需要写至少 300 个字符。这听起来很多,但如果使用模型回答,就很容易达到所需的字符数。红色的提示会显示在框下,直到您写满所需字符数时才会消失。

这是我添加的示例:

我正在使用 Twitter 的 API 来收集我时间线上的推文和提及内容,然后将它们读出来。

该构建不会分析推文。

此构建不会发推、转发、点赞或与其他用户及其内容互动。它只会从我的时间轴中流式传输包含我的用户名的推文。

推文将从文本转化为语音并朗读出来。

你可以添加更多细节并自定义你的黑客版本答案。

在问题 你的产品、服务或分析是否会让政府机构获取 Twitter 内容或衍生信息? 下,选择 ,然后点击 继续

在第四阶段,你将看到条款和条件 (图 13-15),如果需要可以阅读。然后滚动到页面底部,选择框以接受条款,并点击 提交申请

Image

图 13-15 确认你接受条款和条件。

你应该会收到来自 Twitter 开发者团队的验证电子邮件。打开此邮件并点击 确认你的电子邮件。干得好!你已经完成了申请过程,并应被重定向到 API 开发者页面。

设置你的 Twitter 应用程序

一旦你的开发者账户设置并验证完毕,你就可以创建你的应用程序。为此,你需要输入一些项目的详细信息,然后生成一组独特的随机代码,称为 密钥。你将在 Python 程序中使用这些密钥,以便连接到你的 Twitter 账户并管理你的推文。开始时选择 创建一个应用程序 选项 (图 13-16)。

Image

图 13-16 开始使用

  1. 创建一个新应用程序: 网站的下一页将显示你已经创建的所有现有 Twitter 应用程序,并向你展示创建新应用程序的选项。点击 创建一个应用程序 按钮 (图 13-17)。Image

    图 13-17 创建一个新的应用程序

  2. 注册你的新应用程序的详细信息: 输入应用程序的名称(例如,mirrorsocial media mirror)。你将使用这个名称在下次登录时识别你的项目。在下一个框中,输入项目的简短描述,让其他人了解你的应用程序的功能 (图 13-18)。Image

    图 13-18 为你的应用程序添加详细信息

    你还需要输入一个网站地址。如果你有自己的官方网站,请在这里输入。如果没有,你可以输入你的 Twitter 账户的网址,通常是 www.twitter.com/your_user_name/

    确保取消选择“启用使用 Twitter 登录”选项,跳过其他 URL 和网站提示。同时,保持回调 URL 为空,并通过勾选框同意开发者协议。点击 创建你的 Twitter 应用程序 按钮。

    在最后一个框中,输入你将如何使用这个应用的简短总结。我添加了关于社交媒体状态镜像黑客功能的简短描述。然后点击创建。接下来会出现两个选项;首先选择权限 (图 13-19)。

    图片

    图 13-19 选择你所需的访问权限。

    你需要选择你的应用所需的访问类型。仅读取 允许你从时间线中读取推文,而 读取与写入 允许你读取并发送自己的推文。选择读取与写入选项,尽管它可能已经是默认设置。

创建访问密钥和令牌

最后的选项窗口会显示你的 API 密钥和访问令牌 (图 13-20)。这些使你的 Raspberry Pi 能够通过 Python 与 Twitter 互动。记录以下密钥和令牌:

消费者密钥 用于识别你和你的应用作为独特的用户

消费者密钥 将此作为密码使用

访问令牌 你将在程序中使用的代码

访问令牌密钥 与你的访问令牌一起使用,用于授权与 Twitter 应用的连接

图片

图 13-20 生成你的密钥和令牌。

要创建访问令牌和访问令牌密钥,点击创建

保护这些密钥和令牌非常重要。如果你忘记了访问令牌或令牌密钥,或者它们被泄露了,你可以点击重新生成来创建一个新的令牌和 API 密钥集。记得你将在 Python 程序中使用这些密钥,所以一定要把它们记录在一个安全的地方。

你现在已经完成了 Twitter 应用的设置程序。你准备好使用 Raspberry Pi 发送你的第一条推文了。记住,你也可以将这些凭证用于其他项目。

使用应用编程你的 Raspberry Pi

在开始编写主程序之前,你将创建一些测试程序,以便你学习如何使用 Raspberry Pi 发送和读取推文。这是一项有用的技能,你可以在未来的其他项目中使用和调整!它也是一种简单快速的方法来测试配置是否正常工作。

发送你的第一条推文

要发送你的第一条推文,你需要使用一个新的 Python 库 tweepy,它使你的程序能够与 Twitter 通信。

  1. 安装 tweepy 启动你的 Raspberry Pi 并打开终端窗口。然后输入以下命令:

    pi@raspberrypi:- $ sudo pip3 install tweepy
    
  2. 发送推文: 你将创建一个 Python 程序,将推文从你的 Raspberry Pi 发布到你的 Twitter 时间线。推文会立即出现在你的公共时间线上,所以要注意你发送的内容。打开你的 Python 编辑器,创建一个新文件并保存为 Sending.py,然后输入 示例 13-2 中的代码。

    ❶ import sys, subprocess, urllib, time, tweepy
    ❷ consumer_key= "xxxxxxxxxx"
    ❸ consumer_secret= "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    ❹ access_token= "xxxxxxxxxxxxxxxxxxxxxxx"
    ❺ access_token_secret= "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    ❻ auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    ❼ auth.set_access_token(access_token, access_token_secret)
    ❽ api = tweepy.API(auth) 
    ❾ api.update_status("Tweet sent from my Pi")
    print ("Tweet Sent")
    

    示例 13-2 使用 Raspberry Pi 编写并发布推文

    通过导入 syssubprocess 模块来启动程序,subprocess 是一个用于处理 URL 的包。同时,导入 urllib,这是一个处理 URL 或网站地址的模块。这个 Python 模块允许你通过特定的 URL 将数据发送到你的 Twitter 账户并接收数据。接着导入 timetweepy ❶。

    接下来,输入你的消费者密钥 ❷、消费者密钥密文 ❸、访问令牌 ❹ 和访问令牌密文 ❺。在 ❻、❼ 和 ❽ 处,分别授权你的 Twitter 账户、你的应用程序和 Raspberry Pi。这将允许你访问你的 Twitter 账户。

    使用函数 api.update_status() ❾ 发布你的消息;将消息输入在引号之间。然后打印一条确认推文已发送的消息。

  3. 运行程序: 在运行程序之前,确保你已经输入了自己的消息;然后按 F5 保存并执行代码。推文会发布,记得检查你的 Twitter 时间线。

  4. (可选)更改 Twitter 账号: 如果你想使用其他用户的 Twitter 账号发送提及,只需在括号中添加他们的 Twitter 账号,然后再输入你的消息;例如,api.update_status('@dan_aldred, Tweet sent from my Pi')。就是这么简单。你不能发布相同的推文两次,因为 Twitter 会认为这是垃圾邮件。当你再次运行程序时,确保更改 ❾ 处的消息。

阅读推文

要从 Twitter 时间线读取消息和提及,你需要下载你关注的 Twitter 用户的最新推文,并将它们打印在 Python 控制台窗口中。返回到你之前的程序 Sending.py,并将其保存为新文件名 Timeline.py。更新该文件,使其与 Listing 13-3 中的代码一致。

   import sys, subprocess, urllib, time, tweepy
   consumer_key= "xxxxxxxxxxxxxx"
   consumer_secret= "xxxxxxxxxxxxxxxxxxxxxxx"
   access_token= "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
   access_token_secret= "xxxxxxxxxxxxxxxxxxxx"
   auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
   auth.set_access_token(access_token, access_token_secret)
   api = tweepy.API(auth)
❶ public_tweets = api.home_timeline()
❷ for tweet in public_tweets:
       ❸ try:
          ❹ print (tweet.text)
          ❺ time.sleep(2)
          ❻ print ("")
       ❼ except:
          ❽ print ("cannot display")

LISTING 13-3 从 Twitter 时间线读取推文

程序的第一部分,与之前的程序 Sending.py 相同,用于控制对 Twitter 的身份验证和授权。除非你更改了密钥和令牌的详细信息,否则无需调整这些行。

删除 api = tweepy.API(auth) 后面的所有代码行。(不用担心丢失 Sending.py 程序,因为你已经将其保存为不同的名称。)添加 ❶ 处的代码,它会从你的公共时间线拉取所有推文,并将它们存储在名为 public_tweets 的变量中。接下来,使用 for 循环 ❷ 遍历每一条推文。

然后使用 tryexcept 方法,它尝试运行程序的下一部分 ❸,如果不能执行,则运行 ❼ 和 ❽ 处的 except 代码。原因是有时候推文包含一些 Python 代码无法解释的符号和字符,例如表情符号。如果没有 ❸ 和 ❼ 处的代码,程序将崩溃并返回一条消息,提示无法显示文本。通过异常处理,你让代码在不能运行时执行其他操作,而不是崩溃。

在下一步中,打印每条推文 ❹。添加一个短暂的延迟 ❺,以便你有时间阅读每条帖子。你可能希望将此延迟时间设置为超过 2 秒(例如,设置为 5 秒)。

在打印下一条推文之前跳过一行 ❻。这样可以让阅读每条推文变得稍微容易一些。它还使得展示更加整洁,不会让你陷入大量的文本中。然后在❼处添加except语句,并添加一个信息来通知你无法显示该推文 ❽。将文件保存为Timeline.py并像之前一样运行程序。现在你有了一个 Python Twitter 时间轴阅读器(图 13-21)!

Image

图 13-21 从你的时间轴打印推文

自动流式传输推文

在运行Timeline.py程序时,你可能注意到它只下载了时间轴上的前 20 条推文。它还只打印在你开始运行程序之前发布的推文:它不会自动下载在程序运行期间收到的新推文。如果你想实时获取推文或检查某个关键字,这就不太有用了。

一种解决方案是使用while循环,每隔大约 20 秒请求一次时间轴上的推文。但是这个问题在于 Twitter 会限制你的下载次数,因为它希望阻止垃圾邮件机器人。每次请求并下载数据时,Twitter 都会记录这些信息。如果你每小时请求数据的次数过多,程序可能会超时,你需要等待一段时间才能再次请求时间轴数据。

更好的解决方案是创建一个类来流式传输推文。这大大减少了请求的数量,因此 Twitter 不会让你的程序超时。此方法还允许你在推文发布到时间轴时进行流式传输。每次新推文到达时,程序运行时,Python 会将其打印到 Shell 窗口。现在我们来创建一个类:

  1. 使用类流式传输推文: 打开Timeline.py并将其保存为Stream_Tweets.py。编辑文件使其与列表 13-4 中的代码匹配。

       import sys, subprocess, urllib, time, tweepy
       consumer_key= "xxxxxxxxxxxxxx"
       consumer_secret= "xxxxxxxxxxxxxxxxxxxxxxx"
       access_token= "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
       access_token_secret= "xxxxxxxxxxxxxxxxxxxx"
       auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
       auth.set_access_token(access_token, access_token_secret)
       api = tweepy.API(auth) 
    ❶ class Social_Media_Mirror_StreamListener(tweepy.StreamListener):
        ❷ def on_status(self, status):
            ❸ tweet_to_check = status.text
            ❹ does_the_tweet_contain_key_word = tweet_to_check.find("@Dan_Aldred")
            ❺ print (does_the_tweet_contain_key_word)
    
    

    列表 13-4 使用类流式传输推文

    与之前的程序一样,首先导入所需的模块,以及你的密钥和令牌,以便验证和授权你的 Twitter 账户和应用程序。

    然后创建一个新的类,命名为Social_Media_Mirror_StreamListener,该类使用 Python tweepy库的流式功能 ❶。创建一个函数来流式传输推文并检查触发词 ❷。代码status.text可以获取你时间轴上的所有推文。将这些推文存储在变量tweet_to_check中 ❸。

    接下来,使用 .find() 检查触发词。将这个方法与 tweet_to_check 变量结合,搜索每个流式推文中的触发词。在我的示例中,将触发词 @Dan_Aldred 替换为你自己的 Twitter 账号、关键词或一个用于触发 LED 灯的标签。程序接着检查关键词,如果找到它,就返回关键词在推文中的数字位置。如果推文中没有关键词,程序会返回值-1,并将其存储在 does_the_tweet_contain_key_word 变量中 ❹。你会在 ❺ 处打印关键词的位置值。如果推文中包含该关键词,返回值将为 0 或更大,并存储在第❹行的变量中。

  2. 搜索关键词: 程序的以下部分检查推文中是否包含关键词。将 Listing 13-5 中的代码添加到你的程序中。

            ❶ if does_the_tweet_contain_key_word >= 0:
                ❷ who = status.user.screen_name # gets the user name
                ❸ print (who) # prints the user's name
                ❹ print ("LIGHTS ON") 
            ❺ else:
                ❻ print ("LIGHTS OFF")
                ❼ time.sleep(1)
    
    

    LISTING 13-5 检查推文流中的关键词

    首先,通过查看触发词的位置信息是否大于或等于 0 ❶,来检查传入的推文是否包含触发词。如果该值大于或等于 0,表示触发词在推文中的某个位置。例如,如果该值为 0,说明触发词位于推文的最开始。如果该值为 7,说明关键词位于推文的第 7 个位置。

    接下来,获取包含触发词的用户的 Twitter 账号 ❷,并打印出来 ❸。

    在最终的程序中,你还将使用关键词作为触发器来开启 LED 灯。通常这个操作会发生在程序的这一点,但由于你还没有添加 LED 的代码,所以此时只会添加一个测试语句来表示这一点 ❹。

    然后,在推文中没有找到触发词的情况下,添加响应代码。该响应会在值为-1 时触发。如果发生这种情况,你需要添加一个通知,表示灯光已关闭 ❺ ❻,然后稍作暂停,约 1 秒钟 ❼。这就完成了流式处理程序的主要功能。

  3. 启动流式处理: 最后一步是启动流式处理。将 Listing 13-6 中的代码添加到你的程序中。

    ❶ myStreamListener = Social_Media_Mirror_StreamListener()
    ❷ myStream = tweepy.Stream(auth = api.auth, listener=myStreamListener)
    ❸ myStream.filter(follow=["xxxxxxxxx"])
    
    

    LISTING 13-6 启动流式处理

    创建一个名为 myStreamListener 的变量,用于组合并保存你对 Twitter 应用的授权信息以及之前步骤中编写的 Social_Media_Mirror_StreamListener() 函数 ❶。然后将这些功能组合在一起,认证你的凭据并部署流 ❷。

    要流式获取正确 Twitter 账号的时间线,你需要添加 Twitter 账号的 ID 号码 ❸。你可以通过打开 tweeterid.com/ 并输入你想要追踪的账号名来找到这个号码(见图 13-22)。将返回的 ID 号放入第❸行。你可以添加自己的账号,以确保接收到提到你的消息和提及;你也可以搜索你喜欢的名人账号、朋友和家人,或者某个趋势事件的标签。

Image

FIGURE 13-22 查找 Twitter 账户的 ID 号

保存并运行你的Stream_Tweets.py程序。你可以通过在推文中提到你的关键词来测试它(或者让朋友来做)。尝试将关键词放在不同的位置,看看程序的反应。然后进入代码的最后一部分,我们将把这个程序与 LED 灯和音频结合,完成社交媒体状态镜像。

最终程序

为了完成项目,你将把包含关键词的推文流代码与使 LED 闪烁并朗读推文的代码结合起来。

启动最终程序

开始一个新的程序文件,并将其保存为Social_Media_Mirror.py。此代码结合了你在 Listings 13-4 和 13-5 中使用的代码,完成程序。

   import sys, subprocess, urllib, time, tweepy
❶ from espeak import espeak
❷ from gpiozero import LED 
   consumer_key= "xxxxxxxxxxxxx"
   consumer_secret= "xxxxxxxxxxxxxxxx"
   access_token= "xxxxxxxxxxxxxxxxxxxxxxx"
   access_token_secret= "xxxxxxxxxxxxxxxxxx"
   auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
   auth.set_access_token(access_token, access_token_secret)
   api = tweepy.API(auth)
❸ led = LED(3)
❹ led.off() 

LISTING 13-7 启动最终程序

导入espeak()函数❶,用它来朗读推文。然后从gpiozero库中导入LED类❷。你使用它来打开或关闭 GPIO 引脚,从而控制 LED 的电池电源。将 GPIO 3 引脚分配给 LED ❸。使用这个引脚来控制灯的开关。接下来,关闭 LED❹,这样每次程序重置时灯不会亮起。

添加主程序代码

现在你将添加主程序代码Social_Media_Mirror.py

class Social_Media_Mirror_StreamListener(tweepy.StreamListener):
    def on_status(self, status): 
     ❶ led.off() 
     ❷ try:
           print(status.text)
        except:
            pass
            print ("Cannot display Tweet")
            led.off()   
        tweet_to_check = status.text # checks the tweet 
        does_the_tweet_contain_key_word = tweet_to_check.find("@Dan_Aldred")
        # replace with your Twitter user name or a keyword
        print (does_the_tweet_contain_key_word) 
        if does_the_tweet_contain_key_word >= 0: 
            who = status.user.screen_name # gets the user name
            print (who) # prints the user's name 
         ❸ # flash the lights 
            led.on()
            time.sleep(0.6)
            led.off()
            time.sleep(0.5)
            led.on()
            time.sleep(0.4)
            led.off()
            time.sleep(0.3)
            led.on()
            time.sleep(0.2)
            led.off()
            time.sleep(0.1)
            led.on()
            time.sleep(0.05)

            led.on()
            print ("LIGHTS ON")
         ❹ espeak.synth(who)
         ❺ espeak.synth("said")
         ❻ time.sleep(1)
         ❼ espeak.synth(status.text) # reads out the tweet
         ❽ time.sleep(4)
         ❾ led.off() 
     ❿ else:
            print ("LIGHTS OFF")
            led.off()
            time.sleep(1) 

LISTING 13-8 添加代码使 LED 闪烁

再次使用❶处的代码关闭 LED 灯,然后添加tryexcept方法❷(如你在 Listing 13-3 中所做的),以防止推文中包含无法作为文本显示的未知字符、符号或表情符号时程序崩溃。接下来,点亮 LED 灯,等待几秒钟,然后关闭它们。❸处的代码仅为示例,能够使灯光闪烁更快,直到保持常亮。你可以根据自己的喜好调整这段代码:让灯保持点亮 5 秒钟,或者只闪烁一次。在 Listing 13-8 中的程序中,LED 在朗读消息时保持点亮。通过更改time.sleep()中的值❸来改变 LED 的模式。例如,time.sleep(10)会让 LED 保持点亮 10 秒钟,然后关闭。

使用espeak.synth()函数读取发送包含你 Twitter 名字的推文的 Twitter 用户的名字 ❹。将此代码与单词said ❺结合,这样当程序读取一条推文(比如我的推文)时,它会首先宣布:“@Dan_Aldred said”。在❻处添加一个小延迟。然后让程序读取推文 ❼,再添加最后的延迟 ❽并关闭 LED 灯 ❾。关闭灯光表示消息已被接收并转发。

如果你收到一条没有包含你关键词的推文,闪烁一次 LED 来表示你的时间线有新帖子,但并不是一个具体的感兴趣的消息❿。然后添加最后一行代码,授权你的 Twitter 凭证,并实时流式传输你的 Twitter 时间线中的推文。你的程序会检查这些推文是否包含关键词,并以设定的方式做出响应。

添加 Listing 13-9 中的结束行代码,开始流式传输并完成主程序代码。

myStreamListener = Social_Media_Mirror_StreamListener()
myStream = tweepy.Stream(auth = api.auth, listener=myStreamListener)
myStream.filter(follow=["xxxxxxxxx"]) 

LISTING 13-9 结束程序

保存 Social_Media_Mirror.py 代码并运行它。要进行测试,可以在另一台设备上加载你的 Twitter 动态并在推文中提到你的 Twitter 用户名或关键词。观察 LED 灯在收到新消息时闪烁,然后听到消息被朗读出来。联系你的朋友,让他们也试试看。

记住,你还可以将你的 Twitter ID 替换为你喜欢的 Twitter 账户或用户的 ID。使用网站 tweeterid.com/ 来查找 ID 号码。你也可以将 ID 代码替换为像 No Starch 这样的关键字。在测试程序时,记住如果你发送相同的消息重复内容,Twitter 会将其视为垃圾信息,所以一定要更改你的测试词。

自动启动 Twitter 流

一旦你对项目感到满意,你可以设置程序在树莓派开机时自动运行。你可以通过配置一个 crontab 文件来实现,就像在本书的其他项目中做的那样。

打开终端并输入以下命令来打开 cron 控制台:

pi@raspberrypi:- $ sudo crontab -e

控制台将为你提供三种编辑 crontab 文件的方法。选择选项 2 并按 ENTER,使用 nano 文本编辑器打开 crontab 文件。

滚动到 crontab 文件的底部,找到空白区域。然后添加以下代码行:

@reboot sudo python3   /home/pi/name_of_your_program.py &

该命令表示每次树莓派重新启动时,都应该以超级用户模式运行 Python,打开 home/pi/ 文件夹,并执行你的程序。将 name_of_your_program.py 替换为你保存程序时的名称。

代码行末尾的 & 告诉你的程序在后台运行,这样你就可以同时进行其他任务。

一旦你检查了命令详情,并确认它们是正确的,按 CTRL-X 保存并退出 crontab 文件。现在,每次你打开或重新启动树莓派时,crontab 文件都会运行,启动 Social_Media_Mirror.py 程序,你的社交媒体状态镜像就绪。

如果你想停止程序的自动运行,可以通过在终端中输入以下命令再次打开 crontab 文件:

pi@raspberrypi:- $ crontab -e

然后删除你添加的代码行。保存文件并重新启动。

汇总所有内容

现在所有硬件都已正常工作,你可以将 LED 灯添加到镜子上——或者其他你选择的任何物体上。(你也可以制作一个社交媒体架。)确保扬声器和 LED 灯已连接好,然后将电池包附着在镜子背面(图 13-23)。你可以将它粘贴在框架的背面,或者使用双面胶带。记得将电池仓朝外放置,这样在需要时可以更换电池。然后挂上你的镜子,等待消息传入。

Image

图 13-23 将 Pi 和电池包连接到镜子背面。

项目总结

为了进一步开发你的项目,尝试以下一个或多个方法:

  • 通过改变时间来更改 LED 闪烁模式。

  • 添加一段音乐或庆祝的 MP3 文件,在每条推文朗读之前播放。

  • 将该项目与第三章中的粘合光结合,或者接入另一组 LED 灯,使其响应特定的第二触发词。

第十四章:使用 Sense HAT 进行计算机游戏

在本书的最终项目中,你将使用 Sense HAT 硬件和 Python 代码,制作经典游戏石头、剪子、布的一个版本——不过有所改动。你将编程一个五彩斑斓的 LED 矩阵和一个摇杆,为你的程序添加游戏显示和控制功能。

2015 年底,欧洲航天局(ESA)向国际空间站(ISS)发送了一枚补给火箭。其中包括两台 Raspberry Pi 计算机,每台都安装了一个特殊的附加板,名为 Astro Pi。这些设备是为英国宇航员蒂姆·皮克上校的首次访问做准备的。每个 Astro Pi 都配备了硬件和传感器阵列,并拥有一个 8 × 8 的 LED 显示屏——非常适合用来采集和显示诸如温度、压力、加速度和磁场强度等读数。

此前,在 2015 年夏季,Raspberry Pi 基金会举办了一项比赛,鼓励孩子们为 Astro Pi 编写程序或实验,计划在国际空间站上运行。在皮克的驻留期间,Astro Pi 运行了获胜者设计的预加载实验。Astro Pi 将一直留在国际空间站直到 2021 年,如果你符合参赛资格,仍然可以参加比赛。更多细节请见 astro-pi.org/。谁知道呢,也许你会受到启发,编写自己的程序,在国际空间站上运行,见图 14-1。

2015 年 12 月,Astro Pi 开始面向公众销售,并更名为 Sense HAT。你今天可以购买的 Sense HAT 硬件和传感器与 Astro Pi 完全相同。

Image

图 14-1 国际空间站

Sense HAT 的 LED 显示屏可以作为屏幕使用,使其成为制作游戏的完美工具。在本章中,你将用它编程制作经典的石头、剪子、布游戏的变种。这一版本——称为石头、剪子、布、蜥蜴、斯波克——为游戏玩法带来了新的可能性。

不过,首先你将查看一些简单的程序,以便探索 Sense HAT 的关键特性。你将滚动文本,制作一个互动的温度显示,并使用一个叫做数组的工具来绘制基本图像。这些技能也可以独立使用,你可以将它们适应到自己的项目中。

你将需要的工具

这是你构建这个游戏项目所需的工具:

  • Raspberry Pi 3 或 4(建议使用)

  • Sense HAT

  • USB 便携电池(可选)

  • 小型十字螺丝刀

什么是 Sense HAT?

让我们来看看 Sense HAT 的传感器和硬件,如图 14-2 所示。

Image

图 14-2 Sense HAT 及其组件

Sense HAT 的部件如下:

多彩 8 × 8 LED 矩阵 你可以使用这 64 个可编程 LED 灯来滚动消息,或者显示传感器数据、动画或简单的游戏。

温度传感器 这个内置传感器在 0-65°C(32-150°F)范围内的准确度约为 2°C(约 4°F)。它可以用于测量周围环境的温度。

磁力计 通过检测磁场强度来像指南针一样工作。你可以使用它的读数来测量磁场并找到相对于北方的方向。

加速度计 测量上下和横向的加速度。你可以利用它来跟踪运动,或将其改造为游戏控制器。

多方向摇杆 可以用于在游戏中移动角色,或作为选项的选择工具。

开始使用 Sense HAT

设置 Sense HAT 非常简单,因为它是专为树莓派设计的。将 Sense HAT 插入 GPIO 引脚,并将其按紧固定到位。然后,取下树莓派的偏移安装架(这些应该随 Sense HAT 一起提供),并将其固定到 Sense HAT 上。安装这些支架可以防止在使用摇杆时板子移动。接下来,插入电源并启动树莓派。

接下来,我们将查看一些基本程序,展示 Sense HAT 的各种功能和能力。

在 Sense HAT 上滚动文本

让我们编写第一个程序,滚动显示一条信息。

使文本滚动

打开 Python 编辑器,启动一个新文件,并在 Listing 14-1 中输入程序代码。

❶ from sense_hat import SenseHat
   sense = SenseHat()
❷ sense.show_message("My name is Ada")

Listing 14-1 滚动显示信息

首先导入 SenseHat 模块 ❶。然后创建一行代码 ❷,负责滚动显示消息。将引号中的文本替换为你自己的消息,保存文件为 scroll_text.py,并按 F5 运行。你的信息将会滚动显示在 LED 上。

更改文本的颜色

你可以通过更改文本的红色、绿色和蓝色(RGB)值来更改文本的颜色。你可以使用 0 到 255 之间的值将红色、绿色和蓝色的任意组合结合起来,其中 0 表示无颜色,255 表示最大颜色量,从而创建出一种整体颜色。

将 Listing 14-1 中的最后一行代码替换为以下内容:

sense.show_message("Hi, this is Ada!", text_colour=[255, 0, 0])

这段代码将颜色设置为最大红色值(255)和最小绿色与蓝色值(0),从而将文本颜色更改为红色。像之前一样保存并运行程序代码。

尝试其他颜色变体。更改代码行末尾的颜色值,然后运行程序查看颜色变化。下表列出了你可以使用的颜色值。

颜色 RGB 值
粉色 255, 102, 255
黄色 255, 255, 0
橙色 255, 128, 0
紫色 102, 0, 102
浅蓝色 0, 255, 255

测量温度

现在,你将结合滚动文本的代码和 Sense HAT 的温度传感器,显示周围环境的温度。打开一个新的 Python 文件,并输入清单 14-2 中的代码。

   from sense_hat import SenseHat
   sense = SenseHat()
❶ temp = sense.get_temperature()
❷ print ("Temperature: %s C" % temp)
❸ sense.show_message("Temp: %s C" % temp)

清单 14-2 在 LED 显示屏上滚动温度

同样,首先导入SenseHat模块。然后命令温度传感器进行读取,并将此值存储在名为temp ❶的变量中。接下来,打印出该值 ❷。在最后一行,使用与清单 14-1 中相同的代码来滚动文本,但将消息改为来自temp变量的值 ❸。

现在温度(以摄氏度为单位)将滚动显示在 LED 屏幕上。如果温度看起来比较高,那是因为传感器靠近树莓派的 CPU,CPU 会发热。因此,读取的温度可能比实际环境温度高出 5 到 6 摄氏度。如果你想要更准确的温度读取,可以通过将第❶行替换为temp = sense.get_temperature() - 6来减去 6 度。

制作实时温度显示

到目前为止,我们的程序仅在执行程序的瞬间读取一次温度。如果你想监控温度变化,这种方式就不太实用了,因此你需要编辑代码,使其持续更新温度读数。你还将利用 LED 来制作虚拟温度计,而不是将温度表示为数字。你将通过将温度读取值分配给多个像素,创建一个 LED 温度计。打开一个新的 Python 文件,复制清单 14-3 中的代码,这段程序演示了如何实现这个功能。

   from sense_hat import SenseHat
   sense = SenseHat()
❶ red = (255, 0, 0)
❷ blue = (0, 0, 255) 
❸ while True:
    ❹ temp = sense.temp
    ❺ print (temp)
    ❻ pixels = [red if i < temp else blue for i in range(64)]
    ❼ sense.set_pixels(pixels)

清单 14-3 制作更新的虚拟温度计

通过导入SenseHat模块来启动程序。然后为温度显示分配两种颜色。在这个示例中,使用red ❶表示当前温度,使用blue ❷表示其余的温度范围。

接下来,创建一个while循环,确保程序重复运行,并不断更新温度 ❸。和之前一样,读取温度并将值存储在temp ❹中,然后打印出该值 ❺。

计算应该有多少个 LED 显示红色,多少个 LED 显示蓝色 ❻。每个 LED 代表一个温度单位,因此如果温度是 26°C,你希望前 26 个 LED 是红色的,其余的 LED 是蓝色的(蓝色 LED 表示温度为 27°C 或更高)。代码中指示,如果 LED 的编号大于温度,则应显示蓝色;如果编号小于或等于温度,则显示红色。然后将这些颜色值写入 LED 显示屏 ❼。

随着温度的变化,循环程序将重新计算红色和蓝色 LED 的组合。温度越高,红色 LED 越多;温度越低,蓝色 LED 越多。图 14-3 展示了 LED 温度计。

Image

图 14-3 Sense HAT 实时温度传感器

将程序保存为 temp.py 并运行。尝试通过将 Sense HAT 安全地放置在热源附近或将其放入冰箱几分钟来调整温度!

构建一个指南针

Sense HAT 配备了一个内置的磁力计,你可以用它作为指南针,计算 Sense HAT 相对于磁北的位置。打开一个新的 Python 文件,并尝试使用清单 14-4 中的代码。

   from sense_hat import SenseHat
   sense = SenseHat()
   import time
   while True:
❶ north = sense.get_compass()
❷ print ("North: %s" % north)
❸ time.sleep(1)

清单 14-4 寻找磁北

添加前三行代码,这是标准的导入和赋值操作。然后创建一个 while 循环,使程序不断重复;这样,像在温度计项目中一样,Sense HAT 会不断更新其位置。接下来,从磁力计 ❶ 读取数据,并将其存储在名为 north 的变量中。打印出该值 ❷,然后添加一个 1 秒钟的暂停 ❸,以便在下次读取前让你有时间稳定位置。

保存并运行程序。移动 Sense HAT,观察读数的变化。

制作一个神奇的闪光屏幕

这个来自树莓派基金会的简单程序将 Sense HAT LED 显示屏变成了一个闪亮的色彩墙,如图 14-4 所示。它选择一个随机 LED 并分配一个随机颜色,点亮该 LED,然后暂停十分之一秒后,选择并点亮另一个随机 LED,并赋予它一个随机颜色。这个模式不断循环,创造出闪光的效果。把一个透明物体放在顶部,你就能拥有自己的氛围灯。

Image

图 14-4 Sense HAT 闪光效果。

打开 Python 并保存一个新文件,命名为 sparkles.py。输入清单 14-5 中的代码。

   from sense_hat import SenseHat
❶ from random import randint
❷ from time import sleep  
   sense = SenseHat() 
❸ while True:
   ❹ x = randint(0, 7)
   ❺ y = randint(0, 7)
   ❻ r = randint(0, 255)
   ❼ g = randint(0, 255)
   ❽ b = randint(0, 255)
   ❾ sense.set_pixel(x, y, r, g, b)
   ❿ sleep(0.01)

清单 14-5 选择随机的 LED 和颜色

通过导入 SenseHat 模块开始程序。然后从 random 模块导入 randint() 函数 ❶。randint() 函数是 随机整数 的缩写,用于在程序中选择一个随机整数(即非小数数字),它将为你提供随机的颜色选择。接下来,导入 sleep() 函数来给程序添加一个短暂的延迟 ❷。

创建一个 while True 循环,使程序不断选择新的随机 LED,并为它们分配新的随机颜色,从而产生闪光效果 ❸。在这个 while True 循环中,构建随机 LED 的细节。首先,LED 的位置是通过为显示屏上的 x 和 y 坐标选择随机整数来确定的。x 位置是一个介于 0 和 7 之间的随机值 ❹,并将其存储在一个名为 x 的变量中。y 值也是介于 0 和 7 之间的随机值,存储在名为 y 的变量中 ❺。尽管你有八个 LED 横向和纵向排列,但在编程中我们从零开始编号,因此第一个 LED 位置是 0,第二个是位置 1。这意味着第八个 LED 是编号 7。

第二步,为红色、绿色和蓝色创建变量,程序将随机组合这些颜色,生成 LED 的随机颜色。将 RGB 值存储在变量 rgb 中 ❻ ❼ ❽。

最后,将所有这些值合并并使用 sense.set_pixel() ❾ 写入 LED,后跟 xyrgb 变量。当程序运行时,它会为这五个变量选择一个随机值。最后添加一个小的暂停 ❿,然后程序进入循环。保存并运行程序,享受这场灯光秀吧!

使用 LED 显示屏创建图像

图像由像素组成,你可以将它们看作是微小的彩色点。如果你把每个 Sense HAT 显示屏上的 LED 当作一个像素,你可以创建一个 8 × 8 像素的图像,如 图 14-5 所示。

Image

图 14-5 8 × 8 像素绘图

在这个程序中,你将使用一个数组来保存每个 LED 的位置和颜色。数组是一个使用多行的列表。像列表一样,数组用于保存数据,可以是数字、字符串或符号。例如,shopping = ['eggs', 'chocolate', 'bread'] 是一个列表。你将列表用方括号 [ ] 括起来,并可以通过引用索引编号来选择列表中的任何项目。如你所知,第一个位置的项目(eggs)是 0,接下来的项目(chocolate)是 1,然后是 2,以此类推。

你的数组将包含 64 个条目,每个条目对应一个 LED。这样,你就可以单独控制每个 LED,开关它并设置颜色。为简单起见,数组被分为八行,每行包含八个条目,模仿了 LED 显示屏的布局。创建一个新的 Python 文件并保存为 face.py。输入 清单 14-6 中的程序代码,创建一个笑脸表情符号。

   from sense_hat import SenseHat
   sense = SenseHat()
❶ R = [255, 0, 0] # Red
❷ O = [0, 0, 0] # Black or off
❸ B = [0, 0, 255] # Blue
❹ Y = [255, 255, 0] # Yellow 
❺ face = [
❻ O, O, O, Y, Y, O, O, O,
   O, Y, Y, Y, Y, Y, Y, O,
   O, Y, B, Y, Y, B, Y, O,
   O, Y, Y, Y, Y, Y, Y, O,
   O, Y, Y, Y, Y, Y, Y, O,
   O, Y, Y, R, R, Y, Y, O,
   O, O, Y, Y, Y, Y, O, O,
   O, O, O, Y, Y, O, O, O
   ] 
❼ sense.set_pixels(face)

清单 14-6 绘制一个笑脸表情符号

通过导入 SenseHat 模块来启动程序。然后将 RGB 值分配给四个颜色变量:R 代表红色 ❶,O 代表黑色 ❷,B 代表蓝色 ❸,Y 代表黄色 ❹。为了创建我们所见的黑色,你需要通过将 RGB 设置为 0, 0, 0 来关闭所有颜色 ❷。当然,你也可以调整 RGB 值,创建你自己的自定义颜色。

接下来,创建一个名为 face ❺ 的数组,用来保存每个 LED 的颜色和位置。用适当的颜色变量 ❻ 填充数组。每行列表包含八个条目。例如,输入 B, B, B, B, B, B, B, B 会将 LED 显示屏的顶部一行设置为蓝色,因为变量 B 代表蓝色的 RGB 值。

请注意,数组的最后一行没有以逗号结尾。这表示数组的结束,Python 应该不再期待更多的值。用一个右方括号来关闭数组。

最后,告诉 Python 将数组中的元素写入 LED 显示屏 ❼。这将点亮每个 LED,并赋予其相应的颜色,从而构建出图像。

保存并运行程序。您将在 Sense HAT 显示器上看到一个微笑的脸,如图 14-6 所示。

Image

图 14-6 微笑脸显示在 Sense HAT 上

尝试创建另一个图形——也许是一个动物、汽车、飞机或树。你可能会发现提前通过在类似的 8 × 8 方格中填充方块来绘制图像会很有帮助。

图 14-7 显示了一些示例。你能猜出它们是什么吗?

Image

图 14-7 尝试在 Sense HAT 上显示这些图标。

使用 Grid Draw 程序创建图像

使用数组创建图像虽然很有趣,但也可能很耗时。如果您试图精确地绘制某个图形,使用数组可能会让人沮丧。创建图像的另一种方法是使用 8x8GridDraw,这是一个交互式程序,旨在使在 Sense HAT 上创建图像变得更加容易。

安装后,8x8GridDraw 程序允许您执行以下操作:

  • 在 8 × 8 的网格上创建图像并直接将其写入 LED 显示器。

  • 从九种颜色中进行选择。

  • 将图像导出为一组代码,您可以将其添加到您的 Python 程序中。

  • 导出并保存图像为 PNG 文件。

  • 在 LED 显示器上旋转图像。

  • 添加新帧以创建简单的动画。

这个项目会比您在本章中完成的那些稍微复杂一些,所以我会一步一步地指导您:

  1. 安装软件: 要安装该软件,请打开终端窗口并输入以下命令:

    pi@raspberrypi:- $ sudo pip3 install pypng
    

    这将安装软件库,使您能够将图像导出并保存为一个小的 8 × 8 像素 PNG 文件。

    接下来,输入以下命令安装主程序:

    pi@raspberrypi:- $ git clone https://github.com/topshed/RPi_8x8GridDraw
    

    这会将所需文件下载到一个新文件夹中。完成后,您将拥有一个新文件夹,其中包含多个与 8x8GridDraw 相关的程序。

  2. 创建图像: 让我们尝试创建一个图像。首先,您需要启动程序。返回终端并输入以下命令:

    pi@raspberrypi:- $ cd RPi_8x8GridDraw
    

    该命令导航到 8x8Grid-Draw 程序文件夹。要运行程序,请输入以下命令:

    pi@raspberrypi:- $ python3 8x8grid-sense.py
    

    这将加载主窗口,如图 14-8 所示。

    Image

    图 14-8 Sense HAT 网格编辑器

    点击网格上的圆圈将填充周围的方块颜色。要更改颜色,从右侧选择一个新的颜色。要关闭 LED,双击该方块,它将恢复到原始的透明轮廓。您通过填充特定方块并选择颜色来绘制图像。继续尝试创建一个简单的图像吧。

  3. 显示图像: 一旦您创建了图像(如图 14-9 中的树),您可以将图像写入 Sense HAT。找到并点击LED 上播放按钮,您的图像将出现在 Sense HAT 的 LED 上。快来看看吧!!Image

    图 14-9 点击LED 上播放按钮,将您的绘图显示在 Sense HAT 上。

  4. 导出图像: 完成图像后,你可以导出它。你可以将其导出为代码,然后将其添加到其他程序中,但导出为 PNG 格式要简单得多,所以现在你就会这么做。

    点击图形用户界面上的导出为 PNG按钮,并将文件保存在RPi_8 x 8 GridDraw文件夹中(图 14-10)。要加载该图像,必须将其与程序代码位于同一文件夹中,因此你可能需要将图像从该文件夹复制到保存 Python 程序代码的文件夹中。

    Image

    图 14-10 将文件导出为 PNG 格式会将其保存在RPi_8x8 GridDraw文件夹中。

    这将生成一个小的 8 × 8 PNG 文件,你可以使用以下代码将其加载到 Sense HAT 中:

    sense.load_image("name_of_your_file.png")
    

现在你可以创建图像了,你具备了编写石头、剪刀、布、蜥蜴、斯波克游戏所需的一切。

构建石头、剪刀、布、蜥蜴、斯波克游戏

注意

这个游戏的变种最初由 Sam Kass 和 Karen Bryla 创建( www.samkass.com/theories/rpssl.html)*。

你可能已经熟悉了石头、剪刀、布。你和对手通过用手形成一块石头、一张纸或者一把剪刀来玩,每个物品击败—并被—另外的物品击败。原始游戏的问题是,结果太容易预测(因为只有三种可能的结果,除了平局)。在热门电视剧《生活大爆炸》的第二季中,角色谢尔顿通过使用一种替代版本将经典游戏变得更加刺激和具有挑战性,该版本将斯波克和蜥蜴添加到原本的三种选择中。

要表示斯波克,你可以使用来自电视剧星际迷航的瓦肯手势。要表示蜥蜴,你可以将手做成类似袜子木偶的嘴巴形状。

想知道它们如何工作吗?斯波克击败剪刀并蒸发石头。然而,他会被蜥蜴毒死,并被纸张驳斥。蜥蜴毒死斯波克并吃掉纸张,但它会被石头压碎并被剪刀砍头。太血腥了!添加这些选项创造了更多可能的组合,使整个游戏更刺激,且不易预测。图 14-11 展示了所有手势(箭头指向被给定箭头起点的手势所击败的动作)。

在我们版本的游戏 RPSLS 中,玩家将通过使用 Sense HAT 的摇杆滚动五个图像中的一个来选择其中一个选项。玩家按下摇杆,就像按下按钮一样来选择一个选项。然后,树莓派会随机选择一个选项。游戏将比较两个选项并选出一个胜者。然后,玩家可以选择继续玩下一局或者退出游戏。

Image

图 14-11 如何玩石头、剪刀、布、蜥蜴、斯波克

首先,你需要为每个选项准备一张 8 × 8 的 PNG 图像。你可以自己制作这些图像,或者从 www.nostarch.com/raspiforkids/ 下载本章使用的示例图像,并将它们保存在你为这个项目使用的文件夹中。请注意,如果你自己创建图像,你需要将它们保存为 0.png1.png2.png3.png4.png。这些是程序将要查找的图像文件名。

导入模块并创建变量

打开你的 Python 编辑器,添加 Listing 14-7 中的代码。将程序保存为 RPSLS.py,并确保程序文件保存在与图像相同的文件夹中,否则程序将无法找到它们。

❶ from sense_hat import SenseHat
   import random
   import time
❷ global playersChoice
❸ global count
❹ global computer_choice
❺ gameRunning = True

LISTING 14-7 添加模块和全局变量

通过导入 SenseHat 模块 ❶ 开始程序,该模块使玩家能够控制摇杆、滚动文本并在 LED 矩阵上显示图像。接着导入 random 模块,以便树莓派能够选择一个随机数,并导入 time 模块来添加短暂的暂停。

接下来,设置三个全局变量,程序中的任何地方都可以访问这些变量(与定义在特定函数内部的局部变量不同,局部变量只能在该函数内访问)。

第一个变量 ❷ 存储玩家选择的石头、剪刀、布、蜥蜴或斯波克。你使用第二个变量 ❸ 来存储树莓派做出选择的时间。这个时间将是随机的,制造出树莓派花时间选择的假象。接下来使用第三个变量 ❹ 来存储树莓派的选择,以便稍后使用。最后,创建一个名为 gameRunning ❺ 的变量,用于保存游戏的状态。值为 True 表示游戏正在进行。

准备游戏

Listing 14-8 中的代码为 RPSLS 游戏准备了 Sense HAT。

   # Prepare Sense Hat
❶ sense = SenseHat()
❷ sense.load_image("0.png")
❸ sense.low_light = True # Save your eyes!
❹ playersChoice = 0

LISTING 14-8 创建初始游戏设置

首先,初始化 Sense HAT ❶ 并加载游戏的第一张图像 ❷。然后调低 LED 的亮度 ❸。如果 LED 太亮,这个步骤很有用;较暗的灯光对眼睛更友好。你可以稍后根据自己的喜好调整亮度。

在游戏开始时,将 playersChoice 设置为 0 ❹。这是玩家可以选择的列表中的第一个选项。请记住,在编程中,列表中的第一个选项是位置 0PlayersChoice 变量将保存一个介于 04 之间的数字,表示玩家的选择:0 代表石头,1 代表斯波克,2 代表布,3 代表蜥蜴,4 代表剪刀。游戏开始时,玩家可以通过摇杆浏览选项来选择一个。

设置玩家选择

现在,你将创建一个函数,将玩家的选择从数字转换为对应的石头、剪刀、布、蜥蜴或斯波克图像。输入 Listing 14-9 中的代码。

   # Converts the Number into the choice i.e. lizard, spock, etc.
❶ def number_to_name(number):
       if number == 0:
           return "Rock"
    ❷ elif number == 1:
           return "Spock"
       elif number == 2:
           return "Paper"
       elif number == 3:
           return "Lizard"
       elif number == 4:
           return "Scissors" 

LISTING 14-9 设置玩家的选择

创建并命名函数❶,该函数检查玩家通过操纵杆选择了哪个选项(选项存储为一个数字,然后将该数字转换为选项的名称)。

这个函数使用条件判断❷来检查每个数字,然后为每个被选中的数字分配对应的选项名称。第一个条件判断检查是否选择了值为0的选项,如果是,则返回Rock(石头)。如果没有选择0,程序会检查是否选择了1。如果选择了1,程序返回Spock(斯波克),如果没有选择1,程序会继续检查数字2。程序会一直继续检查直到所有可能的选择都被检查完。

选择一个对象

程序的下一部分创建了一个包含主要游戏机制的函数。玩家将使用 Sense HAT 的操纵杆滚动浏览五个选项。当玩家滚动浏览时,LED 显示屏将显示每个选项。要选择一个选项,玩家必须使用操纵杆点击。将 Listing 14-10 中的代码添加到你的程序中。

def mainGame():
    ### PLAYER SELECTION ###
    # Loops while running variable is True
    running = True
 ❶ global playersChoice
    global computer_choice
    while running == True:
     ❷ sense.set_rotation(90)
     ❸ for event in sense.stick.get_events():     
        ❹ if event.action == "pressed" and playersChoice < 5:
           ❺ if event.direction == "up":
                 print (playersChoice) 
              ❻ sense.load_image(str(playersChoice) + ".png")
                 playersChoice = playersChoice + 1
        ❼ if playersChoice == 5:
                playersChoice = 0    
   # Checks for a 'select / Enter' Choice
         if event.action == "pressed":
          ❽ if event.direction == "middle":
                running = False
             ❾ break # Ends loop and moves onto main game 

LISTING 14-10 编码玩家的对象选择

通过导入之前创建的全局变量❶开始这个函数。添加你的while running = True语句,表示游戏正在进行中。

旋转 LED 上显示的图像,使其与操纵杆正确对齐❷。现在开始检查操纵杆的移动❸,这些移动在 Sense HAT 的操纵杆事件中被称为事件

首先,程序检查操纵杆是否已被按下(移动),并且检查playersChoice是否小于 5。如果值小于 5,说明玩家还没有到达图像的末尾。在程序开始时,你将playersChoice的值设置为 0,这意味着在第一次运行程序时,❹行的条件为True,因为 0 小于 5,因此接下来的代码行会被执行。

现在检查操纵杆是否向上移动,使用event.direction == 'up' ❺。下面的代码将打印出玩家选择的选项名称。在游戏过程中你通常不会看到这个输出,因为 RPSLS 被设计为独立运行。然而,这一行代码对于测试程序中的选择部分是否正常工作非常有用。

程序随后加载第一个图片文件,0.png,它是石头的图片 ❻。接下来的代码会将playersChoice变量增加 1,下一次摇杆向上移动时会加载1.png。最终,playersChoice值会达到列表中的最大值 5,对应最后一个选项,斯波克 ❼。此时,列表中没有其他选项可以选择,因此playersChoice会回到 0,重新加载石头的图片。这个循环将继续,直到玩家通过按下摇杆中的间接按钮 ❽来选择一个物品,这就像按下 ENTER 键,因此选择了玩家的选项。此时,running变量设置为False,从而停止循环。第❾行的 break 将程序带到下一个部分。

通知玩家他们的选项

玩家选择物品后,Listing 14-11 中的代码将通知玩家他们的选择。

   # Message for player about their choice
❶ print ("Your Choice is", playersChoice)
   number = playersChoice - 1
❷ playerMessage = number_to_name(number)
   sense.set_rotation(0) 

LISTING 14-11 通知玩家其选择

第❶行是一个可选的测试,检查程序是否选择了正确的图片文件;当你确认程序正确运行后,可以通过加上井号注释掉该行。第❷行通过number_to_name(number)函数将物品编号传入,该函数你在 Listing 14-9 中编写过。该函数将数字转换为选项名称,并返回该名称,以便在 LED 上显示。选项的名称会被存储在playerMessage变量中。

编写树莓派的选择代码

现在轮到树莓派选择它的物品了。将 Listing 14-12 中的代码复制到你的程序中。

   ### RASPBERRY PI SELECTION ###
   # Raspberry Pi selects a random choice from the options
❶ count = random.randrange(5,50)
   sense.set_rotation(90)
   while count > 1:
    ❷ computer_choice = random.randrange(0,5)
       # print (computer_choice)
       time.sleep(0.1)
    ❸ sense.load_image(str(computer_choice) + ".png")
    ❹ count = count - 1

LISTING 14-12 编写树莓派的物品选择代码

使用random.randrange()函数 ❶生成一个 5 到 50 之间的数字,然后将该数字存储在名为count的变量中。此count值为玩家选择选项与树莓派选择之间创建 0.10 到 5 秒之间的延迟。在此期间,五张图片会反复显示和更换,营造出树莓派在浏览选项并思考选择哪个的错觉。

图片在第❷行被选择,程序会从 0 到 5 之间随机选择一个数字。每个数字都对应一个图片文件。在第❸行,将computer_choice的值转化为字符串并添加.png扩展名,这样传递给sense.load_image()的文件名就会与其中一个图片文件的名字匹配,随后该图片文件被加载。

使用一个小的暂停来让玩家在循环再次运行并显示另一张图片之前查看该图片。通过从count中减去 1 ❹来保持计数递减,这确保延迟最终会达到 0 并结束。

如果count值大于 1,循环继续,显示一张图像,并从count中减去 1。这将一直持续,直到 count 值达到 0。然后,当前显示的图像被选定为树莓派的选择;它已经做出了选择。

显示树莓派的选择

在程序的下一部分,如 Listing 14-13 所示,你将通过在 Sense HAT 上显示来告诉玩家树莓派选择了哪个选项。

   # Message for player about the Raspberry Pi's choice
❶ number = computer_choice
❷ sense.set_rotation(0)
❸ sense.show_message("Raspberry Pi = ", text_colour=[0, 150, 255],
   scroll_speed = 0.06)
   sense.set_rotation(90)
❹ sense.load_image(str(computer_choice) + ".png")
❺ time.sleep(1)
   sense.set_rotation(0)

LISTING 14-13 显示树莓派的选择

将程序的选择存储在number变量中❶,以便在程序的其他部分使用number来表示树莓派的选择。

将图像旋转调整到正确的角度❷。然后,在 LED 显示屏上滚动一条消息,宣布树莓派的选择❸。代码text_colour=[0, 150, 255],其中三个值代表 RGB 值,设置文本的颜色。你还可以调整滚动的速度,设定在 0 到 1 之间的任何值。

接下来,加载与树莓派选择的数字对应的图像❹。例如,如果树莓派选择了第 3 个选项,程序将加载并显示蜥蜴的图像。在程序计算游戏的赢家之前,添加一个短暂的暂停❺。

选择一个赢家

mainGame()函数的最后部分,如 Listing 14-14 所示,比较玩家的选项与树莓派的选项,并计算该轮的赢家。这使用了一种叫做模运算的数学分支。

   ### WINNER CALCULATED ###
❶ result = (int(computer_choice - (playersChoice-1))) % 5

❷ if result == 0:
   ❸ sense.show_message("Player and Pi Tie!", text_colour=
      [0, 0, 255], scroll_speed = 0.08)

❹ elif result >=3:
   ❺ sense.show_message("Player Wins!", text_colour=
      [0, 255, 0], scroll_speed = 0.08)

❻ else:
   ❼ sense.show_message("Raspberry Pi Wins!", text_colour=
      [255, 0, 0], scroll_speed = 0.08)##?? 

LISTING 14-14 计算赢家

首先,创建一个名为result❶的变量来存储一个特定的值,您将用它来确定谁赢了。这个计算背后的数学原理很有趣。通常,这一行代码将树莓派选项对应的值从玩家选项值中减去(玩家选项已经减去 1)。例如,如果树莓派的值是 5,而玩家的值是 3,您会计算 5 - (3 - 1),即 5 - 2,结果为 3。然后,求这个数字对 5 的模;是除法的余数。在这个例子中,答案是 2,因为 3 除以 5 的商是 0,余数是 2。数字 2 随后用于确定玩家或树莓派是否赢得了游戏。

如果result值等于 0,说明两位玩家选择了相同的选项,游戏以平局结束❷。使消息Player and Pi Tie!滚动显示在 LED 屏幕上❸。同样,你可以调整文本滚动的颜色和速度。

如果result的值大于或等于 3,说明你的选项击败了树莓派的选项,你赢得了游戏❹。使消息Player Wins!滚动显示在 LED 屏幕上❺。

游戏的最终可能结果是树莓派的选择胜过你的选择❻。如果发生前三种以外的任何情况,就会出现这种情况。如果发生这种情况,显示一条消息告诉玩家树莓派获胜。你可以自定义消息的颜色和速度❼。

这完成了mainGame()函数的所有代码,控制着石头、剪子、布、蜥蜴、斯波克游戏的基本玩法机制。回顾一下:这个函数让玩家选择一个选项,然后让树莓派选择一个选项(延迟一段时间,使其看起来像是树莓派在思考)。接着,它比较两个选项来计算游戏的胜者,并滚动一条更新玩家结果的消息。

开始新游戏

你快完成了!Listing 14-15 是游戏的初始欢迎部分。它介绍了游戏,然后运行mainGame()函数(而之前你只是定义了mainGame()函数)。一轮结束后,它还提供了玩家通过向上移动摇杆重新开始游戏或通过向下移动摇杆结束游戏的选项。

### START THE GAME ###
sense.show_message("Welcome to R.P.S.L.S!", text_colour=[155, 100,
30], scroll_speed = 0.08)
sense.show_message("Please use 'Up' to select", text_colour=[155,
255, 255], scroll_speed = 0.05)
sense.load_image("0.png")

LISTING 14-15 启动游戏

开始时添加欢迎消息。你可以自定义消息,但根据我的经验,我发现简单的消息效果最佳,因为大多数玩家只希望游戏开始。然后添加第二条消息,告诉玩家如何选择选项。再次强调,可以调整每条消息的颜色和滚动速度。如果一开始你想要更慢的滚动速度,可以将其设置为 0.60。一旦你熟悉了游戏,你就不想再等待慢速滚动的文本了。

然后添加sense.load_image("0.png"),该代码加载第一张图片并显示在 LED 显示屏上。这是你的石头图片。

这三行代码只会在程序第一次执行时运行,因为你不需要每次玩游戏时都显示欢迎消息和说明,而是只需要在程序第一次加载时显示。因此,将这些代码行保留在mainGame()函数外。

重新开始游戏?

用代码结束你的程序,检查玩家是否想要再次玩游戏。将代码添加到 Listing 14-16 中。

❶ while gameRunning == True:
    ❷ play_again = 1 
    ❸ mainGame()
       sense.show_message("Play Again?", text_colour=[255, 255, 255],
       scroll_speed = 0.08)
❹ while play_again == 1:
    ❺ for event in sense.stick.get_events():
           if event.action == "pressed":
               if event.direction == "up":
                ❻ play_again = 0

       ❼ if event.action == "pressed":
              if event.direction == "down":             
               ❽ sense.show_message("Bye Bye", text_colour=
                  [255, 255, 255], scroll_speed = 0.08)
               ❾ play_again = 0
               ❿ gameRunning = False 

LISTING 14-16 开始新游戏

为此,使用一个名为gameRunningwhile循环,并将其设置为True;只要变量设置为True,游戏就会继续运行❶。

现在创建另一个变量,用来保存一个值,表示玩家是否想要重新开始游戏❷。使用值 1 表示玩家想要开始一局新游戏。值为 0 则表示玩家不想重新开始游戏。

下一行代码调用了mainGame()函数,负责主游戏元素并启动游戏。游戏结束后,LED 显示屏上滚动出现一条消息,询问玩家是否想再次游戏 ❸。现在,由于play_again变量被设置为 1 ❹,程序等待玩家移动摇杆 ❺。如果玩家将摇杆向上移动,play_again变量的值会被设为 0 ❻。这将打破play_again循环 ❹并返回主循环,加载mainGame()函数 ❸并重新开始游戏。

如果玩家将摇杆向下移动 ❼,一条消息会显示,告知玩家游戏即将结束 ❽。play_again变量被重置为 0 ❾,最重要的是,gameRunning变量被设置为False ❿。因此,当程序返回到起始点时,gameRunning不再是True,意味着mainGame()函数不会加载,程序结束。要重新开始游戏,必须通过按 F5 重新启动整个程序。

这就是“石头、剪刀、布、蜥蜴、斯波克”游戏的完整程序代码。请确保你的五张图片与程序代码保存在同一文件夹中,然后进行测试运行。很多缩进层级需要完全准确,因此如果你的程序无法按预期运行,请确保仔细检查它们。祝你玩得开心!

总结

当你让 RPSLS 游戏正常运行后,你可以自定义你的游戏版本。为什么不尝试为石头、剪刀、布、蜥蜴、斯波克创建自己的图像呢?加入音效或得分系统。修改代码,以便你可以与另一个人类玩家而非树莓派对战。如果你想看到 Sheldon 的完整效果,可以查看https://www.youtube.com/watch?v=hoV-SNpdyW8

适合年龄:11 岁及以上

12 个有趣的项目

Image

树莓派^®是一款廉价的口袋大小计算机,能够帮助你构建和编写自己的硬件项目。

Raspberry Pi 项目儿童版将教你如何利用树莓派的强大功能,通过简单的代码和常见的材料(如网络摄像头、麦克风和 LED 灯)创建 12 个酷炫的项目。逐步的说明和详细的图解将引导你完成每一个项目。

在简要介绍 Python 编程语言后,你将学习如何:

Image 创建一个能自动开关的 LED 夜灯

Image 设置树莓派相机来拍摄自拍照和视频

Image 设置网络摄像头将视频流传输到你的手机

Image 在 Minecraft^®中操控环境

Image 劫持本地无线电波播放你自己的歌曲和录音

Image 配置树莓派发送短信到手机

Image 通过 Wi-Fi 和蓝牙追踪家人位置

Image 创建一个 MP3 播放器

Image 设置相机,拍摄野生动物的运动触发照片

Image 使用手机控制家中的电子设备

Image 教授 Raspberry Pi 从你的 Twitter 动态中朗读帖子

Image 与 Raspberry Pi 一起玩石头剪刀布

Raspberry Pi 项目:为孩子们 将带来数小时的乐趣和无尽的灵感!

关于作者

Dan Aldred 是一位计算机科学教师、自由作家和黑客。他一直倡导使用 Raspberry Pi 作为学习和创造力的工具,并且是 Raspberry Pi 认证教育者。Aldred 曾领导赢得首届 Astro Pi 比赛的学生团队;他们的代码现在在国际空间站绕地球运行。他目前居住在英国。

Image

极客娱乐中的顶级之选™

www.nostarch.com

脚注

第九章

^(1) www.ripe.net/about-us/press-centre/understanding-ip-addressing

posted @ 2025-11-30 19:37  绝不原创的飞龙  阅读(0)  评论(0)    收藏  举报