Arduino-项目手册卷二-全-
Arduino 项目手册卷二(全)
原文:
zh.annas-archive.org/md5/bdc217a3e4481b8d06ded3609a8d8149译者:飞龙
前言
欢迎来到Arduino 项目手册第二卷。如果你没有阅读第一卷,别担心——本书中的每个项目都是完全独立的,并旨在温和地向你介绍使用 Arduino 的世界。我们将在这里和下一章中涵盖一些开始使用 Arduino 的重要方面,因此如果你已经读过第一卷,你可以快速浏览作为复习,或者直接跳到新的项目部分。
本书使用 Arduino Uno,这是一个小巧、便宜的计算机,可以编程控制无数的设备和创作。你很快就会使用 Arduino 控制一系列项目,比如音乐键盘、温控风扇、数字温度计、指纹输入系统等。
Arduino 板由两个主要部分组成:硬件,即微控制器,它是板子的“大脑”;以及你将用来将程序发送到微控制器的软件。这个软件叫做 Arduino 集成开发环境(IDE),可以免费下载安装,我将在本书的入门部分教你如何使用它来设置一个简单的项目。
关于本书
是什么激发了我写这本书的灵感?互联网上充斥着关于 Arduino 和潜在项目的教程、视频和文章,但许多缺乏详细的视觉效果或构建这些项目所需的代码。本书旨在帮助你构建简单的项目,激励你在应用你将学到的技能和技术时,创造自己的发明。
注意
在本书中,你将在面包板上创建你的项目。这是学习电路工作原理的最佳方式,因为连接不是永久性的;如果你犯了错误,可以直接拔掉电线或组件,再试一次。
每个项目都包括它将要做的事情的描述、你需要的物品、设置的图片、简单的逐步说明以及用于快速连接参考的表格、一个电路图(见图 1)和必要的代码,因此你无需担心在开始之前学习编程。早期的项目提供了对代码中发生的事情的简单解释,帮助你理解编程过程,以便在需要时进行自己的修改。如果你不想输入那么多代码,可以下载草图,链接在www.nostarch.com/arduinohandbook2/。
图 1: 本书中的电路图是使用 Fritzing(www.fritzing.org/)创建的,这是一个免费的开源程序。

在每个项目的开始,我会列出除了 Arduino Uno 之外所需组件的费用(见表 1)和预计的构建时间。在结尾,我会提供针对该项目的故障排除部分。
表 1: 本书中使用的费用指示
| 指示 | 费用 |
|---|---|
| $ | $1–$9 | |
| $$ | $10–$19 |
| $$ | $20–$29 | |
| $$$ | $30+ |
我写这本书是为了教你如何创建自己的电子产品。通过提供技术知识,我让你能够专注于创意设计部分。我的目的是让你理解电路的功能,这样你就可以发挥想象力,将这些电路应用到实际中。虽然我没有深入讲解电子学理论或编程,但本书中的项目逐步增加难度,会为你提供一个很好的起点。
本书为你提供了实用的信息,例如,你可以参考引脚连接并在需要时在不同的项目中复制它们。你还可以将多个项目结合起来,制作更复杂有趣的电子产品。许多 Arduino 书籍侧重于编程元素,这对于某种学习方式非常有帮助,但我认为即插即用的电子学也有其独特的价值。通过按照项目中的步骤操作,你将在过程中不断学习。
我写了这本书,它是我当初开始学习 Arduino 时所寻找却找不到的那本书。我希望你能像我写这本书时那样享受阅读和动手实践的过程。
本书的组织结构
我建议你先尝试一些早期的项目,因为你会在那里找到对更复杂项目有帮助的信息。但如果你看到一个自己喜欢的项目并且有信心完成它,你也可以跳过其他部分直接开始。本书的结构如下:
入门:开始使用 了解 Arduino Uno 的基本知识,学习如何使用面包板,然后通过一个简单的程序测试你的板子,最后学习如何进行焊接。
第一部分:LEDs 在这里,你将从学习如何使用可变电阻控制简单的发光二极管(LEDs)开始,然后将组件组合起来,构建一个光敏 LED、一个滚动文本显示器、一个闪烁的多彩指南针等等。
第二部分:声音 在这一部分,你将使用一个压电元件,这是一种发出声音的设备,通过一个音乐键盘来制作旋律,并创建一个简单的音频可视化器,让 LED 灯随着你的音乐跳动。
第三部分:马达 这些项目使用各种类型的马达来使你的创意作品动起来。你将构建一个模拟拨号器来测量光照强度,学习步进电机的工作原理,并制作一个温控风扇来保持凉爽。
第四部分:LCD 屏幕 LCD 屏幕在许多项目中都非常有用,用于显示信息和结果。在这些项目中,你将学习如何设置串行 LCD 屏幕,并构建一个可拆解的炸弹游戏、一个超声波测距仪、一个移动版乒乓游戏,甚至一个酒精呼吸测试仪。
第五部分:安全 使用运动传感器保护你的空间,当有人进入时触发超声波喷水枪,并使用指纹扫描仪创建一个安全系统,防止未经授权的人进入。
第六部分:智能机器 在这一部分,你将结合 Arduino 与电机和传感器,创建一个智能机器人,使用蓝牙技术控制灯光,甚至还可以制作一个 GPS 测速仪来跟踪你的运动。
在本书的结尾,我提供了一些有用的参考信息,包括对一些常见程序错误的回顾及其修复方法、书中使用的组件及购买地点的信息,以及一个关于 Arduino Uno 引脚的参考表。

第一章:入门:开始使用
在你开始使用 Arduino 之前,有一些你需要了解和做的事情。首先,让我们看一看本书所需的硬件和软件。接下来,你将通过一个简单的 LED 项目来测试 Arduino,并开始一些有用的技巧,比如焊接和下载有用的代码库。
硬件
首先,让我们来看看 Arduino Uno 板和一些你将在几乎每个项目中使用的硬件。
Arduino Uno
市面上有许多类型的 Arduino 板,但本书仅使用最流行的 Arduino Uno,如图 0-1 所示。Arduino Uno 是开源的(意味着其设计可以自由复制),因此除了官方板(价格约为 25 美元),你还可以找到许多兼容的克隆板,价格约为 15 美元。
图 0-1: Arduino Uno 板

Arduino 通过向你连接的组件发送信息来控制它们,例如电机或 LED,这些信息作为输出(从 Arduino 发送出去的信息)。Arduino 从传感器读取的数据则是输入(进入 Arduino 的信息)。Arduino 上有 14 个数字输入/输出引脚(引脚 0–13),每个引脚可以设置为输入或输出(有关完整的引脚参考表,请参见“Arduino 引脚参考”,见第 253 页)。
电源
当你将 Arduino Uno 板连接到电脑上传程序时,它通过电脑的 USB 端口供电。当 Arduino 未连接到电脑时,你可以通过连接一个 9 伏交流适配器或一个带 2.1 毫米插头的 9 伏电池包来使其独立运行,插头的中心引脚连接到正极电源,如图 0-2 所示。只需将插头插入 Arduino 的电源插座即可。
图 0-2: 9 伏电池包,你可以将其插入 Arduino,为其提供电源

面包板
面包板作为电子原型制作的构建基础。在本书的所有项目中,你都将使用面包板,而不是将零件焊接在一起。
面包板这个名称可以追溯到电子项目最初是在木板上制作的时代。当时的电子爱好者将钉子钉入木板,并将电线绕在钉子上,通过这种方式连接组件,而无需将其永久焊接。今天的面包板由塑料制成,预先钻有孔(称为连接点),你可以将组件或电线插入这些孔中,连接点下方有夹子将其固定。连接点之间通过导电材料连接,这些导电材料沿着板底部延伸,如图 0-3 所示。
图 0-3: 面包板连接

面包板有不同的大小。为了构建本书中的项目,理想情况下你需要三块面包板:一块全尺寸面包板,通常有 830 个孔;一块半尺寸面包板,约有 420 个孔;以及一块迷你面包板,有 170 个孔。全尺寸面包板适用于使用 LCD 屏幕或许多组件的项目,而半尺寸和迷你面包板更适合较小的项目。对于本书中的项目,我建议你购买像图 0-3 中显示的那种面包板,带有红蓝线和中心断裂的孔。
提示
使用红色导线连接 5V 电源、黑色导线连接地线(GND)是很有用的。其余的导线可以选择任何颜色。
主面包板区域有 30 列连接点,它们是垂直连接的,如图 0-3 所示。你常常需要将组件放置在跨越面包板中心断裂的地方,以完成电路。这个断裂有助于防止组件短路,这可能会干扰你的项目,甚至损坏你的组件。随着你开始搭建电路,你会学到更多关于这一点的知识。
顶部和底部的蓝色和红色线条是电源轨,用于为插入主面包板区域的组件供电(参见图 0-4)。电源轨将轨道中的所有孔连接在一起;红线是正电源,蓝线是负电源(或地线,如你常常看到的那样)。
图 0-4: 正负面包板电源轨

跳线
你将使用跳线在面包板上进行连接。跳线是带有塑料固定件的实心导线,每个端头都有一个塑料外壳,使得插入和拆卸导线变得更容易。(如果你有自己的导线,也可以使用,但要确保使用实心导线——多股导线不足以牢固地插入孔夹中。)
当你将跳线插入面包板的孔时,它会通过下面的小弹簧夹固定住,从而在该行中建立电气连接。然后你可以将一个组件放置在相邻的孔中,帮助完成电路,正如图 0-5 所示。
图 0-5: 一个示例面包板电路

注意
由于 IDE 版本更新较快, 我不会详细介绍安装过程,但安装应该很简单,而且 Arduino 网站上的安装说明非常清晰。所有版本的 IDE 和针对您操作系统的安装详细信息都可以在 www.arduino.cc/ 找到。
编程 Arduino
为了让我们的项目按照预期运行,我们需要编写程序来给 Arduino 发出指令。我们通过 Arduino 集成开发环境(IDE) 来做到这一点。Arduino IDE 可以从 www.arduino.cc/ 免费下载,并且支持在 Microsoft Windows、OS X 和 Linux 上运行。它使你能够编写计算机程序(一系列逐步指令,在 Arduino 的世界里称为 sketches),然后通过 USB 电缆将其上传到 Arduino。你的 Arduino 将根据与外部世界的交互执行这些指令。
IDE 界面
当你打开 Arduino IDE 时,它应该看起来像 图 0-6。IDE 界面分为顶部的工具栏,包含最常用的功能按钮;中央的 sketch 窗口,你将在其中编写或查看程序;以及底部的串口输出窗口。串口输出窗口显示了你的 PC 和 Arduino 之间的通信信息,如果 sketch 编译出错,它也会列出相关错误。
图 0-6: Arduino IDE

Arduino Sketches
我将在每个项目的相关部分为你提供相应的 sketch,并在那里详细讲解。所有的 sketch 都可以从 www.nostarch.com/arduinohandbook2/ 下载。
和任何程序一样,sketches 是一组非常严格的指令,对错误非常敏感。最好下载 sketch 文件并在 IDE 中打开,而不是直接从书中复制它。为了确保程序正确运行,点击屏幕顶部的绿色勾号按钮。这是验证按钮,它会检查是否有错误,并通过串口输出窗口告诉你 sketch 是否已正确编译。
库
在 Arduino 的世界里,库 是执行特定功能的代码片段。你可以通过简单地添加一个命令,从库中调用代码,而不必每次在 sketch 中反复输入相同的代码。这个快捷方式节省了时间,使你更容易连接传感器、显示器或模块等设备。
Arduino IDE 包含了多个内置库——例如 LiquidCrystal 库,它使得与 LCD 显示屏的通信变得简单——并且还有许多更多库可以在网上找到。为了完成书中的项目,你需要导入以下库:PololuLedStrip、FastLED、HMC5883L、Keypad、Tone、Adafruit_GFX、Adafruit_SDD1306、NewPing、Adafruit 指纹传感器以及 Adafruit 电机扩展板。你可以在 www.nostarch.com/arduinohandbook2/ 的资源中找到所有需要的库。
安装库
下载完库之后,你需要安装它们。对于 Arduino 版本 1.0.5 及更高版本,按照以下步骤安装库:
-
选择 Sketch ▸ Include Library ▸ Add .ZIP Library。
-
浏览到你下载的 ZIP 文件并选择它。在旧版本的 Arduino 中,解压库文件并将整个文件夹及其内容放入 sketchbook/libraries 文件夹(Linux),My Documents\Arduino\Libraries(Windows),或 Documents/Arduino/libraries(OS X)。
要手动安装库,首先进入包含库的 ZIP 文件并解压它。例如,要安装一个名为 keypad 的库,压缩文件名为 keypad.zip,你需要解压 keypad.zip,它将扩展为一个名为 keypad 的文件夹,里面包含类似 keypad.cpp 和 keypad.h 的文件。解压后,将 keypad 文件夹拖到操作系统中的 libraries 文件夹:Linux 中为 sketchbook/libraries,Windows 中为 My Documents\Arduino\Libraries,OS X 中为 Documents/Arduino/libraries。然后重启 Arduino 应用程序。
库在草图的开始部分列出,并且很容易识别,因为它们以 #include 命令开头。库名被 < > 括起来,并以 .h 结尾,像这样调用 Servo 库的代码:
include <Servo.h>
现在就安装你在项目中需要的库,以便稍后节省一些时间。
测试你的 Arduino:让 LED 闪烁
让我们从经典的第一个 Arduino 项目开始:让 LED 闪烁(LED 是 发光二极管 的缩写,类似于一个小灯泡)。这不仅是确保你的 Arduino 正常工作的最简单方式,还能让你了解一个简单的草图。Arduino 一次只能运行一个程序,因此,一旦你将草图上传到 Arduino,该草图将在每次打开 Arduino 时运行,直到你更改它为止。
构建
在这个项目中,我们将使用 IDE 中提供的 Blink 示例草图。Blink 程序让 LED 点亮 1 秒钟,然后熄灭,反复进行。LED 只能在电流流动的一个方向上工作,因此它的长引脚必须连接到正电源。LED 需要一个 限流电阻,否则灯泡可能会烧坏。Arduino 的 13 号引脚内置了一个电阻,我们将使用它。
按照以下步骤设置你的测试:
-
将 LED 的长引脚(正极)插入 Arduino 的 13 号引脚,如 图 0-7 所示。将短引脚(负极)连接到 13 号引脚旁边的 GND 引脚。
图 0-7: Blink 项目设置
![Image]()
-
使用 USB 电缆将 Arduino 连接到你的计算机。
-
打开电脑上的 Arduino IDE,然后从下拉菜单中选择 文件 ▸ 示例 ▸ Blinking LED。草图将在 IDE 的主程序区域显示。
➊// Blinking LED 项目 - 这个示例代码属于公共领域
➋ int led = 13;
➌ void setup() {
➍ pinMode(led, OUTPUT);
}
➎ void loop() {
➏ digitalWrite(led, HIGH);
➐ delay(1000);
➑ digitalWrite(led, LOW);
➒ delay(1000);
➓ }
-
在 IDE 中,点击 验证 按钮检查草图是否工作正常。
-
点击 上传 按钮,将草图发送到你的 Arduino。运行此代码应该会让你的 LED 闪烁。
理解草图
以下是草图中每行代码的作用:
➊ 这是一条注释。程序中以 // 开头的任何行都仅供用户阅读,Arduino 会忽略它,因此你可以使用这种方式输入备注并描述你的代码(这叫做 注释 代码)。如果注释超过一行,开始的第一行用 /*,结束时用 */,中间的内容 Arduino 会忽略。
➋ 这给引脚 13 命名为 led。草图中每次提到 led 时都将指代引脚 13。
➌ 大括号 {} 中的代码在程序启动时运行一次。左大括号 { 开始了设置代码。
➍ 这告诉 Arduino 引脚 13 是一个输出引脚,表示我们要从 Arduino 向 LED 发送电力。闭括号 } 结束了设置代码。
➎ 这创建了一个循环。loop() 语句后的所有内容(即括号 {} 中的内容)将在 Arduino 上电后运行一次,并持续重复直到断电。
➏ 这告诉 Arduino 将 led(引脚 13)设置为 HIGH,从而向该引脚提供电源。可以将其理解为开启该引脚。在此草图中,这会让 LED 点亮。
➐ 这告诉 Arduino 等待 1 秒钟。Arduino 上的时间以毫秒为单位,所以 1 秒 = 1,000 毫秒。
➑ 这告诉 Arduino 将 led(引脚 13)设置为 LOW,这会切断电源并关闭该引脚,从而关闭 LED。
➒ 再次告诉 Arduino 等待 1 秒钟。
➓ 这个闭括号结束了循环。所有在初始 setup 代码后的代码都必须包含在大括号内。缺少括号很容易被忽视,是导致草图无法正确编译的常见原因。此闭括号之后,代码会返回到循环开始处的 ➎。
现在你已经测试了你的 Arduino,并理解了草图的工作原理以及如何上传它,我们将看看你完成本书中所有项目所需的组件。有关每个组件的更多细节、外观以及功能,请参考 第 238 页 的 “组件” 部分。
项目组件清单
这是完成本书中所有项目所需物品的完整列表。最重要的部分当然是 Arduino 主板,所有项目都使用 Arduino Uno R3 版本。只有官方的板子被称为 Arduino,但你会发现来自像 SlicMicro、Sainsmart 和 Adafruit 等公司的兼容克隆板。(你可以在 arduino.cc/en/Main/Buy/ 上找到官方供应商的列表。)
你可以单独购买每个项目,但我建议购买一个电子爱好者入门套件或 Arduino 套件,其中会提供你这里列出的一些物品。有关推荐供应商的清单,请参见第 249 页中的“零售商名单”部分。或者,每个项目都会开始列出所需的零件清单,所以你可以翻到一个感兴趣的项目,获取你所需的组件。
1 个 Arduino Uno R3(或兼容型号)
1 个 9V 电池包,带 2.1 mm 插孔,可容纳 6 节 AA 电池
1 个 9V 电池卡扣和电池
3 块面包板:1 块全尺寸,1 块半尺寸,1 块迷你尺寸
50 根公对公跳线
10 根母对公跳线
固体芯线
9 个 220-欧姆电阻
4 个 10k-欧姆电阻
8 个 1k-欧姆电阻
40 个 5 毫米 LED,颜色包括红色、绿色、黄色、蓝色(每种 10 个)
1 个 RGB 公共阴极 LED
1 个 RGB LED 灯带(WS2812B 5V 32-LED 灯带)
1 个 Adafruit NeoPixel 环形灯带,含 16 个 RGB LED
1 个 HMC5883L 三轴传感器
2 个 50k-欧姆电位器
1 个 10k-欧姆电位器
8 个瞬时触摸按钮
1 个七段单数字公共阴极 LED
1 个压电蜂鸣器
1 个 3.5 毫米母耳机插孔
1 个 Tower Pro SG90 9g 伺服电机
1 个光敏电阻(光依赖电阻,或 LDR)
1 个 28BYJ-48 步进电机,带 ULN2003 驱动模块
1 个 HC-SR04 超声波传感器
1 个 3×4 薄膜键盘
1 个 LM35 温度传感器
1 个 12V 小型计算机冷却风扇
1 个 5V 单通道继电器模块
1 个 HD44780 16×2 LCD 屏幕
1 个诺基亚 5110 LCD 屏幕
1 个串行 LCD 屏模块
1 个 OLED 单色屏(128×64)
1 个 8×8 LED Maxim 7219 矩阵模块
1 个 Keyes MQ3 酒精传感器模块
1 个光学指纹传感器(ZFM-20 系列)
1 个 L293d 电机驱动板
1 个机器人底盘套件,包括两个直流电机和车轮、中央轮、底座和配件
1 个以太网扩展板 W5100 LAN
1 根以太网电缆
1 个 WLToys V959-18 水射流手枪
1 个 HC-06 蓝牙模块
1 个 Ublox NEO-6M GPS 模块,飞机飞行控制器和天线
快速焊接指南
本书的大多数项目不需要焊接,但有一些组件可能会带有未焊接的引脚(图 0-8),以便运输时更为方便。引脚带有条状,可以根据需要轻松折断为适合的长度。
图 0-8: 引脚

例如,项目 25 中使用的 GPS 模块没有附带引脚,因此我将解释如何将这些引脚焊接到位。一个通用的 30 瓦特焊接铁,配有细尖头,应该能够满足你的需求。值得购买一个包含焊接铁、支架和焊料的套件(图 0-9)。
图 0-9: 焊接铁

-
插入你的焊接铁,并至少等待 5 分钟让它达到工作温度。
-
要进行焊接,取下所需数量的引脚条,并按图 0-10 所示插入到模块中。
图 0-10: 插入引脚到模块中
![Image]()
-
现在开始固定引脚,从最左边的引脚开始。将加热的烙铁尖端同时接触到引脚和模块接触点。只需保持大约 2 秒钟。保持烙铁不动的同时,向接点处添加焊锡;焊锡应该融化并流动,形成一个焊点。注意,不要将焊锡直接加到烙铁上,而是加到你正在焊接的接点上。迅速移开烙铁和焊锡——接触超过几秒钟可能会损坏你的元件。
-
一个好的焊点应该看起来像一个光亮的锥形体(图 0-11)。只要稍加练习,你就能迅速焊接得很干净。
图 0-11: 焊点应该像这样。
![Image]()
安全第一
焊接烙铁非常非常热,必须在成人监督下小心使用。以下是一些安全小贴士:
• 确保使用烙铁架,切勿将热的烙铁直接放在桌面上。
• 在通风良好的房间内焊接。融化的焊锡释放出的烟雾可能有害。
• 将易燃物质远离工作区域。
• 将设备放在儿童接触不到的地方。
• 戴好眼部防护。
• 等待烙铁完全冷却后再存放。
LED
第二章:LED 灯条**
在这个项目中,我们将使一排 LED 按顺序来回闪烁,类似于 1980 年代 TV 剧集 Knight Rider 中的 KITT。


所需组件
Arduino 板
面包板
跳线
8 个 LED
8 个 220 欧姆电阻
工作原理
当小电流通过 LED 时,它会发光。LED 是极性化的,这意味着一端是正极,另一端是负极。这是因为 LED 只能在电流单向流动时工作,从正极到负极。LED 的长脚是正极,必须连接到正电源。Arduino 的草图控制闪烁的顺序。
LED 是非常精细的元件,只需少量电压就能点亮——比 Arduino 提供的电压还要小。为了防止 LED 因过多电压而烧坏,我们使用电阻,它限制了通过 LED 的电压。
你可以更改 LED 的颜色,并使用这个灯条来装饰汽车、电动滑板车、自行车、相框、低音炮或几乎任何其他物品。在 Uno 上,你最多可以连接 10 个 LED,而不会用尽引脚。
搭建过程
-
将 LED 插入面包板时,较短的负极脚应插入面包板顶部的 GND 导轨。然后将此导轨连接到 Arduino 的 GND,如图 1-1 所示。
图 1-1: LED 按顺序来回闪烁。LED 的短脚连接到面包板的 GND 导轨,长脚通过电阻连接到 Arduino。
![Image]()
-
按照下图的电路图,将 LED 依次连接到 Arduino 的数字引脚 2 到 9。每个 LED 与数字引脚之间需要放置一个 220 欧姆电阻,确保电阻跨越面包板的中间分隔。
LED Arduino 正极脚 通过电阻连接到 2–9 引脚 负极脚 GND -
检查你的设置与图 1-2 中的对比,然后上传下面的代码“草图”。
图 1-2: LED 灯条的电路图
![Image]()
草图
该草图将连接到 LED 的引脚设置为输出,然后定义一个函数以同时关闭所有 LED。这个函数会在循环周期中调用,将 LED 关闭,然后依次点亮每个 LED,每次点亮之间有 200 毫秒的延迟,从而创建一个扫过效果。另一个循环则将顺序反向传输。
// 经由友好许可使用
// Warwick A Smith, startingelectronics.com
// Knight Rider 在 8 个 LED 上显示
void setup() {
for (int i = 2; i < 10; i++) { // 选择引脚 2-9
pinMode(i, OUTPUT); // 将引脚设置为输出模式
}
}
// 定义函数以同时关闭所有 LED
void allLEDsOff(void) {
for (int i = 2; i < 10; i++) {
digitalWrite(i, LOW);
}
}
// 按顺序从左到右点亮 LED
void loop() {
for (int i = 2; i < 9; i++) { // 每个 LED 执行一次循环
allLEDsOff(); // 关闭所有 LED
digitalWrite(i, HIGH); // 点亮当前 LED
delay(200); // 延迟 200 毫秒
// 然后重复循环以移动到下一个 LED
}
for (int i = 9; i > 2; i--) { // 从右到左点亮 LED
allLEDsOff();
digitalWrite(i, HIGH);
delay(200);
}
}
故障排除
问: 代码编译通过,但部分或所有 LED 未按预期点亮。
• 如果没有任何 LED 亮起,请确保将 Arduino 的 GND 线连接到面包板的正确电源轨,并且 Arduino 已经连接了电源。
• 如果只有部分 LED 亮起,检查 LED 是否正确插入,较长的引线应连接到正电源,较短的引线应连接到 GND。由于 LED 是有极性的,必须正确连接。检查电阻是否已完全插入,并与相应的 LED 引脚对齐在同一排。
• 确保 LED 连接到 Arduino 中定义的引脚,详见“草图”第 19 页(page 19)。草图的第一部分将引脚 2-9 定义为输出,因此应该使用这些引脚。
• 如果 LED 仍然无法点亮,可能是 LED 已经烧坏或出现故障。一种简单的检查方法是将该 LED 与序列中的另一个 LED 交换,看看是否能解决问题。如果发现 LED 在另一个位置能正常工作,说明电阻可能损坏或没有完全插入。根据结果,替换 LED 或电阻为正常的组件。
光敏夜灯**
这个项目是一个简单的光敏电阻功能测试:我们将制作一个夜灯,根据检测到的光线强度,夜灯的亮度会变化。


所需部件
Arduino 板
面包板
跳线
光敏电阻
LED
10k 欧姆电阻
工作原理
光敏电阻 是一种对光线敏感的可变电阻;光线越少,它提供的电阻值越大。这个电阻值会改变发送到 Arduino 输入引脚的电压,进而将该电压值发送到输出引脚作为 LED 的电源电平,因此在光线较弱的情况下,LED 将会变得更亮。光敏电阻有不同的类型,但通常都有一个小而透明的椭圆形头部,带有波浪形的线条(见图 2-1)。光敏电阻没有极性,因此连接引脚时方向无关紧要。
这里的原理类似于儿童夜灯的工作方式。你可以使用光敏电阻来控制的不仅仅是 LED,正如我们将在接下来的章节中看到的那样。由于我们只有两个电源和 GND 连接,这里我们不会使用面包板电源轨。
图 2-1: 光敏电阻

制作过程
-
将光敏电阻插入面包板,一端连接到 Arduino 的 GND,另一端连接到 Arduino 的 A0。
-
将一个 10k 欧姆电阻的一端连接到+5V,另一端连接到 A0 光敏电阻引脚,如图 2-2 中的电路图所示。
图 2-2: 光敏 LED 的电路图
![图片]()
-
将 LED 的长脚(正极)直接插入 Arduino 的 13 号引脚,短脚(负极)直接插入 Arduino 的 GND。我们通常会使用电阻来限制 LED 的电流,但在这里不需要电阻,因为 Arduino 的 13 号引脚已内置电阻。
-
上传代码到"草图"下面。
草图
草图首先将光敏电阻连接到 Arduino 的 A0 引脚作为我们的输入,将 LED 连接到 13 引脚作为我们的输出。我们通过Serial.begin(9600)来启动串行通信,这将在 Arduino 连接到计算机时将信息发送到 Arduino 的串口监视器。这意味着光敏电阻的电阻值将显示在你计算机上的串口监视器中,如图 2-3 所示。
图 2-3: 串口监视器将显示光敏电阻的电阻值。

循环读取光敏电阻的模拟值,并将其作为电压值发送到 LED。A0 引脚可以读取 1,024 个值,这意味着 LED 有 1,024 个可能的亮度级别。这么多级别之间的微小变化不容易被察觉,因此我们将这个数字除以 4,缩小到仅有 256 个值,使得检测 LED 电压变化变得更加容易。
int lightPin = A0; // 连接到光敏电阻的引脚
int ledPin = 13; // 连接到 LED 的引脚
void setup() {
Serial.begin(9600); // 开始串行通信
pinMode(ledPin, OUTPUT); // 设置 LED 引脚为输出
}
// 这个循环读取模拟引脚的值并
// 将值发送到 LED 作为输出
void loop() {
// 读取光敏电阻的值
Serial.println(analogRead(lightPin));
// 将值写入串口监视器
// 将值发送到 ledPin 并除以 4
analogWrite(ledPin, analogRead(lightPin) / 4);
delay(10); // 在循环再次开始之前稍作延迟
}
故障排除
问: 代码编译成功,但在黑暗中 LED 不亮。
• 确保 LED 的长脚(正极)插入 13 号引脚,短脚(负极)插入旁边的 GND。
• 确保光敏电阻按照图 2-2 中的电路图连接到 Arduino 的 A0。打开串口监视器查看是否有读取。如果你能看到读取值但 LED 不亮,LED 可能有故障,尝试更换一个。
七段数码管倒计时计时器**
在这个项目中,我们将创建一个简单的计时器,从 9 倒数到 0。这个计时器可以应用于各种有用的项目!


所需零件
Arduino 板
面包板
跳线
七段单数共阴极 LED
8 个 220 欧姆电阻
工作原理
七段 LED 显示器使用 LED 段显示一个数字或字符。每个段都是一个独立的 LED,通过控制哪些段在任何时刻点亮,我们可以显示数字值。在这个项目中我们使用的是单数字显示,如图 3-1 所示,但也有双位、三位、四位和八位的变化版本可供选择。
图 3-1: 七段 LED

注意
设备的 阴极 是负极连接,通常用负号(–)表示,有时也称为 地(GND)。它连接到负电源。设备的 阳极 是正极连接,通常用加号(+)表示,并连接到正电源。
本项目将创建一个简单的计时器,从 9 倒计时到 0。七段 LED 有 10 个引脚。七个引脚控制七个 LED,这些 LED 亮起来形成每个数字,第八个引脚控制小数点。其他两个引脚是共阴极(–)或共阳极(+)引脚,为项目提供电力。我们的七段 LED 是共阴极的,这意味着每个 LED 的一侧需要连接到地。需要注意的是,代码只适用于共阴极 LED。如果你想使用共阳极 LED,在上传草图之前,请查阅本章末尾的故障排除部分。每个 LED 段都需要电阻来限制电流,否则它会烧毁。
引脚上标有字母,如图 3-2 所示。编号引脚控制右侧显示的段。Arduino 通过不同的组合打开或关闭 LED 来创建数字。
图 3-2: 七段 LED 的典型引脚布局

构建步骤
-
按照图 3-3 所示,将七段显示器放置在面包板上,确保引脚跨越中心断开部分。将 LED 的引脚 3 和 8 连接到 GND 轨。
图 3-3: 七段 LED 引脚应跨越面包板的中心断开部分。
![Image]()
-
按照下表连接 LED 引脚 1、2、4、5、6、7 和 9,并记得在 LED 与 Arduino 连接之间插入 220 欧姆电阻。电阻需要跨过面包板的中心断开部分,如图 3-4 中的电路图所示。
ARDUINO 七段 LED 部分 七段 LED 显示器 Pin 2 A Pin 7 Pin 3 B Pin 6 Pin 4 C Pin 4 Pin 5 D Pin 2 Pin 6 E Pin 1 Pin 7 F Pin 9 Pin 8 G Pin 10 Pin 9 DP Pin 5 图 3-4: 七段 LED 倒计时计时器电路图
![图片]()
-
上传代码到 “The Sketch” 中的 第 32 页。
草图
草图首先定义了数字 0 到 9 作为关闭(0)和打开(1)LED 的组合。控制 LED 的引脚被设置为输出,因此它们可以将相应的 LED 设置为HIGH或LOW。通过 1 和 0 的组合点亮 LED,形成数字。
请注意,这些模式适用于共阴极显示器。对于共阳极显示器,将每个 1 改为 0,每个 0 改为 1。在代码中,1 表示 LED 亮,0 表示 LED 灭。
// Arduino 七段显示示例代码
// hacktronics.com/Tutorials/arduino-and-7-segment-led.html
// 许可证:www.opensource.org/licenses/mit-license.php
// 定义要点亮的 LED 以创建一个数字
byte seven_seg_digits[10][7] = { { 1, 1, 1, 1, 1, 1, 0 }, // = 0
{ 0, 1, 1, 0, 0, 0, 0 }, // = 1
{ 1, 1, 0, 1, 1, 0, 1 }, // = 2
{ 1, 1, 1, 1, 0, 0, 1 }, // = 3
{ 0, 1, 1, 0, 0, 1, 1 }, // = 4
{ 1, 0, 1, 1, 0, 1, 1 }, // = 5
{ 1, 0, 1, 1, 1, 1, 1 }, // = 6
{ 1, 1, 1, 0, 0, 0, 0 }, // = 7
{ 1, 1, 1, 1, 1, 1, 1 }, // = 8
{ 1, 1, 1, 0, 0, 1, 1 } // = 9
};
// 设置七段 LED 引脚为输出
void setup() {
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
writeDot(0); // 从小数点关闭开始
}
void writeDot(byte dot) {
digitalWrite(9, dot);
}
void sevenSegWrite(byte digit) {
byte pin = 2;
for (byte segCount = 0; segCount < 7; ++segCount) {
digitalWrite(pin, seven_seg_digits[digit][segCount]);
++pin;
}
}
void loop() {
for (byte count = 10; count > 0; --count) { // 开始倒计时
delay(1000); // 每个数字之间间隔 1 秒
sevenSegWrite(count - 1); // 倒数 1
}
delay(4000);
}
故障排除
问: 一些 LED 段没有点亮。
检查 LED 的接线是否插好,并确保它们与面包板上的电阻对齐。
问: 显示屏没有正确显示数字,看起来不稳定。
• 请重新检查你的接线是否与图示一致,因为容易将一些线接错位置。
• 如果所有线路正确连接,但定时器仍然无法工作,可能是你的七段 LED 显示器配置与这里使用的不同。请查看你的零件数据表,并根据数据表指导电路的连接,同时参考七段引脚表。你还可以通过连接电池来检查每个引脚对应的 LED:将七段 LED 的 GND 引脚连接到电池的负极;将一根跳线连接到电池的正极,通过 220 欧姆电阻连接;依次触碰每个引脚,点亮每个段。注意每个引脚点亮的是哪个段。
• 请记住,这里的接线适用于七段共阴极 LED;对于共阳极显示,请在草图中将每个1改为0,将每个0改为1。
LED 滚动广告牌
在这个项目中,我们将使用内建的驱动模块,在一个 8×8 矩阵上创建一个滚动信息。


所需零件
Arduino 开发板
母对公跳线
8×8 LED Maxim 7219 矩阵模块
所需库
MaxMatrix
工作原理
LED 矩阵是一个 LED 阵列,你可以单独控制每个 LED,创建图案、文字、图片或你可以编程的任何内容。我们将在这个项目中使用的 8×8 LED 矩阵已经预装了一个驱动模块——一个由 Maxim 7219 芯片驱动的电路板,可以让你通过连接到 Arduino 的五个引脚来控制整个矩阵。这些模块价格便宜,且可以串联在一起,这样你就可以用一个草图驱动多个矩阵。
该矩阵模块有三个引脚:DIN、CS 和 CLK,如图 4-1 所示。DIN代表数据输入,CS代表芯片选择,CLK代表时钟。剩余的两个引脚连接到 Arduino,用于为矩阵供电。CLK 引脚感应脉冲,控制 Arduino 与矩阵之间同步通信的速度。该矩阵使用串行外设接口(SPI)通信协议与 Arduino 进行交流,而 CS 引脚检测当前使用的 SPI 设备。DIN 读取来自 Arduino 的数据——在本项目中是草图。
图 4-1: Maxim 7219 芯片控制 LED 矩阵。

每个模块都有额外的连接,可以让你添加另一个模块。通过将多个模块串联起来,并在代码中更改矩阵的数量,你可以在更大的区域内滚动信息。
搭建过程
-
使用母对公跳线将模块直接连接到 Arduino,将母头连接到模块。如下面的表格所示,将 LED 矩阵模块的 VCC 连接到 Arduino 的+5V,GND 连接到 GND,DIN 连接到 Arduino 的引脚 8,CS 连接到引脚 9,CLK 连接到引脚 10。
LED 矩阵模块 Arduino VCC +5V GND GND DIN 引脚 8 CS 引脚 9 CLK 引脚 10 -
确认您的设置与图 4-2 中的电路图匹配,并上传“草图”中的代码,位于第 38 页。
图 4-2: 滚动 LED 广告牌电路图
![Image]()
草图
这个草图通过调用 MaxMatrix 库来控制矩阵模块。然后我们定义要显示的字符,并设置控制矩阵的 Arduino 引脚。您的消息将在 LED 上以连续循环的方式显示。
include <MaxMatrix.h> // 调用 MaxMatrix 库
PROGMEM const unsigned char CH[] = {
3, 8, B00000000, B00000000, B00000000, B00000000, B00000000, // 空格
1, 8, B01011111, B00000000, B00000000, B00000000, B00000000, // !
3, 8, B00000011, B00000000, B00000011, B00000000, B00000000, // "
5, 8, B00010100, B00111110, B00010100, B00111110, B00010100, // #
4, 8, B00100100, B01101010, B00101011, B00010010, B00000000, // $
5, 8, B01100011, B00010011, B00001000, B01100100, B01100011, // %
5, 8, B00110110, B01001001, B01010110, B00100000, B01010000, // &
1, 8, B00000011, B00000000, B00000000, B00000000, B00000000, // '
3, 8, B00011100, B00100010, B01000001, B00000000, B00000000, // (
3, 8, B01000001, B00100010, B00011100, B00000000, B00000000, // )
5, 8, B00101000, B00011000, B00001110, B00011000, B00101000, // *
5, 8, B00001000, B00001000, B00111110, B00001000, B00001000, // +
2, 8, B10110000, B01110000, B00000000, B00000000, B00000000, // ,
4, 8, B00001000, B00001000, B00001000, B00001000, B00000000, // -
2, 8, B01100000, B01100000, B00000000, B00000000, B00000000, // .
4, 8, B01100000, B00011000, B00000110, B00000001, B00000000, // /
4, 8, B00111110, B01000001, B01000001, B00111110, B00000000, // 0
3, 8, B01000010, B01111111, B01000000, B00000000, B00000000, // 1
4, 8, B01100010, B01010001, B01001001, B01000110, B00000000, // 2
4, 8, B00100010, B01000001, B01001001, B00110110, B00000000, // 3
4, 8, B00011000, B00010100, B00010010, B01111111, B00000000, // 4
4, 8, B00100111, B01000101, B01000101, B00111001, B00000000, // 5
4, 8, B00111110, B01001001, B01001001, B00110000, B00000000, // 6
4, 8, B01100001, B00010001, B00001001, B00000111, B00000000, // 7
4, 8, B00110110, B01001001, B01001001, B00110110, B00000000, // 8
4, 8, B00000110, B01001001, B01001001, B00111110, B00000000, // 9
2, 8, B01010000, B00000000, B00000000, B00000000, B00000000, // :
2, 8, B10000000, B01010000, B00000000, B00000000, B00000000, // ;
3, 8, B00010000, B00101000, B01000100, B00000000, B00000000, // <
3, 8, B00010100, B00010100, B00010100, B00000000, B00000000, // =
3, 8, B01000100, B00101000, B00010000, B00000000, B00000000, // >
4, 8, B00000010, B01011001, B00001001, B00000110, B00000000, // ?
5, 8, B00111110, B01001001, B01010101, B01011101, B00001110, // @
4, 8, B01111110, B00010001, B00010001, B01111110, B00000000, // A
4, 8, B01111111, B01001001, B01001001, B00110110, B00000000, // B
4, 8, B00111110, B01000001, B01000001, B00100010, B00000000, // C
4, 8, B01111111, B01000001, B01000001, B00111110, B00000000, // D
4, 8, B01111111, B01001001, B01001001, B01000001, B00000000, // E
4, 8, B01111111, B00001001, B00001001, B00000001, B00000000, // F
4, 8, B00111110, B01000001, B01001001, B01111010, B00000000, // G
4, 8, B01111111, B00001000, B00001000, B01111111, B00000000, // H
3, 8, B01000001, B01111111, B01000001, B00000000, B00000000, // I
4, 8, B00110000, B01000000, B01000001, B00111111, B00000000, // J
4, 8, B01111111, B00001000, B00010100, B01100011, B00000000, // K
4, 8, B01111111, B01000000, B01000000, B01000000, B00000000, // L
5, 8, B01111111, B00000010, B00001100, B00000010, B01111111, // M
5, 8, B01111111, B00000100, B00001000, B00010000, B01111111, // N
4, 8, B00111110, B01000001, B01000001, B00111110, B00000000, // O
4, 8, B01111111, B00001001, B00001001, B00000110, B00000000, // P
4, 8, B00111110, B01000001, B01000001, B10111110, B00000000, // Q
4, 8, B01111111, B00001001, B00001001, B01110110, B00000000, // R
4, 8, B01000110, B01001001, B01001001, B00110010, B00000000, // S
5, 8, B00000001, B00000001, B01111111, B00000001, B00000001, // T
4, 8, B00111111, B01000000, B01000000, B00111111, B00000000, // U
5, 8, B00001111, B00110000, B01000000, B00110000, B00001111, // V
5, 8, B00111111, B01000000, B00111000, B01000000, B00111111, // W
5, 8, B01100011, B00010100, B00001000, B00010100, B01100011, // X
5, 8, B00000111, B00001000, B01110000, B00001000, B00000111, // Y
4, 8, B01100001, B01010001, B01001001, B01000111, B00000000, // Z
2, 8, B01111111, B01000001, B00000000, B00000000, B00000000, // [
4, 8, B00000001, B00000110, B00011000, B01100000, B00000000, // \
2, 8, B01000001, B01111111, B00000000, B00000000, B00000000, // ]
3, 8, B00000010, B00000001, B00000010, B00000000, B00000000, // hat
4, 8, B01000000, B01000000, B01000000, B01000000, B00000000, // _
2, 8, B00000001, B00000010, B00000000, B00000000, B00000000, // `
4, 8, B00100000, B01010100, B01010100, B01111000, B00000000, // a
4, 8, B01111111, B01000100, B01000100, B00111000, B00000000, // b
4, 8, B00111000, B01000100, B01000100, B00101000, B00000000, // c
4, 8, B00111000, B01000100, B01000100, B01111111, B00000000, // d
4, 8, B00111000, B01010100, B01010100, B00011000, B00000000, // e
3, 8, B00000100, B01111110, B00000101, B00000000, B00000000, // f
4, 8, B10011000, B10100100, B10100100, B01111000, B00000000, // g
4, 8, B01111111, B00000100, B00000100, B01111000, B00000000, // h
3, 8, B01000100, B01111101, B01000000, B00000000, B00000000, // i
4, 8, B01000000, B10000000, B10000100, B01111101, B00000000, // j
4, 8, B01111111, B00010000, B00101000, B01000100, B00000000, // k
3, 8, B01000001, B01111111, B01000000, B00000000, B00000000, // l
5, 8, B01111100, B00000100, B01111100, B00000100, B01111000, // m
4, 8, B01111100, B00000100, B00000100, B01111000, B00000000, // n
4, 8, B00111000, B01000100, B01000100, B00111000, B00000000, // o
4, 8, B11111100, B00100100, B00100100, B00011000, B00000000, // p
4, 8, B00011000, B00100100, B00100100, B11111100, B00000000, // q
4, 8, B01111100, B00001000, B00000100, B00000100, B00000000, // r
4, 8, B01001000, B01010100, B01010100, B00100100, B00000000, // s
3, 8, B00000100, B00111111, B01000100, B00000000, B00000000, // t
4, 8, B00111100, B01000000, B01000000, B01111100, B00000000, // u
5, 8, B00011100, B00100000, B01000000, B00100000, B00011100, // v
5, 8, B00111100, B01000000, B00111100, B01000000, B00111100, // w
5, 8, B01000100, B00101000, B00010000, B00101000, B01000100, // x
4, 8, B10011100, B10100000, B10100000, B01111100, B00000000, // y
3, 8, B01100100, B01010100, B01001100, B00000000, B00000000, // z
3, 8, B00001000, B00110110, B01000001, B00000000, B00000000, // {
1, 8, B01111111, B00000000, B00000000, B00000000, B00000000, // |
3, 8, B01000001, B00110110, B00001000, B00000000, B00000000, // }
4, 8, B00001000, B00000100, B00001000, B00000100, B00000000, // ~
};
int data = 8; // 连接到 MAXIM7219 模块的 DIN 引脚
int load = 9; // 连接到 MAXIM7219 模块的 CS 引脚
int clock = 10; // 连接到 MAXIM7219 模块的 CLK 引脚
➊ int maxInUse = 1; // 设置你使用的矩阵数量
MaxMatrix m(data, load, clock, maxInUse); // 定义模块
byte buffer[10];
// 设置滚动显示的消息
➋ char string1[] = " Arduino Project Handbook . . . ";
void setup() {
m.init(); // 启动模块
m.setIntensity(0);
Serial.begin(9600); // 启动串行通信
}
void loop() {
byte c;
while (Serial.available() > 0) {
byte c = Serial.read();
Serial.println(c, DEC);
printCharWithShift(c, 100);
}
delay(100);
m.shiftLeft(false, true);
printStringWithShift(string1, 100);
}
// 该草图的其余部分用于移动滚动字符
// 根据连接的矩阵数量
void printCharWithShift(char c, int shift_speed) {
if (c < 32) return;
c -= 32;
memcpy_P(buffer, CH + 7 * c, 7);
m.writeSprite(maxInUse * 8, 0, buffer);
m.setColumn(maxInUse * 8 + buffer[0], 0);
for (int i = 0; i < buffer[0] + 1; i++) {
delay(shift_speed);
m.shiftLeft(false, false);
}
}
void printStringWithShift(char* s, int shift_speed) {
while (*s != 0) {
printCharWithShift(*s, shift_speed);
s++;
}
}
void printString(char* s) {
int col = 0;
while (*s != 0) {
if (*s < 32) continue;
char c = *s - 32;
memcpy_P(buffer, CH + 7 * c, 7);
m.writeSprite(col, 0, buffer);
m.setColumn(col + buffer[0], 0);
col += buffer[0] + 1;
s++;
}
}
你可以通过修改➋处引号内的文本来更改 LED 矩阵上的消息。如果你想将多个矩阵连接在一起,请将➊处的数字更改为你有的矩阵数量(你最多可以连接七个矩阵)。
故障排除
问 矩阵没有亮起,或者 LED 显示异常符号。
• 如果没有任何 LED 亮起,请确保你已按照图 4-2 中的电路图正确连接矩阵;引脚必须完全匹配。
• 确保你的 Arduino 已供电,且 TX 指示灯在闪烁。如果没有,请重新检查电池或电源。
• 确保 Maxim 7219 芯片已牢固插入模块。
氛围灯**
在这个项目中,我们将使用一个单色 RGB LED 制作一个舒缓的氛围灯。


所需组件
Arduino 板
面包板
跳线
RGB 共阴极 LED
3 个 220 欧姆电阻
工作原理
LED 有许多不同的颜色和形式,但其中最实用的之一就是 RGB LED。顾名思义,RGB LED 实际上是三个 LED 合为一体:红色、绿色和蓝色(见图 5-1)。
图 5-1: RGB LED 的三原色

RGB 是一种加色模型,这意味着通过组合两种或更多颜色的光,我们可以创建其他颜色。红色、绿色和蓝色是加色模型中的原色,它们作为其他颜色的基础,如图 5-2 所示。
图 5-2: RGB 是加色模型。

我们可以在图 5-3 中更详细地查看 RGB LED。
图 5-3: 一个 RGB LED

你会看到 RGB LED 有四个引脚,而不是通常的两个:红色、绿色和蓝色各一个,第四个是阴极或阳极。我们将使用像图中所示的共阴极RGB LED,其中最长的引脚是阴极,并连接到地。
我们可以使用 RGB LED 创建一个随机颜色的输出,循环显示彩虹的颜色,并逐渐变暗和变亮。这种照明效果在俱乐部或酒吧中经常使用,以营造轻松的氛围。你也可以将 LED 放入不透明的花瓶或盒子中,作为一个舒缓的夜灯。
组装
-
首先将共阴极 RGB LED 插入面包板,将红色引脚插入长的 GND(或阴极)引脚左侧的孔中。然后将 220 欧姆电阻连接到三个颜色引脚上。
注意
某些 RGB LED 的绿脚和蓝脚位置正好相反。
-
将红色引脚连接到 Arduino 的 11 号引脚,GND 连接到 Arduino 的 GND,绿色连接到 Arduino 的 10 号引脚,蓝色连接到 Arduino 的 9 号引脚。
共阴极 RGB LED Arduino 红色 11 号引脚 GND GND 绿色 10 号引脚 蓝色 9 号引脚 -
确认你的设置与图 5-4 中的电路图匹配,并上传第 47 页中的“草图”代码。
图 5-4: 氛围灯的电路图
![Image]()
草图
该草图首先将 Arduino 的 9、10 和 11 号引脚设置为输出。该草图通过极快地开关 RGB LED 上的每个灯的亮度(电力值),使它们依次变化——LED 点亮的时间越长,亮度越高。为此,Arduino 使用了一种叫做脉宽调制(PWM)的技术。Arduino 通过快速地开关电源来创建脉冲。电源开关的持续时间(称为脉冲宽度)决定了平均输出,通过改变这个脉冲宽度,Arduino 可以模拟从完全打开(5 伏)到关闭(0 伏)之间的电压。如果 Arduino 的信号开关一半时间为开,另一半时间为关,平均输出将是 2.5 伏,即 0 和 5 之间的一半。如果信号开关时间为 80%,关时间为 20%,电压为 4 伏,依此类推。
我们定义一个 RGB 值在0到255之间,增量为5伏,以创建淡化效果。简单来说,LED 的每种颜色从 0 逐渐亮起到 5 伏,然后在达到最大值255时逐渐变暗。Arduino 可以处理0到1023之间的值(总共 1,024 个值),但因为这个值太高,我们将其除以 4,使用255作为最大 LED 值,以便颜色变化更加明显。
int redPin = 11; // 连接到 RGB LED 红色脚的引脚
int greenPin = 10; // 连接到 RGB LED 绿色脚的引脚
int bluePin = 9; // 连接到 RGB LED 蓝色脚的引脚
void setup() {
setRgb(0, 0, 0); // 将所有颜色设置为 0
}
void loop() {
int Rgb[3]; // 3 个 RGB 引脚
Rgb[0] = 0; // 每个值
Rgb[1] = 0;
Rgb[2] = 0;
// 颜色的增减变化
for (int decrease = 0; decrease < 3; decrease += 1) {
int increase = decrease == 2 ? 0 : decrease + 1;
for (int i = 0; i < 255; i += 1) { // 淡化颜色
Rgb[decrease] -= 1;
Rgb[increase] += 1;
setRgb(Rgb[0], Rgb[1], Rgb[2]);
delay(20);
}
}
}
void setRgb (int red, int green, int blue) {
analogWrite(redPin, red);
analogWrite(greenPin, green);
analogWrite(bluePin, blue);
}
故障排除
问答 代码编译通过,但 RGB LED 没有按预期点亮。
• 如果 RGB LED 完全不亮,确保你已经将 Arduino 的 GND 线连接到 RGB LED 的正确脚——长阴极脚——并且 Arduino 已连接电源。
• 如果你有共阳 RGB LED,则应将长脚连接到 Arduino 的+5V。检查你的部件数据手册以了解你使用的是哪种类型的 RGB LED。
• 如果颜色没有按预期显示,可能是你的 RGB LED 引脚配置不同;检查数据手册或尝试交换绿色和蓝色脚的连接。
彩虹条形灯**
在本章中,我们将使用 RGB LED 条形灯创建一个装饰性的彩虹色氛围条。


所需零件
Arduino 板
实心电线
RGB LED 条带(WS2812B 5V 32-LED 条带)
所需库
PololuLedStrip
工作原理
LED 条灯通常用于创建氛围作为装饰性功能,例如电视的背光或厨房柜下的灯光。它们功率较低,通常在 5 至 12 伏之间,因此它们很容易安装在任何地方,并且具有自己的电源——而且看起来也很漂亮!
条灯通常有两种类型。单色或多色非可寻址条带只能同时点亮所有 LED 为同一颜色。RGB 多色条带通常是可寻址的,这意味着每个 LED 都有自己的芯片,可以单独控制,从而使不同的 LED 能同时显示多种颜色。
我们将使用一条可寻址 RGB LED 条带。与项目 5 中的 RGB LED 不同,条灯上的 LED 是表面贴装的。这意味着组件直接放置在印刷电路板的表面——在这种情况下,是一条柔性条带上——而不是单独插入电路。
可寻址 RGB 条灯有两种主要类型。三引脚 RGB LED 条带具有 GND、数据和+5V 连接,用于控制 LED。数据引脚连接到 Arduino,并使用在项目 5 中解释的相同脉宽调制(PWM)功能来创建条带上的颜色和序列。四引脚 RGB LED 条带具有 GND、时钟、数据输入和+5V 连接,并使用串行外设接口(SPI)来控制它们的 LED。SPI 是一种通信方法,允许设备之间进行双向数据传输。
我们的可寻址 RGB LED 条带,如图 6-1 所示,是使用 PWM 的三引脚类型。它调用了由 Pololu Robotics and Electronics 创建的 PololuLedStrip 库(www.pololu.com/)来控制 LED。
图 6-1: 三引脚可寻址 RGB LED 条灯

我们将使用 RGB LED 条带来创建一个颜色输出,该输出将通过彩虹的颜色进行循环,每种颜色逐渐变亮和变暗,如图 6-2 所示。
图 6-2: RGB LED 条带循环显示彩虹的颜色

构建过程
-
下载并将 PololuLedStrip 库添加到你的 Arduino IDE 中(请查看入门指南了解如何保存库)。
-
该项目的设置非常简单,完成起来也很快。大多数三引脚可寻址 RGB LED 条带没有将导线连接到条带的连接端口,因此你需要自己连接。将 LED 朝上,首先将实心导线焊接到条带左端的三个连接点,如图 6-3 所示。
图 6-3: 焊接左侧连接的导线
![Image]()
-
将 LED 的 GND 引脚连接到 Arduino 的 GND,DI 连接到 Arduino 的 12 号引脚,+5V 连接到 Arduino 的+5V,如下表所示。
RGB LED 条带 Arduino GND GND DI(数据输入) 引脚 12 +5V +5V -
根据图 6-4 中的电路图检查你的设置,然后上传下面的代码“草图”并通过电池包为 Arduino 供电。
图 6-4: 彩虹条形灯电路图
![图片]()
草图
该草图首先调用了 PololuLedStrip 库,我们用它来控制单个 LED。接下来,它定义了用于控制数据从 Arduino 传输到 LED 条的引脚为 12,并将 LED 条上的 LED 数量设置为 32——如果你的 LED 条有不同数量的 LED,你需要修改此值。
接下来是一个计算,用于控制我们 LED 的色调、饱和度和亮度(HSV),从而生成 RGB 颜色。如果你愿意,可以使用 HSV 图表来更改这些值;只需快速搜索互联网即可找到参考图表。
WS2812B 数据手册指出,每个 LED 的颜色是通过三个 LED 亮度值编码的,这些值必须按 GRB(绿色-红色-蓝色)顺序发送。第一个传输的颜色应用于离数据输入连接器最近的 LED,第二个传输的颜色应用于条上的下一个 LED,依此类推。
/* PololuLedStrip 库版权(c) 2012 Pololu 公司。
更多信息,请访问www.pololu.com/;
现特此免费授予任何人许可
获取此软件及相关文档文件的副本
(“软件”),可以在不受限制的情况下使用、复制、修改、合并、
包括但不限于使用、复制、修改、合并的权利,
发布、分发、再许可和/或销售该软件的副本,
并允许软件接收者按此方式使用,
需遵守以下条件:
上述版权声明和此许可声明应包含在
包括在所有副本或实质性部分中。
本软件按“原样”提供,不附任何类型的保证,
明示或暗示的保证,包括但不限于
适销性、特定用途适用性及非
侵权。在任何情况下,作者或版权持有者均不应
对任何索赔、损害或其他责任不承担责任,无论是
合同、侵权或其他原因引起的,或与
与软件相关或使用或其他交易活动相关的
软件。
LedStripRainbow:示例 Arduino 草图,展示如何制作
在 Pololu 的可寻址 RGB LED 条上展示的移动彩虹效果。
要使用此功能,你需要将一个可寻址 RGB LED 条插入
将 Pololu 插入引脚 12。上传草图后,你应该会看到一个
移动的彩虹效果。*/
include <PololuLedStrip.h>
// 创建一个 ledStrip 对象,并指定它将使用的引脚。
PololuLedStrip<12> ledStrip;
// 创建一个缓冲区用于存储颜色(每种颜色 3 字节)。
define LED_COUNT 32
rgb_color colors[LED_COUNT];
void setup() {
}
// 将颜色从 HSV 转换为 RGB。
// h 是色调,范围从 0 到 360。
// s 是饱和度,范围从 0 到 255。
// v 是数值,范围从 0 到 255。
rgb_color hsvToRgb(uint16_t h, uint8_t s, uint8_t v) {
uint8_t f = (h % 60) * 255 / 60;
uint8_t p = (255 - s) * (uint16_t)v / 255;
uint8_t q = (255 - f * (uint16_t)s / 255) * (uint16_t)v / 255;
uint8_t t = (255 - (255 - f) * (uint16_t)s / 255) * (uint16_t)v / 255;
uint8_t r = 0, g = 0, b = 0;
switch((h / 60) % 6) {
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
}
return (rgb_color) {
r, g, b
};
}
void loop() {
// 更新颜色。
uint16_t time = millis() >> 2;
for (uint16_t i = 0; i < LED_COUNT; i++) {
byte x = (time >> 2) - (i << 3);
colors[i] = hsvToRgb((uint32_t)x * 359 / 256, 255, 255);
}
// 将颜色写入 LED 条带。
ledStrip.write(colors, LED_COUNT);
delay(10);
}
故障排除
Q. 代码可以编译,但 RGB LED 没有按预期点亮。
• 如果 RGB LED 条带没有点亮,请确保您的电线连接如图 6-4 所示,并且您的 LED 条带是指定的 WS2812B 类型。
• 如果您还没有这样做,请为 RGB LED 条带使用外部电源。
NeoPixel 指南针**
在本章中,我们将使用三轴传感器和 RGB LED 环创建一个指南针,通过点亮指示北方的 LED 来显示方向。


所需零件
Arduino 板
跳线
HMC5883L 三轴传感器
Adafruit NeoPixel 环,包含 16 个 RGB LED
9V 电池组,包含 6 节 AA 电池
所需库
电线
FastLED
HMC5883L
工作原理
HMC5883L 三轴传感器(见图 7-1)是一个多芯片模块,用于感应磁力。该模块能够测量地球磁场的方向和强度。我们将使用 HMC5883L 库来将我们的项目转化为电子指南针。
图 7-1: HMC5883L 三轴模块运行在 3.3V 而不是 5V。

地球的磁场被认为是由其核心中导电材料内的电流产生的,这些电流是由于热量逸散而形成的。由于地球本身就是一个磁体,指南针的北端会被磁场吸引并与其对齐。
为了可视化我们的指南针方向,我们将使用 Adafruit NeoPixel 环(见图 7-2)。NeoPixel 环由 16 个 RGB LED 组成,每个 LED 都有自己的驱动芯片,因此可以单独控制。通过单一数据线控制这些 LED,我们将使用 FastLED 库来控制颜色。
图 7-2: Adafruit 16 RGB NeoPixel 环

当项目通电时,HMC5883L 模块会检测磁北,并通过点亮 NeoPixel 环形灯带的 LED 来显示它。如果你在拿着已通电的 NeoPixel 指南针时转动方向,LED 灯光会移动,始终指向北方。
构建过程
注意
指南针模块上标有 DRDY 的引脚在此项目中未使用。
您的 HMC5883L 模块可能到达时引脚松动,因此第一步是将引脚焊接到模块上。您需要一条包含五个引脚的引脚条,这应该随模块一起提供。将引脚插入模块上的五个可用孔中,并将每个引脚焊接几秒钟(如果需要帮助,请查看 快速焊接指南 在 第 12 页)。该模块通过 I2C 和 Wire 库与 Arduino 进行通信。
-
为了正确使用指南针,您需要对 HMC5883L 模块进行校准。将模块连接到 Arduino,如下表所示。
HMC5883L 模块 ARDUINO VCC +3.3V GND GND SCL 引脚 A5 (SCL) SDA 引脚 A4 (SDA) -
下载 HMC5883L 库并将其添加到您电脑上的 Arduino 库文件夹中。如果需要提醒如何操作,请查看入门指南中的库部分。保存库后,重新启动 Arduino IDE。重新打开时,库应保存在 Examples 中。选择 文件 ▸ 示例 ▸ Arduino-HMC5883L-Master ▸ HMC5883L_calibrate。如果看不到草图,请确保您已经将库保存在 Arduino 库文件夹中。以下草图将在 IDE 主窗口中显示:
/*
校准 HMC5883L。HMC5883L_calibrate_processing.pde 的输出
阅读更多:
www.jarzebski.pl/arduino/czujniki-i-sensory/3-osiowy-magnetometr-hmc5883l.htmlGIT:
github.com/jarzebski/Arduino-HMC5883L网站:
www.jarzebski.pl(c) 2014 by Korneliusz Jarzebski
*/
include <Wire.h>
include <HMC5883L.h>
HMC5883L compass;
int minX = 0;
int maxX = 0;
int minY = 0;
int maxY = 0;
int offX = 0;
int offY = 0;
void setup() {
Serial.begin(9600);
// 初始化 HMC5883L
while (!compass.begin()) {
delay(500);
}
// 设置测量范围
compass.setRange(HMC5883L_RANGE_1_3GA);
// 设置测量模式
compass.setMeasurementMode(HMC5883L_CONTINOUS);
// 设置数据速率
compass.setDataRate(HMC5883L_DATARATE_30HZ);
// 设置平均样本数量
compass.setSamples(HMC5883L_SAMPLES_8);
}
void loop() {
Vector mag = compass.readRaw();
// 确定最小值 / 最大值
if (mag.XAxis < minX) minX = mag.XAxis;
if (mag.XAxis > maxX) maxX = mag.XAxis;
if (mag.YAxis < minY) minY = mag.YAxis;
if (mag.YAxis > maxY) maxY = mag.YAxis;
// 计算偏移量
offX = (maxX + minX)/2;
offY = (maxY + minY)/2;
/*Serial.print(mag.XAxis);
Serial.print("😊;
Serial.print(mag.YAxis);
Serial.print("😊;
Serial.print(minX);
Serial.print("😊;
Serial.print(maxX);
Serial.print("😊;
Serial.print(minY);
Serial.print("😊;
Serial.print(maxY);
Serial.print("😊; */
Serial.print(offX);
Serial.print("😊;
Serial.print(offY);
Serial.print("\n");
}
-
我们只需要最后一组
Serial.print命令中的 X 和 Y 输出,因此请注释掉草图中加粗部分的Serial.print行。将草图上传到 Arduino,并打开串口监视器。一系列数字将显示出来,如图 7-3 所示。图 7-3: 校准数字将在 IDE 串口监视器窗口中显示。
![Image]()
-
在传感器连接到 Arduino IDE 串口监视器的同时,旋转传感器 360 度,你应该能看到显示两个数字;在图 7-3 中,它们是 13 和 –294。你稍后将在草图中需要这些校准数字,所以请记下它们。
-
你可以通过查找你所在位置的 磁偏角 来提高罗盘的精度。磁偏角或变化是水平面上磁北(罗盘指向的方向)与真北(指向地理北极的方向)之间的角度。你可以通过访问
www.magnetic-declination.com/并在左上角的搜索框中输入你的位置信息来找到你的磁偏角。你的结果将如图 7-4 所示。图 7-4: 你所在位置的磁偏角可以在
www.magnetic-declination.com/网站找到。![Image]()
-
你需要的值是磁偏角和磁偏差;在图 7-4 中,它们分别是 –2° 26' 和负值(西),但你的值会不同。也要记录这些值,因为我们将在项目的最后使用它们—仅有一个小的变化。例如,我的值是 –2 和 26。我们不会把负号(减号)放在第一个值前面,而是放在后面,像这样:
float declinationAngle = (2 - (26.0 / 60.0)) / (180 / M_PI);
如果你所在位置的磁偏角为正值(西),则应该添加正号(加号):
float declinationAngle = (2 + (26.0 / 60.0)) / (180 / M_PI);
接下来,通过将 NeoPixel 的 V 引脚连接到 Arduino 的 +5V,引脚 GND 连接到 GND,引脚 In 连接到 Arduino 的 3 号引脚,将 NeoPixel 环连接到 Arduino。
NEOPIXEL ARDUINO 电压 +5V GND GND 输入 引脚 3 -
根据图 7-5 中的电路图检查你的设置,然后上传下面的 “草图” 代码。
图 7-5: NeoPixel 罗盘的电路图
![Image]()
草图
首先,我们调用 Wire、FastLED 和 HMC5883L 库。Wire 库已与 Arduino IDE 一起安装,但您需要添加其他库。请在本书的资源中下载它们,链接为 www.nostarch.com/arduinohandbook2/,并根据入门指南获取更多添加库的信息。
接下来,我们声明 NeoPixel 环上的 LED 数量(16 个),并将引脚 3 分配给 Arduino 来控制它。然后我们调用 HMC5883L 库中的多个设置来控制罗盘模块。在 ➊ 处,我们添加了X和Y的罗盘偏移值,这些值应与步骤 4 中的校准匹配;我的分别是 13,–294。在 ➋ 处,我们添加了步骤 6 中的磁偏角。同样,记得将其更改为您所在位置的磁偏角。
接下来的一组计算使得传感器能够映射到 360 度旋转。然后我们设置 NeoPixel 上的 LED,根据传感器的读数移动指向北方。点亮了三个 LED:一个指向北方的红色 LED,以及其两侧的绿色 LED。罗盘最好在户外使用,模块远离任何强电磁源,且应该通过电池包供电,而非 USB 连接。
// 代码由 brainy-bits.com 提供并经过许可使用
// brainy-bits.com/tutorials/find-your-way-using-the-hmc5883l/
include <Wire.h>
include "FastLED.h"
include <HMC5883L.h>
define NUM_LEDS 16 // 环形灯带上的 LED 数量
define DATA_PIN_RING 3 // 引脚 3 连接到 RGB 环形灯带
CRGB leds_RING[NUM_LEDS];
HMC5883L 罗盘;
int fixedHeadingDegrees; // 用于存储航向值
void setup() {
Serial.begin(9600);
Wire.begin(); // 设置 I2C
// 设置 FastLED 库与 NeoPixel 环的数据
FastLED.addLeds<NEOPIXEL,DATA_PIN_RING>(leds_RING, NUM_LEDS);
// 设置测量范围
compass.setRange(HMC5883L_RANGE_1_3GA);
// 设置测量模式
compass.setMeasurementMode(HMC5883L_CONTINOUS);
// 设置数据速率
compass.setDataRate(HMC5883L_DATARATE_30HZ);
// 设置平均样本数
compass.setSamples(HMC5883L_SAMPLES_8);
// 设置校准偏移值。请参见 HMC5883L_calibration.ino
➊ compass.setOffset(13, -224);
}
void loop() {
Vector norm = compass.readNormalize();
// 计算航向
float heading = atan2(norm.YAxis, norm.XAxis);
// 设置您所在位置的磁偏角并修正航向
// 查找您的磁偏角: http://magnetic-declination.com/
// (+) 正值或 (-) 负值
// 对于苏格兰邓弗里斯的磁偏角为 -2 '26W(负值)
// 公式:(度 + (分 / 60.0)) / (180 / M_PI);
float declinationAngle = (2.0 – (26.0 / 60.0)) / (180 / M_PI);
➋ heading -= declinationAngle;
// 修正航向 < 0° 和航向 > 360°
if (heading < 0) {
heading += 2 * PI;
}
if (heading > 2 * PI) {
heading -= 2 * PI;
}
// 转换为度数
float headingDegrees = heading * 180 / M_PI;
// 修正 HMC5883L 罗盘模块的旋转速度
如果 (headingDegrees >= 1 && headingDegrees < 240) {
fixedHeadingDegrees = map(headingDegrees * 100, 0, 239 * 100, 0, 179 * 100) / 100.00;
}
else {
如果 (headingDegrees >= 240) {
fixedHeadingDegrees = map(headingDegrees100, 240100, 360100, 180100, 360*100) / 100.00;
}
}
int headvalue = fixedHeadingDegrees / 18;
int ledtoheading = map(headvalue, 0, 15, 15, 0);
// 清除环
FastLED.clear();
// 新的方向
如果 (ledtoheading == 0) {
leds_RING[15] = CRGB::Red;
leds_RING[0] = CRGB::Green;
leds_RING[14] = CRGB::Green;
}
else {
如果 (ledtoheading == 15) {
leds_RING[0] = CRGB::Red;
leds_RING[15] = CRGB::Green;
leds_RING[1] = CRGB::Green;
}
else {
leds_RING[ledtoheading] = CRGB::Red;
leds_RING[ledtoheading+1] = CRGB::Green;
leds_RING[ledtoheading-1] = CRGB::Green;
}
}
FastLED.setBrightness(50);
FastLED.show();
delay(100);
}
故障排除
Q. 代码编译成功,但 RGB LED 灯没有按预期亮起。
• 如果没有 LED 灯亮起,请仔细检查接线,特别是确认 NeoPixel 的数据引脚是否连接到 Arduino 的 3 号引脚。
• 检查你的 NeoPixel 的电源是否连接到 GND 和 +5V。指南针模块应该连接到 GND 和 +3.3V。Arduino 应该由你的电池组供电,而不是通过 PC 的 USB 电缆供电。
• 确保你已经校准了模块,并按照前面所示的步骤输入了数值。指南针模块应该水平持平,并与 RGB 环保持一致。环和模块应始终一起移动。
• 该模块最好在户外使用,因为它对金属和电气干扰非常敏感。
• 尽量将 Arduino 和传感器的电源保持尽可能远,以避免干扰。
第三章:声音
Arduino 钢琴
在这个项目中,我们将使用一些瞬时按钮和一个压电蜂鸣器来制作一个简单的钢琴。


所需组件
Arduino 板
面包板
跳线
压电蜂鸣器
8 个瞬时触觉按钮
8 个 1k 欧姆电阻
工作原理
在我们的项目中,每个按钮(参见图 8-1)都连接到一个 Arduino 引脚,当按钮被按下时,压电蜂鸣器将发出八个音符中的一个。
图 8-1: 瞬时按钮及其电路

当按下按钮时,按键完成电路的闭合,使电路通电。一旦按钮释放,连接将回弹并断开电路,导致电路关闭。按键开关也叫做瞬时开关或常开开关,例如用于计算机键盘中。这与翻转开关不同,后者保持开或关状态,直到你将它切换到另一位置,就像灯开关一样。
这种类型的按钮有四个引脚,但通常只使用其中的两个来连接。在这个项目中,我们使用顶部的引脚,这样更容易触摸按钮并演奏旋律,尽管底部未使用的两个引脚也能完成相同的工作。如图 8-2 所示,引脚在电路中起作用。引脚 A 和 C 始终连接在一起,B 和 D 也是如此。当按钮被按下时,电路就完成了。
图 8-2: 按钮的未完成电路

Arduino 钢琴使用了一个压电蜂鸣器,如图 8-3 所示,用来产生类似可识别音符的频率。压电蜂鸣器,简称为压电元件,是一种常见的低价蜂鸣器,通常用于小玩具中。没有塑料外壳的压电元件看起来像一个金色金属圆盘,连接着正极(通常为红色)和负极(通常为黑色)的电线。压电元件只能发出点击声,这是通过施加电压来实现的。
图 8-3: 压电蜂鸣器

我们可以通过让压电元件以特定频率每秒点击数百次来生成可识别的音符,因此首先我们需要了解我们所需不同音符的频率。表 8-1 展示了音符及其对应的频率。周期是频率产生的周期时长,单位为微秒。例如,为了得到 C 音符(261 Hz),我们需要让压电元件以 3,830 微秒的周期工作。我们将周期除以二,得到 timeHigh 值,这个值用于代码中来生成音符。(音符的产生是由于压电元件被快速打开和关闭,所以压电元件开启的时间,或者称为 HIGH,是周期的一半。)
表 8-1: 代码中使用的音符和频率
| 注意 | 频率 | 周期 | 高电平时间 |
|---|---|---|---|
c |
261 Hz | 3,830 | 1915 |
d |
294 Hz | 3,400 | 1700 |
e |
329 Hz | 3,038 | 1519 |
f |
349 Hz | 2,864 | 1432 |
g |
392 Hz | 2,550 | 1275 |
a |
440 Hz | 2,272 | 1136 |
b |
493 Hz | 2,028 | 1014 |
C |
523 Hz | 1,912 | 956 |
构建
-
将暂时性按键插入面包板,确保引脚跨过面包板的中心断开区域。
-
面对面包板时,从左到右为按键编号 1 到 8。使用跳线将按键 1 的左上角引脚(A)连接到 Arduino 引脚 2。将其他按键的左上角引脚依次连接到 Arduino,如下所示。
PUSHBUTTON NOTE ARDUINO 1 c2 2 d3 3 e4 4 f5 5 g6 6 a7 7 b8 8 C9 -
在面包板上插入一个 1k 欧姆电阻,与第一个按键的左上角引脚成一行,如图 8-4 所示,并将电阻的另一端连接到面包板的 GND 导轨。对其他按键重复此操作。电阻在按钮未按下时将开关拉到 GND,告诉 Arduino 它处于非正状态;当按钮按下时,正电源发出相应音符的声音。
图 8-4: 一个 1k 欧姆电阻连接按键引脚到 GND。
![Image]()
-
使用跳线将每个按键的右上角引脚(B)连接到面包板的正电源导轨。
-
将蜂鸣器的红线连接到 Arduino 引脚 13,黑线连接到面包板的 GND 导轨,然后将电源导轨连接到 Arduino 的 GND 和 +5V。
-
确保你的设置与图 8-5 中的电路图相匹配,然后上传“草图”中的代码,位于第 74 页。
图 8-5: Arduino 钢琴电路图
![Image]()
草图
该草图首先定义了蜂鸣器连接的引脚以及按键的引脚。为每个按键定义了一个值,并分配了与该值对应的音调。将按键设置为输入,将蜂鸣器设置为输出。循环周期检查每个按钮,在按钮按下时播放对应的音调。每次只能播放一个音符,因为每个音符都需要循环重新开始,所以当按钮释放时,蜂鸣器停止播放音调,循环重新开始。
int speakerPin = 13; // 蜂鸣器定义为 13 引脚
int key_c = 2; // 定义 Arduino 引脚用于按键
int key_d = 3;
int key_e = 4;
int key_f = 5;
int key_g = 6;
int key_a = 7;
int key_b = 8;
int key_C = 9;
// 每个按键的值
int keypress_c = 0; int keypress_d = 0; int keypress_e = 0;
int keypress_f = 0; int keypress_g = 0; int keypress_a = 0;
int keypress_b = 0; int keypress_C = 0;
// 定义每个音符的频率
int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };
// 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C'
int keytone = 0; // 给 keytone 赋值
void setup() {
pinMode(key_c, INPUT); // 设置按键引脚为输入
pinMode(key_d, INPUT);
pinMode(key_e, INPUT);
pinMode(key_f, INPUT);
pinMode(key_g, INPUT);
pinMode(key_a, INPUT);
pinMode(key_b, INPUT);
pinMode(key_C, INPUT);
pinMode(speakerPin, OUTPUT); // 将蜂鸣器引脚设置为输出
}
// 开始循环读取每个按键的状态
void loop() {
keypress_c = digitalRead(key_c); keypress_d = digitalRead(key_d);
keypress_e = digitalRead(key_e); keypress_f = digitalRead(key_f);
keypress_g = digitalRead(key_g); keypress_a = digitalRead(key_a);
keypress_b = digitalRead(key_b); keypress_C = digitalRead(key_C);
// 如果按键为 HIGH,则播放相应的音调
if ((keypress_c == HIGH) || (keypress_e == HIGH) ||
(keypress_g == HIGH) || (keypress_d == HIGH) ||
(keypress_f == HIGH) || (keypress_a == HIGH) ||
(keypress_b == HIGH) || (keypress_C == HIGH))
{
if (keypress_c == HIGH) {
keytone = tones[0];
}
if (keypress_d == HIGH) {
keytone = tones[1];
}
if (keypress_e == HIGH) {
keytone = tones[2];
}
if (keypress_f == HIGH) {
keytone = tones[3];
}
if (keypress_g == HIGH) {
keytone = tones[4];
}
if (keypress_a == HIGH) {
keytone = tones[5];
}
if (keypress_b == HIGH) {
keytone = tones[6];
}
if (keypress_C == HIGH) {
keytone = tones[7];
}
digitalWrite(speakerPin, HIGH); // 打开压电蜂鸣器播放音调
delayMicroseconds(keytone);
digitalWrite(speakerPin, LOW); // 短暂延时后关闭
delayMicroseconds(keytone);
}
else { // 如果没有按键被按下,蜂鸣器保持静音
digitalWrite(speakerPin, LOW);
}
}
故障排除
Q. 代码能编译,但部分或所有按钮未发出音调。
• 如果压电蜂鸣器完全没有声音,请检查蜂鸣器的红线是否连接到引脚 13,黑线是否连接到面包板的 GND。确保 Arduino 的 GND 连接到正确的面包板电源轨,并且 Arduino 已连接电源。
• 如果只有一些按钮能发出声音,请重新检查静音按钮的接线。跳线可能在面包板上没有对齐,导致它们与按键引脚没有正确连接。
• 如果你仍然遇到问题,尝试将故障的按键换成你知道能正常工作的按键;如果这样解决了问题,可能是原来的按键有故障。
Audio LED 可视化器**
在这个项目中,我们将使用一个声音传感器,根据它检测到的声音的节拍和音量来点亮一系列 LED。


所需零件
Arduino 板
面包板
实心电线,末端已剥皮
跳线
2 个红色 LED
2 个黄色 LED
5 个绿色 LED
9 个 220 欧姆电阻
3.5 毫米女性耳机插孔
工作原理
在项目 2 中,我们创建了一个由光传感器控制的 LED 夜灯。这个项目与之相似,但 LED 将由声音控制。我们将耳机插孔连接到 Arduino,将插孔连接到 MP3 播放器,然后观看灯光随着音乐“跳动”。MP3 播放器的信号通过耳机插孔被 Arduino A0 引脚接收,并作为脉冲输入。脉冲的模式取决于音乐的节奏和音量。然后,Arduino 根据音乐的脉冲模式向 LED 提供电力。作为替代,你可以添加一个麦克风,并通过彩色灯光来可视化你的声音。
搭建过程
-
将 LED 插入面包板,将短的负极引脚插入 GND 轨道。将面包板上的 GND 轨道连接到 Arduino 的 GND。
-
为每个 LED 插入 220 欧姆的电阻,确保电阻跨越中心断开,并将一端连接到每个正极 LED 的引脚(参见图 9-1)。将每个电阻的另一端通过跳线连接到 Arduino 的数字引脚 2 到 10,如下表所示。
LED Arduino 正极引脚 数字引脚 2-10(通过电阻) 负极引脚 GND 图 9-1: LED 与电源之间需要使用电阻。
![图片]()
注意
这个耳机插孔是从一家美元店购买的收音机中回收来的,但如果你能找到一个购买的耳机插孔,也可以使用。 在耳机插孔上,接脚分别是 GND、右声道和左声道。
-
将耳机插孔的接地引脚直接连接到 GND,插孔的左声道连接到 Arduino 的 A0 引脚,如下表所示。你可以使用跳线来连接,但我使用了实心导线,并剥开了端头以进行连接。细绞线太薄,不容易连接到 Arduino 引脚。(参见图 9-2 了解插孔引脚的位置。)
耳机插孔 Arduino 接地 GND 左声道 A0 图 9-2: 3.5 毫米耳机插孔,插入了 MP3 播放器插头
![图片]()
-
对照图 9-3 中的电路图检查你的设置,然后上传第 81 页中的“程序”代码。
图 9-3: 音频 LED 可视化电路图
![图片]()
将 MP3 播放器插入耳机插孔以进行音频输入。LED 将随着你的音乐节奏和音量舞动!
程序
该草图首先将与 LED 连接的 Arduino 引脚(引脚 2–10)设置为输出模式。此草图中的输入信号来自 MP3 播放器,通过耳机插孔接收,并由模拟引脚 A0 读取。播放器传送的音乐被 A0 作为一系列脉冲接收,音乐的音量和节奏决定了 LED 的点亮方式。音乐越响,点亮的 LED 越多;音乐的节奏越快,LED 闪烁的速度就越快。
// 经 James Newbould 的友好许可使用
int led[9] = {2, 3, 4, 5, 6, 7, 8, 9, 10}; // 与 LED 连接的引脚
int leftChannel = A0; // 连接到耳机插孔的引脚
int left, i; // 创建左和 i 的变量
void setup() {
for (i = 0; i < 9; i++)
pinMode(led[i], OUTPUT); // 将 LED 设置为输出模式
}
void loop() { // 从左到右依次点亮 LED,再反方向点亮
// 根据 A0 的值
left = analogRead(leftChannel); // 读取左声道值
left = left / 10; // 设置灵敏度等级,范围为 1 到 50
if (left == 0) {
for (i = 0; i < 9; i++) { // 如果值很低,关闭 LED
digitalWrite(led[i], LOW);
}
}
else { // 否则按顺序点亮 LED
for (i = 0; i < left; i++) {
digitalWrite(led[i], HIGH);
}
for (i = i; i < 9; i++) {
digitalWrite(led[i], LOW);
}
}
}
故障排除
Q. 代码能够编译,但某些或所有的 LED 灯无法按预期点亮。
• 如果没有 LED 灯亮起,请确保已将 Arduino 的 GND 线连接到正确的面包板电源轨,并确保 Arduino 已连接电源。
• 如果只有部分 LED 点亮,请检查 LED 是否以正确的方向插入,长腿应连接到正电源,短腿应连接到 GND。LED 是有极性的,因此必须正确连接。检查每个电阻是否完全插入,并确保与对应的 LED 腿在同一排。
• 确保 LED 已连接到草图中定义的 Arduino 引脚,并与图 9-3 中的电路图相匹配;草图的第一部分将引脚 2–10 定义为输出模式,因此应该使用这些引脚。
• 如果某个 LED 仍然不亮,可能是它已经损坏或者有故障。检查的方法是将该 LED 与序列中的另一个 LED 交换,看看问题是否解决。如果发现 LED 在另一个位置工作,说明电阻可能有故障或没有完全插入。根据结果,替换 LED 或电阻为正常工作元件。
第四章:电机
老式模拟表盘**
老式模拟显示器具有一定的魅力。在这个项目中,我将展示如何制作你自己的模拟显示器。


所需零件
Arduino 板
面包板
跳线
Tower Pro SG90 9g 伺服电机
光敏电阻
10k 欧姆电阻
所需库
伺服电机
工作原理
今天,测量结果通常通过 LCD 屏幕或 LED 数字显示器以数字方式呈现,但在不久前,模拟指针表盘一直用于显示压力、速度,甚至是时间!Arduino 能够检测来自传感器的电压输入,我们将在这里利用这一能力,创建一个旋转表盘,Arduino 将根据接收到的输入来调整表盘的指针。我们可以在许多项目中使用这个表盘来显示不同的测量值。
在这个项目中,我们将使用光敏电阻来测量光照输入,但你也可以轻松更换成水传感器制作雨水探测器,或者使用气体传感器制作预警仪。光敏电阻,也称为光依赖电阻,根据传感器检测到的光量产生可变电阻,如第 2 个项目中所讨论的那样。
添加模拟传感器的原理对任何选择的传感器都是相同的。大多数传感器有三根连接:接地、+5V 和连接到 Arduino 的模拟 A0 引脚的信号连接——这使得更换不同的传感器变得简单。光敏电阻略有不同,因为它只有两个连接,其中一个连接到电源,另一个连接到 A0 引脚。
我们将使用传感器来测量光照强度,Arduino 会根据该测量值来控制一个小型伺服电机(简称“伺服”)的臂部运动到相应的角度。电机臂的角度表示光输入的强度。
伺服电机,如图 10-1 所示,是一种小型、廉价的大规模生产电机,广泛用于小型机器人和各种电子任务中。伺服电机通过三根电线控制:接地(黑色或棕色)、电源(红色)和信号或控制(通常是橙色、黄色或白色)。通过控制电线发送脉冲,使用脉宽调制(PWM;在第 5 个项目中讨论),光敏电阻接收到的输入决定了伺服电机执行臂的角度。伺服电机每 20 毫秒需要接收一个脉冲,以获取关于角度的正确信息。
脉冲宽度决定了伺服电机的角度运动范围。通常,1.5 毫秒的伺服脉冲宽度将伺服电机设置到其“中立”位置,即 45 度,1.25 毫秒的脉冲宽度将角度设置为 0 度,1.75 毫秒的脉冲宽度将角度设置为 90 度。
图 10-1: 伺服电机

伺服电机臂的角度和伺服硬件的时序因品牌和型号不同而有所不同,但一般来说,伺服电机的角度运动范围为 90 到 180 度,中立位置几乎总是在 1.5 毫秒时。
构建步骤
-
将伺服电机的红色(电源)线直接连接到 Arduino 的 +5V,棕色(接地)线连接到 Arduino 的 GND,黄色(信号)线连接到 Arduino 的引脚 9,如下表所示。
伺服电机 ARDUINO 红色(电源)线 +5V 棕色(接地)线 GND 黄色信号(控制)线 引脚 9 -
将光敏电阻放入面包板,并将一端连接到 Arduino 的 +5V。将光敏电阻的另一端连接到一个 10k 欧姆电阻,如图 10-2 中的电路图所示,并用跳线将该电阻的另一端连接到 Arduino 的引脚 A0(见下表)。将 10k 欧姆电阻的另一端连接到 GND。
光敏电阻 ARDUINO 引脚 1 +5V 引脚 2 通过 10k 欧姆电阻连接到引脚 A0 图 10-2: 光敏电阻连接到 Arduino 的 A0 引脚并测量光照强度。伺服电机连接到引脚 9,并根据光照强度进行移动。
![图片]()
-
上传 “The Sketch” 中的代码,见 第 89 页。
-
制作一个像图 10-3 中那样的刻度盘面板,并将其附加到伺服电机上。确保伺服臂可以像指针一样在刻度盘的测量值上移动。当你为 Arduino 加电时,完全覆盖光敏电阻,然后在面板上标记这个位置为 0。用强光手电照射光敏电阻,得到最大值,并在面板上标记这个位置。然后在 0 和最大值之间等距添加标记,以便你得到一个刻度。
图 10-3: 一个示例面板
![图片]()
伺服电机的执行臂将根据光照强度沿比例尺移动。例如,在图 10-4 的左侧,伺服臂处于 0 位置。在右侧,当光源(在这种情况下是激光)照射到光敏电阻时,伺服臂会显示光照强度的测量值。
图 10-4: 当光照射到光敏电阻上时,伺服臂会移动。

草图
该草图首先调用伺服库,该库已经内置于 Arduino IDE 中(因此无需下载和安装该库)。我们为伺服电机的位置设置了初始值0,并将光敏电阻引脚设为 A0。我们将 Arduino 的 9 号引脚分配给伺服电机,并从模拟引脚读取值。引脚 A0 能够读取光敏电阻的模拟值并将其转换为范围在 0 到 1,023 之间的数字值,因此我们将其缩放到 0 到 179(180 个可能值),以适应伺服臂 180 度的运动范围。如果没有光照射到光敏电阻,值将为0,伺服电机位置将为 0。随着光照的增加,伺服电机的臂会移动,最多可达 180 度。角度取决于亮度。
/* 由 David Cuartielles 创建,Tom Igoe 于 2011 年 8 月 30 日修改
这个示例代码属于公共领域 arduino.cc/en/Tutorial/AnalogInput */
include <Servo.h> // 引用伺服库(内置于 IDE 中)
Servo myservo;
int pos = 0; // 给位置赋值
int lightPin = A0; // 连接到光敏电阻的引脚
void setup() {
myservo.attach(9); // 连接到伺服电机的引脚
}
void loop() {
// 从光敏电阻读取电压,最多可以读取 1024 个可能的值
int lightLevel = analogRead(lightPin);
// 将 1024 值缩放到 180
lightLevel = map(lightLevel, 0, 1023, 0, 179);
// 缩放 0-179(180 个值)
pos = constrain(lightLevel, 0, 179);
myservo.write(pos); // 设置伺服电机的角度
delay(100);
}
故障排除
Q. 代码可以编译,但光照射到光敏电阻时,伺服电机没有移动。
• 如果伺服电机完全没有移动,请确保电线连接与图 10-2 中的电路图一致,并且 Arduino 有电。
• 将 Arduino 连接到 PC,并打开串口监视器以检查是否有来自光敏电阻的读数。如果没有读数,请检查光敏电阻是否牢固插入面包板。如果仍然没有读数,可能是光敏电阻故障,换一个试试。
步进电机**
在这个项目中,我将介绍步进电机(或步进马达),设置它,并讨论它的工作原理。


所需部件
Arduino 板
面包板
跳线
28BYJ-48 步进电机与 ULN2003 驱动模块
50k 欧姆电位器
所需库
步进电机
工作原理
步进电机,如图 11-1 所示,是一种直流电动机,将完整的旋转分成若干个相等的步骤。与项目 10 中使用的伺服电机不同,这种步进电机可以旋转 360 度,且具有高精度定位或持续旋转的优点。
图 11-1: 28BYJ-48 步进电机

步进电机的数据表将说明其每转一圈执行的步数;步进只是一次旋转中的一个运动。一个每转 200 步的电机将通过 200 步旋转 360 度,每步为 1.8 度。步进电机内有两个互锁的盘片,类似于齿轮,齿部具有相反的磁性,交替连接到中心轴或转子上。当电流送入电机的线圈时——这些线圈是当施加电压时成为电磁铁的一系列电线——电磁铁吸引或排斥齿轮状的盘片,从而旋转轴。
你可以通过命令电机移动到并保持在这些步进中的一个位置,来控制电机的位置和速度。由于我们知道每一步代表的角度,因此可以获得准确的转动角度和距离测量。步进电机常用于 CD 和 DVD 播放器以及 3D 打印机中,这些设备需要非常精确的运动。
当你准备购买步进电机时,有几个因素需要考虑。第一个是它是否有齿轮箱。齿轮箱可以提高扭矩(移动力),但会降低每分钟转速(RPM,即速度)。
接下来的考虑是步进电机是双极性还是单极性。双极性电机会改变线圈的极性。极性是电流流动的方向;例如,如果我们反转 5V 和 GND 连接,电机将朝相反的方向转动。双极性电机的线圈较简单,但需要更复杂的驱动器,因为它们为我们反转极性。单极性电机基本上是为每个极性配备一个线圈,但可以使用更简单的驱动器。你可以通过查看连接方式来检查电机是双极性还是单极性:双极性电机有四个连接,而单极性电机有五到八个连接。在这个项目中,我们使用的是单极性电机——28BYJ-48 步进电机配合 ULN2003 驱动器测试模块——这是一个使得通过 Arduino 控制电机变得简单的板,就像项目 4 中的 LED 矩阵模块板一样。一些驱动板的设置可能会有所不同,因此我建议在这个项目中使用此处列出的电机型号,以便能够紧密按照说明进行操作。
转动电位器改变步进电机臂的角度,因此当你将电位器向左或向右移动时,步进电机臂将跟随你的输入。(电位器是一个带旋钮的可变电阻器。)当你转动旋钮时,电位器的电阻值发生变化。它们通常用于电器设备中,如音频设备的音量控制。
构建
-
将步进电机连接到驱动板,如图 11-2 所示。从板外侧的最外层引脚到板中央的最内层引脚,按照以下顺序连接电机的线缆:蓝色、粉色、黄色、橙色、红色。连接器只能以这种方式插入。
图 11-2: 将步进电机连接到驱动板
![Image]()
-
将驱动板的引脚 1、2、3 和 4 直接连接到 Arduino 的引脚 8、9、10 和 11。
步进电机驱动板 Arduino IN1 引脚 8 IN2 引脚 9 IN3 引脚 10 IN4 引脚 11 GND GND +5V +5V -
在面包板上插入一个电位器,将其中间引脚连接到 Arduino A0,引脚两端连接到 Arduino +5V 和 GND,顺序可以任意。
电位器 Arduino 左侧引脚 GND 中间引脚 A0 右侧引脚 +5V -
将驱动板的 GND 和 +5V 分别连接到面包板的 GND 和 +5V,并将面包板的轨道连接到 Arduino。别忘了也要将面包板的电源轨连接到 GND 和 +5V。
-
确保你的设置与图 11-3 中显示的最终配置相匹配,并上传下面的代码“程序代码”。
图 11-3: 步进电机的电路图
![Image]()
程序代码
这段代码是 Arduino IDE 自带的,可以在“文件 ▸ 示例 ▸ 步进电机 ▸ MotorKnob”中找到。我在这里复制了它,你在 IDE 中会看到这段代码。它使用了内置的步进电机库 <Stepper.h>。电位器连接到 Arduino A0 引脚,并根据电位器的旋转提供一个可变电压,进而控制步进电机的位置。
/* MotorKnob
-
此示例代码属于公有领域。
*/
include <Stepper.h>
// 将此值更改为你电机的步数
define STEPS 100
// 创建步进电机类的实例,并指定
// 电机的步数和它所连接的引脚
Stepper stepper(STEPS, 8, 10, 9, 11);
// 来自模拟输入的前一个读数
int previous = 0;
void setup() {
// 设置电机的速度为 700 RPM
stepper.setSpeed(30);
}
void loop() {
// 获取传感器值
int val = analogRead(0);
// 移动的步数等于传感器读数的变化值
stepper.step(val - previous);
// 记住传感器的前一个值
previous = val;
}
故障排除
问: 代码编译成功,但步进电机不转动。
• 当你为电机供电时,驱动电机板上的指示灯应该会闪烁。如果没有闪烁,说明电源有问题,请检查你的设置是否与图 11-3 中的电路图匹配。确保步进电机的连接牢固插入到驱动电机板中,它只能以一种方式插入。
• 如果驱动板的灯亮起但电机不动,请检查电位器的连接是否牢固,并确保与前面的表格匹配。
温控风扇**
在这个项目中,我们将使用 LM35 温度传感器,当温度过高时自动开启风扇。


所需部件
Arduino 主板
面包板
跳线
LM35 温度传感器
5V 单通道继电器模块
12V 小型计算机散热风扇
9V 电池夹和电池
工作原理
LM35 温度传感器(如图 12-1 所示)能够感测温度,并将该测量值以电压形式发送到 Arduino。Arduino 将该电压值转换为摄氏温度,再将该值转换为华氏温度。当温度高于 71 华氏度时,Arduino 向继电器发送电源,继电器开启计算机风扇。
图 12-1: LM35 温度传感器:左引脚为 +5V,中间为数据输出,右引脚为 GND。

计算机风扇所需的功率超过了 Arduino 能提供的功率,因此我们需要为其提供独立的电源:一个 9V 电池。这个电路由电子继电器控制——一种电子操作的开关,在本例中使用电磁铁机械性地打开或关闭电路(如图 12-2 所示)。继电器通常在需要用低功率设备开关高电压设备时使用。我们的继电器通过 5 伏电源供电,用于操作机械开关。在这个项目中,电路的电压为 9 伏,但该继电器可以控制高达 240 伏的电路。然而,增加高电压电路可能非常危险,因此只有在你熟悉电气工作或能寻求专业建议时,才应进行此操作。
图 12-2: 一个 5V 单通道继电器

构建步骤
-
将 LM35 传感器插入面包板,传感器的前面(印有文字的平面)朝向你。将左引脚连接到面包板上的 +5V 导轨,中间引脚连接到 Arduino 的 A0 引脚,右引脚连接到 GND 导轨,具体连接方式见下表。
LM35 传感器 Arduino 左引脚 +5V 中间引脚 A0 右引脚 GND -
继电器上有多个连接,如图 12-3 所示。如果你的继电器模块布局不同,请根据数据表或模块上的引脚标记调整接线。我们的继电器有一个标记为 PWR 的 LED,表示它正在接收电源,还有另一个 LED 显示电磁开关是否开启(你通常也能听到它发出令人满意的“咔嗒”声)。继电器可以在触发时设置为
HIGH或LOW,这可以通过小跳线开关或引脚进行设置。对于我们的项目,确保跳线设置为HIGH,这样当继电器被触发时,它就会发送电源。图 12-3: 继电器连接(你的继电器引脚可能不同,因此请遵循提供的数据表)
![Image]()
-
如图 12-3 所示,继电器模块右侧的引脚分别是信号、GND 和 +5V。将继电器的信号引脚连接到 Arduino 引脚 5,GND 连接到 Arduino 的 GND,引脚 +5V 通过面包板的电源轨连接到 Arduino 的电源。
5V 继电器 Arduino 信号 引脚 5 GND GND +5V +5V -
在继电器模块的左侧是电磁开关的连接(图 12-3)。中间引脚是公共连接;左侧引脚标有 NO,表示常开,意味着电路是断开的,默认状态是关闭的;右侧引脚标有 NC,表示常闭,默认状态是开启的。如果继电器没有切换,公共引脚将连接到 NC 引脚。如果继电器切换,公共引脚将连接到 NO 引脚。由于我们希望电路在使用开关之前保持关闭状态,所以我们将使用 NO 引脚。
-
接下来,将风扇的黑色 GND 线连接到 9V 电池的 GND 线。然后,按照下表所示,将风扇的红色正极线连接到继电器的公共引脚,并将 9V 电池的正极线连接到继电器的 NO 引脚。
5V 继电器 风扇/9V 电池 NO(常开) 9V 电池的正极 公共 风扇正极线 NC(常闭) 未连接 -
将面包板的电源轨连接到彼此,并连接到 Arduino 的 GND 和 +5V 引脚。
-
确保你的设置与图 12-4 中的电路图一致,然后上传第 103 页中的“The Sketch”代码。
图 12-4: 温控风扇的电路图
![Image]()
The Sketch
在此草图中,我们首先将 LM35 的传感器引脚设置为 Arduino 的 A0,将风扇定义为引脚 5,并创建一个变量来读取 LM35 的值。然后,我们创建一个变量来存储温度,并将风扇引脚设置为输出。一个小的计算将传感器的电压读数转换为华氏度的温度值。接着,我们启动串口监视器,这样你就可以看到 Arduino 连接到 PC 时的 LM35 读数值,这对于确保传感器正常工作非常有用。一个循环每秒读取传感器,如果温度达到 71 华氏度,电源会传送到风扇引脚,继电器触发并打开风扇。如果温度降到 71 以下,继电器会关闭风扇。
define SENS_PIN A0 // 将 A0 引脚定义为“传感器”
define FAN_PIN 5
int Vin; // 从 Arduino 引脚读取值
float Temperature; // 接收转换后的电压值并转换为温度
float TF; // 接收转换后的 °F 值
void setup() {
pinMode(FAN_PIN, OUTPUT); // 将风扇引脚设置为输出
Serial.begin(9600); // 启动串口监视器
}
void loop() {
// 告诉 Arduino 读取引脚并将值存储在 Vin 中
Vin = analogRead(SENS_PIN);
// 将电压值转换为温度并
// 将值存储在温度(°F)中
温度 = (500 * Vin) / 1023 * (1.8) + 32;
TF = 温度;
Serial.print("Temperature: "); // 向显示屏发送文本
Serial.print(TF); // 在串口监视器中显示温度值
Serial.println(" F"); // 写入 F,表示温度单位为华氏度
if (TF > 71) { // 如果温度超过 71 度
digitalWrite(FAN_PIN, HIGH); // 打开风扇
}
else if (TF < 71) {
digitalWrite(FAN_PIN, LOW); // 或者保持风扇关闭
}
delay(1000); // 等待一秒钟以再次读取引脚
}
故障排除
问: 风扇在预期的时间没有开启。
• 确保 LM35 的连接与本章中的表格以及图 12-4 中的电路图匹配。将 Arduino 连接到计算机,并打开 IDE 串口监视器检查 Arduino 是否正确读取传感器。如果读取结果不正确,请重新检查接线或更换传感器。
• 请记住,您的继电器可能与这里使用的继电器不同,因此连接顺序可能会有所不同;根据您的继电器和数据表调整接线。
• 这里使用的风扇需要 9 到 12 伏特的电压,因此 9V 电池有足够的电力来驱动它。如果使用的是需要更高电压的风扇,则需要根据风扇的电压要求,使用更强大的电池来匹配其电压输入。
第五章:LCD 显示屏
超声波测距仪**
在这个项目中,我们将制作一个简单的超声波测距仪,屏幕上将显示物体距离传感器最多 5 米的距离。


所需零件
Arduino 开发板
面包板
跳线
HD44780 16x2 LCD 屏幕
HC-SR04 超声波传感器
50k-欧姆电位器
所需库
LiquidCrystal
工作原理
超声波测距仪发出一阵超声波,并监听从物体反射回来的回声。Arduino 在触发引脚上发送短脉冲来发射超声波,然后通过 pulseIn 函数监听回声引脚上的脉冲。
发送和接收脉冲之间的时间等于超声波到达物体并返回传感器的时间。Arduino 将此时间转换为距离并显示在 LCD 屏幕上。你可以从 “零售商列表” 中找到 HC-SR04 模块(图 13-1),或者你也可以在线搜索 HC-SR04 超声波模块。
图 13-1: HC-SR04 超声波传感器

LCD(液晶显示屏)由两片偏振材料和其中的液晶溶液组成。通过液晶溶液的电流使屏幕变得不透明,因此,通过控制电流通过屏幕的区域,Arduino 就能在屏幕上创建图像或字符。在使用 Arduino 时,你需要一个与 Hitachi HD44780 驱动器兼容的 LCD 屏幕;市面上有很多此类屏幕,通常可以通过其 16 引脚接口来识别。我们将使用 LiquidCrystal 库将字符发送到 LCD 屏幕(如果需要复习库的内容,请参考入门指南)。LiquidCrystal 库将字符映射并使用 print 命令将消息发送到屏幕。
准备 LCD 屏幕
LCD 屏幕可能需要一些组装。你的屏幕应该带有 16 个孔,如 图 13-2 所示,并且有一条单独的排针。将一排 16 个针脚从排针条上断开。将短的一侧插入 LCD 屏幕的 16 个孔中。你需要将这些针脚焊接到位;如果需要指导,入门指南中有一个快速焊接指南。首先焊接最右边和最左边的针脚,固定排针后等一下让其凝固。然后逐个焊接每个针脚。将电烙铁长时间停留在针脚上会损坏它们,因此焊接时只需要几秒钟。
图 13-2: 16×2 LCD 屏幕

组装过程
-
将 LCD 屏幕放置在面包板上,将引脚插入面包板孔中。同时将电位器放入面包板,并使用跳线将 LCD 屏幕、Arduino 和电位器连接,如下表所示。LCD 屏幕的引脚应有标签或编号,无论是在背面还是正面。如果没有,通常从左侧开始编号,沿顶部排列时引脚从 1 开始。LCD 屏幕与 Arduino GND 之间有多个连接,因此请使用面包板的地轨与 Arduino 的 GND 引脚进行多次连接。
LCD 屏幕 ARDUINO 1 VSS GND 2 VDD +5V 3 VO 对比度 电位器中间引脚 4 RS 引脚 11 5 R/W 引脚 10 6 使能 引脚 9 7 D0 无连接 8 D1 无连接 9 D2 无连接 10 D3 无连接 11 D4 引脚 7 12 D5 引脚 6 13 D6 引脚 5 14 D7 引脚 4 15 A BcL+ +5V 16 K BcL– GND -
你应该已经将 50kohm 电位器的中间引脚连接到 LCD 的引脚 3(VO)。现在将电位器的一个外部引脚连接到 GND,另一个连接到+5V。旋转电位器来调节 LCD 屏幕的对比度。
-
背光 LCD 屏幕内置了电阻器,但如果你使用的是非背光 LCD 屏幕,请在 LCD 15 和+5V 之间插入一个 220 欧姆的电阻。如果不确定,请查看屏幕的数据手册。
-
将超声波传感器模块添加到面包板,将 VCC 连接到+5V,Trig 连接到 Arduino 引脚 13,Echo 连接到 Arduino 引脚 12,GND 连接到 GND,如下表所示。
超声波传感器 ARDUINO VCC +5V Trig 引脚 13 Echo 引脚 12 GND GND -
将面包板的电源轨连接到 Arduino 的+5V 和 GND。
-
检查你的设置是否与图 13-3 中的电路图一致,并上传“草图”中的代码,该代码位于 112 页。
图 13-3: 超声波测距仪的电路图
![Image]()
草图
草图首先调用 LiquidCrystal 库,并定义连接到 Arduino 的 LCD 引脚。Arduino 的引脚 13 连接到传感器的触发引脚,发送超声波信号,Arduino 的引脚 12 连接到传感器的回波引脚,接收返回信号。Arduino 将发送和接收信号之间的时间转换为距离,并将结果显示在 LCD 屏幕上,单位为英寸和厘米。此草图可以在 Arduino 网站上找到,因此我已按原样复制在这里。
/*
2008 年 11 月 3 日,由 David A. Mellis 创建;
2011 年 8 月 30 日,Tom Igoe 修改
本示例代码属于公共领域。
*/
include <LiquidCrystal.h>
LiquidCrystal lcd(11, 10, 9, 7, 6, 5, 4);
int pingPin = 13;
int inPin = 12;
void setup() {
lcd.begin(16, 2);
lcd.print("testing...");
}
void loop() {
// 为 ping 的持续时间建立变量,
// 和距离结果(以英寸和厘米为单位):
// 长时间的持续时间、英寸、厘米;
// PING))) 通过一个 2 毫秒或更长的高脉冲触发
// 预先发送一个短暂的低脉冲,以确保清晰的高脉冲:
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(10);
digitalWrite(pingPin, LOW);
// 使用相同的引脚读取 PING))) 信号:
// 高脉冲,其持续时间为时间(以微秒为单位)
// 从发送 ping 信号到接收到其回声的时间
// 物体的距离。
pinMode(inPin, INPUT);
duration = pulseIn(inPin, HIGH);
// 将时间转换为距离
inches = microsecondsToInches(duration);
cm = microsecondsToCentimeters(duration);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(inches);
lcd.print("in, ");
lcd.print(cm);
lcd.print("cm");
delay(100);
}
long microsecondsToInches(long microseconds) {
// 根据 Parallax 的 PING))) 数据表,
// 声音速度是 1130 英尺/秒,即每英寸 73.746 毫秒。
// 这给出了 ping 信号行进的距离,来回,
// 并返回,因此需要除以 2 来得到障碍物的距离。
return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds) {
// 声音速度是 340 米/秒,或每厘米 29 毫秒。
// Ping 信号发射出去并返回,所以要找出距离
// 物体的距离,取行进距离的一半。
return microseconds / 29 / 2;
}
故障排除
问: LCD 屏幕上没有显示任何内容。
• 确保你已经将电源连接到面包板的电源轨,并且连接与之前给出的表格匹配。
• 调整可调电阻的旋钮,改变屏幕的对比度,直到看到文本为止。
• 如果屏幕上显示的是乱码信息,说明你没有正确连接电线;请重新检查你的接线,参考 图 13-3。
数字温度计
这个项目将添加一个 LM35 温度传感器到 LCD 屏幕和 Arduino 上,给你一个数字温度计。


所需零件
Arduino 板
面包板
跳线
HD44780 16×2 LCD 屏幕
LM35 温度传感器
50k-欧姆可调电阻
所需库
LiquidCrystal
工作原理
Arduino 从我们在第 12 个项目中使用的相同 LM35 温度传感器读取电压,并将该值转换为摄氏度温度。然后,草图通过将该值乘以 9,结果除以 5,再加上 32 来转换为华氏度。LiquidCrystal 库通过使用 lcd.print 命令在 LCD 屏幕上显示温度,完成了所有的繁重工作。这个项目可以很容易地添加更多传感器,变成一个全功能的天气中心。
构建
首先,按照 第 109 页上 “准备 LCD 屏幕” 的说明准备 LCD 屏幕。然后按照以下步骤操作:
-
将 LCD 屏幕和电位器插入面包板,然后使用面包板和跳线按下表所示的方式连接 LCD 屏幕。
LCD 屏幕 Arduino 1 VSS GND 2 VDD +5V 3 VO 对比度 电位器中间引脚 4 RS 引脚 12 5 R/W GND 6 使能 引脚 11 7 D0 无连接 8 D1 无连接 9 D2 无连接 10 D3 无连接 11 D4 引脚 5 12 D5 引脚 4 13 D6 引脚 3 14 D7 引脚 2 15 A BcL+ +5V 16 K BcL– GND -
将 GND 和 +5V 电源轨连接到 Arduino 的 GND 和 +5V。
-
你应该已经将 50kΩ 电位器的中间引脚连接到 LCD 的引脚 3(VO)。现在将其中一个外部引脚连接到 GND,另一个连接到 +5V。
-
将 LM35 温度传感器的中间引脚连接到 Arduino 的 A0,引脚左侧连接到 +5V 电源轨,右侧连接到 GND 电源轨,如下表所示。
LM35 传感器 Arduino 左侧 +5V 中间 A0 右侧 GND -
确保你的设置与图 14-2 中显示的电路图匹配,并上传在 第 118 页 的“程序代码”。
图 14-1: 数字温度计的电路图
![Image]()
程序代码
此程序使用 LiquidCrystal 库根据 LM35 传感器检测到的值在屏幕上显示结果。LM35 传感器向 Arduino 的 A0 引脚发送读取值,该值以电压形式读取。程序将电压值转换为摄氏温度,然后使用一系列计算将最终结果以华氏温标显示。程序每秒更新并显示温度值。
include <LiquidCrystal.h> // 调用 LCD 库
define sensor A0 // 连接到 LM35 传感器的引脚(A0)
int Vin; // 从 Arduino 引脚读取的值
float Temperature; // 接收转换后的温度电压值
float TF; // 接收转换后的华氏温度值(°F)
LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // LCD 连接引脚
void setup() {
lcd.begin(16, 2); // 显示屏为 16x2
lcd.print("Temperature: "); // 将文字发送到 LCD 屏幕
}
void loop() {
// 读取 A0 引脚并存储值到 Vin
Vin = analogRead (sensor);
// 将电压值转换为温度,并
// 存储温度值(单位:°C)
Temperature = (500 * Vin) / 1023;
TF = ((9 * Temperature) / 5) + 32; // 将摄氏度转换为华氏度
lcd.setCursor(0, 1); // 将光标移到 LCD 的下一行
lcd.print(TF); // 在 LCD 屏幕上显示温度
lcd.print(" F"); // 在显示器上写入 F,表示华氏温标
delay(1000); // 等待一秒钟再读取引脚
}
故障排除
问: LCD 屏幕上没有显示任何内容。
• 确保你已经将电源连接到面包板电源轨,并且连接符合前面给出的表格。
• 调整电位器以改变屏幕的对比度,直到显示出文字。
• 如果屏幕上显示乱码,可能是接线不正确;请根据图 14-2 重新检查接线。
• 如果显示的值看起来太高,确保 LM35 传感器已牢固插入面包板,并稍等片刻让读数稳定。
炸弹解码器游戏**
在这个项目中,我们将构建一个破解炸弹解码的游戏。我们将使用 LCD 屏幕和键盘为玩家提供指令并接受输入。


所需零件
Arduino 板
面包板
跳线
HD44780 16×2 LCD 屏幕
10k 欧姆电位器
蜂鸣器
3×4 薄膜键盘
3 个 220 欧姆电阻
红色 LED
黄色 LED
绿色 LED
所需库
LiquidCrystal
键盘
音调
工作原理
当你给 Arduino 上电时,一个玩家输入四位数字代码启动炸弹计时器。然后他们将计时器交给另一个玩家,后者按 * 键开始解码炸弹——这个玩家(“拆弹员”)必须破解第一个玩家输入的代码,及时拆除炸弹。如果拆弹员按错了键,可以按 # 键删除输入并重新开始。如果输入错误的代码或计时器归零,炸弹将爆炸,游戏失败。
在游戏过程中,黄色 LED 会闪烁,蜂鸣器与倒计时同步发出声音。LCD 屏幕显示倒计时和代码输入。当炸弹引爆时,所有 LED 会闪烁,蜂鸣器发出爆炸声。
进一步推进这个游戏的一个好方法是向拆弹员提四个问题,每个问题为拆弹员提供炸弹代码的一位数字。拆弹员有固定时间来回答问题并输入四位数的代码。如果回答错误或超时,炸弹就会爆炸!
构建
-
如果需要,按 “准备 LCD 屏幕” 中的说明,使用焊接头引脚准备 LCD 屏幕,见 第 109 页。
-
将 LCD 屏幕放置在面包板上,将焊接头引脚插入面包板孔中。还将电位器放置在面包板上,并使用面包板和跳线将 LCD 屏幕、Arduino 和电位器连接起来,如下表所示。有多个 GND 连接点,因此请使用面包板导轨将这些连接到 Arduino GND 引脚。
LCD 屏幕 ARDUINO 1 VSS GND 2 VDD +5V 3 VO 对比 电位器中间引脚 4 RS 引脚 7 5 R/W GND 6 Enable 引脚 8 7 D0 无连接 8 D1 无连接 9 D2 无连接 10 D3 无连接 11 D4 引脚 10 12 D5 引脚 11 13 D6 引脚 12 14 D7 引脚 13 15 A BcL+ +5V 16 K BcL– GND -
你应该已经将 10k 欧姆电位器的中间引脚连接到 LCD 引脚 3(VO)。现在将外侧的一个引脚连接到 GND,另一个引脚连接到 +5V,如图 15-1 所示。这控制 LCD 屏幕的对比度。
图 15-1: 电位器控制 LCD 屏幕的对比度。
![图片]()
-
从正面看键盘,如图 15-2 所示,按键引脚从左到右编号为 1–7。按照以下表格连接键盘引脚。
图 15-2: 具有七个引脚连接的 3×4 数字键盘
![图片]()
键盘 ARDUINO 引脚 1 引脚 5 引脚 2 引脚 A5 引脚 3 引脚 A4 引脚 4 引脚 A2 引脚 5 引脚 A1 引脚 6 引脚 A0 引脚 7 引脚 A3 -
将蜂鸣器的红色电线直接连接到 Arduino 引脚 9,黑色电线连接到 Arduino GND。
蜂鸣器 ARDUINO 红色电线 引脚 9 黑色电线 GND -
将绿色 LED 放置在面包板上,短的负极腿通过 220 欧姆电阻连接到负电源轨。将绿色 LED 的长正极腿连接到引脚 2。同样,将黄色 LED 连接到引脚 3,将红色 LED 连接到引脚 4,如图 15-3 所示,以及下面的表格。
图 15-3: 通过 220 欧姆电阻将 LED 连接到 Arduino。
![图片]()
LED ARDUINO 负极腿 GND 绿色正极 通过 220 欧姆电阻连接至引脚 2 黄色正极 通过 220 欧姆电阻连接至引脚 3 红色正极 通过 220 欧姆电阻连接至引脚 4 -
将面包板上的正负电源轨分别连接到 +5V 和 GND。
-
确保你的完成项目电路与图 15-4 相匹配,记得将所需的库添加到你的 库 文件夹中,然后上传“草图”中的代码,代码位于第 127 页。
图 15-4: 炸弹解码游戏的电路图
![图片]()
玩游戏
图 15-5 展示了游戏不同阶段的过程。
图 15-5: 玩游戏

-
输入代码以设置炸弹。
-
炸弹确认输入的代码。
-
定时器开始倒计时。
-
黄色 LED 随着倒计时闪烁。
-
将键盘交给另一个玩家(解码员)。他们按下键盘上的 * 按钮,然后输入解除炸弹的代码。
-
屏幕不显示输入的数字来解除炸弹。
-
如果输入正确的代码,炸弹解除 . . .
-
. . . 但是如果没有 . . . 爆炸!
注意
所有库和代码可以从 www.nostarch.com/arduinohandbook2/ 下载。
草图
草图调用了 Keypad、LiquidCrystal 和 Tone 库。LiquidCrystal 库已经包含在你的 IDE 中,但你需要从书本的资源中下载 Keypad 和 Tone 库,地址是www.nostarch.com/arduinohandbook2/,并将它们保存到你的 Arduino 的Libraries文件夹中(如果不确定如何操作,请参考入门指南)。
首先,草图定义了定时器持续时间、密码长度、LED 引脚和键盘。它通过显示“输入代码:”来请求第一个玩家输入代码,然后将该值存储为拆弹代码。当第二个玩家(拆弹者)按下*时,定时器开始并等待输入代码,同时黄色 LED 与倒计时同步闪烁。如果拆弹者输入的代码与拆弹代码不匹配,屏幕上会显示“炸弹已爆炸!”的文字,LED 和蜂鸣器会指示爆炸。如果拆弹者输入正确,定时器停止,绿色 LED 亮起,屏幕上会显示“炸弹已拆除”的信息。如果定时器达到零且没有输入,炸弹也会爆炸。游戏结束时,代码会重置,准备开始新一轮游戏。
// 原始代码由 Joey Meyer 和 Chase Cooley 编写
// 并得到友好的许可使用
include <Keypad.h>
include <LiquidCrystal.h>
include <Tone.h>
Tone tone1;
int Scount = 10; // 设置从多少秒开始
int Mcount = 5; // 设置从多少分钟开始
int Hcount = 0; // 计数小时
int DefuseTimer = 0; // 将定时器设置为 0
long secMillis = 0; // 存储上次的秒数
long interval = 1000; // 秒的间隔
char password[4]; // 密码字符数
int currentLength = 0; // 当前正在输入的数字位置
int i = 0;
char entered[4];
int ledPin = 4; // 红色 LED
int ledPin2 = 3; // 黄色 LED
int ledPin3 = 2; // 绿色 LED
// 我们在 LCD 上使用的引脚
LiquidCrystal lcd(7, 8, 10, 11, 12, 13);
const byte ROWS = 4; // 四行
const byte COLS = 3; // 三列
char keys[ROWS][COLS] = {
{'1', '2', '3'},
{'4', '5', '6'},
{'7', '8', '9'},
{'*', '0', '#'}
};
byte rowPins[ROWS] = {5, A5, A4, A2}; // 连接到行引脚
// 键盘的部分
byte colPins[COLS] = {A1, A0, A3}; // 连接到列引脚
// 键盘的部分
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
void setup() {
pinMode(ledPin, OUTPUT); // 设置数字引脚为输出模式
pinMode(ledPin2, OUTPUT); // 设置数字引脚为输出模式
pinMode(ledPin3, OUTPUT); // 设置数字引脚为输出模式
tone1.begin(9);
lcd.begin(16, 2);
Serial.begin(9600);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("输入代码:");
while (currentLength < 4) {
lcd.setCursor(currentLength + 6, 1);
lcd.cursor();
char key = keypad.getKey();
key == NO_KEY;
if (key != NO_KEY) {
if ((key != '*')&&(key != '#')) {
lcd.print(key);
password[currentLength] = key;
currentLength++;
tone1.play(NOTE_C6, 200);
}
}
}
if (currentLength == 4) {
delay(500);
lcd.noCursor();
lcd.clear();
lcd.home();
lcd.print("您输入了: ");
lcd.setCursor(6, 1);
lcd.print(password[0]);
lcd.print(password[1]);
lcd.print(password[2]);
lcd.print(password[3]);
tone1.play(NOTE_E6, 200);
delay(3000);
lcd.clear();
currentLength = 0;
}
}
void loop() {
timer();
char key2 = keypad.getKey(); // 获取按键
如果 (key2 == '*') {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("密码: ");
当 (currentLength < 4) {
timer();
char key2 = keypad.getKey();
如果 (key2 == '#') {
currentLength = 0;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("密码: ");
}
否则如果 (key2 != NO_KEY) {
lcd.setCursor(currentLength + 7, 0);
lcd.cursor();
lcd.print(key2);
entered[currentLength] = key2;
currentLength++;
tone1.play(NOTE_C6, 200);
delay(100);
lcd.noCursor();
lcd.setCursor(currentLength + 6, 0);
lcd.print("*");
lcd.setCursor(currentLength + 7, 0);
lcd.cursor();
}
}
如果 (currentLength == 4) {
如果 (entered[0] == password[0] && entered[1] == password[1] && entered[2] == password[2] &&entered[3] == password[3]) {
lcd.noCursor();
lcd.clear();
lcd.home();
lcd.print("炸弹解除");
currentLength = 0;
digitalWrite(ledPin3, HIGH);
delay(2500);
lcd.setCursor(0, 1);
lcd.print("重置炸弹");
delay(1000000);
}
否则 {
lcd.noCursor();
lcd.clear();
lcd.home();
lcd.print("密码错误!");
如果 (Hcount > 0) {
Hcount = Hcount - 1;
}
如果 (Mcount > 0) {
Mcount = Mcount - 59;
}
如果 (Scount > 0) {
Scount = Scount - 59;
}
delay(1500);
currentLength = 0;
}
}
}
}
void timer() {
Serial.print(Scount);
Serial.println();
如果 (Hcount <= 0) { // 如果计时器达到 0,LCD 显示爆炸
如果 ( Mcount < 0 ) {
lcd.noCursor();
lcd.clear();
lcd.home();
lcd.print("炸弹已 ");
lcd.setCursor(0, 1);
lcd.print("爆炸了!");
当 Mcount < 0 时 {
digitalWrite(ledPin, HIGH); // 打开 LED
tone1.play(NOTE_A2, 90);
delay(100);
digitalWrite(ledPin, LOW); // 关闭 LED
tone1.play(NOTE_A2, 90);
delay(100);
digitalWrite(ledPin2, HIGH); // 打开 LED
tone1.play(NOTE_A2, 90);
delay(100);
digitalWrite(ledPin2, LOW); // 关闭 LED
tone1.play(NOTE_A2, 90);
delay(100);
digitalWrite(ledPin3, HIGH); // 打开 LED
tone1.play(NOTE_A2, 90);
delay(100);
digitalWrite(ledPin3, LOW); // 关闭 LED
tone1.play(NOTE_A2, 90);
delay(100);
}
}
}
lcd.setCursor(0, 1); // 设置光标到第二行
lcd.print("计时器:");
如果 (Hcount >= 10) {
lcd.setCursor(7, 1);
lcd.print(Hcount);
}
如果 (Hcount < 10) {
lcd.setCursor(7, 1);
lcd.write("0");
lcd.setCursor(8, 1);
lcd.print(Hcount);
}
lcd.print("😊;
如果 (Mcount >= 10) {
lcd.setCursor(10, 1);
lcd.print(Mcount);
}
如果 (Mcount < 10) {
lcd.setCursor(10, 1);
lcd.write("0");
lcd.setCursor(11, 1);
lcd.print(Mcount);
}
lcd.print ("😊;
如果 (Scount >= 10) {
lcd.setCursor(13, 1);
lcd.print(Scount);
}
如果 (Scount < 10) {
lcd.setCursor(13, 1);
lcd.write("0");
lcd.setCursor(14, 1);
lcd.print(Scount);
}
如果 (Hcount < 0) {
Hcount = 0;
}
如果 (Mcount < 0) {
Hcount --;
Mcount = 59;
}
如果 (Scount < 1) { // 如果是 60 执行此操作
Mcount --; // Mcount 减 1
Scount = 59; // 重置 Scount
}
if (Scount > 0) { // 执行此操作 59 次
unsigned long currentMillis = millis();
if (currentMillis - secMillis > interval) {
tone1.play(NOTE_G5, 200);
secMillis = currentMillis;
Scount --; // 给 Scount 加 1
digitalWrite(ledPin2, HIGH); // 打开 LED
delay(10); // 等待一秒
digitalWrite(ledPin2, LOW); // 关闭 LED
delay(10); // 等待一秒
}
}
}
故障排除
Q. LCD 屏幕上没有显示任何内容。
• 确保你已经给面包板的电源轨供电,并且连接与本章中的表格一致。
• 转动电位器来调整屏幕的对比度,直到看到文本。
• 如果屏幕上显示乱码信息,说明你没有正确连接电路;请按照图 15-4 中的电路图重新检查接线。
Q. LED 没有在预期时亮起。
• 根据图 15-4 中的电路图检查接线,确保 LED 的短腿连接到面包板的地线轨。
• 容易忘记给面包板的电源轨加电,因此确保通过跳线将面包板两侧的地线和电源轨连接到 Arduino。
• 检查你的 LED 和电阻是否牢固地插入面包板,并且它们是否对齐。
• 如果错误的 LED 亮起,你可能错误地连接了引脚号,只需要调整连接即可。
Q. 压电音响器没有发出声音。
• 音响器的红色正极导线应连接到 9 号引脚,黑色接地导线应连接到 GND。如果音响器仍然没有发出声音,可以尝试换一个。
Q. 按下键盘时,数字不正确或没有注册。
• 确保键盘与 Arduino 的连接完全符合图 15-4 中的电路图。
• 配置专门为本项目的 3×4 数字键盘设置,因此如果你的键盘不同,请查看数据手册,找出需要连接的引脚。
Serial LCD 屏幕**
在本项目中,我们将使用一个 16×2 字符 LCD 屏幕和一个串行模块,创建一个只用两根线控制的串行 LCD。


所需部件
Arduino 开发板
母对母跳线
HD44780 16×2 LCD 屏幕
串口 LCD 屏幕模块
所需库
导线
LiquidCrystal_I2C
工作原理
LCD 屏幕在项目中非常有用,但它们占用了 Arduino 的许多引脚。这意味着如果你将它们应用到更复杂的项目中,可能会用尽引脚。幸运的是,解决方法是使用串行LCD 屏幕。串行 LCD 屏幕使用通信协议I2C(即集成电路间通信),与普通的 16×2 LCD 屏幕不同,它们只需要电源和两个引脚就能通过 Arduino 控制。
串口 LCD 屏幕通常以套件形式提供,需要您焊接引脚,本章稍后会讲解。您通常会分别收到 16×2 LCD 屏幕和串口模块,如图 16-1 所示。
图 16-1: 16×2 LCD 屏幕与串口模块

准备串口 LCD 屏幕
-
串口模块一侧已附加一排 16 个引脚。翻转 LCD 屏幕,您将看到 16 个对应的孔,如图 16-2 所示。
图 16-2: LCD 屏幕的反面
![Image]()
-
将串口控制器的引脚插入这些孔中,如图 16-3 所示。
图 16-3: 将串口模块插入 LCD 屏幕的孔中。
![Image]()
-
小心地为每个引脚加一点焊锡,以建立连接,并将串口监视器固定在屏幕上。有关快速焊接指南,请参考前言部分。
构建过程
您的串口 LCD 屏幕有一个分配的地址,Arduino 需要该地址才能与之通信。不同制造商的地址不同,您需要检查您特定屏幕的地址,因为稍后在草图中需要使用该地址。要检查地址,请将 LCD 屏幕连接到您的 Arduino 并运行一个快速的扫描草图——或者您也可以参考屏幕的数据手册。
-
将您的女性对男性跳线连接到 LCD 屏幕控制器的四个引脚上。
-
将串口 LCD 屏幕按以下方式与 Arduino 连接:GND 对 GND,VCC 对 +5V,SDA 对 Arduino 引脚 A4,SCL 对 Arduino 引脚 A5,如下表和图 16-4 中的电路图所示。
串口 LCD 屏幕 ARDUINO GND GND VCC +5V SDA 引脚 A4 (SDA) SCL 引脚 A5 (SCL) 图 16-4: 串口 LCD 屏幕的电路图
![Image]()
-
上传以下草图到 Arduino。我们将得到 十六进制 地址,这是一种用字母和数字简化表示更大数字的数字系统。
include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("I2C 扫描仪");
}
void loop() {
byte error, address;
int nDevices;
Serial.println("扫描中...");
nDevices = 0;
for (address = 1; address < 127; address++) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("发现 I2C 设备,地址为 0x");
if (address < 16)
Serial.print("0");
Serial.print(address, HEX);
Serial.println(" !");
nDevices++;
}
else if (error == 4) {
Serial.print("地址 0x 的未知错误");
if (address < 16)
Serial.print("0");
Serial.println(address, HEX);
}
}
if (nDevices == 0)
Serial.println("未找到 I2C 设备\n");
else
Serial.println("完成\n");
delay(5000); // 等待 5 秒钟以进行下一次扫描
}
该草图会扫描 Arduino 的 I2C 总线上的所有地址,并在串行监视器中显示输出,如图 16-5 所示。
图 16-5: 你的模块的十六进制地址将在 IDE 的串行监视器中显示。

地址是紧跟在 0x 后面的数字。在我的例子中是 27,所以我需要记下 0x27。你将在最终草图中使用这个地址。
草图
这个草图调用了 Wire 和 LiquidCrystal_I2C 库。Wire 库已包含在 Arduino IDE 中,但你需要通过从 www.nostarch.com/arduinohandbook2/ 下载来安装 LiquidCrystal_I2C 库。这些库使得 Arduino 可以通过仅使用 SDA 和 SCL 引脚的串行通信来控制模块。
在➊处更改代码,将0x27替换为你在测试草图中扫描得到的地址。
include <Wire.h> // 调用 Wire 库
include <LiquidCrystal_I2C.h> // 调用 I2C 库
LiquidCrystal_I2C lcd(0x27➊,16,2); // 设置 LCD 地址为 0x27
// 16 字符和 2 行显示
void setup() {
lcd.begin(); // 初始化 LCD
lcd.backlight();
lcd.print("Arduino Handbook"); // 将消息打印到 LCD
}
void loop() { // 再次循环
}
模块内有一个电位器,用于调节 LCD 屏幕的对比度,如图 16-6 所示。用小螺丝刀小心转动这个电位器,直到屏幕的对比度合适。
图 16-6: 模块背面的蓝色小盒子是一个电位器,用于调节对比度。

故障排除
问: 代码编译通过,但屏幕上什么也不显示。
• 请仔细检查 SDA 和 SCL 引脚是否连接到正确的 Arduino 引脚。如果 LCD 屏幕亮起但没有显示字符,请小心地转动模块背面的微型电位器,直到字母显示出来。
• 如果屏幕仍然没有显示任何内容,并且所有连接都正确,那么可能是因为插针的焊接没有良好连接,或者你把多个引脚焊接在一起。再次用烙铁加热该区域融化焊料,然后使用吸焊器清除多余的焊料,并重新焊接插针。
超声波计数器
本项目教你如何使用 HC-SR04 超声波传感器来感知人们的经过,并在串行 LCD 屏幕上显示计数。


所需材料
Arduino 板
迷你面包板
跳线,公对公和母对公
LED
串行 LCD 屏幕模块
220 欧姆电阻
HC-SR04 超声波传感器
所需库
NewPing
Wire
LiquidCrystal_I2C
工作原理
人员计数器通常用于商店或旅游景点来计算访客数量,但你也可以用它来记录高速公路或停车场的车流量,或者在你不在时记录某人进出你房间的次数!
我们将使用的超声波传感器是 HC-SR04,如图 17-1 所示,它首次出现在第 13 项目中。它使用超声波信号,或称为ping,来计算传感器与物体之间的距离。在这个项目中,我们将利用这个功能来统计每次有人或物体经过传感器前方时的次数。当计数被记录时,LED 将闪烁,串行 LCD 屏幕将显示总计数。
图 17-1: HC-SR04 超声波传感器使用 ping 信号来计算距离。

构建
-
使用母对公跳线将 HC-SR04 超声波传感器连接到 Arduino,将 VCC 引脚连接到 Arduino 的+5V,GND 连接到 GND,Trig 和 Echo 分别连接到 Arduino 的 7 号和 8 号引脚,如下表所示,并参见图 17-2。使用迷你面包板进行多重连接。
超声波传感器 ARDUINO VCC +5V Trig 引脚 7 Echo 引脚 8 GND GND 图 17-2: 超声波传感器的连接
![Image]()
-
确保下载 LiquidCrystal I2C 和 NewPing 库,并将其添加到计算机的相关文件夹中(请参阅入门指南)。Wire 库随 Arduino IDE 附带,因此不需要额外添加。
-
按照以下方式将串行 LCD 屏幕连接到 Arduino,使用迷你面包板连接到+5V。
串行 LCD 屏幕 ARDUINO GND GND VCC +5V SDA 引脚 A4 (SDA) SCL 引脚 A5 (SCL) -
将 LED 插入迷你面包板,使短的负极(GND)引脚在左侧,长的正极(+5V)引脚在右侧,如下表所示,并参见图 17-3。将 220 欧姆电阻连接到 LED 的正极,确保电阻的另一端横跨面包板的断开部分。将电阻的另一端连接到 Arduino 的 13 号引脚。将 LED 的短引脚连接到 Arduino 的 GND。
LED ARDUINO GND GND +5V 通过 220 欧姆电阻连接到引脚 13 图 17-3: 我们使用迷你面包板来固定 LED,并与 Arduino 的+5V 进行多重连接。
![Image]()
-
确保您的最终电路与图 17-4 相符,然后将“草图”中在第 146 页的代码上传到 Arduino。
图 17-4: 超声波人员计数器电路图
![Image]()
草图
该草图首先调用了 LiquidCrystal I2C、NewPing 和 Wire 库,以控制串口 LCD 屏幕和超声波传感器。接下来,它将超声波传感器的 Trig 和 Echo 针脚分别定义为 Arduino 的 7 和 8 号针脚。我们将传感器读取的最大距离设置为 200 厘米(超过 200 厘米的读数会被忽略)。然后我们将 Arduino 的 13 号针脚定义为 LED,用作计数指示器,并创建变量来存储距离和人数。我们创建一个count状态,允许 Arduino 判断是否为有效记录,然后定义 LCD 屏幕的类型。我们初始化 LCD 屏幕,使其显示People:,并将 LED 针脚设置为输出。
循环部分会发送传感器的 ping 信号,如果返回的 ping 信号来自 100 厘米以外的距离,则认为传感器前方为空,没有记录任何东西。如果记录的距离小于 100 厘米,则表示有物体出现在传感器的范围内。为了让people:变量增加,必须有某人经过传感器前,然后离开。传感器将每次接收到有效记录时进行计数,LCD 屏幕上会显示最新的总数。
传感器可以放置在入口的一侧,面向门槛,当有人进入时,传感器会检测到并注册一个计数。如果传感器指向距离墙壁不到 100 厘米的地方,则需要将以下代码行修改为小于到墙壁的距离,否则传感器会每次检测到墙壁时都记录一次计数。
if (distance < 100 && distance != 0 && !count)
下面是完整代码:
include <LiquidCrystal_I2C.h> // 引入库文件
include <NewPing.h>
include <Wire.h>
define TRIGGER_PIN 7 // 超声波传感器触发针脚连接 Arduino 的针脚 7
define ECHO_PIN 8 // 超声波传感器回声针脚连接 Arduino 的针脚 8
define MAX_DISTANCE 200
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
int LEDPin = 13; // 将 LED 连接到针脚 13
int distance; // 距离变量
int people = 0; // 人数变量
boolean count = false; // 计数状态
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() { // 初始化 LCD 屏幕和 LED
lcd.begin();
lcd.backlight();
pinMode(LEDPin, OUTPUT); // 将 LED 设置为输出
lcd.print("People:"); // 在 LCD 屏幕上打印 "People:"
}
void loop() { // 该部分会无限循环,检查人数
delay(50);
distance = sonar.ping_cm(); // 每 50 毫秒发一次 ping 信号
// 如果距离大于 100 厘米,则不计数
if (distance > 100 && count) {
count = false;
digitalWrite(LEDPin, LOW);
}
// 如果距离小于 100 厘米,则计数 1
if (distance < 100 && distance != 0 && !count) {
count = true;
people ++; // 每次计数增加 1
digitalWrite(LEDPin, HIGH);
lcd.setCursor(10, 0);
lcd.print(people); // 将人数打印到 LCD 屏幕
}
}
故障排除
问答 代码编译通过,但屏幕上什么也不显示。
• 请再次检查 SDA 和 SCL 引脚是否连接到正确的 Arduino 引脚。
• 如果 LCD 屏幕亮起但什么也不显示,请小心调节模块背面的细小电位器,调整对比度,直到字母出现。
问答 传感器没有注册计数或 LED 未按预期亮起。
• 确保超声波传感器的触发引脚连接到 Arduino 的 7 号引脚,回声引脚连接到 Arduino 的 8 号引脚,并且电源已连接到 GND 和+5V。
• 如果计数已注册,但 LED 未亮,请重新检查 LED 的短脚是否连接到 GND,长脚是否连接到+5V。电阻应跨越面包板的断开处,一侧连接到 LED 的长脚,另一侧连接到 Arduino 的 13 号引脚。
• 记住传感器的位置非常重要。如果到固定物体(如墙壁)的距离小于示例中的距离,计数将不正确。
• 您的设备可能与我们这里使用的地址不同。要检查您的设备地址,可以使用 Arduino 网站上的 I2C 扫描器示例(playground.arduino.cc/Main/I2cScanner)。将示例与设备连接到 Arduino 后运行,并打开 IDE 串口监视器,您应该会看到设备的地址。使用显示的地址更新以下行:
LiquidCrystal_I2C lcd(0x27,16,2);
Nokia 5110 LCD 屏幕 Pong 游戏**
本项目向您展示了如何将 Nokia 5110 LCD 屏幕连接到 Arduino,以重现一个Pong风格的街机游戏。


所需组件
Arduino 板
面包板
跳线
Nokia 5110 LCD 屏幕
4 个 10k 欧姆电阻
2 个 1k 欧姆电阻
2 个 50k 欧姆电位器
工作原理
Nokia 5110 LCD 屏幕曾用于几年前所有的 Nokia 手机,因此您应该能在网上找到很多。我们将其连接到 Arduino,并通过添加一些电位器作为控制器来创建一个简单的Pong风格游戏。
注意
有关焊接插针的说明,请参阅项目 13;如果您以前没有焊接过,请参阅焊接入门指南。
屏幕是 84×48 像素,字符之间有间距,以免字符相互接触,这样我们就得到了一个 12×6 字符的屏幕。该屏幕的工作方式与项目 13 中的 LCD 屏幕相同:通过从 Arduino 发送电流到液晶显示屏,使特定像素不透明,形成字母或图像。
大多数屏幕都带有分离的插针,以便运输,因此如果您想将屏幕插入面包板,可能需要将它们焊接到位。您需要将一排八个插针焊接到屏幕一侧的孔排中,如图 18-1 所示。
图 18-1: 诺基亚 5110 LCD 屏幕的背面,显示引脚连接

本项目连接到 Arduino 的 +3.3V,而不是 +5V。
构建过程
-
将诺基亚 5110 屏幕插入面包板。
-
诺基亚屏幕有八个引脚。为诺基亚引脚 1、3、4 和 5 插入 10k-欧姆电阻器,确保它们跨越中心断点。为诺基亚引脚 2 和 7 插入 1k-欧姆电阻器,如图 18-2 所示。
图 18-2: 如此处所示,插入诺基亚 LCD 屏幕的电阻器。
![Image]()
警告
对于本项目,使用 Arduino 的 +3.3V 电源供电给诺基亚 5110 屏幕,而不是 +5V;否则,您将损坏屏幕,这是非常重要的。
-
使用跳线将诺基亚屏幕与 Arduino 的引脚 3–7 以及面包板电源轨连接。确保将正确值的电阻器连接到正确的引脚,如下表所示。某些扩展板上的引脚位置可能不同,因此请将诺基亚屏幕上的引脚名称与 Arduino 引脚进行匹配。
诺基亚 5110 屏幕 电阻器 Arduino 1 RESET 10k-欧姆 引脚 6 2 CE 1k-欧姆 引脚 7 3 DC 10k-欧姆 引脚 5 4 DIN 10k-欧姆 引脚 4 5 CLK 10k-欧姆 引脚 3 6 VCC 无 +3.3V 7 Light 1k-欧姆 GND 8 GND 无 GND -
按照图 18-3 所示,将可调电阻插入面包板。将其中一个可调电阻的中间引脚连接到 Arduino A0,将另一个可调电阻的中间引脚连接到 Arduino A1。将每个可调电阻的一个外侧引脚连接到面包板的 +5V 电源轨,另一个外侧引脚连接到 GND 电源轨。
-
将面包板的电源轨连接到 Arduino 的 +5V 和 GND(这仅用于可调电阻)。
-
确保您的设置与图 18-3 相匹配,并上传下方的代码“草图”。
图 18-3: 诺基亚 5110 LCD 屏幕 乒乓 游戏的电路图
![Image]()
草图
游戏开始时,屏幕的两侧各有一个条形,球在它们之间反弹。游戏的目标是使用可调电阻来移动条形,就像挡板一样,将球反弹回来,防止它越过屏幕边界(即超出屏幕外)。球会在挡板上反弹,并逐渐加速。游戏结束时,若球越过屏幕限制,显示会反转,游戏会重新开始。请注意,由于屏幕图形的限制,球越快移动时,可能会显得较为模糊。
草图的第一部分定义了连接到 Nokia 5110 LCD 屏幕的引脚。接着定义了屏幕的大小,也就是我们游戏中计算为有效区域的部分,以及条形和球的大小和起始位置。电位器从 Arduino 的 A0 和 A1 引脚读取模拟信号,并根据旋转的角度来移动对应的条形。在后续的计算中,判断球和条形是否在某些坐标处相遇。如果相遇,球会弹回;如果没有相遇,说明条形错过了球,屏幕将反转并闪烁,表示游戏结束。
// Arduino Pong by Onur Avun and reproduced with kind permission
define PIN_SCE 7
define PIN_RESET 6
define PIN_DC 5
define PIN_SDIN 4
define PIN_SCLK 3
define LCD_C LOW
define LCD_D HIGH
define LCD_X 84
define LCD_Y 6
int barWidth = 16;
int barHeight = 4;
int ballPerimeter = 4;
unsigned int bar1X = 0;
unsigned int bar1Y = 0;
unsigned int bar2X = 0;
unsigned int bar2Y = LCD_Y * 8 - barHeight;
int ballX = 0;
int ballY = 0;
boolean isBallUp = false;
boolean isBallRight = true;
byte pixels[LCD_X][LCD_Y];
unsigned long lastRefreshTime;
const int refreshInterval = 150;
byte gameState = 1;
byte ballSpeed = 2;
byte player1WinCount = 0;
byte player2WinCount = 0;
byte hitCount = 0;
void setup() {
LcdInitialise();
restartGame();
}
void loop() {
unsigned long now = millis();
if (now - lastRefreshTime > refreshInterval) {
update();
refreshScreen();
lastRefreshTime = now;
}
}
void restartGame() {
ballSpeed = 1;
gameState = 1;
ballX = random(0, 60);
ballY = 20;
isBallUp = false;
isBallRight = true;
hitCount = 0;
}
void refreshScreen() {
if (gameState == 1) {
for (int y = 0; y < LCD_Y; y++) {
for (int x = 0; x < LCD_X; x++) {
byte pixel = 0x00;
int realY = y * 8;
// 如果在框架内,绘制球
if (x >= ballX && x <= ballX + ballPerimeter -1 && ballY +
ballPerimeter > realY && ballY < realY + 8 ) {
byte ballMask = 0x00;
for (int i = 0; i < realY + 8 - ballY; i++) {
ballMask = ballMask >> 1;
if (i < ballPerimeter)
ballMask = 0x80 | ballMask;
}
pixel = pixel | ballMask;
}
// 如果在框架内,绘制条形
if (x >= bar1X && x <= bar1X + barWidth -1 && bar1Y +
barHeight > realY && bar1Y < realY + 8 ) {
byte barMask = 0x00;
for (int i = 0; i < realY + 8 - bar1Y; i++) {
barMask = barMask >> 1;
if (i < barHeight)
barMask = 0x80 | barMask;
}
pixel = pixel | barMask;
}
if (x >= bar2X && x <= bar2X + barWidth -1 && bar2Y +
barHeight > realY && bar2Y < realY + 8 ) {
byte barMask = 0x00;
for (int i = 0; i < realY + 8 - bar2Y; i++) {
barMask = barMask >> 1;
if (i < barHeight)
barMask = 0x80 | barMask;
}
pixel = pixel | barMask;
}
LcdWrite(LCD_D, pixel);
}
}
} else if (gameState == 2) {
}
}
void update() {
if (gameState == 1) {
int barMargin = LCD_X - barWidth;
int pot1 = analogRead(A0); // 读取电位器并设置条形位置
int pot2 = analogRead(A1);
bar1X = pot1 / 2 * LCD_X / 512;
bar2X = pot2 / 2 * LCD_X / 512;
if (bar1X > barMargin) bar1X = barMargin;
if (bar2X > barMargin) bar2X = barMargin;
// 现在移动球
if (isBallUp)
ballY -= ballSpeed;
else
ballY += ballSpeed;
if (isBallRight)
ballX += ballSpeed;
else
ballX -= ballSpeed;
// 检查碰撞
if (ballX < 1) {
isBallRight = true;
ballX = 0;
}
else if (ballX > LCD_X - ballPerimeter - 1) {
isBallRight = false;
ballX = LCD_X - ballPerimeter;
}
if (ballY < barHeight) {
if (ballX + ballPerimeter >= bar1X && ballX <= bar1X + barWidth) {
// 球从 bar1 反弹
isBallUp = false;
if (ballX + ballPerimeter / 2 < bar1X + barWidth / 2)
isBallRight = false;
else
isBallRight = true;
ballY = barHeight;
if (++hitCount % 10 == 0 && ballSpeed < 5)
ballSpeed++;
} else { // 玩家 2 胜利
gameState = 2;
player2WinCount++;
}
}
if (ballY + ballPerimeter > LCD_Y * 8 - barHeight) {
if (ballX + ballPerimeter >= bar2X && ballX <= bar2X + barWidth) {
// 球从 bar2 反弹
isBallUp = true;
if (ballX + ballPerimeter / 2 < bar2X + barWidth / 2)
isBallRight = false;
else
isBallRight = true;
ballY = LCD_Y * 8 - barHeight - ballPerimeter;
if (++hitCount % 10 == 0 && ballSpeed < 5)
ballSpeed++;
} else { // 玩家 1 胜利
gameState = 2;
player1WinCount++;
}
}
} else if (gameState == 2) {
for (int i =0; i < 4; i++) {
LcdWrite(LCD_C, 0x0D); // LCD 反向模式。
delay(300);
LcdWrite(LCD_C, 0x0C); // LCD 反向模式。
delay(300);
}
restartGame();
}
}
void LcdInitialise(void) {
pinMode(PIN_SCE, OUTPUT);
pinMode(PIN_RESET, OUTPUT);
pinMode(PIN_DC, OUTPUT);
pinMode(PIN_SDIN, OUTPUT);
pinMode(PIN_SCLK, OUTPUT);
delay(200);
digitalWrite(PIN_RESET, LOW);
delay(500);
digitalWrite(PIN_RESET, HIGH);
LcdWrite(LCD_C, 0x21 ); // LCD 扩展命令
LcdWrite(LCD_C, 0xB1 ); // 设置 LCD Vop(对比度)
LcdWrite(LCD_C, 0x04 ); // 设置温度系数。 //0x04
LcdWrite(LCD_C, 0x14 ); // LCD 偏置模式 1:48. //0x13
LcdWrite(LCD_C, 0x0C ); // LCD 正常模式。
LcdWrite(LCD_C, 0x20 );
LcdWrite(LCD_C, 0x80 ); // 选择 LCD RAM 的 X 地址 0
LcdWrite(LCD_C, 0x40 ); // 选择 LCD RAM 的 Y 地址 0
LcdWrite(LCD_C, 0x0C );
}
void LcdWrite(byte dc, byte data) {
digitalWrite(PIN_DC, dc);
digitalWrite(PIN_SCE, LOW);
shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data);
digitalWrite(PIN_SCE, HIGH);
}
故障排除
问: LCD 屏幕上什么都不显示。
• 确保你已将 LCD 屏幕的电源连接到 Arduino 的 +3.3V 电源引脚,并且连接匹配本章中的表格。
• 确保电阻与正确的 LCD 引脚对接,并且连接到 Arduino 引脚的电缆正确。
• 如果 LCD 屏幕的背光亮起,但没有图像,可能是某些连接线搞混了;它们需要与图 18-3 中的电路完全匹配。
问: 当玩家转动电位器时,某个或两个挡板没有移动。
• 确保电位器牢固地连接在面包板上,并且连接到电源轨和 Arduino 的电缆与电位器的引脚对齐。
• 记住电位器需要 Arduino 提供 +5V 电源和 GND。这些引脚应通过跳线连接到面包板的电源轨。
• 确保你还使用跳线将面包板两侧对应的电源轨连接起来。
OLED 呼气酒精测试仪
在这个项目中,我们将使用 MQ3 酒精传感器和 OLED LCD 屏幕制作一个迷你呼气酒精测试仪。


所需部件
Arduino 板
母对公跳线
Keyes MQ3 酒精传感器模块
OLED 单色屏幕 (128×64)
所需库
SPI
电线
Adafruit_GFX
Adafruit_SSD1306
工作原理
MQ3 是一系列气体传感器中的一员,其他传感器还包括 MQ2(对甲烷、丁烷和烟雾敏感)、MQ4(对压缩天然气敏感)、MQ6(对丁烷和液化石油气敏感)以及 MQ7(对一氧化碳敏感)。MQ3 对酒精和乙醇敏感,因此我们将在呼气酒精测试仪中使用它。
免责声明
此项目仅供娱乐使用,不应用于准确判断任何人的酒精摄入量。
Keyes MQ3 模块(图 19-1)包含了我们此项目所需的接线,包括一个内置的电位器和电阻器。模块上的三个引脚分别是 OUT、VCC 和 GND。
图 19-1: Keyes MQ3 酒精传感器模块。与大多数 MQ 传感器一样,该模块内部有一个小加热器和一个电化学传感器,用于测量气体浓度。读取的数值通过 OUT 引脚发送,随后由 Arduino 上的模拟引脚读取。

为了显示传感器读数,我们将使用 OLED 屏幕(图 19-2)。OLED,代表有机发光二极管,是一种发光技术,由薄而多层的有机膜构成,放置在阳极和阴极之间。当施加电压时,图像通过电致发光的方式产生,这意味着屏幕不需要背光。我们的 OLED 屏幕是 I2C 128×64 单色版本,意味着我们只需要通过两根引脚连接到 Arduino,并且它的分辨率为 128×64 像素。这个屏幕使用与第 16 项目中的串口 LCD 相同的通信协议,具体内容可以参考该项目的解释。
图 19-2: 128×64 OLED 单色屏幕。当 MQ3 读取到值时,Arduino 会向 OLED 屏幕发送一条消息,指示是否检测到酒精。

警告
如前所述,MQ3 传感器使用内部加热器作为传感过程的一部分。这个加热器在通电时可达到 120–140 摄氏度,因此在使用时处理时需要小心。
构建过程
-
在第一次使用传感器之前,你需要“烧录”它。这个过程就是将其通电几个小时以加热内部机制,从而提高传感器的准确性。为此,使用母对公跳线将传感器的 VCC 和 GND 引脚分别连接到 Arduino 上的 +5V 和 GND。当你通电 Arduino 时,它会向 MQ3 发送正确的电压。让其通电两到三小时,你可能会闻到烧焦的气味,传感器也会变热,但这都是正常现象。
-
一旦传感器烧录完成,断开 Arduino 的电源,并使用母对公跳线将传感器连接到 Arduino,MQ3 的 OUT 引脚连接到 Arduino 引脚 A0,电源和 GND 按照之前的连接方式保持不变(参见下表)。
MQ3 酒精传感器 ARDUINO OUT 引脚 A0 VCC +5V GND GND -
接下来,将 OLED 屏幕按照下表连接到 Arduino,SCL 连接到引脚 A5,SDA 连接到引脚 A4,VCC 连接到 +3.3V,GND 连接到 GND。
OLED 屏幕 ARDUINO SCL 引脚 A5 SDA 引脚 A4 VCC +3.3V GND GND -
这个项目需要一些库才能正常工作;SPI 和 Wire 库是 Arduino IDE 中自带的,但我们还需要 Adafruit_GFX 和 Adafruit_SSD1306 库来控制 OLED 屏幕。两个库都可以从
www.nostarch.com/arduinohandbook2/获取。如果你需要提醒如何将库添加到 IDE,请参考入门手册。 -
检查你的设置是否与图 19-3 中的电路图一致,并上传下面的代码 “草图”。
图 19-3: OLED 呼气酒精测试仪的电路图
![Image]()
-
MQ3 传感器内部的加热器需要加热约 4 分钟,才能准确工作。草图中有一个计时器,当你首次通电时,直到所需时间过去,屏幕上才会显示值。屏幕上会显示“正在预热”的文字,并有一个小倒计时条,直到传感器准备好。
草图
草图通过调用 SPI、Wire、Adafruit_GFX 和 Adafruit_SSD1306 库来控制通信和 OLED 屏幕。我们为预热阶段设定了时间(4 分钟),并将模拟引脚设置为 Arduino A0。
接下来我们设置 OLED 屏幕。根据从模拟引脚读取的值,Arduino 向屏幕发送不同的消息。例如,如果传感器读数超过 200,Arduino 会问你是否喝过啤酒。如果读数低于这个值,Arduino 会显示你是清醒的。MQ3 能读取的最低酒精值大约是 180。对于超过 450 的值,酒精测试仪会提示你已经醉了!
该草图每秒循环一次,读取模拟传感器。要使用酒精测试仪,请等待传感器加热 4 分钟,然后轻轻呼气到传感器上。尽量避免让传感器湿润或暴露在烟雾环境中,因为这会影响读数。
// 经 Nick Koumaris 允许重新创建,来源于 educ8s.tv
// 调用 SPI、Wire、Adafruit_GFX 和 Adafruit_SDD1306 库
include <SPI.h>
include <Wire.h>
include <Adafruit_GFX.h>
include <Adafruit_SSD1306.h>
define OLED_RESET 4 // 定义 OLED 屏幕
int TIME_UNTIL_WARMUP = 4; // 热身延迟时间,单位为分钟
unsigned long time;
int analogPin = 0; // 设置模拟引脚为 A0
int val = 0; // 设置一个值,从模拟引脚读取
Adafruit_SSD1306 display(OLED_RESET);
void setup() { // 设置 OLED 屏幕
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
}
void loop() { // 读取数据并显示在屏幕上
delay(100);
val = readAlcohol();
printTitle();
printWarming();
time = millis() / 1000;
time /= 60;
if (time <= TIME_UNTIL_WARMUP) { // 如果热身时间少于 4 分钟
time = map(time, 0, TIME_UNTIL_WARMUP, 0, 100); // 显示倒计时
display.drawRect(10, 50, 110, 10, WHITE); // 空白条
display.fillRect(10, 50, time, 10, WHITE);
} else { // 当热身时间已过
// 值和消息将显示在屏幕上
printTitle();
printAlcohol(val);
printAlcoholLevel(val);
}
display.display();
}
void printTitle() { // 屏幕上标题的位置和文本
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(22, 0);
display.println("呼气分析仪");
}
void printWarming() { // 热身消息
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(30, 24);
display.println("正在热身");
}
void printAlcohol(int value) { // 将酒精值打印到屏幕上
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(50, 10);
display.println(val);
}
void printAlcoholLevel(int value) { // 将消息打印到屏幕上
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(20, 25);
if (value < 200) { // 如果读数小于 200,你是清醒的
display.println("你是清醒的...");
}
if (value >= 200 && value < 280) {
display.println("你喝了一瓶啤酒吗?");
}
if (value >= 280 && value < 350) {
display.println("两瓶或更多啤酒。");
}
if (value >= 350 && value < 450) {
display.println("我闻到了伏特加!");
}
if (value > 450) {
display.println("你醉了!");
}
}
// 通过求三个读数的平均值来计算
// 为了更好的精度,除以 3
int readAlcohol() {
int val = 0;
int val1;
int val2;
int val3;
display.clearDisplay();
val1 = analogRead(analogPin);
delay(10);
val2 = analogRead(analogPin);
delay(10);
val3 = analogRead(analogPin);
val = (val1 + val2 + val3) / 3;
return val;
}
故障排除
Q. 显示屏没有正确显示读数。
• 请重新检查你的接线是否与图 19-3 中的图示相符。
• 如果所有接线都正确,确保你已经完成了之前的步骤,将传感器通电几个小时以进行预热。
• 要检查你的组件是否有故障,可以暂时将电位器替换为传感器。将电位器的中间引脚连接到 A0,并为两侧加电。如果电位器正常工作,说明你的传感器可能有故障,应该更换传感器——它们非常便宜。
第六章:安全
超声波浸泡器
在这个项目中,我们将使用超声波传感器来触发一个玩具水枪。你可以设置这个装置,让它在未经察觉的人进入禁区时进行喷水!


所需部件
Arduino 板
面包板
跳线
HC-SR04 超声波传感器
WLtoys V959-18 水射流手枪
所需库
NewPing
工作原理
对于我们的浸泡器,我们将使用 WLtoys V959-18 水射流手枪(图 20-1)附件,这是一种便宜且在网上广泛可得的 RC 直升机配件。该手枪配有一个小水槽,用于储存水,并且有一个小型水泵,通过前面的喷嘴将水射出。手枪只有两根电线:红色是正电源,白色是接地。它只需要一点电流,这使得我们能够通过 Arduino 提供的电流来触发水泵。
图 20-1: WLtoys V959-18 水射流手枪

注意
记住水和电不相容,所以尽量将你的 Arduino 远离水喷射口,以减少水短路你 Arduino 板的风险。
正如我们在第 13 项中讨论的,超声波传感器会发出一束超声波,并监听从物体反射回来的回声,以确定物体的距离。在这里,超声波传感器寻找一个回波,表示物体距离小于 15 厘米,这时 Arduino 会向浸泡器提供电力,向我们的目标喷射水。
组装
-
将超声波传感器模块(图 20-2)添加到你的面包板上,并将 VCC 连接到 +5V,Trig 连接到 Arduino 引脚 12,Echo 连接到 Arduino 引脚 13,GND 连接到 GND,具体连接如下表所示。
超声波传感器 Arduino VCC +5V Trig 引脚 12 Echo 引脚 13 GND GND 图 20-2: 超声波传感器
![Image]()
-
将手枪的红色电源线连接到 Arduino 引脚 3,将白色电线通过面包板的电源轨连接到 Arduino GND。将面包板的电源轨连接到 Arduino 电源。手枪配有一根小移液管,帮助你填充水槽。图 20-3 展示了如何填充水槽。
图 20-3: 使用提供的移液管通过顶部的开口向显示的水槽中加入水。
![Image]()
-
一旦确认你的设置与图 20-4 中的电路图相匹配,将 “The Sketch” 中的代码上传到你的 Arduino,确保将 NewPing 库添加到 Arduino IDE 中。
图 20-4: 超声波浸泡器的电路图
![Image]()
程序代码
在进入草图之前,下载 NewPing 库,链接为 www.nostarch.com/arduinohandbook2/。草图会调用 NewPing 库并定义 Arduino 引脚连接。Arduino 引脚 12 连接到传感器的触发引脚,并发送超声波信号,Arduino 引脚 13 连接到传感器的 Echo 引脚,并接收返回的信号。Arduino 将发送和接收信号之间的时间转换为距离。喷水器连接到 Arduino 引脚 3,循环检查检测到的物体的距离。如果距离小于 15 厘米,则会向引脚 3 发送电源,喷水器会向毫无防备的朋友们喷射水!
include <NewPing.h> // 调用 NewPing 库
define trigPin 12 // Trig 引脚连接到 Arduino 12
define echoPin 13 // Echo 引脚连接到 Arduino 13
define soakerPin 3
define MAX_DISTANCE 500
NewPing sonar(trigPin, echoPin, MAX_DISTANCE);
void setup() {
Serial.begin(9600);
pinMode(soakerPin, OUTPUT);
}
void loop() {
int distance;
distance = sonar.ping_cm();
Serial.print(distance);
Serial.println(" cm");
if (distance <= 15) { // 如果距离小于 15
digitalWrite(soakerPin, HIGH); // 喷水器射水
delay(250);
digitalWrite(soakerPin, LOW); // 短暂的水脉冲
}
else {
digitalWrite(soakerPin, LOW); // 喷水器将保持关闭
}
delay(500);
}
故障排除
Q. 超声波喷水器没有射水。
• 通过重新检查本章表格和 图 20-4 中的电路图,确保连接与超声波传感器的设置匹配。
• 记住,只有当传感器检测到有人或物体在前方时,水才会喷出。
• 确保你已为面包板的电源轨提供电源。
• 通过将水喷射器从电路中断开,再直接将电线连接到 Arduino 的 +5V 和 GND 上,检查水射流是否正常工作。你应该能听到泵电机的嗡嗡声;如果没有听到,可能是你的组件有问题。
指纹扫描仪**
在这个项目中,我们将使用指纹传感器、舵机和一些 LED,制作一个酷炫的生物识别进入系统。


所需部件
Arduino 板
面包板
跳线
红色 LED
绿色 LED
2 个 220 欧姆电阻
Tower Pro SG90 9g 舵机
光学指纹传感器 (ZFM-20 系列)
所需库
Adafruit_Fingerprint
舵机
SoftwareSerial
注意
我们在这个项目中使用的软件仅支持 Windows。
工作原理
生物识别技术用于通过特定的生物特征来识别一个人,这些生物特征即使在很长时间内也保持不变,例如指纹或虹膜图案。由于指纹对每个人都是独一无二的,它们通常用于帮助识别个人,例如在刑事调查和安全身份验证中。在本项目中,我们将使用指纹传感器读取指纹,并且如果它与记录中具有适当安全权限的指纹匹配,则通过移动伺服电机允许访问。
我们将使用的传感器是 ZFM-20 系列指纹识别模块(见图 21-1),但通常称为光学指纹传感器模块。该传感器拍摄指纹的照片,将其添加到模块的数据库中,然后检查扫描的指纹是否匹配。它最多可以存储 162 个指纹。该传感器可以在线购买,也可以从如 Adafruit 等零售商处购买,Adafruit 还为该模块创建了一个专门的 Arduino 库,我们将在草图中使用。
图 21-1: ZFM-20 系列指纹识别模块是一个光学指纹传感器。

准备指纹传感器
要使用传感器,首先需要获取 SFG Demo 软件,可以从www.adafruit.com/datasheets/SFGDemoV2.0.rar下载。SFG Demo 软件是一个简单的免费程序,通过 Arduino 将您的 PC 与指纹识别模块连接,您可以控制它,添加或删除指纹,并为每个指纹分配一个 ID。
-
下载SFGDemoV2.0.rar文件并解压到您选择的目标位置。
-
一旦解压了.rar文件,双击SFGDemo.exe文件运行程序,您将看到图 21-2 所示的界面。
图 21-2: SFGDemo 控制界面
![Image]()
-
现在,您需要通过 Arduino 将指纹传感器模块连接到 PC。模块与 Arduino 的连接如下表所示。
指纹传感器 ARDUINO GND(黑线) +5V RX(白线) Pin 0(RX) TX(绿线) Pin 1(TX) +5V(红线) +5V -
您将使用 Arduino 作为中介,通过 USB 电缆将指纹扫描仪连接到 PC,因此需要加载一个空白的草图,使 Arduino 能够连接到 PC,而无需执行任何功能。最简单的方法是打开最新版本的 Arduino IDE 并上传默认的草图,如下所示。
void setup() {
// 在此处输入您的设置代码,只执行一次:
}
void loop() {
// 在此处输入您的主代码,将重复执行:
}
-
然后,连接 Arduino 到你的 PC,并在 SFGDemo 程序中选择打开设备按钮。在弹出的COM 端口下拉菜单中,选择 Arduino 连接的端口并点击确定。你会看到一个消息,表示你的模块已连接并被识别,如图 21-3 所示。这里我的模块通过 COM 端口 4 连接到 Arduino,但你可能需要使用不同的端口。
图 21-3: 当设备连接正确时,程序会显示“设备打开成功!”的消息。
![Image]()
-
接下来,你将向数据库添加一个指纹。在 SFGDemo 控制屏幕上点击注册。当你看到消息“等待指纹”时,牢牢地把手指按在指纹传感器模块窗口上,并等待几秒钟。当指纹注册成功时,你将看到“成功!”的消息(如图 21-4 所示)。
图 21-4: 模块成功捕捉到指纹,并在左上角窗口中显示指纹预览。
![Image]()
-
现在,你将测试模块是否能识别你刚刚录入的指纹。在 SFGDemo 控制屏幕上点击匹配按钮。当提示时,将你的手指紧紧按在窗口上几秒钟。如果演示找到匹配的指纹,你将看到图 21-5 所示的“通过!”消息。
图 21-5: 指纹匹配成功,且“SFGDemo 控制面板”信息面板中显示“通过!”的消息。
![Image]()
-
现在,你需要检查模块是否能够识别当它连接到 Arduino 而不是 PC 时的指纹。关闭 SFGDemo 程序,然后从你下载的资源中,[
www.nostarch.com/arduinohandbook2/],将 Adafruit 指纹传感器库添加到你的 IDE 中。如果你需要回顾如何添加库,请查阅本书开头的库部分。 -
一旦你添加了 Adafruit 指纹传感器库,打开 IDE 并选择文件 ▸ 示例 ▸ Adafruit-fingerprint-sensor-master ▸ Fingerprint,选择如图 21-6 所示的库指纹草图。将此草图上传到你的 Arduino。
图 21-6: 来自 Adafruit 指纹传感器库的指纹演示草图
![Image]()
注意
你的传感器可能附带六根线,其中两根线对于演示是不必要的。
-
上传指纹草图到你的 Arduino 后,断开与 PC 的连接。接下来,你需要更改模块/Arduino 的引脚设置。将模块连接到 TX 和 RX 引脚的连接,分别更改为 Arduino 的引脚 2 和引脚 3,如下表所示。这可以保持 TX 和 RX 串口通信空闲,以便在下一步中使用 Arduino IDE 的串口监视器。
指纹传感器 Arduino GND(黑色电线) +5V TX(绿色电线) 引脚 2 RX(白色电线) 引脚 3 +5V(红色电线) +5V -
现在,将你的 Arduino 重新连接到 PC,并打开 Arduino IDE。打开 IDE 的串口监视器。当你将手指按在模块窗口时,你应该看到类似图 21-7 的内容。
图 21-7: 模块过程显示在 Arduino IDE 串口屏幕上。
![Image]()
构建
现在你知道模块正常工作,你将利用学到的内容创建指纹录入系统。
-
现在指纹模块应该已经连接到 Arduino,如果你是从这一点开始,请按照步骤 10 中给出的连接进行操作,然后继续。
-
将舵机电机连接到面包板的 GND 和+5V 电源轨,并将信号引脚连接到 Arduino 的引脚 9,如下表所示。
舵机 ARDUINO 信号(黄色电线) 引脚 9 正电源(红色电线) 面包板+5V 电源轨 负极电源(黑色电线) 面包板 GND 电源轨 -
将 LED 插入面包板,使得短的负极腿连接到面包板的 GND 电源轨,长的正极腿通过 220 欧姆电阻连接到 Arduino 的 7 号和 8 号引脚,如下表所示。电阻应该跨越面包板的中心,如图 21-8 所示。
LED ARDUINO 绿色 LED(正极,长腿) 通过 220 欧姆电阻连接到引脚 7 红色 LED(正极,长腿) 通过 220 欧姆电阻连接到引脚 8 两个 LED 的负极(短脚) 面包板 GND 电源轨 图 21-8: LED 通过 220 欧姆电阻连接到 Arduino 引脚。
![Image]()
-
将面包板的电源轨连接到 Arduino 的+5V 和 GND,然后检查你的电路是否与图 21-9 匹配。
-
上传第 183 页中的“草图”代码。
图 21-9: 指纹扫描仪的电路图
![Image]()
草图
草图首先调用了 Servo、SoftwareSerial 和 Adafruit_Fingerprint 库。LED 和舵机的引脚分别定义为 7、8 和 9,引脚 2 和 3 则定义为与指纹传感器模块的串口连接。指纹库处理模块的功能,草图中有一系列步骤来读取和存储指纹。
传感器每 5 秒自动扫描,并在手指按压到窗口时读取指纹。如果指纹与模块内存中的某个指纹匹配(我们在项目中提前存储了该指纹),红色 LED 将熄灭,绿色 LED 会亮起,舵机将旋转 180 度。这个状态将持续 5 秒钟,之后系统将重置并等待下一个有效的录入。
// 指纹传感器库已获许可转载
// 来自 Adafruit Industries
/***************************************************
这是我们光学指纹传感器的示例代码
专为与 Adafruit BMP085 Breakout 配合使用而设计
----> www.adafruit.com/products/751
这些显示器使用 TTL 串行通信,需用到 2 根引脚
接口
Adafruit 投入时间和资源提供此开源代码,
请通过购买支持 Adafruit 和开源硬件
来自 Adafruit 的产品!
由 Limor Fried/Ladyada 为 Adafruit Industries 编写。
BSD 许可协议,任何重新分发必须包含上面所有文字
****************************************************/
include <Servo.h>
include <Adafruit_Fingerprint.h>
if ARDUINO >= 100
include <SoftwareSerial.h>
else
include <NewSoftSerial.h>
endif
int getFingerprintIDez();
int ledaccess = 7; // 绿色 LED 引脚
int leddeny = 8; // 红色 LED 引脚
int servoPin = 9; // 伺服电机引脚
Servo doorLock;
// 引脚 #2 是来自传感器的输入(绿色电缆)
// 引脚 #3 是来自 Arduino 的输出(白色电缆)
if ARDUINO >= 100
SoftwareSerial mySerial(2, 3); // 指纹传感器的引脚
else
NewSoftSerial mySerial(2, 3);
endif
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
void setup() {
doorLock.attach(servoPin); // 我们定义了伺服电机的引脚
pinMode(ledaccess, OUTPUT); // 将绿色 LED 引脚设置为输出
pinMode(leddeny, OUTPUT); // 将红色 LED 引脚设置为输出
pinMode(servoPin, OUTPUT); // 将伺服电机引脚设置为输出
Serial.begin(9600); // 开始向串口监视器发送消息
Serial.println("fingertest");
finger.begin(57600); // 设置传感器串口的数据传输速率
// 启动模块并检查指纹
if (finger.verifyPassword()) {
Serial.println("已找到指纹传感器!");
} else {
Serial.println("未找到指纹传感器 😦");
while (1);
}
Serial.println("等待有效的手指...");
}
void loop() {
int ID = getFingerprintIDez(); // 获取指纹 ID#
// 重置设备为测试状态
digitalWrite(ledaccess, HIGH);
digitalWrite(leddeny, HIGH);
doorLock.write(0);
if (ID >= 0) { // 有效的 ID。解锁状态
// 启用访问 LED,关闭拒绝 LED
digitalWrite(ledaccess, HIGH);
digitalWrite(leddeny, LOW);
// 解锁伺服电机
doorLock.write(180);
}
else if (ID == -3) { // ID 与任何已注册的指纹不匹配
// 锁定状态
// 启用拒绝 LED,关闭访问 LED
digitalWrite(ledaccess, LOW);
digitalWrite(leddeny, HIGH);
}
delay(5000);
}
uint8_t getFingerprintID() {
uint8_t p = finger.getImage();
switch (p) {
case FINGERPRINT_OK: // 当手指放置时,传感器拍照
// 放置在模块窗口上
Serial.println("图像已拍摄");
break;
case FINGERPRINT_NOFINGER:
Serial.println("未检测到手指");
return p;
case FINGERPRINT_PACKETRECIEVEERR:
Serial.println("通信错误");
return p;
case FINGERPRINT_IMAGEFAIL:
Serial.println("成像错误");
return p;
default:
Serial.println("未知错误");
return p;
}
p = finger.image2Tz(); // 成功!我们获得了指纹,且
// 现在检查是否能读取
switch (p) {
case FINGERPRINT_OK:
Serial.println("图像转换完成");
break;
case FINGERPRINT_IMAGEMESS:
Serial.println("图像太混乱");
return p;
case FINGERPRINT_PACKETRECIEVEERR:
Serial.println("通信错误");
return p;
case FINGERPRINT_FEATUREFAIL:
Serial.println("无法找到指纹特征");
return p;
case FINGERPRINT_INVALIDIMAGE:
Serial.println("无法找到指纹特征");
return p;
默认:
Serial.println("未知错误");
return p;
}
p = finger.fingerFastSearch(); // 成功转换!它有效,所以
// 与模块内存进行比对
if (p == FINGERPRINT_OK) {
Serial.println("找到了指纹匹配!");
} else if (p == FINGERPRINT_PACKETRECIEVEERR) {
Serial.println("通信错误");
return p;
} else if (p == FINGERPRINT_NOTFOUND) {
Serial.println("未找到匹配"); // 未找到匹配,
// 返回起始位置
return p;
} else {
Serial.println("未知错误");
return p;
}
// 我们找到匹配了!接下来将执行:
Serial.print("找到 ID #"); Serial.print(finger.fingerID);
Serial.print(" 匹配置信度为 "); Serial.println(finger.confidence);
return finger.fingerID;
}
// 如果失败,返回 -1,否则返回 ID #
int getFingerprintIDez() {
int p = finger.getImage();
if (p != FINGERPRINT_OK) return -1;
p = finger.image2Tz();
if (p != FINGERPRINT_OK) return -2;
p = finger.fingerFastSearch();
if (p != FINGERPRINT_OK) ; {
Serial.println("未找到匹配");
return -3;
}
// 找到匹配!
Serial.print("找到 ID #"); Serial.print(finger.fingerID);
Serial.print(" 匹配置信度为 "); Serial.println(finger.confidence);
return finger.fingerID;
}
故障排除
Q. 代码能编译,但指纹传感器没有亮起或无法工作。
• 确保你的接线与第 181 页和第 182 页中的表格一致。此代码仅适用于我在本项目中使用的指纹传感器。
• 如果你的传感器有六根线,而不是预期的四根,而且线的颜色与描述不符,实际上你需要的是前四根引脚:GND、TX、RX 和 +5V。其他两根连接线在本项目中不会使用,所以可以移除这些线。
• 如果你的模块仍然无法亮起,请查看数据手册中的实际引脚配置,并按照该配置重新连接线。
• 请记得首先设置模块并按照“准备指纹传感器”中描述的步骤进行测试,见第 176 页。
Q. LED 灯没有按预期亮起。
• 确保 LED 灯牢固插入面包板,并且电阻与 Arduino 连接端口对齐。
• 请记得将电源连接到面包板的电源轨道。
Q. 伺服电机没有按预期移动。
• 仔细检查接线是否与图 21-9 中的伺服连接一致。
• 该模块、伺服电机和 LED 灯会消耗相当大的电量,虽然 Arduino 可以在较低电压下继续工作,但伺服电机则无法正常运作。请更换新电池。
第七章:智能机器
超声波机器人**
在本项目中,我们将结合超声波传感器、两台直流电机和一台伺服电机,创建一个简单的避障机器人。


所需零件
Arduino 主板
跳线
L293d 电机驱动板
2 个直流电机和车轮*
HC-SR04 超声波传感器
9V AA 电池包
带配件的机器人底座*
中心轮*
Tower Pro SG90 9g 伺服电机
所需库
伺服电机
NewPing
Adafruit 电机驱动板 V1
*** 这些物品可以作为套件的一部分购买**
工作原理
超声波机器人的关键部件是 HC-SR04 超声波传感器、L293d 电机驱动板和电机。我使用的电机是作为一个套件购买的;如果你在网上搜索“Arduino 机器人套件”,你也应该能够找到包含电机和车轮、底座、电池包、中心轮及所需配件的套件。我购买的套件名为“2WD 智能电机机器人车底盘套件,适用于 Arduino 1:48”,因此尝试几个类似的关键词,直到找到与 图 22-1 中的套件相似的东西。还可以尝试 第 249 页 上的 “零售商列表” 中列出的供应商。
图 22-1: 机器人电机套件

超声波传感器发送和接收信号以测定物体的距离。如果物体距离小于 15 厘米,机器人将停止,四处查看,转向没有感知到任何物体的方向,并朝该方向移动。超声波传感器安装在伺服电机上,使机器人能够移动并寻找清晰的路线。有关 HC-SR04 超声波传感器工作原理的更多信息,请参见第 13 项工程。L293d 电机驱动板安装在 Arduino 上,使用 Adafruit 电机驱动库控制直流电机。
制作过程
-
你需要像在 图 22-2 中所示那样给直流电机焊接电线。如果你需要复习如何操作,请参阅 第 12 页 中的 “快速焊接指南”。将红色正极电源线焊接到一个直流电机的左引脚,将黑色地线焊接到右引脚;另一台电机则反转此顺序。直流电机没有极性,因此不管你如何拿电机确定左右,都无妨,但电源和地线必须放置在电机的相反位置,这样电机旋转的方向才会一致。
图 22-2: 将红色正极电源线焊接到一个直流电机的左引脚,将黑色地线焊接到右引脚。另一台电机则反转此顺序。
![图片]()
-
将单轮固定到机器人底座前部,使用提供的螺丝和配件将两个后轮固定到背面。机器人的底部应与图 22-3 相似。
图 22-3: 组装 Arduino 机器人底座。
![图片]()
-
现在你需要 L293d 电机电路板(图 22-4);我们将给它焊接一些导线来控制超声波传感器。
图 22-4: L293d 电机电路板。我们将焊接四根导线到图中高亮的引脚。
![图片]()
-
取四根母跳线,并将每根跳线的一端去皮约 5 毫米,如图 22-5 所示。
图 22-5: 去皮四根母跳线的末端,以便焊接到电机电路板。
![图片]()
-
按照图 22-6 所示,将去皮的导线焊接到电机电路板上的高亮引脚。这可能比较棘手,所以请花时间确保焊接连接最好。
图 22-6: 如图 22-4 所示,将跳线焊接到电机电路板。电源连接下方的两个引脚应连接到模拟 A4 和 A5,以控制传感器。
![图片]()
-
一旦你将导线焊接到电机电路板,将电路板放在 Arduino 上,使电路板的引脚与下面 Arduino 上的插座对齐。电路板应精确贴合,但要小心对齐引脚与孔位,轻轻放置到位。
-
接着,将超声波传感器连接到你焊接到电机电路板的母端跳线。将传感器上的 VCC 连接到电机电路板上的+5V,Trig 连接到 A4,Echo 连接到 A5,GND 连接到 GND(见下表)。
超声波传感器 电机电路板 VCC +5V Trig 引脚 A4 Echo 引脚 A5 GND GND -
按照下表和图 22-7 所示,将直流电机的导线连接到电机电路板。你可以通过将导线穿过插针并使用螺丝将导线固定在适当位置来连接导线。
左电机 电机电路板 Arduino 红色导线 M1 +5V 黑色导线 M1 GND 右电机 电机电路板 Arduino --- --- --- 红色导线 M3 +5V 黑色导线 M3 GND 图 22-7: 如图所示,连接直流电机的电源导线。
![图片]()
-
接下来,将伺服电机安装到电路板上,如下表和图 22-8 所示。
伺服电机 电机电路板 Arduino 棕色导线 Servo_2 - GND 红色导线 Servo_2 + +5V 黄色导线 Servo_2 s 信号 图 22-8: 如图所示,将伺服电机连接到电路板。
![图片]()
-
使用胶水或胶带将伺服电机固定在机器人的前方。然后将超声波传感器固定在伺服电机的舵上,这样它就可以随着伺服臂一起移动,你的机器人也能环顾四周。在此阶段,机器人应该看起来像 图 22-9 中的样子。
图 22-9: 完成的机器人,超声波传感器附加在伺服电机上
![Image]()
-
确保你已经下载了 NewPing 和 Adafruit Motor Shield 库,并将它们添加到 IDE 中。Servo 库已包含在 IDE 中,因此无需安装。
-
一旦确认你的设置与 图 22-10 中的电路图相匹配,上传 “The Sketch” 代码至 第 198 页,并将 9V 电池包连接到你的 Arduino,看看你的机器人如何运作!
图 22-10: 超声波机器人电路图
![Image]()
草图
草图开始时调用了 Adafruit Motor Shield、NewPing 和 Servo 库。超声波传感器的 Trig 引脚被定义为 Arduino A4,Echo 引脚为 Arduino A5。超声波传感器的最大距离设置为 200 厘米,直流电机的速度设置为 190(满分 255)为中速。直流电机被定义为使用电机屏蔽板的 M1 和 M3 接口。
伺服电机被命名并连接到 Arduino 的 9 号引脚(通过电机屏蔽板的连接)。接下来的循环从超声波传感器读取数据,如果检测到物体距离小于 15 厘米,电机会停止并略微反转,伺服电机会左右移动一次以环顾四周,机器人会转向左侧并继续向前移动,直到发现另一个物体。
// 由 Nick Koumaris 特别授权转载
include <AFMotor.h>
include <NewPing.h>
include <Servo.h>
define TRIG_PIN A4
define ECHO_PIN A5
define MAX_DISTANCE 200
define MAX_SPEED 190 // 设置直流电机的速度
define MAX_SPEED_OFFSET 20
NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE);
AF_DCMotor motor1(1, MOTOR12_1KHZ); // 第一个电机连接到 1 号接口
AF_DCMotor motor2(3, MOTOR12_1KHZ); // 第二个电机连接到 3 号接口
Servo myservo; // 给伺服电机命名
boolean goesForward = false;
int distance = 100; // 定义一个用于距离和速度的 int 类型变量
int speedSet = 0;
void setup() {
myservo.attach(9); // 伺服电机连接到 9 号引脚
myservo.write(115); // 设置伺服电机为 115 度
delay(2000);
distance = readPing(); // 读取传感器的距离
delay(100);
distance = readPing();
delay(100);
distance = readPing();
delay(100);
distance = readPing();
delay(100);
}
void loop() {
int distanceR = 0;
int distanceL = 0;
delay(40);
// 如果距离小于 15 厘米,执行此功能
if (distance <= 15) {
moveStop();
delay(100);
moveBackward();
delay(300);
moveStop();
delay(200);
distanceR = lookRight();
delay(200);
distanceL = lookLeft();
delay(200);
if (distanceR >= distanceL) {
turnRight();
moveStop();
} else { // 否则继续
turnLeft();
moveStop();
}
} else {
moveForward();
}
distance = readPing();
}
int lookRight() { // 伺服电机向右看
myservo.write(50);
delay(500);
int distance = readPing();
delay(100);
myservo.write(115);
return distance;
}
int lookLeft() { // 伺服电机向左看
myservo.write(170);
delay(500);
int distance = readPing();
delay(100);
myservo.write(115);
return distance;
delay(100);
}
int readPing() {
delay(70);
int cm = sonar.ping_cm();
if (cm == 0) {
cm = 250;
}
return cm;
}
void moveStop() {
motor1.run(RELEASE);
motor2.run(RELEASE);
}
void moveForward() {
if (!goesForward) { // 如果区域清空,电机向前移动
goesForward = true;
motor1.run(FORWARD);
motor2.run(FORWARD);
// 缓慢加速以避免负载过重
// 电池消耗过快
for (speedSet = 0; speedSet < MAX_SPEED; speedSet += 2) {
motor1.setSpeed(speedSet);
motor2.setSpeed(speedSet + MAX_SPEED_OFFSET);
delay(5);
}
}
}
void moveBackward() {
goesForward = false;
motor1.run(BACKWARD);
motor2.run(BACKWARD);
// 缓慢加速以避免负载过重
// 电池消耗过快
for (speedSet = 0; speedSet < MAX_SPEED; speedSet += 2) {
motor1.setSpeed(speedSet);
motor2.setSpeed(speedSet + MAX_SPEED_OFFSET);
delay(5);
}
}
void turnRight() { // 向右转动的动作
motor1.run(FORWARD);
motor2.run(BACKWARD);
delay(300);
motor1.run(FORWARD);
motor2.run(FORWARD);
}
void turnLeft() { // 向左转动的动作
motor1.run(BACKWARD);
motor2.run(FORWARD);
delay(300);
motor1.run(FORWARD);
motor2.run(FORWARD);
}
故障排除
Q. 代码已编译,但 Arduino 机器人未按预期工作。
• 确保您的接线与步骤 7、8 和 9 中的表格以及图 22-10 中的电路图匹配。
• 如果您的机器人转圈而不是向前移动,请反接其中一个直流电机的接线——如前所述,直流电机没有极性,但更改电源连接将反转电机的旋转方向。
• 使用一组 1.5V AA 电池串联为机器人供电,而不是使用 9V 电池,后者电流较小,且更容易耗尽。
互联网控制的 LED
在本项目中,我们将使用以太网扩展板将我们的 Arduino 连接到互联网,并通过网页浏览器控制一个 LED。


所需部件
Arduino 主板
面包板
跳线
W5100 以太网扩展板
以太网电缆
LED
220 欧姆电阻
所需库
SPI
以太网
物联网(IoT)正在彻底改变我们使用日常物品的方式。这个术语指的是通过网络连接的物体或智能设备,通常涉及互联网。这使我们能够远程控制设备,无论是在家里还是在外面!亚马逊 Echo 和谷歌 Home 通过允许多个设备通过中央控制中心连接和控制,即使您不在家里,也能进一步推动这一进程。我们将以物联网项目的最基本形式来展示相关原理。
工作原理
以太网扩展板 W5100 LAN 扩展板,如图 23-1 所示,直接安装在 Arduino 上,为板子提供额外的功能。我们将使用内置于 Arduino IDE 中的以太网库,通过以太网线将我们的板子连接到互联网,如图 23-2 所示。
图 23-1: 以太网扩展板

图 23-2: 以太网电缆

该库允许 Arduino 充当服务器以接收传入命令,充当客户端发送命令,或同时充当两者。该扩展板通过串行外设接口(SPI)连接与 Arduino 进行通信。在 Arduino Uno 上,SPI 连接位于数字引脚 10、11、12 和 13 上。在我们的项目中,Arduino 将同时使用这两种功能,将信息以简单网页的形式发送到互联网,并从该页面接收命令以控制 LED。网页上的按钮将允许我们开启或关闭 LED,只要 Arduino 处于通电状态并连接到互联网。
设置您的以太网连接
您需要知道您扩展板的 MAC 地址,以使该项目正常工作。MAC 地址是分配给设备的唯一编号,用于通信,并作为以太网和 Wi-Fi 的网络地址。如果您的扩展板较新,MAC 地址将打印在产品标签上。如果您使用的是较旧的通用以太网扩展板,如我们正在使用的这款,您可以使用 MAC 地址 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED 来完成本项目。
我们将使用端口 80 进行通信,这是 HTTP 的默认端口。HTTP 是超文本传输协议的缩写,是通过互联网传输数据的一套规则。在本例中,端口 80 负责将数据传输到网页。
我们的草图中包含一些 HTML(超文本标记语言)代码,告诉网页浏览器如何显示互联网页面。如果您右键单击任何网页并选择“检查”,您可以看到该页面背后的部分 HTML 代码。
我们草图中包含的 HTML 代码部分如下,并生成了图 23-3 中显示的网页。
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println();
client.print("
互联网控制 LED
图 23-3: 我们控制 LED 的简单网页

搭建过程
-
将以太网扩展板按照图 23-4 所示安装在 Arduino 板上。该扩展板直接与 Arduino 连接,所以轻轻按压扩展板的插针,使其与 Arduino 下方的孔对接。
图 23-4: 将以太网扩展板安装在 Arduino 板上。
![Image]()
-
将 LED 插入面包板,使其两脚跨过面包板的中间断点。然后,如下表所示,将 LED 的较短负极脚通过 220 欧姆电阻连接到面包板的 GND 轨道,将 LED 的较长正极脚连接到 Arduino/以太网扩展板的引脚 7。将面包板的 GND 轨道连接到 Arduino 的 GND。
LED Arduino 负极 通过 220 欧姆电阻连接到 GND 正极 引脚 7 -
将以太网扩展板安装在 Arduino 上后,通过以太网电缆将扩展板连接到路由器。
注意
请注意你的 IP 地址,它将不同于图 23-5 中显示的我的地址。
-
将 Arduino 连接到 PC,并使用 IDE 上传项目末尾的代码。上传代码后,打开 IDE 的串口监视器,以确定 Arduino 的IP 地址(一个用于识别连接到互联网设备的唯一数字字符串),它作为我们的服务器运行。你应该看到类似于图 23-5 的内容。
图 23-5: Arduino 扩展板的 IP 地址将在串口监视器中显示。
![Image]()
-
打开任意网页浏览器,输入你的 IP 地址。你应该能看到一个网页,其中有一个开(On)和一个关(Off)按钮,如图 23-3 中所示。按下开按钮点亮 LED,按下关按钮则关闭 LED。
-
这个项目即使在没有连接本地网络时也可以运行,只要你在互联网路由器上开放了 80 端口。许多互联网服务提供商(ISP)出于安全原因会屏蔽此端口,因此如有需要,请按照 ISP 的指示修改设置。
警告
仅在你了解安全风险并且知道如何最小化这些风险的情况下执行此操作。
-
确认你的连接与图 23-6 中的电路图一致,然后在第 208 页上传 “程序代码” 中的代码。
图 23-6: 用于互联网控制 LED 的电路图
![Image]()
程序代码
该草图调用了 SPI 和以太网库来控制与互联网的通信。我们为扩展板定义了 MAC 地址。如果您的扩展板自带 MAC 地址,您需要修改这一行;如果没有,则本项目中早些时候提供的地址和代码中显示的地址应该能正常工作。然后,我们将服务器设置为使用 80 端口,并将 Arduino 的 7 号引脚定义为 LED 引脚。
setup 定义了 LED 引脚为输出,启动了以太网扩展板,并启动了串行通信,以便我们可以看到服务器的 IP 地址。loop 函数设置了网页,当它被调用时会传送给浏览器,并等待浏览器的输入。按下打开按钮时,服务器指示 Arduino 将 LED 引脚设置为 HIGH,LED 会亮起。按下关闭按钮时,LED 的电源被设置为 LOW,LED 会关闭。
你可以轻松地将 LED 替换为继电器开关,例如第 12 项项目中使用的那种,用来控制更大电压的设备。
include <SPI.h>
include <Ethernet.h>
// 扩展板的 MAC 地址
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
EthernetServer server(80); // 使用端口 80
int led = 7; // LED 连接到引脚 7
void setup() {
pinMode(led, OUTPUT); // 设置 LED 为输出模式
Ethernet.begin(mac); // 启动以太网扩展板
server.begin();
Serial.begin(9600); // 启动串行通信
Serial.println("服务器地址:"); // 打印服务器地址
// (Arduino 扩展板)
Serial.println(Ethernet.localIP());
}
void loop() {
EthernetClient client = server.available();
if (client) {
boolean currentLineIsBlank = true;
String buffer = "";
while (client.connected()) {
if (client.available()) {
char c = client.read(); // 从以太网扩展板读取数据
buffer += c; // 将字符添加到字符串缓冲区
// 客户端发送请求,现在等待响应
if (c == '\n' && currentLineIsBlank) {
client.println("HTTP/1.1 200 OK"); // HTTP 响应
client.println("Content-Type: text/html");
client.println(); // HTML 代码
client.print("
互联网控制 LED
break;
}
if (c == '\n') {
currentLineIsBlank = true;
buffer = "";
}
else if (c == '\r') { // 来自网页的命令
// 按下了打开按钮吗
if (buffer.indexOf("GET /?status=ON") >= 0)
digitalWrite(led, HIGH);
// 按下了关闭按钮吗
if (buffer.indexOf("GET /?status=OFF") >= 0)
digitalWrite(led, LOW);
}
else {
currentLineIsBlank = false;
}
}
}
client.stop(); // 结束服务器
}
}
故障排除
问。 代码已编译,但 LED 没有按预期点亮。
• 首先,确保你已经将 Arduino 的 GND 线连接到正确的面包板电源轨,并且 Arduino 已连接电源。
• 检查电阻是否完全插入并与对应的 LED 引脚对齐。
• 尝试通过连接到局域网并使用该 PC 连接 Arduino 来检查项目是否正常工作。
Q. 你在调用网页时收到错误。
• 确保你按照之前给出的步骤准确输入了服务器的 IP 地址。
• 最好通过连接到局域网并使用该 PC 连接 Arduino 来检查项目是否正常工作。
• 如果项目在你将其连接到局域网时能正常工作,但在连接到外部互联网时收到 HTTP 403 错误,则可能是你的 ISP 阻止了传入的流量。你可以在路由器上为端口 80 添加端口转发。这对每个设备来说都不同,所以请与 ISP 联系,获取详细的说明。你可以快速搜索你的 ISP 和“端口转发”作为关键词,按照说明进行操作,但要注意:这可能会危及你 PC 的安全,仅在你理解风险并能够保护你的网络时进行。
语音控制 LED**
在这个项目中,我们将使用蓝牙模块、智能手机和语音识别应用程序来通过语音命令控制 LED。


所需组件
Arduino 板
面包板
跳线
HC-06 蓝牙模块
LED
220 欧姆电阻
安卓智能手机
工作原理
蓝牙无线技术利用无线电波在短距离内传输和交换数据。智能手机、笔记本电脑和多媒体设备(如扬声器)使用蓝牙作为常见标准。我们将使用廉价的 HC-06 蓝牙模块(图 24-1)将 Arduino 与智能手机配对(连接),这样我们就可以通过语音识别应用程序远程开关 LED。该模块有六个引脚,但我们只使用中间的四个。引脚应在前面标注。
图 24-1: HC-06 蓝牙模块

我们将使用的应用程序是 BroxCode 的 Arduino Bluetooth Control,安卓设备可以在 Google Play 商店免费下载。还有许多其他类似的免费应用程序适用于安卓和苹果设备,使用原理应该相同,但 BroxCode 应用程序具有一些额外功能,我们在这个项目中使用了这些功能,例如通过 Google 助手进行语音识别。
构建
在构建蓝牙控制器之前,你需要将代码上传到 Arduino。这是因为从 PC 到 Arduino 的串行通信使用了与我们将连接到蓝牙模块的相同引脚。
-
上传“草图”中的代码,见第 220 页,然后将蓝牙模块插入面包板,并将 VCC 连接到面包板的正电源轨,GND 连接到 GND 电源轨,TXD 连接到 Arduino 的 0 号引脚(RX),RXD 连接到 Arduino 的 1 号引脚(TX),如下面的表格所示。
HC-06 蓝牙模块 ARDUINO VCC +5V GND GND TXD 引脚 0 (RX) RXD 引脚 1 (TX) -
将 LED 插入面包板,确保其引脚跨越中心断开。使用 220 欧姆电阻将 LED 的较短负极引脚连接到面包板的 GND 轨道。使用跳线将 LED 的较长正极引脚连接到 Arduino 的引脚 9,具体如下面的表格所示。
LED ARDUINO 正极腿 引脚 9 负极腿 GND -
将面包板的 GND 轨道连接到 Arduino 的 GND,正极轨道连接到 Arduino 的+5V。
-
检查你的电路图是否与图 24-2 一致。
图 24-2: 蓝牙语音控制 LED 的电路图
![Image]()
ARDUINO 蓝牙控制
Arduino 蓝牙控制应用程序提供了六种控制选项,所有选项都通过不同的方式将数据发送到 Arduino(见图 24-3)。你可以根据自己的偏好定制每个选项。
图 24-3: Arduino 蓝牙控制应用程序的菜单屏幕

• 方向键: 这里你可以找到可定制的箭头按钮。
• 终端: 这是一个经典的终端,用于发送和接收数据,每个操作都有时间戳。
• 加速度计: 这个工具通过手机的手势传感器读取运动。
• 按钮和滑块: 这里你可以找到六个完全可定制的按钮以及一个在旋转设备时显示的滑块视图。你可以设置该滑块的数据范围。
• 度量标准: 该工具已优化,能够通过 Arduino 的println()函数接收数据,这使得你配对的手机可以通过短信从另一部手机接收通知。你只需在设置部分指定号码即可。此功能稍后会进一步解释。
• 语音控制: 这个强大的工具利用你的 Android 设备上的 Google 语音命令,让你定制自己的语音命令,并用它们控制 Arduino。
现在你需要从 Google Play 应用商店下载 Arduino 蓝牙控制应用程序并进行设置。
-
访问
play.google.com/store/并搜索 “Arduino Bluetooth Control”。你可能会在搜索结果中找到多个应用程序,但你需要的正是名为 “Arduino Bluetooth Control” 的那个,正如图 24-4 所示。点击 安装 将其下载到你的设备。该应用程序是免费的,但包含一些广告。图 24-4: 来自 BroxCode 的 Arduino 蓝牙控制应用程序,来自 Google Play
![Image]()
-
下载应用程序后,给 Arduino 供电以启动蓝牙模块。进入手机的蓝牙设置,打开蓝牙,选择更多设置以查看可见设备。你应该看到 HC-06 模块作为可用设备,选择它与手机配对。系统会要求输入密码:默认密码为 1234,有时是 0000,如果第一个密码不行,可以尝试第二个。
-
当设备配对完成后,打开 Arduino 蓝牙控制应用程序。在出现的窗口中显示所有可用设备,选择 HC-06 模块,如图 24-5 所示。你不需要每次开机时都选择设备——应用程序会记住它。
图 24-5: 配对你的设备
![Image]()
-
你将使用语音控制功能,通过对智能手机说出特定命令来打开和关闭 LED。选择语音控制功能,系统将带你进入设置菜单,如图 24-6 所示。选择语音命令配置。我们将使用这个来定义输入和输出功能。
图 24-6: 选择语音命令配置设置
![Image]()
-
选择语音命令 n°1,如图 24-7 所示。
图 24-7: 设置你的第一个语音命令
![Image]()
-
在这里,你提供触发第一个功能的输入。输入
light on作为文本,如图 24-8 左侧屏幕所示。应用程序将询问你在输入命令时要发送到 Arduino 的输出数据。在这个屏幕上,输入1来开启 LED 或输入HIGH,正如我们在之前的 LED 项目中看到的那样(如图 24-8 右侧屏幕所示)。当应用程序通过手机接收到语音命令light on时,数字1将作为输入发送到 Arduino,电源将供应给 LED,从而点亮它。 -
执行相同的步骤,定义语音命令 n°2,输入为
light off,输出数据为0,如图 24-9 所示。此命令将关闭 LED。
现在你已经配置了命令,按下语音命令功能并点击屏幕上的麦克风按钮后,应用程序将监听你的命令,并根据输入内容切换 LED 的开关。
图 24-8: 配置我们通过语音命令“light on”打开 LED

图 24-9: 配置“light off”功能

这个应用程序还有一个功能,可以让你通过短信控制 Arduino。一旦应用程序启动并连接到 Arduino,你可以通过向与蓝牙模块配对的手机发送短信,将数据发送到 Arduino,只要配对的手机在模块的范围内。只需向连接到 Arduino 的手机发送Arduino 1,该手机将发送1到模块以点亮 LED。发送Arduino 0,则会发送0来关闭 LED。通过这种方式,你可以在全球任何地方通过蓝牙进行控制!
草图
这个项目的草图非常简单。它首先创建一个变量来保存来自蓝牙模块的数据。它将串口通信的数据传输速率设置为9600,并将 9 号引脚设置为输出,以控制我们的 LED。在循环中,它检查是否有数据从蓝牙模块发送到 Arduino。循环读取数据,并将其发送到串口监视器,以便我们检查它是否正常工作。如果 Arduino 从应用程序接收到1,9 号引脚将被设置为HIGH,这将点亮 LED。如果 Arduino 接收到0,9 号引脚将设置为LOW,LED 将关闭。
使用这些原理,你可以在 LED 的位置添加多个继电器,并开始从任何地方自动化你的家。你可以设置它,在你进入家之前打开客厅的灯,回家时设置恒温器,或者在你走进门时已经播放你最喜欢的音乐。
char data = 0; // 创建一个用于存储数据的变量
void setup() {
Serial.begin(9600); // 串口通信的数据传输速率
pinMode(9, OUTPUT); // 设置 9 号引脚为输出
}
void loop() {
if (Serial.available() > 0) { // 发送数据
data = Serial.read(); // 读取传入的数据并
// 将其存储到变量 data 中
Serial.print(data); // 将数据值打印到串口监视器
Serial.print("\n"); // 开始新的一行
if (data == '1') // 如果值为 1,点亮 LED
digitalWrite(9, HIGH);
else if (data == '0') // 如果值为 0,关闭 LED
digitalWrite(9, LOW);
}
}
故障排除
问: 代码编译通过,但 LED 没有亮。
• 确保你已经将 Arduino 的 GND 和电源引脚连接到正确的面包板电源轨,并且 Arduino 已连接电源。
• 确保 LED 插入正确,较长的引脚连接到正电源,较短的引脚连接到 GND。检查电阻是否完全插入并与相应的 LED 引脚对齐。
• 在项目通电并连接到 PC 后,打开 Arduino IDE 的串口监视器,查看 Arduino 是否从应用程序接收数据。如果在串口监视器中没有看到数据流,检查模块的 TXD 是否连接到 Arduino 的 RX,模块的 RXD 是否连接到 Arduino 的 TX。
• 如果应用程序在智能手机上打开时无法正常工作,请检查手机与开发者网站上的应用程序的兼容性。你可能需要使用替代应用程序。
• 你应用中的数据集必须与草图中预期的数据匹配,因此确保使用1表示开启,0表示关闭。
GPS 速度计**
在这个项目中,我们将连接一个 OLED 屏幕和 GPS 模块到 Arduino 上,创建一个简单的 GPS 速度计,用于通过卫星跟踪你的速度。


所需零件
Arduino 板
母对公跳线
OLED 单色屏(128×64)
Ublox NEO-6M GPS 模块 航空器飞行控制器与天线
所需库
U8glib
工作原理
本项目中使用的 Ublox NEO-6M GPS 模块(图 25-1)是一款廉价的设备,通常用于跟踪模型飞机或无人机的位置。该模块在供应商列表中有广泛的供应,供应商列表可以在《零售商列表》中找到,或你可以在网上搜索“Ublox NEO-6M GPS 模块”。请确保购买的模块也附带 GPS 天线,如图 25-2 所示。
图 25-1: Ublox NEO-6M GPS 模块

图 25-2: GPS 天线

该模块使用GPS(全球定位系统)技术来确定 Arduino 的精确位置,并在 OLED 屏幕上以公里每小时为单位显示其速度(有关 OLED 屏幕的更多内容,请参见项目 19)。GPS 由 32 颗卫星组成,这些卫星环绕地球运转,并被广泛应用于日常技术中,如汽车卫星导航系统、智能手机和追踪器。
Navstar 全球定位系统最初由美国政府在 1970 年代创建,最初用于军事目的,但现在任何拥有 GPS 接收设备的人都可以自由使用该系统,如果你拥有智能手机,可能也包括你。为了定位接收器的位置,该系统使用卫星、地面控制站和你的设备来计算信号传送和接收的距离、速度和时间——通过这些,它可以确定你的位置信息。
Ublox NEO-6M GPS 模块持续接收卫星信号并将其发送到 Arduino,以确定你的位置信息。一旦你开始移动,你的速度将以公里每小时的单位发送到 OLED 屏幕,作为我们的速度计。
尽管这个项目的功能相当复杂,但构建过程非常简单。板子附带的针脚是分开的,因此在开始之前需要将它们焊接到位。如果你需要焊接指导,请参阅《快速焊接指南》中的内容,该指南位于第 12 页。该板已内置所有 GPS 电路,但你需要将 GPS 天线夹到板子上;我稍后会向你展示如何操作。
构建过程
-
请使用图 25-3 中显示的 OLED 单色屏,并使用母对公跳线连接以下表格中的内容。由于 OLED 屏幕使用 3.3V 电压,请确保将其连接到 Arduino 的 3.3V,而不是 5V,否则可能会损坏屏幕。
图 25-3: OLED 单色屏显示运动速度(右侧数字为时速,单位为公里/小时)。
![图片]()
OLED 屏幕 Arduino VCC +3.3V GND GND SCL 引脚 A5 SDA 引脚 A4 -
GPS 模块使用 Arduino 的 RX 和 TX 引脚进行通信,但在从 PC 上传草图时,你也需要这些引脚。现在上传“草图”中的代码,该代码位于第 227 页,这样这些引脚就能空闲出来。将 Arduino 连接到你的 PC。记得首先下载 U8glib 库并将其添加到 Arduino IDE 的相关文件夹中。
-
上传草图后,断开 Arduino 与电脑的连接,并将 GPS 的 VCC 连接到 Arduino 的 +5V,GND 连接到 GND,GPS 的 TX 连接到 Arduino 引脚 0 (RX),GPS 的 RX 连接到 Arduino 引脚 1 (TX),如下表所示。
GPS 模块 Arduino VCC +5V GND GND TX 引脚 0 (RX) RX 引脚 1 (TX) -
如图 25-4 所示,将天线的末端夹入模块的插座中。
图 25-4: 将天线的末端夹到 GPS 模块的插座上。
![图片]()
-
确认你的连接与图 25-5 中的电路图一致。
图 25-5: GPS 车速计的电路图
![图片]()
-
给 Arduino 供电,GPS 车速计就可以开始使用了。天线需要朝上才能工作,如图 25-4 所示,最好在户外使用,因为 GPS 模块需要与轨道上的卫星保持视距才能正常工作(尽管我也在室内靠近窗户时成功过,所以可以试试看哪种方式适合你)。
-
GPS 模块大约需要 30 秒左右连接卫星。当连接成功时,模块的 LED 灯会闪烁,OLED 屏幕左上角的符号会旋转。
草图
草图首先调用 U8glib 库,然后定义 OLED,以便我们可以控制屏幕。我们将 GPS 模块定义为串行连接,并告诉它我们希望从卫星接收哪些信息。
注意
记得在上传草图之前断开 Arduino 0 (RX) 引脚连接,上传完成后再重新连接。
代码的下一部分包含一个长列表的数据。这部分相当复杂,Ublox NEO-6M 的数据手册详细列出了模块可以接收到的所有信息,如果你感兴趣的话。对于我们的项目,➊处的代码包含相关数据:NAV-PVT 数据,包括模块连接的卫星数量和 GPS 测速仪的地面速度。其余的请求信息未被使用,设置为关闭。
接下来的部分定义了 NAV-PVT 设置,并通过一些计算检查从卫星接收到的数据是否有效。
草图结尾的循环检查是否接收到数据,如果有,则会在 OLED 的左上角动画化符号。第一个符号显示屏幕是否正确刷新,第二个符号显示是否从卫星接收到 GPS 数据包。屏幕还会显示连接到的卫星数量,位于左上方。
如果所有数据都按预期接收,地面速度将显示在屏幕右上方,以公里每小时为单位。
// 草图由 Chris Campbell 的友好许可复制作
/*
连接:
GPS TX -> Arduino 0 // 断开 Arduino 0 以上传此草图
GPS RX -> Arduino 1
屏幕 SDA -> Arduino A4
屏幕 SCL -> Arduino A5
*/
include "U8glib.h" // 调用 U8glib 库以控制 OLED 屏幕
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_NO_ACK|U8G_I2C_OPT_FAST); // 快速 I2C/TWI
define GPS Serial // 将串口连接定义为 GPS 模块
const unsigned char UBLOX_INIT[] PROGMEM = {
// 这些代码行请求来自卫星的数据。大多数已禁用并关闭。
// 禁用 NMEA
0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x24, // GxGGA 关闭
0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x2B, // GxGLL 关闭
0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x02,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x32, // GxGSA 关闭
0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x39, // GxGSV 关闭
0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x04,0x40, // GxRMC 关闭
0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x05,0x47, // GxVTG 关闭
// 禁用 UBX
0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0xDC, // NAV-PVT 关闭
0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xB9, // NAV-POSLLH 关闭
0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xC0, // NAV-STATUS 关闭
// 启用 UBX——这是我们需要的关键信息
➊ 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x01,0x00,0x00,0x00,0x00,0x18,0xE1, //NAV-PVT 开启
//0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x13,0xBE, //NAV-POSLLH 开启
//0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x14,0xC5, //NAV-STATUS 开启
// 频率
0xB5,0x62,0x06,0x08,0x06,0x00,0x64,0x00,0x01,0x00,0x01,0x00,0x7A,0x12, // (10Hz)
// 0xB5,0x62,0x06,0x08,0x06,0x00,0xC8,0x00,0x01,0x00,0x01,0x00,0xDE,0x6A, // (5Hz)
// 0xB5,0x62,0x06,0x08,0x06,0x00,0xE8,0x03,0x01,0x00,0x01,0x00,0x01,0x39 // (1Hz)
};
常量无符号字符 UBX_HEADER[] = { 0xB5, 0x62 };
结构体 NAV_PVT { // 设定 GPS 导航数据
无符号字符 cls;
无符号字符 id;
无符号短整型 len;
无符号长整型 iTOW; // 导航周期的 GPS 时间周(毫秒)
无符号短整型 year; // 年份 (UTC)
无符号字符 month; // 月份,范围 1..12 (UTC)
无符号字符 day; // 月日,范围 1..31 (UTC)
无符号字符 hour; // 小时,范围 0..23 (UTC)
无符号字符 minute; // 分钟,范围 0..59 (UTC)
无符号字符 second; // 分钟中的秒数,范围 0..60 (UTC)
字符 valid; // 有效性标志(见下图)
无符号长整型 tAcc; // 时间精度估计(UTC)(纳秒)
长整型 nano; // 秒的小数部分,范围 -1e9 .. 1e9 (UTC)(纳秒)
无符号字符 fixType; // GNSS 修复类型,范围 0..5
字符 flags; // 修复状态标志
无符号字符 reserved1; // 保留
无符号字符 numSV; // 导航解算中使用的卫星数量
长整型 lon; // 经度(度)
长整型 lat; // 纬度(度)
长整型 height; // 椭球体以上的高度(毫米)
长整型 hMSL; // 平均海平面以上的高度(毫米)
无符号长整型 hAcc; // 水平精度估计(毫米)
无符号长整型 vAcc; // 垂直精度估计(毫米)
长整型 velN; // NED 北向速度(毫米/秒)
长整型 velE; // NED 东向速度(毫米/秒)
长整型 velD; // NED 向下速度(毫米/秒)
长整型 gSpeed; // 地面速度(2-D)(毫米/秒)
长整型 heading; // 运动的航向 2-D(度)
无符号长整型 sAcc; // 速度精度估计
无符号长整型 headingAcc; // 航向精度估计
无符号短整型 pDOP; // 位置精度衰减
短整型 reserved2; // 保留
无符号长整型 reserved3; // 保留
};
NAV_PVT pvt;
无返回类型 calcChecksum(无符号字符* CK) {
memset(CK, 0, 2);
对于 (int i = 0; i < (int)sizeof(NAV_PVT); i++) {
CK[0] += ((无符号字符*)(&pvt))[i];
CK[1] += CK[0];
}
}
长整型 numGPSMessagesReceived = 0;
布尔类型 processGPS() {
静态整型 fpos = 0;
静态无符号字符 checksum[2];
常量整型 payloadSize = sizeof(NAV_PVT);
当 GPS.available() 时 {
字节 c = GPS.read();
如果 ( fpos < 2 ) {
如果 ( c == UBX_HEADER[fpos] )
fpos++;
否则
fpos = 0;
}
否则 {
如果 ( (fpos-2) < payloadSize )
((无符号字符*)(&pvt))[fpos-2] = c;
fpos++;
如果 ( fpos == (payloadSize+2) ) {
calcChecksum(checksum);
}
否则如果 ( fpos == (payloadSize+3) ) {
如果 ( c != checksum[0] )
fpos = 0;
}
否则如果 ( fpos == (payloadSize+4) ) {
fpos = 0;
如果 ( c == checksum[1] ) {
返回 true;
}
}
否则如果 ( fpos > (payloadSize+4) ) {
fpos = 0;
}
}
}
返回 false;
}
无返回类型 setup() {
GPS.begin(9600);
u8g.setColorIndex(1);
// 通过 UBX 协议发送配置数据
for (unsigned int i = 0; i < sizeof(UBLOX_INIT); i++) {
GPS.write( pgm_read_byte(UBLOX_INIT+i) );
delay(5); // 模拟 38400 波特率的速度(或更低)
// 或者设备未接受命令
}
}
long gSpeed = 0;
int numSV = 0;
unsigned long lastScreenUpdate = 0;
char speedBuf[16];
char satsBuf[16];
char* spinner = "/-\|"; // 屏幕上的旋转符号
// 显示通信状态
byte screenRefreshSpinnerPos = 0;
byte gpsUpdateSpinnerPos = 0;
void loop() {
if (processGPS()) {
numSV = pvt.numSV;
gSpeed = pvt.gSpeed;
gpsUpdateSpinnerPos = (gpsUpdateSpinnerPos + 1) % 4;
}
unsigned long now = millis();
if (now - lastScreenUpdate > 100) {
updateScreen();
lastScreenUpdate = now;
screenRefreshSpinnerPos = (screenRefreshSpinnerPos + 1) % 4;
}
}
void draw() {
u8g.setFont(u8g_font_courB24);
u8g.drawStr( 36, 45, speedBuf);
u8g.setFont(u8g_font_fur11);
u8g.drawStr( 2, 12, satsBuf);
}
void updateScreen() {
int kmh = gSpeed * 0.0036;
sprintf(speedBuf, "%3d", kmh);
sprintf(satsBuf, "%c %c %d", spinner[screenRefreshSpinnerPos], spinner[gpsUpdateSpinnerPos], numSV);
u8g.firstPage();
do {
draw();
} while( u8g.nextPage() );
}
故障排除
Q. 代码编译通过,但预期信息未显示在屏幕上。
• 如果 OLED 屏幕上什么都没有显示,请重新检查接线是否与图 25-5 一致;TX 和 RX 线很容易接错。
• 屏幕左上角的符号会旋转,表示屏幕工作正常并且 GPS 模块正在接收数据。如果最左边的符号旋转而 GPS 符号没有旋转,说明你的 TX 和 RX 线接错了;请重新检查模块的接线。
• GPS 模块在户外效果最佳,应该能看到绕地球轨道的卫星,所以请尝试重新放置模块,直到获得信号读取。稳定信号的获取可能需要 30 到 60 秒。
• 请记住,OLED 屏幕应连接到 3.3V,而不是 5V。
第八章:常见错误的故障排除提示
本书中所有项目的草图都可以从 www.nostarch.com/arduinohandbook2/ 下载,并且已经过验证可以正常工作。然而,当你在 Arduino IDE 中编译一个草图时,可能会遇到一些问题。
本节将介绍三种最常见的错误类型,解释它们的原因以及如何修复。当出现错误时,IDE 底部的监视框会高亮显示导致错误的代码行,如 图 A-1 所示。此信息对于修复代码非常有价值。
图 A-1: IDE 将高亮显示出错的代码行。

上传错误
当你上传代码时,你会看到类似于 图 A-2 中的错误信息,内容如下:
avrdude: ser_open(): 无法打开设备 "COM1":没有此文件或目录
目录
图 A-2: 错误信息“上传到板子时出现问题”

解决方案
这个错误通常意味着 IDE 无法找到你的 Arduino 板。尝试以下解决方案之一:
• 检查你的 USB 连接是否已牢固插入 PC 的 USB 端口。
• 在 IDE 中,打开 工具 标签并选择 端口。从下拉菜单中,你应该看到一个 COM 端口被高亮显示。如果这不是你的 Arduino 所连接的端口,请选择正确的端口。
• 如果正确的端口已经被高亮显示,验证是否选择了正确的板类型:打开 工具 标签,选择 板,从下拉菜单中确保你连接的 Arduino 板被高亮显示。默认设置为 Arduino Uno。
• 你还可以查看 Arduino 文档以获取更多可能的解决方案: www.arduino.cc/en/Guide/Troubleshooting#upload。
代码验证错误 #1
当你验证代码时,你会收到类似于 图 A-3 中的错误信息,内容如下:
expected '}' at end of input
图 A-3: 错误信息“expected '}' at end of input”

解决方案
检查每个开括号 ({) 是否都有一个闭括号 (}),如果没有,则添加闭括号。大括号定义了代码块的开始和结束,每个打开的括号都需要有一个闭括号来完成一个函数或循环。在这种情况下,你需要在代码末尾添加一个闭括号。
代码验证错误 #2
当验证代码时,你会收到如 图 A-4 中所示的错误信息,内容如下:
expected ';' before '}' token
图 A-4: 错误信息“expected ';' before '}' token”

解决方案
这个错误是你最常遇到的错误之一,它表示你在某行末尾漏掉了一个分号(;)。请在 IDE 中高亮的那一行上方添加一个分号。
缺少库错误
在验证代码时,你会收到类似这样的错误:
致命错误:#NewPing.h 没有这个文件或目录
图 A-5 中的示例来自项目 20,该项目使用了 NewPing 库。
图 A-5: 错误信息“为板子 Arduino/Genuino Uno 编译时出错”

解决方案
这个错误也很常见,意味着 IDE 无法在库文件夹中找到预期的库。请按照“安装库”中的说明,在第 8 页确保你已安装代码中所需的库,尤其是那些 IDE 默认未包含的库。记住,仅仅下载这些库是不够的,你还需要安装它们。
本书中的每个项目都会在章节开始时列出所需的库。你可以从www.nostarch.com/arduinohandbook2/下载那些 IDE 中未包含的库。
第九章:元件
本节提供了本书中使用的元件的更多信息。每个元件都附有照片和一些简要细节,方便快速参考和识别。最后,我还附上了一个方便的零售商列表,您可以通过这些零售商购买所需的零件,并且提供了一节关于读取电阻值的简易课程。
元件指南
这些元件按它们在书中的出现顺序列出。许多物品可以通过在 eBay 和 Amazon 等网站上进行简单搜索找到,但也提供了一个专业供应商列表,您可以在 第 249 页的 “零售商列表” 中找到。
Arduino Uno R3
Arduino Uno R3 微控制器板是本书的主要元件,是所有项目的“大脑”。

• 数量:1
• 连接:14
• 项目:所有项目
9V 电池包
9V 电池包配有 2.1 毫米插孔,适用于 6 个 AA 电池,可以插入 Arduino 的电源端口,为您的项目供电。请注意,Arduino 还可以通过 USB 电缆供电。

• 数量:1
• 连接:1
• 项目:所有项目可选
面包板
面包板是用于将元件连接在一起以创建项目的原型板。有关更多详细信息,请参阅简介。

• 数量:1 个全尺寸板,1 个半尺寸板,1 个迷你板
• 连接:全尺寸板上 940 个,半尺寸板上 420 个,迷你板上 170 个
• 项目:除了项目 4, 6, 7, 16, 19, 22 和 25 外的所有项目
LED
LED,或称 发光二极管,是一种小型灯泡,当低电流通过它时会发光。它有两根引脚,其中较长的是正极连接。LED 通常需要电阻,否则可能会烧坏。LED 是有极性的,这意味着电流只能单向流动。

• 数量:40 个(10 个红色,10 个蓝色,10 个黄色,10 个绿色)
• 连接:2
• 项目:1, 2, 9, 15, 17, 21, 23, 24
电阻
电阻限制电流流过电路的量,以防止元件过载。电阻看起来像一个带有彩色带和每端都有一根引线的圆柱体。电阻值通过色码表示——有关更多详细信息,请参阅 “解码电阻值” 在 第 250 页。请仔细检查电阻值,因为选择错误的电阻可能很容易。电阻有四带、五带和六带的不同类型,因此请注意,例如,一个四带 220 欧姆的电阻可能与一个五带的相同值电阻略有不同。

• 数量:9 个 220 欧姆,4 个 10k 欧姆,8 个 1k 欧姆
• 连接:2
• 项目:1–3, 5, 8–10, 15, 17, 18, 21, 23, 24
七段显示器
七段 LED 显示屏通过 LED 段组成数字或字符,通常用于显示计数器、时钟或定时器中的数字。你可以购买单数位到八数位的显示屏,而四位数显示屏通常用于数字时钟。

• 数量:1
• 连接数:10
• 项目:3
8×8 LED Maxim 7219 矩阵模块
这个预装的 8×8 LED 矩阵模块只需要将五个引脚连接到 Arduino 就能工作。

• 数量:1
• 连接数:5
• 项目:4
RGB LED
RGB LED 结合了红色、绿色和蓝色三种颜色,可以制造任何一种彩虹色。它是一个四脚的透明 LED,每个引脚都需要一个电阻来限制电流,以防止 LED 烧坏。最长的引脚是公共阴极或阳极。

• 数量:1
• 连接数:4
• 项目:5
RGB LED 带状灯条(WS2812B 5V 32-LED 灯条)
LED 灯条有单色和多色两种,可以在控制方式上有所不同。单色或不可寻址的多色灯条一次只能亮一种颜色。RGB 多色灯条通常是可寻址的,这意味着每个 LED 都有自己的芯片,可以单独控制,允许多个颜色同时亮起。

• 数量:1
• 连接数:3
• 项目:6
Adafruit NeoPixel 环形带有 16 个 RGB LED
Adafruit NeoPixel 环形带有 16 个 RGB 表面贴装 LED,每个 LED 都是可寻址的,允许你单独控制每个 LED。

• 数量:1
• 连接数:3
• 项目:7
HMC5883L 三轴传感器
HMC5883L 三轴传感器是一个多芯片模块,用于感知磁场——我们用它来检测磁北以作为指南针。该模块可能需要你焊接头针。

• 数量:1
• 连接数:4
• 项目:7
按钮
按钮是一个简单的开关,按下时会连接电路。也叫瞬时开关,按钮按下时会接通电路,松开时弹簧恢复,断开连接。按钮的尺寸各异,但大多数有四个引脚。

• 数量:8
• 连接数:4
• 项目:8
声压蜂鸣器
声压蜂鸣器是一种非常基础的扬声器,常用于廉价玩具中。电流脉冲使其极快地发出点击声,一连串脉冲则发出音调。声压蜂鸣器通常看起来像一个带有两根线的小黑盒子。取出外壳后,它看起来像一个小小的金色圆盘。

• 数量:1
• 连接数:2
• 项目:8,15
3.5 毫米女性耳机插孔
3.5 毫米女性耳机插孔是一个简单的插孔,允许你将音频设备连接到你的 Arduino 上。它可以单独购买,或者从一台一美元商店的收音机中回收。

• 数量:1
• 连接数:3
• 项目:9
伺服电机
伺服电机是一种带有臂部附件的电机,你可以通过向伺服电机发送编码信号来将其定位到特定的角度。电机位于一个小盒子中,配有三根电线和一个输出轴,你可以将臂部(称为舵机臂)连接到该输出轴。
本书使用的是 Tower Pro SG90 9g 伺服电机,它的转动角度为 180 度;其他类型的伺服电机是连续转动的,能完成 360 度的全旋转。

• 数量:1
• 连接:3
• 项目:10,21,22
光敏电阻
光敏电阻,也叫光敏电阻或二极管,通过根据照射到其上的光线强度改变电阻值来检测光线水平。它有不同的类型,通常看起来像一个小的透明椭圆形,带有波浪线和两根引脚。你需要对光敏电阻进行校准,以确定光线水平,然后才能在程序中使用它。

• 数量:1
• 连接:2
• 项目:10
28BYJ-48 步进电机配 ULN2003 驱动模块
步进电机是一种直流电动机,它将完整的 360 度旋转分为若干均匀的步进,从而提供更精确的控制。我们使用的是 28BYJ-48 步进电机,它配有一个 ULN2003 驱动模块来进行控制。

• 数量:1
• 连接:5
• 项目:11
LM35 温度传感器
LM35 温度传感器可以检测温度并将读数以电压值的形式发送给 Arduino,从而可以测量温度。

• 数量:1
• 连接:3
• 项目:12,14
12V 迷你计算机冷却风扇
12V 迷你计算机冷却风扇是计算机内部使用的冷却风扇。我们使用的是一个 4 厘米 × 4 厘米的风扇,但如果需要,你也可以使用更大的风扇。你也可以回收一台旧电脑上的风扇,只要它不再使用。

• 数量:1
• 连接:2
• 项目:12
5V 单通道继电器模块
继电器是一种电子控制开关,在本例中,继电器使用电磁铁通过机械方式打开或关闭电路。

• 数量:1
• 连接:6
• 项目:12
电位器
电位器是一种电阻器,其电阻值可以调节,从而控制通过它的电压,进而控制传输到组件的功率。它有一个旋钮可以调节,并且底部有三个插脚。中间的插脚是控制脚,电源连接在两侧。电位器通常用于控制输出设备,例如收音机的音量。你可以将电源连接到第 1 和第 3 插脚,连接方式不重要。

• 数量:2 50k-欧姆,1 10k-欧姆
• 连接:3
• 项目:11,13–15,18
LCD 屏幕
LCD(液晶显示器)屏幕是一种用于输出字符或图像的显示屏。它由两片偏振材料组成,中间夹有液晶溶液。通过电流流经液晶,液晶变得不透明,从而在背光下形成图像。
屏幕有不同的尺寸。此处展示的是一个 HD44780 16×2(16 字符×2 行)屏幕,有 16 个连接。

• 数量:1
• 连接数:16
• 项目:13–16
超声波传感器
超声波传感器发送一个信号(通常称为ping),该信号会反射到物体上并返回到传感器。根据信号返回的时间计算距离。本书中使用的超声波传感器是 HC-SR04 超声波传感器,它是一个带有两个圆形传感器和四个引脚的模块板。

• 数量:1
• 连接数:4
• 项目:13, 17, 20, 22
键盘
一个 3×4 的膜式键盘本质上是一系列开关。此处展示的示例有 12 个串联的按钮,但也有 16 个按钮版本可供选择。七个连接中,四个控制行,三个控制列。Arduino 将复制按下的按钮的数字。

• 数量:1
• 连接数:7
• 项目:15
串口 LCD 屏幕模块
这款 16×2 的 LCD 屏幕附带了一个串口模块,因此只需要连接电源和两个引脚到 Arduino 即可。

• 数量:1
• 连接数:4
• 项目:16
诺基亚 5110 LCD 屏幕
这是一个诺基亚 84×48 像素的屏幕,考虑到字符之间的间隙,它给我们提供了一个 12×6 字符的屏幕。它的工作原理类似于项目 13 中的 LCD 屏幕,通过 Arduino 将电流传送到液晶的特定像素点,从而形成字母或图像。

• 数量:1
• 连接数:8
• 项目:18
OLED 单色屏幕(128×64)
OLED(有机发光二极管)屏幕是一种发光技术,由一层薄薄的多层有机薄膜组成,置于阳极和阴极之间。本书中使用的 OLED 屏幕尺寸为 128×64。

• 数量:1
• 连接数:4
• 项目:19, 25
Keyes MQ3 酒精传感器模块
MQ3 是一个对酒精和乙醇敏感的气体传感器。我们在项目 19 中的酒精测试仪中使用了它。

• 数量:1
• 连接数:3
• 项目:19
WLToys V959-18 水射流手枪
V959-18 水射流手枪包括一个小水箱用于储水,并有一个迷你水泵将水通过喷嘴喷出。

• 数量:1
• 连接数:2
• 项目:20
光学指纹传感器(ZFM-20 系列)
ZFM-20 指纹传感器是一个指纹比对模块,能够拍摄指纹照片并将其添加到数据库中,从而允许检查新指纹是否与存储的指纹匹配。该传感器可以存储最多 162 个指纹。

• 数量:1
• 连接:4
• 项目:21
L293d 电机扩展板
L293d 电机扩展板是一个用于控制电机的模块,我们在项目 22 中用到它来控制机器人。

• 数量:1
• 连接:安装在 Arduino 顶部
• 项目:22
机器人底盘套件
如果你在线搜索“Arduino 机器人套件”,应该能找到一个包含两个直流电机和轮子、底板、电池组、中心轮以及组装 Arduino 机器人所需配件的套件。我购买的套件专门命名为“2WD 智能电机机器人车底盘套件 for Arduino 1:48”。

• 数量:1
• 连接:4(每个电机 2 个)
• 项目:22
以太网扩展板 W5100 LAN
以太网扩展板 W5100 LAN 直接安装在 Arduino 顶部,提供额外的功能,比如 Web 服务器或客户端,使得 Arduino 可以连接到网络。

• 数量:1
• 连接:多个
• 项目:23
以太网电缆
以太网电缆用于在互联网连接或网络与设备之间传输数据。

• 数量:1
• 连接:1
• 项目:23
HC-06 蓝牙模块
HC-06 模块提供蓝牙无线功能,使得 Arduino 能够通过短距离交换数据的方式传输无线电波。智能手机、笔记本电脑和多媒体设备如扬声器都使用蓝牙技术作为常见标准。

• 数量:1
• 连接:4
• 项目:24
Ublox NEO-6M GPS 模块 飞行器飞控和天线
Ublox NEO-6M GPS 模块是一种追踪设备,能够连接到顶级 GPS 卫星,通常用于追踪模型飞机或无人机的位置。该模块在此处列出的来源中广泛有售,或者你可以直接在线搜索“Ublox NEO-6M GPS 模块”。确保购买一个同时附带 GPS 天线的模块。

• 数量:1
• 连接:5,包括天线
• 项目:25
零售商列表
如前所述,大部分电子元件可以在 Amazon 或 eBay 等通用网站上找到,但如果你在找东西时遇到困难,列出的零售商应该能够提供帮助。
美国零售商
Adafruit www.adafruit.com/
DigiKey www.digikey.com/
Jameco Electronics www.jameco.com/
Newark www.newark.com/
RS 组件 www.rs-components.com/
Seeed Studio www.seeedstudio.com/
SparkFun www.sparkfun.com/
澳大利亚零售商
Core Electronics core-electronics.com.au/arduino.html
Little Bird Electronics www.littlebirdelectronics.com.au/
欧洲零售商
Electronic Sweet Pea’s www.sweetpeas.se/
Element 14 www.element14.com/
Farnell www.farnell.com/
英国零售商
4tronix www.4tronix.co.uk/store/
Cool Components www.coolcomponents.co.uk/
CPC cpc.farnell.com/
爱好者组件 www.hobbycomponents.com/
Mallinson Electrical www.mallinson-electrical.com/shop/
Maplin www.maplin.co.uk/
Oomlout oomlout.co.uk/
The Pi Hut thepihut.com/
Proto-pic proto-pic.co.uk/
Rapid Electronics www.rapidonline.com/
Spiratronics spiratronics.com/
解码电阻值
在本书的大多数项目中,我们使用了电阻器。电阻器是限制电流通过电路的电气组件(以欧姆为单位)。它们用于保护像 LED 这样易受过载和烧坏影响的组件。电阻器的数值通过电阻器上的彩色带来识别。电阻器可以有四个、五个或六个彩色带。
能够确定电阻的数值非常重要,这样你就能确保在项目中使用正确的电阻。让我们试着确定 图 B-1 中显示的四环电阻的值。
图 B-1: 四环电阻

查看电阻时,银色或金色带位于右侧,注意从左到右的颜色顺序。如果电阻没有银色或金色带,确保有三个彩色带的一侧在左边。
使用 表 B-1 来确定电阻值。
表 B-1: 计算电阻值
| 颜色 | 第一环 | 第二环 | 第三环 | 乘数 | 公差 |
|---|---|---|---|---|---|
| 黑色 | 0 | 0 | 0 | 1Ω | |
| 棕色 | 1 | 1 | 1 | 10Ω | +/–1% |
| 红色 | 2 | 2 | 2 | 100Ω | +/–2% |
| 橙色 | 3 | 3 | 3 | 1KΩ | |
| 黄色 | 4 | 4 | 4 | 10KΩ | |
| 绿色 | 5 | 5 | 5 | 100KΩ | +/–0.5% |
| 蓝色 | 6 | 6 | 6 | 1MΩ | +/–0.25% |
| 紫色 | 7 | 7 | 7 | 10MΩ | +/–0.10% |
| 灰色 | 8 | 8 | 8 | +/–0.05% | |
| 白色 | 9 | 9 | 9 | ||
| 金色 | 0.1Ω | +/–5% | |||
| 银色 | 0.01Ω | +/–10% |
第一条和第二条带给出了数字值,第三条带告诉你要加多少个零,第四条带告诉你公差——即实际值与目标值之间可以有多大的偏差。
注意
尽管表示公差的带通常是银色或金色,但它也可以是其他带有公差百分比的颜色。如果你有一个公差带不是银色或金色的电阻,值带和公差带之间应该有一个小间隙,这样你就可以知道它是什么颜色。
所以,对于图 B-1 中的电阻:
• 第一条带为棕色(1)= 1。
• 第二条带为黑色(0)= 0。
• 第三条带为红色(2)= 00(2 是零的数量)。
• 第四条带为金色,所以公差(精度)为+ / – 5%。
所以这个电阻是 1,000 欧姆或 1 千欧姆,公差为 5%,这意味着实际值可以比 1 千欧姆多或少 5%。我们也可以对五条或六条带的电阻进行相同的计算。
如果你不确定电阻的值,可以通过快速在线搜索电阻表面上的彩色带来查找。只需要确保按正确的顺序列出颜色,从左到右读取,公差带在右侧。
第十章:Arduino 引脚参考
没有过多的细节,这一部分为你提供了 Arduino Uno 上引脚的参考、它们的技术名称和功能。引脚的详细信息会在它们被使用的项目中进一步解释,所以在你完成一些项目后,这些信息可能会变得更加清晰。
| ARDUINO PIN | 功能与标签 | 附加功能 |
|---|---|---|
| 0 | RX—用于接收 TTL 串行数据 | |
| 1 | TX—用于传输 TTL 串行数据 | |
| 2 | 外部中断 | |
| 3 | 外部中断 | 脉宽调制 |
| 4 | XCK/TO—外部时钟输入/输出(定时器/计数器 0) | |
| 5 | T1(定时器/计数器 1) | 脉宽调制 |
| 6 | AIN0—模拟比较器正输入 | 脉宽调制 |
| 7 | AIN1—模拟比较器负输入 | |
| 8 | ICP1—输入捕获 | |
| 9 | OC1A—定时器寄存器 | 脉宽调制 |
| 10 | SS—从设备选择(串行数据),用于 SPI 通信 | 脉宽调制 |
| 11 | MOSI—主设备输出从设备输入(数据输入),用于 SPI 通信 | 脉宽调制 |
| 12 | MISO—主设备输入从设备输出(数据输出),用于 SPI 通信 | |
| 13 | SCK—串行时钟(主设备输出),用于 SPI 通信 | |
| AREF | 模拟输入的参考电压 | |
| A0 | 模拟输入可以给出 1,024 种不同的值。 | |
| A1 | 模拟输入可以给出 1,024 种不同的值。 | |
| A2 | 模拟输入可以给出 1,024 种不同的值。 | |
| A3 | 模拟输入可以给出 1,024 种不同的值。 | |
| A4 | 模拟输入可以给出 1,024 种不同的值。 | SDA(串行数据线)引脚支持 TWI(双线接口),可通过 Wire 库与 I2C 组件配合使用。 |
| A5 | 模拟输入可以给出 1,024 种不同的值。 | SCL(串行时钟线)引脚支持 TWI,可通过 Wire 库与 I2C 组件配合使用。 |
| RESET | 可用于重置微控制器 | |
| 3.3V | 3.3 伏输出,用于低电压组件。这是唯一的 3.3V 电源。数字和模拟引脚在 5V 下工作。 | |
| 5V | 标准 +5V 输出 | |
| GND | 地/负电源 | |
| Vin | 9V 电源可以通过此处输入,或通过电源插孔访问。 |
串行:0(RX)和 1(TX) 这些引脚用于接收(RX)和传输(TX)晶体管-晶体管逻辑(TTL)串行数据。在项目 21、24 和 25 中,我们使用了 TX 和 RX 引脚。
外部中断:2 和 3 这些引脚可以配置为在低电平、上升或下降沿(信号从低到高或从高到低)或值发生变化时触发中断。中断是一个信号,告诉 Arduino 在检测到外部事件(如按下按钮)时暂停并执行另一个功能。
PWM:3、5、6、9、10 和 11 这些引脚可以通过 analogWrite() 函数使用脉宽调制。关于这一点,项目 5 中有更多信息。
SPI: 10(SS)、11(MOSI)、12(MISO)、13(SCK) 这些引脚支持使用 SPI 库的 SPI 通信,并在项目 4 中使用。
LED: 13 引脚 13 上有一个内置 LED。当引脚为HIGH时,LED 亮;当引脚为LOW时,LED 灭。引脚 13 上的内置 LED 用于显示板载 ATmega328p 引导程序正在运行,通常在 Arduino 启动时会亮起。
AREF 这是模拟输入的参考电压;它与analogReference()一起使用。我们可以输入 0 到 5V 之间的电压,因此,如果你的传感器需要低于 5V 的电压,可以使用这个引脚提高分辨率,从而获得更精确的读数。
模拟输入:A0–A5 Uno 有六个模拟输入,每个输入提供 1024 个不同的值。
TWI: A4 和 A5 这些引脚支持使用 Wire 库的TWI(双线接口)通信。用于控制和与 I2C 设备进行通信,例如串口 LCD 屏幕,只需两根线。
RESET 将此设置为LOW以重置微控制器。通常用来添加一个重置按钮。
如果你现在觉得这些信息对你没什么意义,不用担心。你可能会在未来的 Arduino 项目中用到它,随着你完成书中的项目,你也可以参考这些信息。
Arduino 项目手册,第 2 卷 使用了 Helvetica Neue、Montserrat、True North 和 TheSansMono Condensed 字体。
更新
访问www.nostarch.com/arduinohandbook2/获取更新、勘误和其他信息。
更多实用的书籍来自
NO STARCH PRESS

ARDUINO 项目手册,第 1 卷
25 个实用项目,帮助你入门
作者 MARK GEDDES 2016 年 6 月,272 页,$24.95 ISBN 978-1-59327-690-4 全彩色

ARDUINO 发明家指南
通过制作 10 个精彩项目来学习电子学
作者 BRIAN HUANG 和 DEREK RUNBERG 2017 年 6 月,336 页,$29.95 ISBN 978-1-59327-652-2 全彩色

ARDUINO 工作坊
通过 65 个项目的动手实践来进行入门介绍
作者 JOHN BOXALL 2013 年 5 月,392 页,$29.95 ISBN 978-1-59327-448-1

制造商的僵尸末日指南
使用简单的电路、Arduino 和 Raspberry Pi 来保护你的基地
作者 SIMON MONK 2015 年 10 月,296 页,$24.95 ISBN 978-1-59327-667-6

ARDUINO PLAYGROUND
面向经验丰富的创客的极客项目
作者 WARREN ANDREWS 2017 年 3 月,344 页,$29.95 ISBN 978-1-59327-744-4

电气学的漫画指南
作者 KAZUHIRO FUJITAKI, MATSUDA, 和 TREND-PRO CO., LTD. 2009 年 3 月,224 页,$19.95 ISBN 978-1-59327-197-8
电话:
1.800.420.7240 或
1.415.863.9900
电子邮件:
SALES@NOSTARCH.COM
网站:










































































浙公网安备 33010602011771号