Arduino-发明者指南-全-
Arduino 发明者指南(全)
原文:
zh.annas-archive.org/md5/88b283a825ea1326352a66e72499e2cc译者:飞龙
前言
欢迎来到 Arduino 发明家指南!本书将帮助你开始接触电子学、编程和制作酷炫的东西。任何人都可以成为发明家,而本指南将带领你完成一系列项目,将常见材料与强大的 Arduino 相结合,激发你开始制作自己的发明。
这本书讲了什么?
本书的核心是 Arduino (www.arduino.cc/), 一个开源微控制器开发板,你可以编程控制 LED、测量温度、响应光线、连接 GPS 卫星,做更多的事情。Arduino 也是我们在本书中将要使用的编程语言和开发环境的名称。
Arduino 是创客们为他们的项目添加控制功能的终极工具。在线搜索“Arduino 项目”会返回数百万条结果。像 Instructables、hackster.io 和 YouTube 这样的网站上有成千上万的项目和创意。这显示了有多少创客在使用 Arduino。
在 SparkFun 电子公司,我们鼓励人们通过改变和整合电子元件,尝试、玩耍和动手制作常见的家居物品。这有时被称为 黑客行为。本书将教你一些基本的电子学和编程技能,我们希望它能激发你用家里找到的材料制作一些新的、独特的东西。
为什么选择 Arduino?
市面上有数十种不同的微控制器和开发平台,那么为什么我们要为 Arduino 创建另一套项目呢?
答案很简单:Arduino 最初并不是为创客、工程师或爱好者设计的,而是为意大利伊夫雷亚的设计学生提供的学习平台,帮助他们在不需要多年的电气工程课程或大量数学和理论知识的情况下,使他们的项目能够正常运行。它的设计目的是缩短从“零”到“精彩!”的时间——即从想法到实际产品——特别是针对非技术性的人群。
Arduino 做得非常好,以至于创客和爱好者们采纳了这个平台,并广泛使用。其原因有很多——低廉的价格、良好的文档、开源硬件——但我们认为 Arduino 如此受欢迎的核心原因是它易于学习。Arduino 是任何人进入创作、发明和原型制作的入门工具。本书中的项目是为那些希望学习,并且受到 Arduino 初衷启发的人设计的:将想法变为现实。
这本书与其他书籍有什么不同?
许多编程书籍读起来像是参考手册,直接跳入编码或电气概念,而没有提供背景信息,直到你需要查找一个已忘记的命令或概念时,它们才会从书架上取下来。
本书不同于传统的教材。它旨在通过有趣、实用且富有创意的项目来教授新概念。项目的难度逐步递增,从项目 1 到项目 10,它们将帮助解答这些古老的问题:我为什么要学习这个?它为什么重要?我为什么要关心?
我们假设你正在阅读本书是因为你渴望学习新知识或寻找可以与他人分享的材料。无论你是一个有兴趣的初学者、一位老师、一名图书管理员,还是一位家长,本书都是一本动手操作的指南,适合那些想要学习的人,而不是一本放在书架上的参考手册。
你无需任何电子或编程经验即可开始使用 Arduino。我们假设你从一无所知开始,并且你可能对跳入这一领域感到有些忐忑。没关系!本书从基础内容开始,逐步过渡到更复杂且具有挑战性的项目。
我们也知道,很多有经验的人正在寻找一些新东西——也许是对旧主题的新解读,比如重新审视闪烁的 LED。许多项目可以作为你进一步开发、修改或使用更好材料制作、更精细耐用的项目的起点。最终,本书适合那些主动学习的人——那些敢于全力投入、解决问题的读者。
我们鼓励你在实践中构建项目,通过实践学习。这些项目经过精心设计,旨在介绍编程和电路构建的工具与技能,以及使用纸板、废料和其他家庭物品进行制作。学习的乐趣就在于完整体验这个过程,这正是本书所提供的内容。
材料
本书中使用的电子元件以我们的旗舰产品 SparkFun Inventor's Kit(KIT-13969)为基础,并且也可以通过多个在线来源单独购买。我们还使用了一些不在 Inventor's Kit 中的零件,这些额外的零件也可以作为一个单独的套件购买(www.sparkfun.com/NoStarchArduino/)。
如果你已经拥有电子入门套件,或者你更愿意单独购买零件而不是购买 Inventor's Kit,你可以在表格 1 和表格 2 中找到本书的完整材料清单(BOM)。我们还在每个项目的开始处列出了单独的材料清单。
我们还将使用许多基本的建筑材料,如纸板、卡纸、彩纸、吸管和纸盘来构建项目的外壳。当你开始与电子元件合作并将它们与常见材料结合时,你会以一种全新的视角看待日常家居物品。
表格 1: 本书中使用的 SparkFun Inventor's Kit 零件
| 数量 | 零件编号 | 描述 |
|---|---|---|
| 1 | DEV-13975 | SparkFun RedBoard(或其他兼容 Arduino 的板) |
| 1 | CAB-11301 | USB Mini-B 数据线 |
| 1 | PRT-12002 | 无焊面包板 |
| 1 | PRT-11026 | 公对公跳线(数量 30) |
| 1 | COM-12062 | 各种颜色的 LED(数量 20) |
| 1 | COM-09264 | RGB LED(共阴极) |
| 1 | COM-11508 | 10 kΩ 电阻(数量 20) |
| 1 | COM-11507 | 330 Ω 电阻(数量 20) |
| 2 | COM-10302 或 COM-09190 | 按钮开关 |
| 1 | COM-08588 | 二极管 |
| 1 | COM-09806 | 10 kΩ 电位器 |
| 1 | COM-13689 | NPN 晶体管 - 2N2222 或 BC337 |
| 1 | SEN-09088 | 光敏电阻 |
| 1 | SEN-10988 | TMP36 温度传感器 |
| 1 | SEN-08680 | 50 毫米软电位器薄膜 |
| 1 | COM-07950 | 压电蜂鸣器 |
| 1 | LCD-00709 | 16 × 2 字符 LCD |
| 1 | ROB-09065 | 微型伺服电机 |
| 1 | ROB-11696 | 兴趣电机 |
表 2: 本书中使用的额外零件(不包括在 SparkFun 发明者工具包中)
| 数量 | 零件编号 | 描述 |
|---|---|---|
| 1 | PRT-12043 | 小型面包板 |
| 1 | PRT-13870 | 短 4 英寸公对公跳线(数量 30) |
| 2 | PRT-09140 | 公对母跳线(数量 10) |
| 1 | COM-12062 | 各种颜色的 LED(数量 20)(额外的 LED) |
| 1 | SEN-09088 | 光敏电阻(额外) |
| 1 | PRT-09835 | 4 AA 电池座 |
| 4 | PRT-09100 | AA 电池 |
| 1 | ROB-13845 | TB6612FNG H 桥电机驱动器 |
| 1 | ROB-13302 | 齿轮小电机(数量 2) |
| 1 | ROB-13259 | 适合齿轮小电机的橡胶轮(数量 2) |
| 1 | COM-00102 | 小型滑动开关 |
所需工具
你进行这些项目所需的工具仅有一把剪刀、一把工艺刀和一支热熔胶枪。然而,别觉得只能局限于这些工具。如果你有激光切割机可用,可以使用它。如果你迫不及待想要 3D 打印某个项目,也可以随意。我们的设计是以纸艺和纸板为基础,但它们可以与任何你想使用的制造技术结合。
如果你不想花钱购买本书的材料,你不必花钱。事实上,你可能会发现有些项目可以直接用书本作为材料。如果你这么做了,那太棒了!作为前教师,我们非常熟悉在预算有限的情况下工作,我们的重点始终是最具成本效益的建材,如纸板、纸张、木材以及回收的塑料和金属。
本书中的大多数项目都设计为可以轻松拆卸并重复使用的原型。然而,如果你真的喜欢某个项目并希望让它变得永久,可以查看附录,了解如何焊接你的项目。焊接和电子原型制作的工具相对便宜,你可以从 SparkFun (www.sparkfun.com/) 或任何附近的五金店购买。
所需计算机
最后,你将通过计算机和特定的工具软件来编程 Arduino。几乎任何一台普通计算机都能运行 Arduino 软件。如果你使用的是 Windows PC,你需要 Windows XP、Vista、7、8/8.1、10 或更新版本。如果是 Mac 用户,目前最新版本的 Arduino 与 OS X 10.7 Lion 及更新版本兼容。如果你运行的是一个标准版本的 Linux,可能已经有可用的 Arduino 版本。
截至本文撰写时,iOS 和 Android 设备仅通过仍在测试和开发中的测试版软件包得到支持。你可以尝试这些软件包,但它们可能无法正常工作,如果能工作,也可能不够稳定。
对于 Windows、Mac 或 Linux 用户,我们将在第一个项目中带你完成设置计算机的过程。
这本书包含了什么?
本书包含了 10 个动手项目,以及电子学入门和涵盖焊接及其他实用技巧的附录。项目从一个简单的闪烁 LED 开始,随着项目的进展,逐渐加入不同的电气组件、编程概念和构建的复杂层次。每个项目都有独立的接线、编程和构建部分,便于你专注于各个方面。我们在每个项目的最后提供了“深入探索”部分,给你提供修改和改造项目的思路。记住,我们希望你将这些项目作为自己发明的起点,而不是终极目标。
• 电子学入门 在开始项目之前,我们介绍了电学和电子学的基础知识,并介绍了本书中使用的关键概念。
• 项目 1: Arduino 入门 介绍了安装软件并通过一个点亮 LED 的项目为你奠定了构建和编程电路的基础。
• 项目 2: 为你的房子做个红绿灯 探讨了使用面包板并同时控制多个组件来构建一个三 LED 的红绿灯。
• 项目 3: 九像素动画机 将红绿灯项目扩展到一个 3×3 的九个 LED 矩阵,并教授你在 Arduino 中创建自定义函数。
• 项目 4: 反应时间计时器 带你使用按钮和 LED 制作一个测试你反应速度的游戏。
• 项目 5: 颜色混合夜灯 探讨了使用电压分压器和光传感器来检测房间的光照强度,并根据环境的光暗自动开启或关闭多色 LED。
• 项目 6: 平衡梁 介绍了伺服电机,并教你如何通过外部设备控制它,制作一个平衡梁桌面游戏。
• 项目 7: 迷你桌面温室 教你如何制作一个温室,能够感应温度并在温度过高时自动打开风扇和通风口。这个项目介绍了如何使用晶体管控制电机等概念。
• 项目 8: 绘图机器人,机器人艺术家 探索使用 H-桥电机控制器的简单桌面机器人。你将构建一个简单的机器人,能够为你绘制图片。
• 项目 9: 赛车计时器 教你如何搭建一个赛车道,用于记录玩具车的速度。你将使用伺服电机、光传感器和 LCD 来构建一个比赛计时器。这个项目是一个圣诞早晨的梦想!
• 项目 10: 迷你电子钢琴 教你如何使用 Arduino 制作音乐,使用软电位计作为小型键盘。这个项目探索了压电蜂鸣器以及如何使用 tone() 函数。是时候发挥你的钢琴才华了!
• 附录: 更多电子知识 包含使用万用表、焊接和读取电阻器色环的小贴士。
在线资源
你将需要的所有资源都可以下载、参考、使用和修改。这些资源包括书中展示和讨论的所有示例代码、最终构建的切割模板,以及帮助你开始黑客和修改项目以进一步实验的代码。
你将可以在一个 ZIP 文件中找到所有这些资源,下载地址是 www.nostarch.com/arduinoinventor/。如果遇到困难或某些内容无法正常工作,你可以随时参考这些文件,作为指导和后备方案。
传播消息:分享你的作品
SparkFun 是一家硬件和电子公司,专注于开源——这是公司创立时的核心理念之一。当我们构建项目时,我们喜欢与社区分享我们的想法、代码和设计文件,这样你就可以利用我们的知识库来帮助你进行下一个项目。随着你积累项目经验,我们也鼓励你分享你的工作。给朋友展示,或者发布到网上。有很多地方我们都希望看到你的作品!
使用 Twitter、Instagram 或 Facebook 等社交媒体与我们分享你正在制作的作品。标记我们 @sparkfunedu 和 @nostarch。我们还有一个在线项目画廊叫做 InventorSpace,地址是 invent.sparkfun.com/。如果你有一个想展示的创意或项目,可以在那里发布。希望这本书能为你提供一些灵感,开始一个令人惊叹的项目!
最后,你也可以通过电子邮件将你的项目、照片或一般的评论和问题发送给 ArduinoInventorsGuide@sparkfun.com。我们会偶尔挑选一些精彩的照片和项目,在我们的博客上进行展示。谁知道呢,我们也许会请求在下一本书中使用你的项目!
第一章:电子学入门
本章为那些几乎没有电子学或电力经验的读者提供了一个电子学的广泛概述。如果你已经对本章的一些话题感到熟悉,你可以像看选择自己冒险的书一样,跳到你想进一步了解的主题,甚至直接跳到项目 1。
如果你是电子学的新手或只是想复习一下,我们建议你完整阅读本章。虽然它不是一本完整的电子学指南(关于这个话题有整本书、课程和学位),但本章是一本实用的参考书,旨在帮助你掌握基本概念和词汇。如果你想深入了解电力、电子学和电路,请参阅本章末尾的推荐阅读清单。
电力、导电性和基本术语
电力是一种奇特的存在。在许多方面它是可以预测的,但有时它也有些狡猾。如果你查找电力一词的字典定义,你可能会发现它对电力是什么、如何运作或最重要的,如何使用它,几乎没有提供什么线索。让我们从基础开始。
什么是电力?
要理解电力,首先需要了解原子的结构。原子是构成你周围一切的基本单元。一个原子由质子、中子和电子组成。电子带负电荷,质子带正电荷。一个典型的原子中电子和质子的数量相同,因此它是电中性的。电力是一种涉及电荷的移动或储存的能量形式;当我们推动或强迫电荷按照规定的方式或定义的路径移动时,就会发生电力现象。如果你曾经见过雷暴,你就看到了云层与地面之间电荷转移的证据。这些电荷通过我们大气中的空气分子转移,随着它们的移动,天空会被点亮。电荷的移动和转移被称为电流。电流的单位是安培(A)或毫安(mA)。
注意
传统上,我们把电流看作是正电荷的运动。尽管从技术上讲,电子是原子中可以移动的部分,但仍然习惯性地将电流称为从正极到负极的运动。
除了闪电、弧焊和偶尔的静电震动,我们通常看不见电力的直接表现。即使是我们在闪电中看到的明亮光线,也仅仅是空气分子在电荷通过时发生了形态变化。
当电荷受到电场力作用并且有路径供电荷移动时,电荷就会运动。这个电场力是由电势差产生的,或者我们通常称之为电压。电压是最终导致电荷运动的原因,其单位是伏特(V)。作为参考,典型的电池电压通常在 1.5 V 到 12 V 之间。12 V 电池会使电荷比 1.5 V 电池更快地移动。
电的种类
一般来说,电可以分为两种基本类型:直流电(DC)和交流电(AC)。交流电是你家外面的电力线和墙上插座中的电。交流电非常适合发电(例如发电厂)、长距离传输电力(如从发电厂到家中)以及驱动大型设备(如电动机和加热器)。然而,我们并不使用交流电来为大多数家用电子设备供电。大多数插入墙壁插座的小型电器和家用电子设备需要直流电,并使用变压器将交流电转换为直流电。关于交流电和直流电的详细内容超出了本书的范围,但你在这里构建的项目将专注于直流电。
什么是电路?
即使有电场力的推动,电荷也需要一条路径,从高电势点到低电势点。电荷从电池的正极(高电势)到负极(低电势)移动的路径称为电路。电路由一条从正极到负极的闭合路径组成,路径中包含如发光二极管(LED)、电阻器、灯或电动机等设备。图 1 显示了一个包含 LED、电池和电阻器的简单电路。请注意,电路的形状大致呈现一个环或圆形,因此得名电路。
图 1: 一个基本的直流电路

为了使电荷能够移动,路径必须由导电材料构成。导电性不是一个绝对的量度,而是一个连续体。虽然一些材料通常被认为是导体或绝缘体,但大多数材料的导电性占据一个范围。换句话说,有些材料允许电荷更自由地移动。想象一下在不同路面上开车。在平滑的高速公路上,你的车可以比在越野或土路上行驶时更快。不同的道路允许不同的速度,就像不同的材料允许不同的导电性一样。我们用电阻这个术语来描述材料减缓电荷运动的程度。
欧姆定律
正如你可能已经猜到的,电流、电压和电阻之间存在关系。这个关系通常被称为欧姆定律,并且可以用以下数学公式表示:
V = I × R
在这个公式中,V 代表电压,I 代表电流,R 代表电阻。(不要让这个数学公式吓到你:这是你在本书中看到的约三条公式之一。)
将电流可视化为管道中的水
要理解电路中的运作,最有用的方法是将电流想象成水在管道中的流动。想象水流通过花园里的水管。当你打开阀门时,水开始流过水管到达另一端,如图 2 所示。
图 2: 水与电流模型

在水管中移动的水分子代表电荷的流动(电流)。如果我们调节水阀的开关,我们可以改变管道中的水压。水管中的水压类似于电路中的电压。如果你增加水压,流量也会增加。在电路中也是一样:如果你增加电压,电流也会增加。比喻的最后一部分在于水管本身。如果我们在水管中打一个结或限制管道的直径,就会产生电阻。电阻的增加会减慢流量(降低电流)。
这个模型在描述电流流动方面效果不错,但你并不想搭建一整套水管、阀门和管道系统,仅仅是为了让水流到地面上(除非你的目的是浇灌草坪)。你希望用它做些事情;你希望它能做工作。在电路中,我们使用一些设备将电能转换为其他有用的能量形式,比如照亮灯泡、旋转马达或发出蜂鸣声。将电能转换为其他形式能量的设备被称为负载。托马斯·爱迪生发现,他可以通过灯泡将电能转化为光能;在本书中,你将做的事情远不止于此。
电路原理图、线路蓝图和接线图
虽然图示很不错,但细致地绘制每个组件来显示电路的接线方式并不高效。在本书中,你会看到像图 3 这样的原理图以及其他插图,帮助你理解电路。
图 3: 这个简单的原理图展示了一块电池、一个 LED 灯和一个电阻器。

原理图是电路的简化图示。我们有时也称这些为接线图或电路蓝图。原理图显示了各个组件之间的连接关系以及构建电路时需要使用的组件。在本书中,我们将使用 IEEE(美国电气和电子工程师协会)的标准来绘制电路。 图 3 中的原理图实际上代表了图 1 中的相同电路。直线代表导线,每个组件都有自己独特的符号。图 4 展示了本书中你将看到的一些常见原理图符号。
图 4: 一些标准的 IEEE 原理图符号

IEEE 原理图符号格式是国际公认的,用于在全球范围内交流和共享电路图。其目的是通过非常简单的线条和图形快速表示组件。
原型电路
在本书的项目中,你将构建和测试各种设计。在构建电路时,你可能还希望重新排列部件、交换位置或添加新组件。这个过程叫做原型设计。你可以像使用木块或乐高积木一样,通过使用无焊接面包板来制作电子原型,正如图 5 所示。
图 5: 一个透明的无焊接面包板,具有水平行和垂直电源轨

无焊接面包板是一个带有许多孔的塑料矩形。这些孔按照 0.100 英寸的网格间隔,并且大小适合大多数电子组件牢固地插入。孔下方是由软导电金属制成的小夹子,如图 6 所示。
图 6: 无焊接面包板的内部结构(左)和内部金属夹的特写(右)

插入同一行孔中的电线通过这些金属夹子在电气上连接在一起。就像是把电线扭在一起,只不过没有扭转的部分。请注意,这些夹子只跨越五个孔的宽度。面包板中间有一个“沟槽”,将面包板分为左右两部分,右侧的夹子与左侧的夹子没有连接。
注意
将面包板竖着拿(纵向排列),并确保顶部的字母是正向的。我们将水平排列的五个孔组称为行,将面包板两侧的垂直部分称为列,假设此方向。
面包板有多种形状和尺寸,但大多数仍然会在板子的外缘有垂直的列。这些列被称为电源轨或电源总线,每个列都有一个从顶部到底部连接的连续夹子,如图 7 所示。面包板通常还会有+和–标签,用于指示电源连接的位置,并配有红色和蓝色的颜色编码。
图 7: 面包板的底部,展示了水平行和垂直电源轨

你可以在图 8 中看到一个无焊接面包板在实际工作中的样子,图中展示了一个带有八个 LED 的电路原型设计。
图 8: 面包板上的电路

在本书中,您将使用无焊面包板构建电路,这样如果您犯了错误,可以轻松更改或修复。如果您想进一步探索某个部分,您也可以快速地将其添加到电路中。当您开始构建更大、更复杂的电路时,我们建议您准备多个面包板,这样您可以将电路分块构建。这允许您逐步构建并测试项目的每个部分,而不必在出现问题时重做、排除故障或拆解整个项目。
离散元件与接线板
我们之前提到过元件,这里简单介绍一下。世界上有成百上千种不同的电子元件。当我们提到元件时,我们指的是离散元件——最基础的、您可以购买的零件。例如,图 9 中的电阻、电容和 LED 就是离散元件。
另一方面,接线板是将多个元件预先连接到一个单一电路板上的组件,这种电路板设计为便于在面包板上使用。接线板有助于加速原型制作过程。您可以在图 10 中看到一个很好的示例。
图 9: 电阻(左)、电容(中)和 LED(右)是离散元件的示例。

图 10: 一个小型加速度计(左)及其接线板(右)。注意接线板左侧的镀金孔。

图 10 比较了一个复杂的元件——集成加速度计传感器模块(型号:ADXL345,来自 Analog Devices)与 SparkFun 为其生产的接线板。这个芯片仅有 5 × 3 毫米!它有非常小的金属连接引脚,类似于离散元件上您看到的长金属引脚。它们太小了,几乎不可能直接将连接线接到上面。接线板将这些小的连接点引导到板边缘的镀金孔上,孔之间的间距精确为 0.100 英寸,使得板上的孔可以与无焊面包板上的孔对接。每个孔都经过金属镀层,这样您就可以直接在其上焊接线材。或者,如果您希望与面包板配合使用,您也可以像图 11 所示那样焊接上公头接头。(如果您从未焊接过,也不用担心;请参阅“如何焊接”的说明,详见 302 页。)
请注意,孔上都有丝印标签,这样您就知道如何连接传感器。接线板上有这些标签,您可以直接在面包板上使用它们,而无需花费数小时的时间研究和构建,才能单独使用裸件元件。
图 11: 带接头的 ADXL345 接线板

模拟与数字
通过定义电路、组件、电压、电流和电阻的概念,我们现在可以讨论两种不同的电子学方法:模拟和数字。这两种方法并不是互相排斥的,要真正理解你所构建的电路,必须同时理解这两个概念。
模拟处理在设定范围内变化的数值。想象一下某些餐厅的调光开关;那就是模拟的。模拟值可以是开、关以及介于两者之间的任何状态。而数字值则只有两种状态:开或关。
数字电子学通常包括一个微控制器或微处理器,该处理器被编程以响应条件开关设备,而模拟电路则倾向于使用组件来调节电流、电压和电阻,从而实现相同的结果。
这两种思维方式各有优缺点,但你不能只使用其中一个而不使用另一个。例如,你无法仅用微控制器而不使用一些模拟组件来读取温度。
什么是微控制器?
微控制器是一种小型计算机,你可以通过上传程序或指令集来对其进行编程。微控制器用于自动化简单的任务,比如控制房屋的温度或在草地干旱时浇水。
本书中的项目使用了 SparkFun RedBoard 微控制器板,它与 Arduino Uno 完全兼容。两者如图 12 所示。
图 12: SparkFun RedBoard(左)和 Arduino Uno(右)微控制器板

在一天的时间里,你可能使用 15 到 20 个微控制器,而你甚至没有意识到它们的存在。它们出现在你的咖啡机、闹钟和微波炉中。仅仅是你的汽车,就有 5 到 10 个微控制器来控制刹车、音响和点火系统。我们的世界几乎是由微控制器运行的。本书将帮助你学习如何利用这一点,重新掌控你周围的世界。
注意
随着你构建项目,你将学习更多关于 Arduino 的内容,包括如何编程以及它的功能。现在,你只需要知道微控制器是一种可编程的大脑,使得任何人都可以更容易地构建电子产品,并原型化自动化周围世界的创意。
我们希望这本简短的入门书籍为你提供了一些背景知识,并预览了本书接下来将涉及的内容。我们很高兴你决定与我们一起开始这次冒险。现在,让我们开始构建我们的第一个项目吧!
关于基础电学和电子学的附加资源
如果你渴望更详细地了解电学和电子学,我们强烈建议你阅读以下书籍:
• 基础电学,海军人员局(Dover Publications,1970)
• Arduino 工作坊,John Boxall 著(No Starch Press,2013)
• 电子学入门 由 Forrest M. Mims III 著(Master Publishing, 2003)
• 发明家的实用电子学,第 4 版 由 Paul Scherz 和 Simon Monk 著(McGraw-Hill Education, 2016)
第二章:1 开始使用 Arduino
本项目涵盖了让你启动 Arduino 所需的一切!我们将介绍硬件,展示如何安装编程环境,并帮助你通过加载一个简单的程序来确保一切正常。完成后,你应该能够拥有一个自己的闪烁灯,并激发继续前进的兴奋感。我们开始吧!
收集材料
你将需要以下硬件(如图 1-1 所示)来完成本项目:
• 一个 SparkFun RedBoard(DEV-13975),Arduino Uno(DEV-11021)或任何其他兼容 Arduino 的板子
• 一条 USB Mini-B 数据线(CAB-11301 或你板子的 USB 数据线)
• 一个 LED(COM-09590,或一包 20 个的 COM-12062)
图 1-1: 所需组件

关于 Arduino
Arduino(发音为 är·də’wēn·ō 或 “arr-dween-oh!”)是一个小型可编程设备,能够为没有智能的物体添加智能功能。你可以使用 Arduino 来驱动机器人、创造 LED 艺术,甚至充当手持游戏机。在这一部分,我们将更详细地介绍 Arduino 是什么,以及它如何改变你对周围世界的看法。
一个易于接入的硬件平台
Arduino 就像一台小型计算机。你可以使用非常简单的指令对其编程,并且只需少量 AA 电池就能为其供电。与普通计算机不同的是,Arduino 使用微控制器而不是 CPU 来处理信息并执行操作。这个小芯片充当了你项目的大脑,它可以接收传感器(如光传感器、温度传感器或按钮)的输入,并输出信号来控制 LED、马达、蜂鸣器等。像图 1-2 中的 Arduino 板就包含了使微控制器正常工作所需的所有支持组件和电路。
图 1-2: Arduino Uno 是一个开源、可编程的电子平台,适用于爱好者。

Arduino 使用的编程语言本质上是 C/C++ 的一种版本。编程环境是Arduino IDE(集成开发环境)。开发该环境的团队将许多预先编写的函数和库打包在一起,以简化与硬件接口的代码编写过程。例如,这些库将打开 LED 所需的多行代码简化为一条指令!
关于 SparkFun RedBoard
有许多官方认证的 Arduino 品牌电路板,但由于该平台是开源的(意味着硬件设计和软件的源代码可以供任何人查看和修改),因此也有许多 Arduino 衍生板、克隆板和兼容板。所有的电路板设计都采用了创意共享署名相同方式许可协议,Arduino 的常见问题解答(* www.arduino.cc/en/Main/FAQ *)中指出,任何人“都可以自由使用和修改这些设计以满足自己的需求,无需请求许可或支付费用。”衍生板与官方 Arduino 使用相同的编程环境,但硬件通常已经经过某些调整或修改。
如图 1-3 所示的 SparkFun RedBoard 是一个 Arduino 兼容衍生板。它基于 Arduino Uno 的设计,但具有更稳定的 USB 接口,并使用 USB 迷你连接器而不是 Type-A 连接器。除此之外,它与 Uno 完全相同,大小和形状一致。
图 1-3: Arduino 兼容的 SparkFun RedBoard。注意它的形状与图 1-2 中的 Arduino Uno 相匹配。

RedBoard 是 SparkFun 推荐的 Arduino 板,包含了一些你需要了解的关键组件,以帮助你顺利阅读本书的前几个章节。我们在图 1-3 中标出了每个术语。
ATmega328 微控制器 电路板中间的方形黑色芯片。它是 Arduino 的大脑。
插针 微控制器上的细小金属引脚,用于读取输入和发送输出。你可以通过 Arduino 两侧的四组黑色插针接触到这些引脚。它们已编号并标注了特定用途。你最常用到的引脚是标有数字(0-13)、模拟输入(A0-A5)和电源的引脚。
迷你 USB 端口 这是你向 Arduino 发送代码并与其通信的方式。对于本书中的大多数应用,你也可以通过 USB 端口为电路板供电。如果需要外部电源,我们会特别指出。
电源 LED 这个 LED 指示灯用来显示 Arduino 是否已经通电。如果电路板上出现短路或电源连接不良,这个指示灯将不会亮起。
TX/RX LED 当数据(如代码或数字)在你的 Arduino 和笔记本电脑之间传输时,这些 LED 会闪烁。
板载 LED 13 调试指示灯。如果你是第一次插入 Arduino,LED 13 应该每秒闪烁一次。它连接到 Arduino 的第 13 号引脚。
外部电源插孔 一个位于 USB 端口旁的圆柱插孔。Arduino 需要 5V 电源,尽管你可以安全地为 Arduino 提供 7V 到 15V 之间的电压而不会损坏电路板。Arduino 上的一个芯片将输入电压降低到 5V,以便电子元件和电路正常工作。
像所有兼容 Arduino 的板子一样,你将通过 Arduino IDE 来编程 RedBoard。
安装 Arduino IDE 和驱动程序
在第一次将 RedBoard 插入 USB 端口之前,你应该先安装 Arduino IDE。要安装 Arduino IDE,请访问www.arduino.cc/download/。选择适合你电脑操作系统的版本,点击链接进行下载(图 1-4)。你将被询问是否愿意做出贡献;Arduino IDE 的开发和维护依赖于使用该软件的社区的帮助和贡献。
注意
如果你已经插入了你的板子,那也没问题——你可能只需要在安装完成后重启计算机,才能使驱动程序正常工作。
图 1-4: 你可以使用在线 IDE,也可以下载适合你操作系统的最新版本。

即使你已经安装了 IDE,我们仍然建议下载并安装最新版本。Arduino IDE 正在持续更新和改进,最好使用最新发布的版本。本书中的示例使用的是 IDE 1.8.1 及更高版本。
注意
如果你喜欢使用最新的软件版本,Arduino 下载页面还提供了夜间构建版,可以预览下一次发布的版本。不过,本书建议使用最新的稳定版。
Arduino 官网还提供了一个名为 Arduino Create 的在线平台,其中包括一个基于网页的代码编辑器。它允许你通过网页浏览器编程设备,并与他人分享和查看项目。截至本书编写时,它仅支持 Windows 和 OS X。
无论你选择使用 Arduino Create 还是下载的 IDE,按照在线指南完成安装过程。
在 Windows 上安装
如果你使用的是 Windows 电脑,我们建议下载 Arduino 的 Windows 安装包版本。下载此文件,打开它,并点击运行。这将弹出安装选项对话框(图 1-5)。
图 1-5: Arduino 安装选项对话框。确保选择了 USB 驱动程序!

勾选安装 Arduino 软件选项以及其他选项,否则你将需要单独安装驱动程序。然后,告诉安装程序你希望将 Arduino 安装在哪个位置(我们建议接受默认目录),并点击安装。
一旦你开始安装过程,记得吃个零食或者喝杯咖啡,因为完成安装可能需要几分钟。根据你使用的 Windows 版本,你可能会再次被询问是否要安装驱动程序,以及是否信任 Arduino LLC,如图 1-6 所示。
图 1-6: 我们信任 Arduino!

如果你不想再看到类似的提示,勾选“我信任 Arduino”的选项。无论如何,点击安装来安装 USB 驱动程序。就这样!Arduino 通常会在桌面上安装一个快捷方式。现在双击它以运行 Arduino IDE。
在 OS X 上安装
如果你使用的是 Mac,请下载适用于 OS X 的 Arduino IDE 选项,并按照本节中的说明操作。
安装 IDE
下载完成后,将光标悬停在下载文件夹上,并点击在 Finder 中打开,如图 1-7 所示。
图 1-7:下载后,程序将位于下载文件夹中。点击在 Finder 中打开将其移动到应用程序文件夹。

然后,只需点击并拖动Arduino程序文件到应用程序文件夹,如图 1-8 所示。在大多数情况下,你不需要安装其他任何东西,你应该可以像打开其他程序一样打开 Arduino IDE。
图 1-8:点击并将Arduino文件拖动到左侧的应用程序文件夹中。

在 OS X 上手动安装 FTDI 驱动程序
如果你使用的是标准的 Arduino Uno 板,驱动程序应该已经预装并且开箱即用。如果你使用的是 SparkFun RedBoard,则需要额外一步来手动安装驱动程序。SparkFun RedBoard 使用来自未来科技设备国际公司(FTDI)的 USB 芯片与计算机通信。你需要手动安装该芯片的 FTDI 驱动程序。首先,访问www.sparkfun.com/ftdi/。这将引导你查看我们关于安装 FTDI 驱动程序的教程(见图 1-9)。
点击 Mac OS X 的链接。这将引导你根据计算机上运行的 OS X 版本选择要安装的驱动程序选项。如果你使用的是 Mac OS X 10.3(Panther)到 10.8(Mountain Lion),有一个选项;如果你使用的是 Mac OS X 10.9(Mavericks)或更高版本,则有另一个选项。
图 1-9:SparkFun FTDI 安装指南

下载适当的驱动程序并双击它以启动安装过程。你应该会看到熟悉的 Mac 软件安装窗口。找到你的硬盘并点击确定。继续安装过程,当进度条填满(如图 1-10 所示)时,驱动程序应已安装完毕。
图 1-10:在 OS X 上安装 FTDI 驱动程序

就这样!现在,双击你应用程序文件夹中的 Arduino 图标来运行 IDE。如果你在安装 FTDI 驱动程序之前已经打开过 IDE,你需要完全退出并关闭 Arduino IDE,然后重新启动它,才能正确显示串口。
注意
如果在安装驱动程序后遇到错误,请查看解决方案: www.sparkfun.com/macdriver/ 。
在 Linux 上安装
Arduino 也可以供 Linux 用户使用。下载适合你系统的 Linux 文件,文件有 32 位和 64 位版本。然后,使用xz-utils或其他文件压缩工具解压该文件。如果你想在 Linux 上使用最新版本的 Arduino,可能还需要安装一些其他依赖程序。请访问playground.arduino.cc/Learning/Linux/,查看与特定发行版相关的信息。
对于大多数 Linux 发行版(包括 Ubuntu、Debian 和 Fedora),你应该能够使用apt-get包管理器通过命令行安装 Arduino。打开终端并输入以下命令:
sudo apt-get install arduino
安装完成后,打开你刚刚安装的 Arduino 程序。Arduino 使用 Java 运行 IDE,必须在 XWindows 或类似的窗口用户界面环境中运行。
注意
根据你的 Linux 发行版的包管理器,你通过这种方式安装的版本可能不是当前 Arduino 网站上托管的最新版本。
简要介绍 IDE
IDE 是你编写 Arduino 指令并进行测试的地方。这些指令组成一个程序,或者在 Arduino 术语中称为草图。IDE 允许你将草图上传到 Arduino,并控制现实世界中的事物。
如果你还没有这样做,打开你新安装的 Arduino 程序。经过启动屏幕后,你应该会看到 IDE,它看起来像图 1-11 所示。
图 1-11: Arduino IDE

你可以使用菜单栏(包含文件、编辑、草图、工具和帮助菜单) ➊ 来打开或保存文件,上传代码到 Arduino,修改设置等。你还应该看到一组图形按钮 ➋。从左到右依次是验证/编译、上传、新建、打开和保存。我们将在本书中探讨这些菜单和按钮。IDE 的大部分区域是空白区域 ➌;这里是你编写代码的地方。代码区域下方是警报栏 ➍,下面是控制台 ➎;这些区域用于报告状态、警报和错误。例如,如果你的草图中有拼写错误(称为语法错误),IDE 会在那里显示错误。如果你在代码窗口中键入你的名字并点击勾选按钮(验证/编译),Arduino IDE 会稍作思考,然后在警报栏中显示错误,突出显示你的名字,并在控制台中提供更多错误信息,如图 1-12 所示。
图 1-12: Arduino IDE 中的典型错误信息和输出

更改默认偏好设置
Arduino 是一个完全开放且可配置的编程环境。我们有一些小设置可以调整,以便更容易编写代码、调试和制作有趣的东西。选择 文件 ▸ 首选项 来查看和更改 Arduino IDE 的一般设置。你应该会看到一个类似于图 1-13 的窗口。
我们建议调整编辑器的字体大小,使其适合你阅读。我们还喜欢勾选 显示行号,并取消勾选 验证或上传时保存。行号会帮助你更容易地导航代码,取消自动保存功能则可以让你快速测试代码,而不必每次都保存。Arduino 是完全开放的,所以如果你愿意,你也可以点击 preferences.txt 文件,调整更多其他功能。
图 1-13: Arduino 设置窗口

试用:第一次插入 Arduino
当你完全安装好 Arduino IDE 和驱动程序后,使用合适的电缆将 Arduino 板子连接到计算机的 USB 端口。电源 LED 应该会亮起,如果你的板子是全新的,你应该会看到一个标记为 13 的 LED 如图 1-14 所示闪烁。你的计算机通过 USB 电缆为 Arduino 板子供电,并运行出厂时已安装的代码。与计算机不同,Arduino 一次只能存储和运行一个 sketch。加载到 Arduino 上的标准测试 sketch 是一个简单的 LED 闪烁。当你的板子连接好后,你将设置 IDE 以便编写你自己的 sketch。
注意
如果你在安装 IDE 和驱动程序之前插入了板子,可能需要重启计算机。
图 1-14: 当你为新板子供电时,标记为 13 的 LED 会开始闪烁。

在 IDE 中选择你的板子
根据你的计算机或操作系统,可能需要一些时间让计算机识别你刚插入的新硬件,并与已安装的驱动程序关联。在计算机识别新设备后,点击 工具,并将鼠标悬停在 板子 选项上,如图 1-15 所示。
图 1-15: 工具菜单中的板子选择列表

应该会出现一个预支持的 Arduino 板子列表。如果你使用的是标准的 Arduino Uno 或 SparkFun RedBoard,选择 Arduino/Genuino Uno 选项。如果以后使用的板子不同于 Uno 或 RedBoard,请根据板子的文档选择正确的 Arduino——本书假设你使用的是 Uno 或 Uno 衍生板。
选择通信端口
每个连接到你计算机的设备都有一个独特的通信端口标识符。你需要配置 IDE,以便它知道你的 Arduino 连接到哪个端口。为此,首先选择 工具 ▸ 端口,查看设备的通信端口选项。根据你的操作系统,你会看到不同的选项。
在 Windows 上
如果你使用的是 Windows PC,你可能会看到 COM3、COM4 或其他编号的 COM 端口,如图 1-16 所示。选择此选项。如果没有显示任何选项,请参阅页面 27 上的“基本 Arduino 故障排除”。
图 1-16: 在 Windows 上选择通信端口

在 OS X 和 Linux 上
在 Mac 或 Linux 机器上,通信端口应该列为 /dev/cu.usbserial-A
图 1-17: 在 OS X 上选择通信端口

一个 Arduino 的“Hello, World!”
Hello, world! 是许多初学者编写的经典第一个程序。在大多数其他编程语言中,这个程序会在屏幕上显示 Hello, world!。由于 Arduino 没有屏幕,它的Hello, world!版本是一个闪烁的 LED。
对于你的第一个程序,我们将向你展示如何使用 Arduino IDE 自带的示例。将你的板子连接到计算机后,点击 文件 下拉菜单,选择 示例 ▸ 01.基础 ▸ Blink,如图 1-18 所示,打开名为 Blink 的程序。
应该会打开一个包含 Blink 程序的新 IDE 窗口。在这个窗口中,点击 程序 ▸ 上传 或点击 上传 图标。IDE 会将这段相对易于理解的代码转化为 Arduino 可以理解的 1 和 0(二进制代码),这叫做 编译,然后将程序上传到你的板子上。
图 1-18: 查找 Blink 程序

在点击上传后,注意观察警报区域的状态消息。它应该显示正在编译草图…并显示进度条。编译完成后,你的电脑将开始将草图上传到 Arduino。Arduino 板上的 RX(接收)和 TX(发送)指示灯应该快速闪烁,表示草图正在传输到 Arduino 板上。TX 灯闪烁是因为你正在向 Arduino 发送数据,RX 灯闪烁是因为 Arduino 在接收到草图后,会向你的电脑确认已接收。当上传过程完成时,IDE 上的状态区域应该显示上传完成,并且板上标有 13 的 LED 应该闪烁,正如图 1-19 所示。
图 1-19: 打开 LED 13

如果你收到任何错误消息,可能是你的 Arduino 没有与电脑通信。阅读下一节“基本的 Arduino 故障排除”以了解一些常见问题,并尝试重新上传草图。
基础的 Arduino 故障排除
像其他任何可编程电子设备一样,Arduino 有时也会出现问题。以下是一些编程 Arduino 时遇到问题的故障排除建议。
-
确保你的 Arduino 已插入 USB 电缆,并且电缆完全插入电脑。电缆可能只部分插入了板上。你也可以尝试拔掉电缆再重新插上。
-
始终确认在板菜单中选择的板是已插入电脑的板。我们的示例中将选择 Arduino/Genuino Uno。
-
确认在工具 ▸ 端口菜单中选择了正确的通信端口;该端口旁边应该有一个勾选标记或圆点。如果你不确定哪个端口对应你的 Arduino,可以拔掉 USB 电缆,刷新通信端口列表,观察哪个端口消失。
-
确保你没有在示例草图中错误地输入一些杂散字符。如果有多余的字符,代码将无法编译。
-
在 Windows 上,检查你电脑的设备管理器。确保设备旁没有感叹号。如果有,你需要手动重新安装驱动程序。
-
如果你仍然收到错误消息,重新安装你的板驱动程序。我们在 www.sparkfun.com/ftdi/ 提供了更多的安装说明。
这六个小贴士是解决 Arduino 新手常见问题的方法,建议从这里开始。如果这些建议没有解决问题,请保持冷静,耐心等待,记住你不是第一个遇到问题的人。如果你完全卡住了,可以在官方 Arduino 论坛上寻找解决方案,地址是 forum.arduino.cc/。
Arduino 草图的结构
在这一节中,我们将引导你通过上传到 Arduino 中的 Blink 示例,这个示例在《Arduino "Hello, World!"》的第 25 页中出现。首先,清单 1-1 展示了完整的 Blink 示例,闪烁效果尽显其中。
清单 1-1: Blink 示例代码
➊ /*
Blink
Turns an LED on for one second, then off for one second,
repeatedly.
Most Arduinos have an onboard LED you can control. On the
UNO, MEGA, and ZERO, it is attached to digital pin 13;
on the MKR1000 it's on pin 6\. LED_BUILTIN is set to the
correct LED pin independent of which board is used.
If you want to know which pin the onboard LED is connected
to on your Arduino model, check the Technical Specs of
your board at https://www.arduino.cc/en/Main/Products
This example code is in the public domain.
modified 8 May 2014
by Scott Fitzgerald
modified 2 Sep 2016
by Arturo Guadalupi
modified 8 Sep 2016
by Colby Newman
*/
//the setup function runs once when you press reset or
//power the board
➋ void setup() {
//initialize digital pin LED_BUILTIN as an output
pinMode(LED_BUILTIN, OUTPUT);
}
//the loop function runs over and over again forever
➌ void loop() {
digitalWrite(LED_BUILTIN, HIGH); //turn the LED on
//(voltage level is HIGH)
delay(1000); //wait for a second
digitalWrite(LED_BUILTIN, LOW); //turn the LED off
//(voltage level is LOW)
delay(1000); //wait for a second
}
在编写 Arduino 示例时,你需要非常注意使用的词汇、标点符号和大小写。这些元素是编程语言语法的一部分。为了使 IDE 能够正确编译你的示例,你必须使用它所识别的单词。这些单词被称为关键字,你会注意到它们会变成不同的颜色,比如橙色、青色或绿色。现在,让我们详细看看这个第一个示例中使用的一些特性。
关键示例元素
在示例的顶部,你会声明一个新的全局命名空间 ➊。这个命名空间描述了示例的功能,并且通常会包含其他信息,如变量初始化和库声明。几乎每个示例都会包括一个命名空间。这个示例的命名空间有注释,帮助人类读者理解示例的功能。在 Arduino IDE 中,注释是灰色的。每个注释要么以//开始,要么如果注释内容较长,则用/*和*/符号包围。注意,不是所有注释都出现在代码行之间;有些注释会出现在它们解释的代码同一行。这不会影响示例,因为 IDE 会忽略注释。与代码不同,你可以在注释中写任何你想写的内容,包括常规的单词、拼写或标点。
任何示例的框架都由两个主要的函数定义组成,setup() ➋和loop() ➌。函数仅仅是将多个指令或代码行组合在一起的一种方式。每个函数都有一个数据类型、一个名称和一组指令。函数前面的单词表示函数将返回的数据类型。setup()和loop()都有void类型,因为它们不会返回任何值。
每个函数的名称后面都包括一对圆括号。这对圆括号是你传递参数给函数的地方。参数是函数执行任务所需要的值。setup()和loop()不需要参数,但在后续的项目中,你将使用一些需要参数的函数。最后,构成函数的代码行由一个大括号{和一个闭合的大括号}包围。
setup()和loop()函数是每个 Arduino 示例所必需的;当 Arduino 首次启动或被重置时,setup()代码只会运行一次,而loop()代码则会不断地循环执行。这就像是在烤饼干:setup()中的指令准备好所有的工具和原料,而loop()则一次次地烘烤批次,直到你关掉烤箱(也就是 Arduino)。
现在,让我们来弄清楚setup()和loop()中的每一行代码究竟在做什么。
setup()函数
首先,让我们仔细看看 Blink 示例中的setup()函数;请参见清单 1-2。
清单 1-2: 我们的 Blink 示例中的setup()代码
void setup() {
//initialize digital pin LED_BUILTIN as an output
pinMode(LED_BUILTIN, OUTPUT);
}
setup()函数中的唯一一行代码是对pinMode()函数的调用。Arduino 上的引脚 0–13 被认为是通用输入/输出(GPIO)引脚。它们可以用作输入或输出,而pinMode()允许你告诉 Arduino 你打算如何使用数字引脚。你通过传递两个参数来实现这一点。第一个是引脚编号,范围从 0 到 13。第二个参数是引脚配置。
对于引脚的引用,Blink 示例使用了一个名为LED_BUILTIN的系统常量,来指定你正在使用设备上的默认 LED。在大多数 Arduino 设备上,这与引脚 13 相同。注意,值是全大写且为深青色。这种颜色表示LED_BUILTIN是一个具有预定义值的特殊关键字,用于 IDE 中。
第二个参数将引脚配置定义为OUTPUT。注意,关键字OUTPUT也是深青色的,因为它是 Arduino 中使用的另一个常量。这里还有一些其他选择,我们将在项目 4 和项目 9 中详细讨论,但现在请注意,Blink 将引脚设置为 LED 的OUTPUT。
如果你要将这一行代码描述为一句话,它的意思是:“告诉引脚 13 从 Arduino 输出。”
注意
pinMode() 函数遵循一种称为驼峰命名法的大小写约定。在驼峰命名法中,首字母小写,后续单词的首字母大写。
pinMode()调用中的最后一个字符是分号(;),它标志着一行代码的结束。当你开始编写自己的代码时,始终以分号结束一行代码。如果你忘记了,也不用担心;几乎每个编程过的人都会忘记分号,所以 Arduino IDE 会显示一个有用的警告,帮助你找出哪里需要加上缺失的标点符号。
main()函数在哪里?
如果你对编程有所了解,或者熟悉 C 或 C++,你可能会想知道 Arduino 示例中main()函数在哪里。当你点击验证/编译或上传时,Arduino 实际上会在后台集成许多其他文件,包括一个名为main.cpp的文件。你可以在 Arduino 程序文件夹中找到所有有关操作的详细信息。记住,它是开源的!
这是来自main.cpp文件的一段代码:
int main(void)
{
init();
initVariant();
#if defined(USBCON)
USBDevice.attach();
#endif
➊ setup();
for (;;)
{
➋ loop();
if (serialEventRun) serialEventRun();
}
return 0;
}
看看setup()函数在哪里被调用的➊?并注意loop()函数➋处于一个无限循环中;Arduino 通过一个空的for(;;)来实现这个永远循环。就是这样,它不断运行。
loop()函数
现在让我们再看看loop()函数,它按从上到下的顺序执行每一条指令,并且永远重复自己。请参见清单 1-3。
清单 1-3: Blink 示例的loop()代码
void loop() {
digitalWrite(LED_BUILTIN, HIGH); //turn the LED on
//(voltage level is HIGH)
delay(1000); //wait for a second
digitalWrite(LED_BUILTIN, LOW); //turn the LED off
//(voltage level is LOW)
delay(1000); //wait for a second
}
digitalWrite()函数允许你打开或关闭 Arduino 的引脚;这称为控制引脚的状态。这个函数也使用两个参数。第一个参数指示你想要控制的引脚;在这种情况下,我们再次使用系统常量LED_BUILTIN。第二个参数是你希望引脚处于的状态。要点亮 LED,Blink 示例传入HIGH。要熄灭 LED,则传入LOW。
第二条指令是delay(),它通过传入的毫秒数来延迟你的程序执行。Arduino Uno 以及像 SparkFun RedBoard 这样的衍生板每秒钟能执行 1600 万个指令;这真是非常快!实际上,它非常快,以至于没有延迟,你根本不会注意到 LED 的变化。延迟使我们能够控制 LED 点亮的持续时间。在这个示例中,delay(1000)指示 Arduino 在执行下一条命令之前延迟 1000 毫秒。
接下来的两行代码与前两行类似;它们只是指示 Arduino 关闭 LED,并延迟另外 1000 毫秒。在最后一行之后,loop()函数会从顶部开始重复,并重新点亮 LED。
黑客(你好)世界
学习示例代码的最佳方法之一是修改它的行为。试着将延迟时间减少到500。点击上传。闪烁的变化是什么?如果你传入数字5呢?这是一个 5 毫秒的闪烁!你能看得清楚吗?你能看到的最快闪烁频率是多少?
你的第一个硬件
当板子上的 LED 正常工作并闪烁时,下一步是添加你第一个硬件:一个外部 LED。正如我们之前提到的,Arduino 的引脚用于连接输入和输出到微控制器,我们可以通过 LED 来简单地展示这一点。拿一个 LED 仔细看看,它的样子大致如图 1-20 所示。
你会注意到 LED 有一个短脚和一个长脚。如果你仔细观察,你还会看到 LED 灯泡的边缘有一个平面,且平面位于短脚的同一侧。这些帮助你识别极性;LED 的长脚是正极,位于平面一侧的短脚是负极或地线脚。
图 1-20: 显示长脚和短脚的 LED

记住,LED_BUILTIN指的是 Arduino 上的引脚 13。所以,将 LED 连接到 Arduino 就像是将 LED 的长脚插入引脚 13,将短脚插入紧邻引脚 13 的 GND(地线)引脚一样简单。现在插入 LED,确保板子已通电。如果你正确插入,如图 1-21 所示,LED 会开始闪烁。如果 LED 没有闪烁,可能是你插反了。别担心:拔出来并反过来插。
图 1-21: 将 LED 添加到引脚 13 的快速简便方式

进一步探索
本书中的每个项目都会有一个“进一步探索”部分,介绍如何将你在该项目中学到的概念提升到更高的层次。这些部分会包括关于如何使用现有项目、修改代码以及物理修改项目的建议。
破解
对于这个项目,我们建议你尝试创建一些有趣的闪烁模式。首先,复制并粘贴 loop() 函数中的四行代码,让它重复执行,这样你就得到了八行代码。这将产生两个闪烁序列,并且提供更多的代码可以使用。你可以通过修改延迟时间来创建模式,从而控制 LED 的点亮时机。例如,我们制作了一个看起来像心跳的模式;我们修改过的 Blink 草图见 列表 1-4。
列表 1-4: 心跳模式的示例代码
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(200);
digitalWrite(LED_BUILTIN, LOW);
delay(200);
digitalWrite(LED_BUILTIN, HIGH);
delay(200);
digitalWrite(LED_BUILTIN, LOW);
delay(800);
}
对于一个真正的挑战,试着编程让你的 Arduino 用摩尔斯电码闪烁出你的名字,使用一系列短点(点)和长线(划)的闪烁。图 1-22 展示了一个摩尔斯电码备忘单,帮助你理解闪烁模式。大多数人开始时使用的经典信息是 S-O-S,或者 . . . - - - . . . (点点点,划划划,点点点)。
图 1-22: 基本摩尔斯电码图表

修改
闪烁的灯光是一件强大的事情!拥有了这个新发现的超级能力,你可以在家里的很多物品上加上 LED 灯。万圣节服装总是一个放入闪烁灯饰的好地方。你可以将 LED 的引脚焊接到一些导线上,延长连接线,这样就能轻松地将 Arduino 隐藏在佩戴者更舒适的地方(比如口袋里)。我们拿了一个从本地超市买来的万圣节蜘蛛,并给它加上了一些会闪烁的红色眼睛(见 图 1-23)。
另一个适合闪烁和控制 LED 的场景是比例模型。为汽车头灯、建筑物或街灯等添加可工作 LED 总是一个很好的方式,可以在任何比例模型或场景中创造出现实的假象,如 图 1-24 所示。
图 1-23: 一个闪烁的可怕蜘蛛

图 1-24: 一个带有 Arduino 控制灯光的模型

保存草图
每个项目在加入一些闪烁的 LED 后都会显得更加时尚,因此我们建议你随时保留你修改过的 Blink 草图,以便在未来的项目中重复使用其中的部分代码。保存你的草图,并确保给它起一个描述性的名字,以便提醒你它是什么。你的文件名不应包含空格;如果包含空格,Arduino 会用下划线(_)替换空格。默认情况下,当你保存草图时,Arduino 会将其保存在 Arduino 草图文件夹中,该文件夹通常位于你计算机的 Documents 文件夹内。你可以选择将它们保存在其他地方,但通常把所有草图保存在一个地方是个好主意。
当你准备好提升你的眨眼技能时,前往项目 2,我们将在那里教你如何构建一个由 Arduino 驱动的红绿灯。
第三章:2 A 你家的红绿灯
在你通过嵌入式电子学迈出的第一大步中,你设置了 Arduino IDE 并让 LED 闪烁。这是一个巨大的进步,但有了 Arduino,任何项目都不必止步于一个 LED。本项目将展示如何扩展你的第一个 LED 草图,使三个 LED 显示闪烁模式。你的任务,如果你选择接受的话,是为你家一个繁忙的走廊制作并编程一个红绿灯(见图 2-1)。
图 2-1: 完成的红绿灯项目

所需材料
本项目中的材料都很简单。所有电子零件都在 SparkFun Inventor’s Kit 标准套件中,除了带星号(*)标记的部分。如果你使用的是自己的工具包或自行拼凑零件,请参见以下零件清单。图 2-2 展示了本项目中使用的所有零件。
电子零件
• 一块 SparkFun RedBoard(DEV-13975),Arduino Uno(DEV-11021),或任何其他兼容 Arduino 的电路板
• 一根 USB Mini-B 电缆(CAB-11301 或你电路板的 USB 电缆;未显示)
• 一块无焊面包板(PRT-12002)
• 一只红色 LED、一只黄色 LED 和一只绿色 LED(COM-12062)
• 三个 330 Ω电阻(COM-08377,或一包 20 个的 COM-11507)
• 公对公跳线(PRT-11026)
• 公对母跳线(PRT-09140*)
• (可选)一个 4 节 AA 电池盒(PRT-09835*;未显示)
图 2-2: 红绿灯组件

其他材料和工具
如果你想要构建像图 2-1 中那样的外壳,或按照第 64 页“进一步学习”中的建议进行操作,你还需要以下材料,这些材料展示在图 2-3 和图 2-4 中:
• 铅笔
• 工艺刀
• 金属尺
• 钳子
• 剥线钳
• 胶水(热熔胶枪或工艺胶水)
• (可选)钻机和 3/16 英寸的钻头
• (可选)电烙铁
• (可选)焊锡
• (可选)辅助工具(未显示)
• 纸板(大约 12 英寸见方)或纸盒
• 两个乒乓球
• 外壳模板(参见图 2-15,第 55 页)
注意
好的干净的纸板在这些项目中将是非常宝贵的。我们建议从手工艺或美术用品商店购买纸板。
图 2-3: 推荐工具

图 2-4: 推荐的建筑材料

新组件:电阻
尽管在项目 1 中你单独使用了 LED,但在大多数情况下,最好使用电阻来保护 LED 免受过多电流的损害。像图 2-5 中所示的电阻是非常常见的。它们在构建电路时是必不可少的,你在完成本项目时也需要用到它们。
图 2-5: 近距离看电阻

如果你将电流比作水流通过管道,那么电阻器就像是管道变窄的地方,减少了水流的流量。(如果你感兴趣,可以参考 “将电流比作管道中的水流” 第 4 页,这里详细描述了这个比喻。)电阻器用于控制或限制电流的流动。
电阻以欧姆为单位(通常缩写为 Ω,希腊字母欧米伽),电阻器上的彩色带表示其电阻值。你可以在 “电阻器与带” 第 308 页找到电阻器色带解码器;然而,在本书中,你只需要识别两种不同的电阻器值:330 Ω 和 10 kΩ。330 Ω 电阻器的色带为橙色、橙色和棕色(见 图 2-5),而 10 kΩ 电阻器的色带则为棕色、黑色和橙色。电阻器上还有第四个带,其颜色表示电阻器的公差。电阻器的值将在一定公差范围内准确:银色表示电阻器有 5% 的公差,而金色则表示 10% 的公差。然而,本书中的项目对公差的要求不高,因此我们将仅按其假定值来使用这些电阻器,这对于任何公差带都适用。
一些元件,如 LED,如果流向它们的电流过高,可能会受到损坏,电阻器可以通过减少电流来保护这些元件。在 LED 上使用电阻器将电流限制在安全范围内,是一种很好的预防措施,避免 LED 灯泡烧坏——或者在最坏的情况下,烧爆!(是的,它们真的会爆炸。)从现在开始,我们将在所有项目中使用限流电阻器。
为什么红绿灯使用 330 Ω 电阻器
一般红色 LED 的最大电流额定值大约为 20 mA,如其数据表所列。为了保护 LED,你需要添加一个电阻器,以确保电流不超过这一限制。那么,如何知道要使用 330 Ω 电阻器呢?
Arduino 上的输出引脚在开启时提供 5 V。根据颜色,每个 LED 需要的电压略有不同,通常在 2.0 到 3.5 V 之间。红色 LED 大约在 2 V 时点亮,剩下 3 V 的电压将通过电阻器或电路中的其他元件被消耗。通常的好做法是将通过 LED 的电流限制在最大电流的一半左右,因此对于最大电流额定值为 20 mA 的红色 LED,你会得到 10 mA 的电流。你可以利用欧姆定律(记得 10 mA = 0.01 A)来计算所需的电阻器:

但是,300 Ω 并不是一个标准的电阻值。最接近的标准电阻值是 330 Ω,通常最接近的标准电阻已经足够使用。这应该能确保 LED 使用很长时间。由于电阻器将决定电流,因此这是一个限流电阻器。
如果你有不同的电阻器可用,可以尝试使用不同阻值的电阻,看看会发生什么。较大的电阻器会使电流减小,而较小的电阻器会使电流增大。如果你使用 10 kΩ 电阻器,会发生什么呢?
构建交通灯原型
现在是时候构建电路了。首先,查看 图 2-6 中显示的原理图。你将在面包板上构建此电路,如 图 2-7 所示。
图 2-6: 交通灯项目的原理图

原理图展示了每个组件如何在电路中连接。Arduino 的引脚 13、引脚 12 和引脚 11 将分别用于控制交通灯电路中的每个 LED。正如原理图所示,每个 LED 都连接到一个独立的电阻器,而每个电阻器又连接到 GND(地)。接下来,让我们来看一下布线。
图 2-7: 将红色 LED 和限流电阻连接到面包板

将红色 LED 连接到面包板
现在你将开始把原理图转换为实际的电路。在第一个项目中,你闪烁了一个内置于 Arduino 板上的 LED。这个 LED 是通过 Arduino 的引脚 13 内部接线的。由于你将使用三个独立的 LED,你需要自己将它们接线。取出你的面包板,并按照 图 2-6 中的原理图或 图 2-7 中的示意图,将引脚 13 连接到 LED 的正极(较长的腿)。
注意
如需复习面包板的工作原理,请参见第 6 页的“电路原型制作”章节。
要在面包板上连接这些,我们建议你首先按照 图 2-7 中所示的位置放置 Arduino 和面包板。(这将在本书中作为标准布局。)然后,找到一个红色 LED 和一个 330 Ω 电阻器。按 图 2-8 中所示弯曲电阻器的腿,这样电阻器就更容易插入面包板。我们建议使用剪线钳将电阻器的两条腿各修剪约一半长度,以便更方便操作。电阻器不像 LED 那样有极性,因此你不必记住哪一条腿是正极或负极。
图 2-8: 弯曲电阻器

图 2-9 展示了典型面包板的示意图。大多数面包板都有标记的列和编号的行作为参考。利用这些参考点,将 LED 按照图 2-7 所示插入面包板。长的正极腿(阳极)应插入面包板的 E 列第 1 行(E1),而短的负极腿(阴极)应插入面包板的 E 列第 2 行(E2)。现在,找到一个 330 Ω(橙色-橙色-棕色)电阻器。将电阻器的一条腿插入面包板第 2 行的任意孔中,以将电阻器连接到 LED 的短腿。在我们的示意图中,我们将电阻器的这一腿插入面包板上的 A2 孔。在所有标准面包板上,每行的 A–E 列是相连的,F–J 列也是相连的。现在,将电阻器的另一条腿插入面包板的负电源轨,即标有蓝色或黑色线条和负号(–)符号的列。
图 2-9: 面包板具有编号的行和标有字母的列。

为面包板供电
拿两根公对公的跳线。我们建议使用黑色表示接地(GND),红色表示电源,这也是本书中采用的标准。
将 Arduino 上的 GND 引脚的黑线连接到面包板上的负电源轨。Arduino 上有三个标有 GND 的引脚,您可以使用其中任何一个。每个 LED 的电源实际上来自数字引脚。由于引脚 13 将为红色 LED 供电,因此将一根线从 Arduino 的 13 号引脚连接到面包板上的 A1。
使用 USB 线将 Arduino 板连接到计算机,并且项目 1 中的“Hello, world!”示例程序应该会运行,从而使您的 LED 开始闪烁。实际上,面包板上的 LED 和 Arduino 上的 LED 都会闪烁,因为它们都连接到 13 号引脚。
如果面包板上的 LED 没有闪烁,而 Arduino 上的 LED 闪烁,请仔细检查您的接线和 LED 的方向。确保短腿插入面包板的第二行,连接到电阻器,并且电阻器通过负电源轨连接到 GND。在红色 LED 闪烁后,请断开 Arduino 与计算机的连接,以便您可以安全地完成电路的其余部分。搭建电路时,最好断开电路板的连接。
添加黄色和绿色 LED
现在,将黄色 LED 连接到 Arduino 的 12 号引脚,将绿色 LED 连接到 11 号引脚;您可以按照为红色 LED 所做的相同基本步骤操作,但为每个新 LED 使用不同的行对,就像在图 2-10 中的最终接线图所示。
图 2-10: 使用 11、12 和 13 号引脚的最终交通信号灯电路

每个 LED 都应该有自己的电阻器连接到地轨,就像图 2-6 中的原理图一样。还要注意,我们给每个 LED 在面包板上留了一些空间,以便有足够的地方插入线缆而不会弄乱电路的其他部分。虽然我们建议了一种特定的接线方式,但请记住,您可以使用面包板的任何部分——只要您要连接的两根线在同一行。一旦完成,您的电路应该类似于图 2-11。
图 2-11: 完成的交通信号灯电路,包括 Arduino、LED 和电阻器

为了模拟一个真实的交通信号灯,该项目需要一种方式来依次点亮每个灯,并在一段时间后切换到下一个。幸运的是,Arduino 草图可以使用各种指令,包括定时命令,来控制电路。
编程交通信号灯
现在,将您的 Arduino 重新连接到计算机。是时候开始编程了!打开 Arduino IDE,开始一个新的草图。
确认您的 IDE 设置
在编写任何草图时,您应该始终从一些基础工作开始。首先,检查板类型和端口是否正确设置。现在点击工具 ▸ 板。如果您使用的是 SparkFun RedBoard 或标准的 Arduino Uno,选择Arduino/Genuino Uno。然后,点击工具 ▸ 端口。在 Windows 上,您的 Arduino 应该设置为最高编号的 COM 端口。在 OS X 或 Linux 上,端口应该列出为/dev/cu.usbserial-A
为引脚编号创建占位符
在确认了 IDE 设置后,您准备好创建草图了。如在“Arduino 草图的结构”第 27 页中讨论的那样,一个基本的 Arduino 草图由两个部分组成:setup()函数和loop()函数。这个简化的描述适用于大多数简单的草图,但更复杂的草图有许多不同的部分。停止灯草图使用的一个新部分是全局命名空间,这是草图中位于setup()函数上方并完全在任何函数之外的部分。在这个区域,您可以将某些名称(变量)定义为值的占位符,并且这些值将在草图的所有部分中使用。Arduino 草图可以处理几种类型的值。
草图能够理解的数据
Arduino 语言包含多种可能的数据类型,在编写草图时,您会经常遇到其中的一些。以下列表并不详尽,但涵盖了主要的类型,并展示了它们在代码中出现的方式:
整数(int) 一个范围从-32,768 到 32,767 的整数
浮点数(float) 一个带小数点的数字,范围从-3.4028235E+38 到 3.4028235E+38
字节(byte) 一个范围从 0 到 255 的数字
字符(char) 一个单一的字母,用一对单引号括起来,例如‘a’
字符串(String) 一系列字符,用一对双引号括起来,例如"hello"
布尔值(Boolean) 一个值,可以是true或false,在程序中映射为1或0,在引脚输出方面则为HIGH或LOW
Arduino 程序要求在定义变量时指定其数据类型。我们来看看这是如何工作的。
可变值
你在编写程序时大部分创建的值将是变量。可以把变量看作是数据的占位符。这个数据可以是一个数字,一个字母,甚至是一个完整的句子。
在使用变量之前,你必须定义它,这包括给它命名、声明它的数据类型,并给它一个初始值。养成在定义变量时同时赋值的习惯,看起来大概是这样:
➊int ➋val = ➌10;
这个变量定义有三个部分:数据类型 ➊、变量名 ➋ 和变量的值 ➌。注意,在这一行的末尾有一个分号——它表示语句或指令的结束。分号非常重要,忘记加它常常是导致许多编译错误或代码中的 bug 的根本原因,因此要小心记住它!
在选择变量名时,你可以使用任何未被打断的字符集,包括字母和数字。这里有一个注意事项:变量不能以数字开头,也不能包含任何特殊字符。我们建议尽可能地使变量名具有描述性,同时保持简洁。这是一个发挥创意的机会,你可以用缩写词和描述来简化变量名。在这个例子中,我们选择将变量命名为val(即value的缩写),而10是变量的初始化值,即给变量赋予的初始值。你在定义变量时不一定要初始化它,但同时进行定义和初始化是一个好习惯,也有助于代码的清晰性。
在这个项目中,你将创建三个变量,用来存储 Arduino 控制的三个 LED 的引脚编号。用一个描述 LED 颜色的变量要比记住哪个 LED 连接到哪个引脚要容易得多!
启动一个新的程序,并将清单 2-1 中的代码添加到你程序的全局命名空间中。
清单 2-1: 表示引脚编号的变量
byte redPin = 13;
byte ylwPin = 12;
byte grnPin = 11;
再次说明,这三个变量存储三个 LED 的引脚编号。在 Arduino 上,引脚编号限制为 0 到 13 之间的整数,因此我们使用byte数据类型。我们可以使用byte,因为我们知道引脚编号不会超过 255。请注意,每个变量的名称都描述了它包含的内容:redPin是红色 LED 的引脚,ylwPin是黄色 LED 的引脚,grnPin是绿色 LED 的引脚。而且正如图 2-10 所示,红色引脚是 13 号引脚,黄色是 12 号引脚,绿色是 11 号引脚。现在,每当你在草图中使用引脚编号时,可以使用描述性的变量名代替。
注意
为了易读性,我们 使用驼峰命名法 为变量命名, 将p 大写, 这就是pin的命名方式。 驼峰命名法是一种编码约定,它允许你在变量中使用大写字母分隔单词,而无需使用空格。
编写 setup()函数
要继续编写停车信号灯草图,请在清单 2-2 中添加setup()函数。
清单 2-2: 停车信号灯的setup()代码
void setup()
{
//red LED
pinMode(redPin➊, OUTPUT➋);
//yellow LED
pinMode(ylwPin, OUTPUT);
//green LED
pinMode(grnPin, OUTPUT);
}
就像在项目 1 中的“Hello, world!”草图一样(见第 30 页中的“setup()函数”),本草图在setup()中使用pinMode()函数来配置 Arduino 的数字引脚。
本项目使用了三个不同的数字引脚,因此草图中有三个单独的pinMode()函数。每个函数调用包括一个引脚编号作为其变量➊(redPin、ylwPin和grnPin)和常量OUTPUT➋。使用OUTPUT是因为本草图控制的是 LED,这些是输出设备。我们将在项目 4 中介绍INPUT设备。
编写 loop()函数
接下来是loop()函数。普通的停车信号灯会从红色转到绿色再转到黄色,然后再回到红色,所以本项目也做到了这一点。将清单 2-3 中的代码复制到草图的loop()部分。
清单 2-3: 停车信号灯的loop()代码
void loop()
{
//red on
digitalWrite(redPin, HIGH);
digitalWrite(ylwPin, LOW);
digitalWrite(grnPin, LOW);
delay(2000);
//green on
digitalWrite(redPin, LOW);
digitalWrite(ylwPin, LOW);
digitalWrite(grnPin, HIGH);
delay(1500);
//yellow on
digitalWrite(redPin, LOW);
digitalWrite(ylwPin, HIGH);
digitalWrite(grnPin, LOW);
delay(500);
}
停车信号灯一次只有一个灯亮,以避免混乱的走廊交通并防止造成混乱。为了保持秩序,每次点亮一个 LED 时,其他 LED 应该熄灭。例如,如果你想让红灯亮起,你会调用digitalWrite(redPin, HIGH),接着调用digitalWrite(ylwPin, LOW)和digitalWrite(grnPin, LOW)。第一次调用会写入HIGH,使红色 LED 在redPin(第 13 号引脚)上点亮,另外两次调用会写入LOW,使ylwPin和grnPin(第 12 号和第 11 号引脚)上的黄色和绿色 LED 熄灭。由于 Arduino 运行在 16 MHz(大约每秒执行 1600 万次指令),这些命令之间的时间只有几微秒。因此,可以认为这三条命令几乎是同时执行的。最后,请注意delay(2000)函数。此函数会暂停草图,并保持红灯亮起 2,000 毫秒(即 2 秒),然后再执行下一组指令。
黄色和绿色 LED 的代码重复了相同的概念,将相应的引脚设置为HIGH,其余引脚设置为LOW,并延迟不同的时间。对于你自己的红绿灯项目,试着调整延迟时间,使其更符合你走廊的交通情况。记住,你传递给delay()函数的值是 LED 保持亮起的时间,单位是毫秒。
上传草图
在你输入完所有代码后,仔细检查它是否与清单 2-4 中的代码一致,保存你的草图,然后点击草图 ▸ 上传或按 CTRL-U 将其上传到你的 Arduino。如果 IDE 给出了任何错误,请仔细检查你的代码,确保它与示例代码完全一致。你的指令应当保持相同的拼写、大小写和标点符号,并且不要忘记在每条指令的末尾加上分号。
当一切正常时,你的 LED 灯应当按照类似真实红绿灯的方式循环开关——首先是红灯,接着是绿灯,然后是短暂的黄灯,最后返回到loop()函数的顶部,再次变为红灯。只要 Arduino 持续供电,你的程序将不断以这种方式运行。
清单 2-4: 红绿灯的完整代码
byte redPin = 13;
byte ylwPin = 12;
byte grnPin = 11;
void setup()
{
pinMode(redPin, OUTPUT);
pinMode(ylwPin, OUTPUT);
pinMode(grnPin, OUTPUT);
}
void loop()
{
//red on
digitalWrite(redPin, HIGH);
digitalWrite(ylwPin, LOW);
digitalWrite(grnPin, LOW);
delay(2000);
//green on
digitalWrite(redPin, LOW);
digitalWrite(ylwPin, LOW);
digitalWrite(grnPin, HIGH);
delay(1500);
//yellow on
digitalWrite(redPin, LOW);
digitalWrite(ylwPin, HIGH);
digitalWrite(grnPin, LOW);
delay(500);
}
让红绿灯变得便捷
当你的 Arduino 连接到电脑时,它是通过 USB 端口接收电源的。但如果你想移动项目或者展示它呢?你需要添加一个便携式电源——也就是电池包。Arduino 板有一个桶形插孔电源端口,可以插入电池包,并且配有一个板载电压调节器,能够接受大约 6V 到 18V 的电压。市场上有很多不同的电池适配器,但我们喜欢使用 4 节 AA 电池适配器,因为它非常适合我们的很多项目,如图 2-12 所示。
图 2-12: 配有桶形插孔适配器的 4 节 AA 电池包

拔掉 USB 线,将四节 AA 电池放入电池包中,然后将便携式电池包插入 Arduino,如图 2-13 所示。如果你的电池已充电,你就可以随意移动项目,或将它嵌入到一个模型红绿灯中!
图 2-13: 通过添加电池包使红绿灯变得便捷

现在你将提升这个项目。在接下来的部分中,我们将向你展示如何将这些 LED 变成一个可以安装在你家高流量区域的模型红绿灯。
制作红绿灯外壳
一旦你的 Arduino 不再依赖计算机,你就可以将任何电子项目制作成更为永久的外壳。面包板上的电路很好,但你可能需要发挥想象力才能将它想象成一个红绿灯。为了达到最佳效果,红绿灯只需要一个好的外壳和能让灯光从远处可见的镜片。如果你仅仅是想进行原型制作,外壳是可选的,但我们希望你尝试一下。
在这个项目中,我们将教你如何使用纸板或卡纸制作一个更逼真的红绿灯模型,但你也可以使用任何手头有的材料。发挥创造力!我们的示例,如图 2-14 所示,是由一些纸板、乒乓球和一点手工技巧制成的。
图 2-14: 用纸板和乒乓球制作的外壳

你可以单独根据这个项目的灵感制作一个红绿灯,或者如果你想完全复制这个项目,就下载www.nostarch.com/arduinoinventor/上的 ZIP 文件,包括模板和草图。书中的每个项目都附有可以打印、描绘和用工艺刀与金属尺手工切割的模板。
注意
如果你有幸可以使用像 Cricut、Silhouette Cameo 或激光切割机这样的切割机器,这些文件也应该能轻松适用于这些工具。
从 ZIP 文件中提取项目 2 文件,如果你需要切割指南,可以将红绿灯模板 PDF 按实际大小打印出来。拿到模板后,收集页面 39 中“其他材料和工具”里列出的其他物品,开始制作。
纸板制作
首先,剪下模板,如图 2-15 所示。在我们的模板中,外壳主体是一个单一的纸板片,设计为切割、压痕并折叠。
将模板描绘到纸板上,并仔细注意虚线的位置,可以用不同的颜色在纸板上标出虚线。你将沿着这些线进行压痕,所以千万不要沿着这些线切割。
图 2-15: 红绿灯外壳模板(非实际大小)

一旦你完成了所有的描绘,沿着实线使用工艺刀和金属尺剪下红绿灯的各个部分,如图 2-16 所示。如果你从未使用过工艺刀,请务必阅读页面 56 中的“安全使用工艺刀”一节。在纸板上为外壳沿每条虚线进行压痕,压痕应在纸板的外侧进行。压痕时,用工艺刀进行几次浅切(不要完全切透)。暂时不要压痕遮光罩部分。
图 2-16: 用工艺刀和金属尺沿模板划线

安全使用工艺刀
你将在本书中经常使用工艺刀,因此了解如何安全使用它们非常重要。就像任何工具一样,使用不当时,像这种工艺刀也可能导致受伤。

以下是使用工艺刀的安全小贴士:
• 切割板材时,始终拉动刀片。向其他方向推或强行操作刀片会增加刀片滑脱或断裂的风险。
• 耐心点。不要试图在一次切割中穿透整个材料的厚度。用适中的压力进行多次切割。这不仅能保护刀片,还能最终产生更干净的成品。
• 使用金属材质的直尺作为直线工具。例如金属尺。如果使用木制或塑料尺,刀片有更大的机会卡住直尺,反弹并最终朝着你的手移去。
• 保持手指远离刀片。这看起来显而易见,但意外时常发生。
• 如果你的刀开始从桌面上滚落,任其掉落,然后从地板上捡起来。如果你在刀掉落前伸手去接,可能会刺伤自己的手。哎呀!
• 最后,使用锋利、新的和完好的刀片。如果刀片断裂,请更换它。如果刀片钝了,也需要更换。切割纸张和纸板会迅速使刀片变钝。保持备用刀片,并且如果开始切割困难,及时更换刀片。
一旦你切出了纸板外壳,添加三个 LED 的安装孔;这些孔应位于大虚线圈内的小实线圆圈处。一个简单的做法是用锋利的铅笔轻轻按压纸板以打孔。然而,为了获得更干净的孔,我们建议使用 3/16 英寸的钻头和电钻在纸板上打孔,如图 2-17 所示。LED 的直径大约是 5 毫米(约 0.197 英寸)。你希望孔的大小恰到好处,因此 3/16 英寸(0.1875 英寸)的孔非常适合 LED 的紧密安装。
完成这一步时要小心,确保注意手指和手的位置,避免钻头伤到自己!如果你没有电钻或不习惯使用电钻,也可以不用电钻,手动旋转钻头穿透纸板。
图 2-17: 为 LED 打孔

一旦打好孔,将三个 LED 从面包板上取下,从纸板的背面插入,如图 2-18 所示。记住,标准的交通信号灯通常是从上到下依次排列为红、黄、绿。注意 LED 在电路板上的连接位置,因为我们最后会重新连接它们。
图 2-18: 所有三颗 LED 灯已压入纸板中

接下来,如图 2-19 所示,沿着刻痕将纸板弯曲。将垂直侧面➊朝内弯曲,然后对顶部和底部侧面➋以及标签➌进行同样操作。(侧面和标签在图 2-15 中有标注。)
图 2-19: 预折叠刻痕纸板,形成交通信号灯的外壳

将标签➌放入垂直侧面➊内,并按照图 2-20 所示将其粘合到位。你可以使用热胶、胶带或工艺胶——我们推荐使用热胶,因为它易于操作、快速固化且粘合力较强。
重复此步骤,用于上下角。你将最终得到一个浅的矩形盒子,后部保持开口。
图 2-20: 折叠和粘合纸板外壳

制作交通信号灯透镜
交通信号灯的透镜是用乒乓球切成两半制成的,但你也可以使用任何适度透明的物品。
如果你使用的是乒乓球或类似的物品,小心地将两个乒乓球切成两半。在切割时,将球放在切割垫或厚纸板上,并用指尖牢牢按住两侧。小心地将刀片推向垫子并切入乒乓球(确保刀片没有指向你或你的手),如图 2-21 所示,进行切割。旋转乒乓球并重复操作,直到完全切割穿透。确保手指远离刀片,并始终在切割垫或纸板上切割。
图 2-21: 安全切割乒乓球

一旦你有了三半个乒乓球(你将有四个,一个是多余的,可以用于未来的项目或作为你最喜欢的填充玩具的小帽子),如图 2-22 所示,用热胶将它们固定在一起。
图 2-22: 使用乒乓球作为透镜的外壳

制作阴影
最后,为交通信号灯添加阴影。为了得到漂亮的曲线,做出多条平行的刻痕,间距约为 1/8 英寸,如图 2-23 所示。模板中有刻痕示例,你可以参照这些进行操作。完成所有刻痕后,将每个阴影弯曲成曲线,如图 2-24 所示。
图 2-23: 刻痕阴影

图 2-24: 将阴影弯曲成曲线

一旦你把遮光罩弯曲并塑形到满意的样子,就可以将它们安装到每个透镜上方的外壳中,如图 2-25 所示,然后用胶水固定。如果你想要更完美或更逼真的外观,可以将外壳喷成黑色。请确保在喷漆之前,要么将透镜取下,要么用遮蔽胶带将其遮住,以免它们被喷漆覆盖。
图 2-25: 将遮光罩安装到外壳中

安装 LED 灯和 Arduino
接下来,你需要将新外壳中的 LED 灯连接到你的 Arduino 上。首先,使用两根公对母跳线(SparkFun PRT-09385)延长每个 LED 灯。你将需要六根这样的跳线。只需将每根 LED 的引脚插入跳线的母头即可。为了保持线路整洁,我们建议使用黑色线材连接负极(较短的引脚),而使用彩色线材连接正极(较长的引脚),如图 2-26 所示。
图 2-26: 连接跳线到 LED 灯

将跳线连接到 LED 灯后,将公头插入面包板中与 LED 灯所在位置相同的插孔,如图 2-27 所示。再次提醒,注意每个 LED 灯的位置。如果不记得了,可以参考原始图示图 2-10。
图 2-27: 将每根跳线的公头插入面包板

通过将 Arduino 插入电脑或电池包,检查你的连接是否正常工作。如果某个灯不亮,尝试轻轻晃动连接处,或者再检查一下跳线是否插入了面包板上的正确位置。
你可以将 Arduino 和面包板留在交通信号灯外面,或者用胶水或双面胶将它们固定在外壳内部。不管你选择哪种方式,完成后,打开你的交通信号灯,去找一个需要交通安全管理的繁忙走廊交叉口。
图 2-28 展示了完成的交通信号灯,尽显其光彩。
图 2-28: 完成的交通信号灯项目

更进一步
在制作交通信号灯的过程中,你学到的一些概念,比如控制输出(LED 灯)的定时,可以应用到你家里和生活中的许多不同场景。以下是对交通信号灯进行调整的几个建议。
改进
交通信号灯的基本概念就是定时。什么时候定时器会有用呢?比如你可以修改代码来帮助你控制煎蛋的时间?你可以将交通信号灯进行改造,当蛋的熟度还处于“生”或“半熟”时,红灯亮起;当蛋快煎熟时,黄灯亮起;蛋煎熟时,绿灯亮起。
我们不能给你具体的时间,因为我们每个人对鸡蛋烹饪的喜好不同。还有许多变量会影响烹饪时间,比如温度、锅具类型和鸡蛋的大小。你得自己摸索出合适的时间。
在代码中,你需要使用比较大的数字来设置延时,因为它是以毫秒为单位的。要设置分钟级别的延时,你只需要做一点乘法运算。记住,1,000 毫秒等于 1 秒;再乘以 60,你就能得出 60,000 毫秒等于 60 秒,或者 1 分钟。对于 3 分钟的延时,你可以直接在delay()函数中将 3 乘以 60,000,如下所示:
delay(60,000 * 3);
你可能会想知道delay()函数的最大延时时间是多少。delay()接收的数据类型是无符号长整型,它的值范围是从 0 到 4,294,967,295。所以最大延时大约是 1,193 小时。相当酷吧!知道这一点后,你是否想到其它用delay()函数可以计时的事情?
修改
如果你想让这个项目更加持久和坚固,你可以选择将导线焊接到 LED 上,而不是使用公对母跳线。如果你从未焊接过,可以参考第 302 页的“如何焊接”部分,在开始前学习一些焊接的基本技巧。你需要剪掉一根公对公跳线的末端,使用剥线钳将绝缘层剥离大约 1/2 英寸,然后将剥去的部分焊接到修剪过的 LED 的每个引脚上,正如图 2-29 所示。注意,我们在焊接时将导线绕在 LED 的引脚上,以确保它牢固。焊接后,连接会更加耐用,而且你还可以在其他项目中使用这些 LED,因为另一端仍然是公头跳线。
图 2-29: 将切割的跳线焊接到 LED 上

尽管这个项目看起来很令人印象深刻,但编程和硬件其实非常简单。在接下来的几章中,阅读关于传感器和逻辑的内容时,我们鼓励你回顾这个项目,并思考如何利用你所学的内容对其进行扩展。
第四章:3 九像素动画机
我们每天都在使用显示器,无论是在手机、电脑、平板还是电视上。大多数现代显示器的显示屏由数百万个像素(即图像元素)组成。像素是计算机可以用不同颜色点亮的微小点;所有像素共同组成了屏幕上的文字、图像和视频。
在这个项目中,你将使用 LED 构建一个简单的显示器。你将扩展之前关于闪烁 LED 的工作,并学习在 Arduino 中使用自定义函数。最后,你将学习如何在你自己的九像素动画机上显示秘密字符。你可以在图 3-1 中看到我们的设备。
图 3-1: 完成的九像素动画机

你可以使用九像素动画机显示字母和数字,绘制基本的几何图形,制作其他有趣的像素艺术。
需要准备的材料
在这个项目中,你需要比项目 2 更多的电子元件,尤其是更多的 LED。然而,这个项目在外壳构建方面更简单。你需要的材料如图 3-2 和图 3-3 所示。
注意
在我们的项目中,所有 LED 的颜色相同,因此图案更容易看清。如果你没有九个相同颜色的 LED,可以混合使用它们。
电子元件
• 一块 SparkFun RedBoard(DEV-13975)、Arduino Uno(DEV-11021)或任何其他兼容 Arduino 的板子
• 一条 USB Mini-B 电缆(CAB-11301 或你的板子自带的 USB 电缆;未显示)
• 一块无焊接面包板(PRT-12002)
• 九个 LED,最好是相同颜色的(COM-10049,一包 20 个红色和黄色 LED)
• 九个 330Ω电阻(COM-11507,一包 20 个)
• 公对公跳线(PRT-11026)
• 公对母跳线(PRT-09140*)
• (可选)一个 4 个 AA 电池的电池架(PRT-09835*;未显示)
注意
带有星号()标记的部分不包含在标准的 SparkFun 发明者套件中,但可以通过单独的附加套件获得。*
图 3-2: 九像素动画机的组件

其他材料和工具
• 铅笔
• 工艺刀
• 金属直尺
• 剪线钳
• 胶水(热熔胶枪或工艺胶水)
• 方格纸(未显示)
• (可选)电钻和 3/16 英寸的钻头
• (可选)电烙铁
• (可选)焊锡
• (可选)辅助工具(未显示)
• 一张纸板(大约 8 × 11 英寸,或 20.5 × 30 厘米;未显示)
• 外壳模板(请参见图 3-13 在 83 页)
图 3-3: 推荐工具

构建九像素动画机原型
这个简单的像素艺术显示器将教你如何在一个电路中管理大量的电线,这对于电路变得越来越复杂时非常重要。首先,你将使用面包板确保电路正常工作,测试一个草图,并熟悉所有的跳线。(我们将在 “纸板结构” 第 83 页中向你展示如何将 LED 转移到显示外壳中。)请注意,图 3-4 中的电路图与第 43 页的 图 2-6 非常相似。原因是这个项目使用的是相同的 LED 电路,不过它不仅仅使用三个 LED,而是使用九个 LED,每个 LED 都由 Arduino 的一个引脚独立控制。
图 3-4: 九像素动画机的原理图

在本节中,你将使用面包板将所有九个 LED 连接到 Arduino 的引脚上。准备好你的元件和跳线后,在面包板上搭建 图 3-5 中的电路。如果你想先练习搭建更小的 LED 电路,可以翻回 “将红色 LED 连接到面包板” 第 44 页,复习一下。
图 3-5: 九个 LED 灯连接到 Arduino,其中引脚 13 的 LED 位于顶部,引脚 5 的 LED 位于底部

接线九个 LED 可能会让面包板显得杂乱无章。为了保持面包板的整洁,首先将面包板左侧的地线(–)连接到 Arduino 的 GND 引脚。这就是 图 3-5 中的黑色线。然后,将第一个 LED 的负极(较短的一端)通过一个 330 Ω 电阻连接到这个地线。在 图 3-5 中,LED 的长腿位于第 1 行,短腿位于第 2 行。最后,将 LED 的长腿用一根跳线从 Arduino 的引脚 13 连接到面包板的第 1 行。以相同的方式,将其他八个 LED 连接到引脚 12 到引脚 5。记住:LED 的短腿是负极。在搭建电路时,确保每个 LED 的短腿通过电阻连接到面包板的地线。当你完成后,你的电路应该像 图 3-6 中的电路一样。
图 3-6: 最终原型电路,九个 LED 灯连接到 Arduino,其中引脚 13 的 LED 位于顶部,引脚 5 的 LED 位于底部

一旦你接好九个 LED,打开 Arduino IDE,并使用 USB 数据线将 Arduino 连接到计算机。如果你在之前的项目中已经将草图上传到 Arduino,你可能会看到 LED 灯亮起,因为它在运行你最后上传的草图。现在,让我们来看看如何编写代码控制这九个 LED。
编程九像素动画机
在以前的项目中,使用一堆digitalWrite()函数和delay()函数来控制 LED 是相对简单的。但如果有九个 LED,你的loop()函数就会变得非常混乱!相反,你可以编写自己的自定义函数来闪烁一个 LED,然后使用这个函数来控制所有的 LED。
什么是自定义函数?
Arduino 语言大约有 60 个 内置(或预定义)函数,它们通过简单的单行指令使你更容易与硬件进行交互。digitalWrite()和delay()函数就是两个例子。在幕后,digitalWrite()函数由 20 多行代码组成。尽管大部分代码比较复杂,但digitalWrite()函数本身很容易理解。
即使你理解了一个大型草图,每次想要打开或关闭一个 LED 时,输入 20 行或更多的代码仍然是繁琐且容易出错的。Arduino 内置的函数可以处理常见任务,但当你需要对草图进行特定操作时,你会希望编写自定义函数。自定义函数可以让你轻松地在其他草图中重用代码,并且它们能让你的loop()函数更易于阅读。
编写自定义函数
你可以使用自定义函数来教 Arduino 新的命令。你的第一个测试函数将使一个 LED 闪烁,先点亮再熄灭,使用修改版的清单 3-1 中的代码。
清单 3-1: 一个简单的草图,用于闪烁 LED
void setup()
{
pinMode(13, OUTPUT);
}
void loop()
{
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);
delay(1000);
}
这段代码应该与项目 1 中的 Blink 示例类似(清单 1-1 在第 28 页)。然而,在这里我们明确使用引脚 13,而不是使用LED_BUILTIN系统常量。这段代码将 LED 点亮,等待一秒钟,再将 LED 熄灭,然后等待另一秒钟后再次执行。你将在以后的项目中频繁使用此代码,因此我们将展示如何将这段代码放入自定义的blink()函数中。为了使你的自定义函数尽可能有用,你将以一种允许你为任何引脚和任何延迟时间使用该函数的方式来编写它。
首先,打开一个新的草图,将清单 3-1 中的代码复制进去并保存。然后,在setup()和loop()函数下方定义blink()函数,如清单 3-2 所示。
清单 3-2: 自定义blink()函数的框架
➊void ➋blink(➌int pinNumber, int delayTime)
{
//custom function code goes here
}
这只是函数的框架。函数定义总是首先指定函数将返回的数据类型➊。blink()函数要求 Arduino 执行一个任务而不期待任何数据返回,因此,和setup()与loop()函数一样,它的数据类型是void。
接下来是函数的名称 ➋,在这个例子中是blink。你几乎可以随意给 Arduino 函数命名,但它们不能以数字开头,也不能包含空格或特殊字符。此外,为了确保在阅读草图时函数的用途清晰明了,我们建议使用一个既具有描述性又容易记住的名称。
命名函数后,在括号中定义函数所需的参数 ➌。为了使blink()函数尽可能多次使用,提供一个引脚号和延迟时间作为参数。这样你就可以指定哪个 LED 要闪烁,以及闪烁的时长。每个blink()函数的参数都是int类型。注意,定义参数类似于声明变量。因为参数本质上是只能在函数内使用的变量。
最后,自定义函数有自己的一组花括号,它们包含了你希望函数调用代表的所有代码。对于blink()函数来说,这就是示例 3-1 中的digitalWrite()和delay()函数,如示例 3-3 所示。现在把代码加到blink()函数的花括号内吧。
示例 3-3: 自定义blink()函数
void blink(int pinNumber, int delayTime)
{
digitalWrite(pinNumber, HIGH);
delay(delayTime);
digitalWrite(pinNumber, LOW);
delay(delayTime);
}
请注意,在digitalWrite()和delay()函数调用中,blink()函数分别用pinNumber和delayTime参数替代了引脚号13和延迟1000毫秒。
使用自定义函数
现在,你可以在loop()函数中使用你的自定义函数,像在示例 3-4 中那样。
示例 3-4: 使用新自定义的blink()函数完成的草图
void setup()
{
pinMode(13, OUTPUT);
}
void loop()
{
blink(13, 1000);
}
void blink(int pinNumber, int delayTime)
{
digitalWrite(pinNumber, HIGH);
delay(delayTime);
digitalWrite(pinNumber, LOW);
delay(delayTime);
}
修改后的loop()函数调用了blink()函数,并传入了要闪烁的 LED 的引脚号(13)以及灯光应该保持开启或关闭的时间,单位为毫秒(建议使用1000,即 1 秒)。就是这样!这个代码是不是看起来更简洁、清晰?
将这个草图上传到你的 Arduino,13 号引脚上的 LED 应该会开始闪烁。恭喜!你刚刚“教会”了 Arduino 什么是blink(),并将四行代码浓缩成了一条指令。
但是,自定义函数只是这个项目的一个关键。你还需要提前规划一些九像素的图案,以便更容易编程,现在就让我们来做这件事。
试试看:玩转图案
在完成九像素动画机之前,花些时间好好玩玩你更强大的blink()草图。例如,将你的loop()函数修改如下:
void loop()
{
blink(13, 100); //short 100 ms blink on pin 13
blink(13, 2000); //longer 2000 ms blink on pin 13
}
这将使 LED 闪烁 100 毫秒,然后进行一次较长的闪烁,时间为 2000 毫秒。试着闪烁其他 LED,创造属于你自己的图案和序列!
设计你的艺术作品
拿出彩色铅笔和方格纸,戴上你的艺术家帽子!你将要制作一些像素艺术,展示在九像素动画机上。
首先画几个 3 × 3 的网格,或者打印出图 3-7 中显示的模板,你可以在本书的资源文件中找到该模板。这些图形不必完美;你只是在草绘一些想法。
图 3-7: 空白网格规划模板

我们在左上角标记了 13 号像素,在右下角标记了 5 号像素。这些数字对应 Arduino 上的 LED 引脚编号;这就是你如何控制九像素动画机中的 LED。当你的画布准备好后,发挥创造力:填充像素以制作自己的图案。图 3-8 展示了我们在部门会议中想出的几个例子……别告诉我们老板!
图 3-8: 一些像素图案示例

当你按顺序显示这些图案和形状时,就可以创建动画了!首先,我们将教你编程两个简单的形状,然后我们将处理一个完整的动画。
测试草图
在 Arduino IDE 中创建一个新的草图,并添加清单 3-5 中的setup()和loop()函数。
清单 3-5: 九像素动画机的setup()函数
//LED array is set up in this arrangement:
// 13 ---- 12 ---- 11
// 10 ---- 9 ----- 8
// 7 ---- 6 ----- 5
void setup()
{
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
pinMode(9, OUTPUT);
pinMode(8, OUTPUT);
pinMode(7, OUTPUT);
pinMode(6, OUTPUT);
pinMode(5, OUTPUT);
}
void loop()
{
}
注意
我们还建议添加一段带有 LED 布置图的注释。在草图中描述你正在编写代码的电路,将帮助其他人重现它,并且在你开发代码时,也能提醒你自己结构。
本项目使用九个 LED,连接到九个数字 GPIO 引脚(从 13 到 5),因此setup()函数中有九个pinMode()函数。它们都被设置为OUTPUT,因为它们控制 LED。
编写一个绘制 X 的函数
在设置好pinMode()函数后,看看你手绘的图形并注意数字;它们会帮助你编写自定义函数。图 3-9 展示了一个可以测试的例子——一个简单的X图案。
图 3-9: 九像素X

图 3-9 中的X图案对应清单 3-6 中的digitalWrite()函数集合。
清单 3-6: 在九个 LED 上显示X的代码
digitalWrite(13, HIGH);
digitalWrite(12, LOW);
digitalWrite(11, HIGH);
digitalWrite(10, LOW);
digitalWrite(9, HIGH);
digitalWrite(8, LOW);
digitalWrite(7, HIGH);
digitalWrite(6, LOW);
digitalWrite(5, HIGH);
这些digitalWrite()函数调用仅打开网格对角线上的 LED,并关闭其余的 LED。但绘制一个单独的X图案需要九行代码!每次手动编写这些代码太麻烦,你可以创建一个自定义函数,用一行代码执行所有这些调用。
在loop()函数的花括号下面,创建一个名为xChar()的自定义函数,并使用清单 3-7 中的代码。
清单 3-7: xChar() 自定义函数
void xChar()
{
digitalWrite(13, HIGH);
digitalWrite(12, LOW);
digitalWrite(11, HIGH);
digitalWrite(10, LOW);
digitalWrite(9, HIGH);
digitalWrite(8, LOW);
digitalWrite(7, HIGH);
digitalWrite(6, LOW);
digitalWrite(5, HIGH);
}
我们将这个自定义函数命名为xChar(),因为它显示了一个X字符。这个函数不会返回任何内容,所以它的数据类型是void。由于列表 3-6 中的digitalWrite()调用都在这个自定义函数内,你可以保持loop()代码简洁。在现有的loop()中调用xChar()函数,如列表 3-8 所示。
列表 3-8: 包含xChar()函数调用的loop()函数
void loop()
{
xChar();
}
这是一个小步骤,但非常重要。如果你忘记调用自定义函数,它的代码将永远不会运行,你的X字符也无法显示。
现在将这个草图上传到你的 Arduino 中。虽然你的 LED 依然排列成垂直线,而不是网格,但这个草图仍然可以帮助你测试电路和代码是否正确。你应该看到每隔一个 LED 点亮,从顶部开始,如图 3-10 所示。如果 LED 没有按预期点亮,请再次检查与图 3-5 中的电路图(第 71 页)对比你的接线,并确保所有的digitalWrite()函数都正确。
当你看到正确的图案时,继续创建第二个字符。
图 3-10: X字符的原型和正确的顺序

编写一个绘制 O 字符的函数
接下来,你将创建一个像图 3-11 中的O,以配合X字符。
图 3-11: 九像素的O

专业提示:你可以通过更聪明的方式工作,而不是更辛苦。复制整个xChar()函数,将副本粘贴到xChar()的最后一个大括号之后,将其名称改为oChar(),并调整其使之像列表 3-9 中的样子。
列表 3-9: oChar()自定义函数
void oChar()
{
digitalWrite(13, HIGH);
digitalWrite(12, HIGH);
digitalWrite(11, HIGH);
digitalWrite(10, HIGH);
digitalWrite(9, LOW);
digitalWrite(8, HIGH);
digitalWrite(7, HIGH);
digitalWrite(6, HIGH);
digitalWrite(5, HIGH);
}
xChar()和oChar()之间唯一的区别在于哪些 LED 被点亮,哪些 LED 熄灭。xChar()会点亮交替的 LED,而oChar()则点亮除了中心 LED 以外的所有 LED。
动手尝试:为你的图像编写一个自定义函数
我们向你展示了如何编写绘制X和O的函数,但我们相信你脑海中一定有自己喜欢的像素艺术图案。创建一个函数来展示你制作的图案,并将其保存,以便在完成九像素动画机时使用。
显示 X 和 O 字符
现在的目标是先显示一个X字符,然后显示一个O字符,最后再返回到X。为了在固定的时间内显示每个字符,你可以将oChar()函数添加到现有的循环中,并使用delay()函数减慢循环速度。更新你的草图,使其像列表 3-10 一样。
列表 3-10: loop()函数看起来类似于 Blink 示例中的函数,但使用了xChar()和oChar()代替了digitalWrite()。
//LED array is set up in this arrangement:
// 13 ---- 12 ---- 11
// 10 ---- 9 ----- 8
// 7 ---- 6 ----- 5
void setup()
{
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
pinMode(9, OUTPUT);
pinMode(8, OUTPUT);
pinMode(7, OUTPUT);
pinMode(6, OUTPUT);
pinMode(5, OUTPUT);
}
void loop()
{
//blink between x and o characters
xChar();
delay(500);
oChar();
delay(500);
}
void xChar()
{
digitalWrite(13, HIGH);
digitalWrite(12, LOW);
digitalWrite(11, HIGH);
digitalWrite(10, LOW);
digitalWrite(9, HIGH);
digitalWrite(8, LOW);
digitalWrite(7, HIGH);
digitalWrite(6, LOW);
digitalWrite(5, HIGH);
}
void oChar()
{
digitalWrite(13, HIGH);
digitalWrite(12, HIGH);
digitalWrite(11, HIGH);
digitalWrite(10, HIGH);
digitalWrite(9, LOW);
digitalWrite(8, HIGH);
digitalWrite(7, HIGH);
digitalWrite(6, HIGH);
digitalWrite(5, HIGH);
}
这个循环会显示一个X,持续 500 毫秒,然后切换为一个O,也持续 500 毫秒。将更新后的草图上传到你的 Arduino,并运行它,看看它是如何工作的。每个 LED 灯(除了中间的那个)都会在调用oChar()时亮起。图 3-12 展示了当 LED 灯闪烁时你会看到的效果。
图 3-12: 在两种图案之间切换

现在保存你的草图,因为稍后你会在其基础上进行扩展。但只要这个电路中的 LED 灯仍然在面包板上,它们就不会显示任何可识别的图像。所以接下来,我们将向你展示如何制作显示器,让你能够看到九像素动画机的微小辉煌。
构建九像素动画机外壳
这个项目的外壳只是一个带有 LED 灯孔的硬纸板显示器。还需要进行一些接线,但一旦完成,你就可以制作各种像素艺术了。
硬纸板构建
找一张干净且没有折痕和弯曲的硬纸板。我们的设计是基于大约 1/8 英寸厚的硬纸板,但你也可以使用任何类似的板材或面板材料。有些材料比其他材料更容易切割,因此可以根据你拥有的工具选择适合的材料。
切割零件
打开本书资源文件中显示的图 3-13 模板,并将其描绘到你的硬纸板上。尽量将模板与硬纸板的边缘对齐,以便更容易切割。
图 3-13: 九像素动画机外壳模板(非实际尺寸)

一旦你描绘好了零件,开始切割它们。我们强烈推荐使用锋利的手工刀和金属尺,以确保切割边缘整齐。记住手工刀的安全:总是拉刀(不要推),并且多次轻轻切割,而不是第一次就用力深切。
切割好硬纸板零件后,在前面板上打上 LED 灯孔。你可以按照图 3-14 中的示意图进行钻孔,使用孔打孔器,甚至用铅笔戳出来。只需确保手边有一个 LED 灯来测试每个孔的大小,确保它们能合适地安装。如果孔稍微大了一点,而且你不介意将 LED 灯固定在项目上,你可以使用热胶将它们固定。
图 3-14: 为 LED 灯钻孔。钻孔时请小心,或寻求成人的帮助。

你应该已经切割出了四个零件,如图 3-15 所示。底座中间有一个大孔;使用其中心部件剪切出两个三角形,作为支撑支架。这些三角形有卡槽,可以将底部零件和前部零件连接起来。然而,在组装零件之前,请添加标签,以便在连接电路时能保持 LED 灯的位置正确。
图 3-15: 外壳的硬纸板零件

标记 LED 灯孔
翻转前面板纸板,并为每个 LED 编号,以便有一个连接指南。从右上角的 13 引脚开始,然后依次向左数,如 图 3-16 所示。你应该在左下角完成编号,最终编号为 5。
图 3-16: 标记 LED 网格的背面

添加 LED
将纸板平放,从背面插入九个 LED。你可以重用面包板原型中的 LED,或者使用新的 LED。当插入 LED 时,确保它们的长脚对齐到右侧,这样在稍后将它们重新接到 Arduino 时会更方便。你希望 LED 紧密地装入孔中,如 图 3-17 所示。如果孔太大,你可以加一点热熔胶将其固定(但请记住,之后你将无法再次使用这些 LED)。
图 3-17: 插入 LED

组装零件
现在,收集待组装的四个纸板部件。你可能需要使用工艺胶水或热熔胶枪将所有部件粘合在一起。
首先,将一个三角形粘到基座上,以支撑前面板,如 图 3-18 所示。请参见 图 3-19 了解支撑三角形的方向。对另一个三角形部件重复此过程。请给胶水一些时间干燥后再继续下一步。
图 3-18: 将支撑三角形添加到基座上

将支撑三角形放置好后,使用胶水将前面板粘到基座上。前面板应紧密地贴合支撑三角形的卡口,并安放在基座纸板的上方,如 图 3-19 所示。为了增强强度,你还可以在前面板连接基座和支撑三角形的内侧边缘处加上热熔胶。
图 3-19: 添加项目的最后一块——前面板

在完成纸板构建部分后,现在是时候接线了。
连接电子元件
这个项目有很多接线,我们将一步一步来。你将重用本章早些时候构建的面包板原型电路,只需使用跳线将九个 LED 连接到 Arduino 上。
这个部分有两种方式来进行:非永久方式,使用公对母跳线,和永久方式,涉及到焊接。我们将介绍非永久方式,但如果你确实想把 LED 焊接到跳线,可以参考 “如何焊接” 页码 302 上的简短教程,再尝试焊接。
如果 LED 的引脚太长,你可以在连接跳线之前将它们剪短,但要注意哪个引脚是正极(较长的引脚)哪个是负极。把正极引脚剪得稍长一点,这样你还能辨别正负引脚;你也可以在盒子背面画个点。记得佩戴护目镜——修剪引脚时,小的金属片可能会飞起来,向你的眼睛飞来!
将每根跳线的母头连接到前面板上的九个 LED 之一。为了保持组织性,使用黑色导线指定负极,并将其连接到 LED 的较短腿,如图 3-20 所示。每个 LED 的正极可以使用任何颜色的导线。
图 3-20: 使用公对母跳线连接 LED

当所有九个 LED 连接到公对母跳线后,将每根跳线的另一端连接到面包板,按照项目背面标注的引脚标签进行连接。如果你之前已经把 LED 插入了面包板,先把它们取下来,然后像图 3-21 中所示的那样插入跳线。
图 3-21: 使用公对母跳线将九个 LED 连接到面包板

每条连接到 LED 负极腿的导线(每条黑色导线)连接到一个与接地轨道相连的电阻器。每条正极导线连接到 LED 阵列背面标注的 Arduino 引脚。
一旦你把所有九个 LED 添加到显示器的前面并完成接线,将 Arduino 通过 USB 电缆连接到电脑。如果所有的接线正确,你的显示器将显示交替的X和O图案。如果你有电池组,可以按照图 3-22 中的示意图连接它。
如果测试图像显示不正常,检查 LED 是否插入到正确的 Arduino 引脚。当你看到正确的图案时,稍作停留,享受你新像素艺术显示器的辉煌,但不要停在这里!当你准备好时,试着制作一个更复杂的动画。
图 3-22: 显示交替的X和O字符的最终显示效果

创建一个 LED 动画
你的显示器可以显示任何你能在 3×3 网格上绘制的图像。动画其实就是一系列按顺序显示的图像,所以如果你连续显示一堆 3×3 的图像,你就会得到一个像素艺术动画。我们将教你如何设计和显示一个旋转的线条。
规划动画序列
让我们从将一个旋转的线条转化为一系列图像开始。我们从一条垂直线开始,并将其在显示器上旋转,分成四个独立的图像,如图 3-23 所示。
图 3-23: 旋转线条的图像进展

保存 Listing 3-10 中的程序,然后创建一个新的草图。将 Listing 3-11 中的 setup() 和 loop() 函数添加到新的草图中。
LISTING 3-11: 所有九个 LED 的 setup() 代码
void setup()
{
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
pinMode(9, OUTPUT);
pinMode(8, OUTPUT);
pinMode(7, OUTPUT);
pinMode(6, OUTPUT);
pinMode(5, OUTPUT);
}
void loop()
{
//animation function call will go here
}
//custom functions to show frames will go here
由于你使用的是之前在 X 和 O 草图中使用的相同 LED,你可以将那里代码复制到新的草图中。
编写自定义函数
现在,为你的动画中的每一帧创建一个自定义函数。这个动画有四帧,因此你需要四个函数。在 loop() 函数的闭括号之后,将 Listing 3-12 中的函数添加到你的草图中。
LISTING 3-12: 用于绘制旋转线的自定义函数
➊ void verticalLine()
{
digitalWrite(13, LOW);
digitalWrite(12, HIGH);
digitalWrite(11, LOW);
digitalWrite(10, LOW);
digitalWrite(9, HIGH);
digitalWrite(8, LOW);
digitalWrite(7, LOW);
digitalWrite(6, HIGH);
digitalWrite(5, LOW);
}
➋ void topLeftDiagonal()
{
digitalWrite(13, HIGH);
digitalWrite(12, LOW);
digitalWrite(11, LOW);
digitalWrite(10, LOW);
digitalWrite(9, HIGH);
digitalWrite(8, LOW);
digitalWrite(7, LOW);
digitalWrite(6, LOW);
digitalWrite(5, HIGH);
}
➌ void horizontalLine()
{
digitalWrite(13, LOW);
digitalWrite(12, LOW);
digitalWrite(11, LOW);
digitalWrite(10, HIGH);
digitalWrite(9, HIGH);
digitalWrite(8, HIGH);
digitalWrite(7, LOW);
digitalWrite(6, LOW);
digitalWrite(5, LOW);
}
➍ void topRightDiagonal()
{
digitalWrite(13, LOW);
digitalWrite(12, LOW);
digitalWrite(11, HIGH);
digitalWrite(10, LOW);
digitalWrite(9, HIGH);
digitalWrite(8, LOW);
digitalWrite(7, HIGH);
digitalWrite(6, LOW);
digitalWrite(5, LOW);
}
verticalLine() 函数 ➊ 显示 图 3-23 中的第一张图,topLeftDiagonal() 函数 ➋ 显示第二张图,horizontalLine() 函数 ➌ 显示第三张图,topRightDiagonal() 函数 ➍ 显示最后一张。和你之前的自定义函数一样,这些自定义函数的返回类型是 void,因为它们不会返回值。
自定义函数也可以调用其他自定义函数,所以我们可以在一个 spinningLine() 函数中调用四个线条函数。将以下代码添加到你的草图中,放在 topRightDiagonal() 函数的闭括号后面。
void spinningLine(int delayTime)
{
verticalLine();
delay(delayTime);
topLeftDiagonal();
delay(delayTime);
horizontalLine();
delay(delayTime);
topRightDiagonal();
delay(delayTime);
}
这段代码展示了垂直线、对角线、水平线和另一条对角线,每条线之间都有延迟。现在,你只需在 loop() 函数内调用 spinningLine() 即可。
注意
你可以在资源文件中找到完整的代码列表,访问地址为 nostarch.com/arduinoinventor/。
调整你的 loop() 函数
在 loop() 函数内添加一个对自定义函数的调用,如 Listing 3-13 中所示。记住,你仍然需要在 setup() 函数中包含所有的 pinMode() 命令。
LISTING 3-13: 完整的 loop() 函数,带有新的自定义函数调用 spinningLine(200);
void loop()
{
spinningLine(200);
}
一旦你添加了函数并传递了延迟时间参数(代码使用的是 200),将你的草图上传到 Arduino。你将看到显示器上旋转的线条。通过了解如何按顺序控制 LED 和使用自定义函数,你可以制作自己的九像素动画!
深入了解
自定义函数将在你以后需要重用代码或组织代码时非常有用。要进一步推进这个项目,尝试设计更复杂的动画;你甚至可以设计自己的字母表,并利用显示器展示一个秘密信息。
技巧
要进一步推进这个项目,首先创建更复杂的动画。在接下来的几个项目中,尝试寻找将显示器整合到项目中的方法——例如,或许你可以使用传感器来控制动画速度,或者以有趣的方式显示传感器值。你可以在 www.nostarch.com/arduinoinventor/ 下载一个空白设计模板。
修改
你已经学会了如何通过使用数字引脚和自定义函数来控制多个电子元件。试着将你的单独 LED 灯换成不同的组件。我们建议使用七段显示器,如图 3-24 所示。
图 3-24: 单个七段显示器

每个段都是一个你可以控制的 LED 灯。有七个独立段(加上小数点),如图 3-25 所示,通过打开和关闭特定的段,你可以显示数字和大多数英文字母。
图 3-25: 七个独立段和小数点的示意图及相应的接线图

你可以像控制九像素动画机一样控制这些显示器:只需为每个数字创建自定义函数。为了挑战自己,创建一个函数,允许你传递一个数字并显示它。
第五章:4 反应计时器
一般来说,人的视觉反应时间大约为 215 毫秒。也就是说,从你看到信号(如灯光打开)到信号传递到大脑,再到肢体反应的时间。反应计时器是一个很好的项目来展示这种时间延迟,同时它也是一个有趣的游戏!你和你的朋友反应有多快?
在这一章,你将学习如何使用 Arduino 制作自己的反应计时器。完整的项目展示在图 4-1 中。其背后的概念很简单:Arduino 会点亮一个 LED,启动计时器,并等待你按下按钮。当你看到 LED 亮起时,你尽快按下按钮,Arduino 会将从灯光亮起到你按下按钮之间的时间反馈给你的计算机。Arduino 的时钟速度为 16 MHz,意味着它每秒可以处理 1600 万个指令!这可是非常快的,因此 Arduino 非常适合这个项目。
图 4-1: 完整的反应计时器项目

所需材料
像本书中的前几个项目一样,反应计时器使用了 LED、 电阻、电线和 Arduino。与其他项目不同的是,这个项目还包括了一个按钮来使游戏更加互动,并且为了让外观更加炫酷,我们建议使用定制的纸板外壳。图 4-2 和 4-3 展示了你需要的零件和材料。
电子元件
• 一块 SparkFun RedBoard(DEV-13975)、Arduino Uno(DEV-11021)或任何兼容 Arduino 的开发板
• 一根 USB Mini-B 数据线(CAB-11301,或你使用的开发板的 USB 线)
• 一块无焊面包板(PRT-12002)
• 一个红色 LED,一个蓝色 LED 和一个绿色 LED(COM-12062)
• 三个 330 Ω电阻(COM-08377,或者 COM-11507,一包 20 个)
• 一个 10 kΩ电阻(COM-08374,或者 COM-11508,一包 20 个)
• 一个按钮(COM-10302)
• 公对公跳线(PRT-11026)
• 公对母跳线(PRT-09140*)
注意
标有星号()的零件不包含在标准 SparkFun 发明者工具包中,但可以在单独的附加工具包中找到。*
图 4-2: 反应计时器的组件和材料

其他材料与工具
• 铅笔
• 工艺刀
• 金属尺
• 胶水(热熔胶枪或工艺胶水)
• (可选)电钻以及 3/16 英寸和 5/16 英寸钻头
• (可选)电线剪(未显示)
• 纸板(大约 12 英寸见方)或一个纸箱
• 外壳模板(见图 4-16,第 115 页)
• (可选)乒乓球
图 4-3: 推荐的工具

新组件:按钮
本项目围绕两个组件:LED 和按钮。按钮开关是 Arduino 引脚的输入,这意味着该草图可以对该引脚上的电压变化作出反应。像按钮这样的输入设备让你可以创建人们可以互动的电路。
按钮如何工作
有很多种按钮,但它们的工作原理都类似。按钮实际上是一个电气开关。像图 4-4 中显示的按钮是小型的弹簧加载设备,当你施加压力时,它将两个端口电连接起来,类似于你键盘上的按键。而且它们无处不在——遥控器、车库门开门器、咖啡机、收音机、游戏手柄等!
图 4-4: 各种按钮

这些巧妙的输入设备内部其实非常简单。图 4-5 展示了按钮和开关的示意图。
图 4-5: 按钮和开关的示意图

当你翻动开关时,内部的一块金属闭合了两个接触点之间的间隙,就像一个门一样。当你按下按钮时,金属直线向下推动,桥接了这个间隙。找到这个项目所需的按钮,并仔细检查它。即使图 4-5 中的示意图仅显示了两个接触点,绝大多数面包板用的标准按钮都有四根引脚。图 4-6 则展示了更准确的内部接触点示意图,并且展示了类似按钮在面包板上的样子。当你将按钮插入时,引脚应该跨越面包板中间的槽口。
图 4-6: 按钮示意图和按钮正确放置在面包板上,脚插入槽口两侧

按钮在项目中是非常好的输入设备,因为每个人都知道它们是如何工作的。按钮连接到 Arduino 电路也相对简单。让我们看看它是如何工作的。
与按钮一起使用电阻
要将任何按钮用作 Arduino 的输入,你需要使用像图 4-7 中的上拉电阻电路。上拉电阻的一端连接到电源,另一端连接到输入组件(如按钮)。需要检测输入的电路部分连接在电阻和按钮的交点处。
在图 4-7 所示的配置中,电阻将 5 V 拉到 Arduino 引脚的默认电压 升高 到 5 V,这被认为是 HIGH。当按钮被按下时,会在 Arduino 引脚和地之间创建一个通路,Arduino 引脚读取 LOW 电压。之所以有效,是因为电流总是沿着电阻最小的路径流动:当按钮没有按下时,Arduino 引脚上的 10 kΩ 电阻是电流唯一能通过的路径,而当按钮 按下 时,它提供了一个几乎没有电阻的路径。
图 4-7: 上拉电阻和按钮电路

构建反应计时器原型
反应计时器结合了类似于前面项目中的 LED 电路和图 4-7 中的按钮电路,组成了图 4-8 中的超级电路,点亮 LED 并检测按钮按压。
图 4-8: 反应计时器原型的电路图

拿出你的面包板,按照图 4-9 和 4-10 所示,接一个 LED 和一个按钮。你将使用这个原型来测试你的代码,然后再构建最终的反应计时器。
图 4-9: 反应计时器电路接线图

图 4-10: 带有单个按钮和单个 LED 的反应计时器最终原型电路

在连接这个电路时,请注意两种不同的电阻值:LED 为 330 Ω,按钮为 10 kΩ。(有关如何根据颜色带确定电阻值的详细信息,请参见“电阻器和色带”第 308 页。)LED 上的电阻是限流电阻,应连接到地,而按钮上的电阻是上拉电阻,将引脚 3 连接到 5 V。但没有代码,你的电路什么也做不了,现在我们来看一下代码。
编程反应计时器
随着草图和电路的复杂化,你会发现按顺序列出 Arduino 要执行的每个动作会有助于整理思路。一些程序员把这样的列表称为伪代码。以下是我们为反应计时器编写的伪代码:
-
在打开 LED 之前,等待一个随机时间(以防预测/作弊反应计时器)。
-
打开 LED。
-
记录起始时间。
-
启动计时器,等待按钮按下。
-
按下按钮时,计算反应时间,即计时器值减去起始时间。
-
返回时间。
很简单,对吧?我们打开 Arduino,看看草图。
编写 setup() 函数
打开一个新的草图,输入初始化和setup()代码,如清单 4-1 所示。
清单 4-1: 反应计时器的setup()和初始化代码
➊ unsigned int waitTime; //random wait time before
//turning on LED
unsigned int startTime; //zero reference time
unsigned int reactTime; //calculated reaction time
void setup()
{
➋ **Serial**.begin(9600); //sets up serial
//communication
pinMode(13, OUTPUT); //sets pin 13 as an OUTPUT for the
//stimulus LED
➌ pinMode(3, INPUT); //sets pin 3 as an INPUT for the
//button
}
注意
unsigned int 数据类型可以存储从 0 到 65,535(2¹⁶ – 1)的值。
首先,命名空间定义了三个unsigned int变量 ➊,用来存储waitTime、startTime和reactTime值。
接下来是setup()函数,它有一个新指令:Serial.begin(9600) ➋。确保将Serial大写,并且Serial、句点和begin之间没有空格。这个指令与之前的命令略有不同,因为它有一个句点来分隔对象和方法。对象是计算机编程中的一个概念,类似于一种特殊类型的变量,可以执行不同的功能或操作。这里的Serial是我们正在使用的对象名称。一个对象可以执行的功能叫做方法。begin()方法初始化或启动串口通信,使得 Arduino 和计算机之间能够通过 USB 线进行数据传输。对于这个命令,括号中的数字9600设置了通信速率为 9,600 位每秒(或波特率)。Arduino 将使用串口通信将你的反应时间报告回计算机。Serial对象还有许多其他方法,我们将在本书中介绍,用于处理计算机和 Arduino 之间的数据。
最后,你将设置引脚。本项目使用第 13 号引脚上的一个 LED 指示何时按下按钮,因此你再次使用pinMode()函数将第 13 号引脚设置为OUTPUT。然后使用pinMode()函数 ➌ 将第 3 号引脚设置为INPUT。这里使用INPUT是因为 Arduino 需要能够检测按钮按下的动作,而不是向按钮输出信号。
编写 loop()函数
现在让我们编写草图中的loop()部分。在setup()函数后,输入 Listing 4-2 中的代码。
Listing 4-2: 反应计时器的loop()函数
void loop()
{
//prints the challenge instructions
➊ **Serial**.println("When the LED turns on, push the button!");
**Serial**.println("Now, watch the LED. Ready?");
➋ waitTime = random(2000, 4000); //random wait time
//from 2000 to 4000 ms
➌ delay(waitTime); //delay random wait time
//turn on the LED!
digitalWrite(13, HIGH);
startTime = ➍millis(); //set zero time reference
//loop to wait until button is pressed
➎ while(digitalRead(3) == HIGH)
{
}
➏ reactTime = millis() - startTime; //calculation of
//reaction time
digitalWrite(13, LOW); //turn off LED!
//display information to Serial Monitor
➐ **Serial**.print("Nice job! Your reaction time was ");
➑ **Serial**.print(reactTime);
➒ **Serial**.println(" milliseconds");
delay(1000); //short delay before starting again
}
首先,这段代码使用Serial.println()方法 ➊ 显示一条消息提示,解释如何玩游戏。println()方法将文本发送到计算机,并添加一个换行符,使光标向下一行移动。当调用此方法时,任何在引号(" ")中的文本都会显示在 Arduino IDE 的串口监视器上。串口监视器就像一个简单的聊天窗口或终端,允许你在 Arduino 和计算机之间发送和接收数据。你可以通过点击 Arduino IDE 右上角的放大镜按钮(如图 4-11 所示),点击工具 ▸ 串口监视器,或使用快捷键 CTRL-SHIFT-M 来打开串口监视器。
图 4-11: 通过 Arduino IDE 打开串口监视器

我们稍后会查看串口监视器。
生成延迟时间
为了使反应计时器更加不可预测,草图调用 random() 函数 ➋ 来生成一个随机的 waitTime。random() 函数接受最小值和最大值作为参数,并返回一个 伪随机数(看似随机但实际上不是;在 “尝试:让 waitTime 更加随机” 第 109 页中有更详细的解释)。在这个示例中,waitTime 变量被设置为介于 2,000 和 4,000 之间的“随机”数,并在 LED 灯亮起之前传递给 delay() ➌。这样可以防止你和你的朋友预测按钮的按下时机。
当 LED 灯亮起时,调用 millis() 函数 ➍ 来捕获起始时间。millis() 函数检查 Arduino 的内部计时器,并返回自 Arduino 开机或重置以来的毫秒数。millis() 函数对于任何涉及计时的 Arduino 项目都非常有用。
检查带有 while() 循环的按钮
在获取起始时间后,草图使用 while() 循环 ➎ 等待按钮按下。在 Arduino 中,像许多其他编程语言一样,while() 循环只要括号中的表达式为 true,就会运行其大括号内的代码。在这种情况下,表达式是
digitalRead(3) == HIGH
digitalRead() 命令读取括号中指定引脚的电压,并根据是否检测到 5V 或 GND 返回 HIGH 或 LOW 的值。此调用检查引脚 3 的电压。还记得那个按钮上的上拉电阻吗?按钮的正常状态是开路,默认情况下引脚为 HIGH,直到按下按钮闭合电路。当按下按钮时,引脚 3 连接到地面,状态变为 LOW。双等号 (==) 用于检查相等性。(参见 “逻辑比较运算符” 第 106 页。)
只要按钮没有被按下,表达式为 true,while() 循环就会重复,从而阻止草图执行后续代码。不过请注意,循环内实际上没有任何代码。这被称为 保持 或 阻塞循环,它并不执行任何代码,而是仅仅阻止其他代码的执行。当按钮被按下时,表达式变为 false,草图继续执行。
计算并显示反应时间
接下来,草图通过从当前计时器值中减去 startTime 来计算反应时间 ➏,当前计时器值通过 millis() 命令获取。
最后一步,关闭 LED,并将 reactTime 值打印到串口通信线上。为了使信息更易读,草图使用 Serial 对象的 print() 方法➐ 将字符串 "Nice job! Your reaction time was " 打印到串口监视器。这种方法发送括号中的文本,并且不会将光标移动到新的一行。实际上,它会保持光标在同一行,以便你可以追加更多信息,比如实际的反应时间,后者通过 Serial.print(reactTime); 在 ➑ 处添加。然后,通过调用 println() 方法 ➒ 完成句子,这将打印字符串 " milliseconds",并将光标移到新的一行。
逻辑比较运算符
逻辑比较运算符 执行将一个值与另一个值进行比较的操作。例如,双等号(==)比较两个值是否相等。逻辑运算只能返回两种值中的一种:true,表示比较正确,或者 false,表示比较不正确。在 Arduino 中,有很多方法可以比较两个值,所有的运算符如下表所示:
| 运算符 | 比较 |
|---|---|
== |
等于 |
!= |
不等于 |
> |
大于 |
>= |
大于或等于 |
< |
小于 |
<= |
小于或等于 |
例如,如果你在 Arduino IDE 中输入 2 == 4,它会返回 false,因为二者不相等。但是,如果你在 Arduino IDE 中输入 2 <= 4,它会返回 true,因为二者小于或相等。在草图中,比较运算符通常与 if() 或 while() 语句一起使用,根据某个条件执行特定的代码块。例如,清单 4-2 中的 while() 循环写道:“当 digitalRead(3) == HIGH 为 true 时,重复执行保持循环;否则,跳到下一段代码。”
一个常见的错误是错误地使用单等号进行比较。记住:单等号用于赋值,而双等号用于比较两个值。
随着你开始评估 Arduino 从各种传感器和输入中收集的数据,你将越来越多地使用比较运算符,接下来我们也会在本章中使用其中的一些。
请注意,每个字符都必须显式打印,包括数字或字符之间的空格。你可以使用 print() 和 println() 方法,如本示例所示,来组合、格式化或组织你在串口监视器中显示的文本。
loop() 函数中的最后一行代码是一个简短的 delay() 函数,用于在草图回到起始位置并重新开始反应计时器之前暂停。
特殊命令字符
打印数据或文本时,必须表示每个单独的字符,包括空格,以及代码中的每个格式化命令。有一组保留字符,称为 转义序列,用于表示特殊格式。例如,\n 将光标移动到新的一行;因此,Serial.print("Hello Arduino!\n"); 等同于 Serial.println("Hello Arduino!");。
你可以在 print 语句中使用转义序列,向文本中添加格式或其他特殊字符。下表列出了一些有用的转义序列。
| 转义序列 | 结果 |
|---|---|
\t |
制表符 |
\n |
换行 |
\' |
单引号 |
\" |
双引号 |
\x*hh* |
ASCII 字符,其中 *hh* 是十六进制数 |
测试反应计时器草图
这就是你需要测试反应计时器游戏电路的所有代码!现在保存你的草图,编译并上传到你的 Arduino。要查看草图通过串行通信线路发送的数据,请通过点击 工具 ▸ 串口监视器 或 IDE 右上角的放大镜按钮打开串口监视器窗口。
注意,串口监视器右下角有一个下拉菜单,可以控制串行数据速率,以 波特率(每秒位数)表示,默认值为 9,600 波特(见 图 4-12)。这是此草图用于初始化 Serial 对象的相同值,使用 Serial.begin(9600) 指令。请始终检查串口监视器的速度是否与草图中 Serial.begin() 设置的速率相同,否则你可能会看到乱码!
图 4-12: Arduino IDE 的串口监视器窗口测试反应计时器

注意
某些组件,例如 GPS 或串行启用的 LCD 屏幕,可能会以不同的波特率与 Arduino 通信,因此在使用新部件时,最好检查它使用的速率并在草图中进行设置。
你可以将串行数据速率设置为从 300 到 250,000 波特的任何标准速率,但 9,600 波特是最常用的速度。一般来说,较慢的速度更可靠,消耗的功率和资源较少,但也会引入延迟,减缓 loop() 的执行速度。如果你需要非常快速的响应并且不太在乎功耗,你可以使用更高的波特率。
现在,让我们开始吧!运行草图并仔细观察你的 LED。当它亮起时,按下按钮。你的代码将把你的反应时间(毫秒为单位)打印到串口监视器上,就像 图 4-12 中所示的那样。你有多快?快去挑战你的朋友吧!平均反应时间约为 215 毫秒。你的表现如何?
试一试:使等待时间更随机
在大多数数字设备,如 Arduino 中,很难获得一个真正的随机数,因为随机数是通过数学计算生成的。在 Arduino 上,每次调用random()函数时,函数会基于上次random()的结果进行计算。由于下一个“随机”数是由上一个结果计算得出的,因此数字序列将始终相同。
例如,在我们的 Arduino 上(也可能是你的),第一次调用random(2000, 4000)总是将waitTime设置为2807。第二个生成的数字总是3249,第三个是2073,依此类推。
你可以通过在setup()函数中调用randomSeed()来让waitTime值看起来更随机。这会生成一个种子值,告诉random()伪随机序列应该从哪里开始。当你的 Arduino 开始运行草图时,种子默认为1,这就是为什么random(2000, 4000)调用时2807总是第一个生成的数字。
为了让你的反应计时器表现得更随机,在setup()例程中,在闭合大括号前添加这一行代码:
randomSeed(analogRead(A5));
这会用 Arduino 模拟引脚 A5 上的当前电压值来为随机数生成器提供种子。我们将在第五章详细讨论analogRead(),但现在只需知道它读取传入的模拟引脚上的电压水平。因为引脚 A5 在反应计时器中没有连接到任何东西,所以电压会浮动,或者说有些不可预测地波动。
如果你选择添加这行代码,那么每次运行草图时,第一次调用random(2000, 4000)应该会返回不同的数字。为确认这一点,在你的草图中的delay(waitTime)调用后添加以下两行代码:
**Serial**.print("waitTime = ");
**Serial**.println(waitTime);
现在,通过在行首添加//来注释掉randomSeed()调用,运行你的草图几次,并记录初始的waitTime值。取消注释randomSeed()调用并重复这个过程。随着时间的推移以及大量数据点的累积,仍然会出现模式,但那是另一本书的内容——或者可能是计算机科学学位的内容!
再玩一次?
要再玩一次,只需按下 Arduino 的重置按钮——即板子角落附近的黄铜色按钮,如图 4-13 所示。这将重新启动你的代码并让你再玩一次。仔细观察 LED。你变得更快了吗?
图 4-13: 按下重置按钮再玩一次。

添加一个游戏元素
要为这个项目增加一点嘉年华游戏的风格,你可以添加一个视觉速度指示器,用来显示你是否比给定的反应时间更快。我们建议以平均反应时间 215 毫秒作为挑战目标。为了实现这一点,你需要再添加两个 LED 灯:一个绿色 LED 表示你比代码设置的时间快,另一个红色 LED 表示你较慢。由于你已经使用了 13 号引脚连接刺激 LED,所以你将把这两个 LED 连接到 11 号和 12 号引脚。将它们按照图 4-14 和图 4-15 所示连接到面包板上。
图 4-14: 带有两个额外 LED 的电路图

图 4-15: 新电路的完整接线图,包含两个额外的 LED

更新代码以支持额外的 LED
既然你已经有了这两个指示 LED,你需要再添加一些代码行,用于在你的反应时间比目标时间快时点亮绿色 LED,在反应时间不如目标时间时点亮红色 LED。
你需要为 12 号和 11 号引脚添加pinMode()命令,并将其设置为OUTPUT,以控制你的新 LED。对setup()函数的修改见列表 4-3(现有代码用浅灰色显示)。
列表 4-3: 添加了额外速度指示 LED 的反应计时器setup()函数修改版
void setup()
{
Serial.begin(9600); //sets up serial
//communication
pinMode(13, OUTPUT); //sets pin 13 as an OUTPUT for the
//stimulus LED
pinMode(12, OUTPUT); //sets pin 12 as an OUTPUT for the
//green LED
pinMode(11, OUTPUT); //sets pin 11 as an OUTPUT for the
//red LED
pinMode(3, INPUT); //sets pin 3 as an INPUT for the
//button
}
你只是插入了两个额外的pinMode()指令,用于配置你将要添加的两个 LED。
使用 if()和 else()控制流程
现在,你需要在你的代码中加入一些决策逻辑。在 Arduino 编程中,if()语句允许你控制代码的执行方向和流程。它告诉代码:“如果这个条件成立,就执行以下大括号内的代码。”if()语句的一般语法见列表 4-4。
列表 4-4: Arduino 中的通用if()语句
if(➊*expression*) //if *expression* is true, run the code in
//the following loop
{
➋
}
➌ else //otherwise, run the code in this loop instead
{
➍
}
expression ➊ 是一个布尔表达式,可以是true或false,类似于我们在“逻辑比较运算符”一节中讨论过的内容,见第 106 页。如果表达式为true,程序将开始执行大括号之间的代码 ➋。如果表达式不为true,程序将跳过大括号,直接执行下一个语句。通常,if()语句会和else ➌一起使用。如果表达式不为true,程序将跳过第一组大括号 ➋,继续执行else语句中的代码 ➍。
在反应计时器游戏中,你将使用if()语句来控制绿色 LED 在反应时间小于或等于 215 毫秒时点亮,红色 LED 在反应时间大于 215 毫秒时点亮。你可以更改这个值,来调整游戏难度,但现在这个值是一个不错的中等难度。列表 4-5 展示了实现此功能的代码。
列表 4-5: 反应计时器游戏的if()语句代码片段
➊ if (➋reactTime <= 215)
{
➌ digitalWrite(12, HIGH); //green LED ON
➍ digitalWrite(11, LOW); //red LED OFF
}
➎ else
{
digitalWrite(12, LOW); //green LED OFF
digitalWrite(11, HIGH); //red LED ON
}
你可以看到➊,草图使用了if()语句来执行这个逻辑。布尔表达式reactTime <= 215 ➋,检查reactTime的值是否小于或等于215,如果是,绿灯就会亮起➌。当绿灯亮起时,红灯需要关闭,所以你会加上一条额外的指令➍来关闭它。最后,你加上else语句➎来在if()语句为false时点亮红灯。
上传完整的反应计时器代码
新的if()语句应放置在loop()中,紧接着关闭红色 LED 的代码,如清单 4-6 所示。(snip表示页面中由于篇幅问题省略的现有代码位置。)
清单 4-6: 为绿灯和红灯游戏指示器添加if()语句
--*snip*--
//loop to wait until button is pressed
while(digitalRead(3) == HIGH)
{
}
reactTime = millis() - startTime; //calculation of reaction
//time
digitalWrite(13, LOW); //turn off LED!
if (reactTime <= 215)
{
digitalWrite(12, HIGH); //green LED ON
digitalWrite(11, LOW); //red LED OFF
}
else
{
digitalWrite(12, LOW); //green LED OFF
digitalWrite(11, HIGH); //red LED ON
}
//display information to Serial Monitor
--*snip*--
在添加完新代码后,将整个草图上传到你的 Arduino。打开串口监视器并进行游戏,确保一切按预期运行。你能让绿灯亮起来吗?你得够快!
玩转游戏:控制难度
和所有游戏一样,反应计时器有时也需要调整难度级别。既然你是程序员,你就能控制游戏。如果 215 毫秒太快或太慢,你可以通过更改if (reactTime <= 215)中的数字来调整游戏的阈值。
想要让它几乎不可能被击败吗?把这个值改成像 100 毫秒这样的小数值。想要让它变得简单点?改成像 500 毫秒这样的数字。你在写代码,所以你可以决定游戏的规则!
构建反应计时器外壳
当你的原型工作正常时,是时候构建一个更永久的外壳了。为了尽可能简化这个项目,我们版本的反应计时器设计为适合放入一个小的 SparkFun 盒子。盒子的顶部尺寸为 3 7/8 英寸×5 英寸,但你可以将游戏放进家里任何现有的东西里——一个旧的谷物盒、燕麦容器或任何其他结实的纸板物品。
我们让我们的反应计时器看起来像一个老式的嘉年华游戏,配上我们找到的一些有趣的剪贴画,但你可以根据自己的喜好来设计外观。图 4-16 展示了我们盒子外部的模板。这个设计中有一个按钮孔、一个刺激 LED 孔和两个指示 LED 孔——一个显示你比忍者还快,另一个显示你像乌龟一样慢。你可以通过本书的资源下载模板,链接在* www.nostarch.com/arduinoinventor/*,或者直接在盒子上随便切割 LED 和按钮的孔。
图 4-16: 反应计时器嘉年华游戏封面艺术模板(非完整尺寸)

剪裁纸板
如果你使用我们的模板,先打印出来,然后将它粘贴或用胶带固定在盒子的前面。不论是否使用模板,你都需要在纸板上打四个孔,用于三个 LED 和一个按钮。你可以使用工艺刀或电钻小心地切出孔洞,如图 4-17 所示。LED 的直径是 5 毫米,所以使用 3/16 英寸的钻头比较合适。对于按钮孔,我们建议使用 5/16 英寸的钻头,如果你在钻孔,也可以用一支尖锐的铅笔。
图 4-17: 从纸板盒中切出孔洞

组装电子元件
现在你已经从反应计时器盒中切出了孔洞,接下来需要添加电子元件。你将把三个 LED 和按钮从面包板上移动到新纸板盒的外部,这样玩家才能看到它们。
将 LED 和按钮固定到纸板上
首先,从纸板的背面把 LED 插入它们的三个孔中,确保每个 LED 均紧密地固定。如果你切出的孔太大,导致 LED 有些松动,可以在孔周围加一点胶水将它们固定,如图 4-18 所示。
图 4-18: 将 LED 移动到项目盒/纸板上

接下来,添加按钮。SparkFun 发明者工具包中的按键有一个可以拆下的按钮帽。移除按钮帽,从盒子的内侧插入按钮,并将按钮粘贴到纸板上,如图 4-19 所示。然后将按钮帽重新安装到纸板的顶部。玩家会在尽力获得最佳分数时疯狂按压这个按钮,所以一定要使用大量胶水来确保它稳固!当胶水干了之后,试试按钮。你需要能够将按钮完全按下,所以确保按下时按钮帽不会卡在纸板上。
图 4-19: 使用大量胶水固定按钮

注意
如果使用热熔胶,在将按钮粘贴到纸板上时要小心。热熔胶很烫!
重新连接电子元件到 Arduino
现在,使用公对母跳线将 LED 连接到面包板。记住,LED 的短腿需要连接到电路的地(GND),而每根长腿应该通过一个 330 Ω 电阻连接到 Arduino 上相应的引脚。由于 LED 的腿有点长,你可能需要用剪线钳把它们剪短。我们常用的一个方法是把短腿剪得稍微短一点,这样你就可以随时辨别哪一腿是负极腿,如图 4-20 所示。
图 4-20: 剪短 LED 的腿。保持短腿短!

如果你分不清哪个引脚更长,还可以查看 LED 的塑料镜头形状。通常,LED 负极一侧的塑料镜头会有一个平边。这个平边虽然不明显,但如果仔细观察,你应该能看到。
将公对母跳线的母端按压到 LED 的端口上,使其紧密贴合,像图 4-21 所示。为了保持组织整洁并便于跟随,我们建议使用黑色线材连接 LED 的负极(短脚)。
图 4-21: 将 LED 连接到跳线

一旦跳线接入所有三个 LED 灯,将公端连接到面包板电路中。刺激 LED 的线应连接到 E2 和 E3,绿色 LED 连接到 E8 和 E9,红色 LED 连接到 E12 和 E13。
接下来,重新将按钮连接到电路中。按钮有四个引脚,但你只需要连接按钮一侧的两个引脚。将一根公对母跳线连接到一个引脚,另一根连接到另一个引脚,如图 4-22 所示。然后,将一根线插入与 10 kΩ电阻和 Arduino 引脚 3 连接的同一面包板行,另一根插入面包板的 GND。如果你的面包板原型与图 4-14 中的示意图相同,那么请将这些线连接到面包板的 E20 和 E22 上。因为按钮只是一个开关,所以接入 GND 的线不需要特别注意。
图 4-22: 将公对母跳线连接到按钮一侧的两个引脚

当所有组件就位后,将 Arduino 连接到计算机并打开串口监视器,确保电路仍然工作。你应该能在串口监视器上看到指令信息。当蓝色 LED 亮起时,尽可能快地按下按钮。串口监视器应显示你的反应时间,并且红色或绿色 LED 应亮起,具体取决于你的反应速度。
如果你的电路似乎不工作,请检查所有连接是否牢固,并将电路与图 4-14 和图 4-15 进行对比,确保连接正确。
装饰你的游戏外壳
最后,为你的新游戏添加一些装饰。发挥你的想象力!你可能想用你喜欢的贴纸装饰反应计时器,或者给盒子涂上颜色。我们在项目中喜欢使用乒乓球,因为我们在项目 2 中剩下了一个半个,所以我们决定把它粘在蓝色 LED 上面,像图 4-23 所示。
图 4-23: 以狂欢节为主题的反应计时器游戏

深入探索
接下来,尝试将前面三个项目中的所学与这个项目的知识结合起来,使游戏更有趣——加入更多的 LED,或者将游戏改造成适合两名玩家。
技巧
添加两个额外的 LED,制作一个四 LED 的速度显示条,以更准确地显示你的反应速度。更快的反应时间将点亮更多的 LED。为了实现这一点,你将需要使用嵌套的 if()–else if() 控制语句。你可以堆叠条件语句,让代码根据不同的条件做出相应的处理,因此如果第一个逻辑表达式为 false,则会测试下一个;如果那个也为 false,则继续测试下一个,以此类推,直到最后的 else() 语句执行,如果之前的条件都没有为 true。列表 4-7 展示了这种条件逻辑的示例。假设你已经添加了两个额外的 LED,分别连接到 10 和 9 引脚。不要忘了你需要在 setup() 中添加的 pinMode() 命令!
列表 4-7: 嵌套的 if()–else if() 语句片段
➊ if (reactTime <= 215)
{
//turn all LEDs on
digitalWrite(12, HIGH);
digitalWrite(11, HIGH);
digitalWrite(10, HIGH);
digitalWrite(9, HIGH);
}
➋ else if (reactTime <= 250)
{
//turn three LEDs on
digitalWrite(12, LOW);
digitalWrite(11, HIGH);
digitalWrite(10, HIGH);
digitalWrite(9, HIGH);
}
➌ else if (reactTime <= 300)
{
//turn two LEDs on
digitalWrite(12, LOW);
digitalWrite(11, LOW);
digitalWrite(10, HIGH);
digitalWrite(9, HIGH);
}
➍ else
{
//turn one LED on
digitalWrite(12, LOW);
digitalWrite(11, LOW);
digitalWrite(10, LOW);
digitalWrite(9, HIGH);
}
➊ 处的 if() 语句检查反应时间是否小于或等于 215 毫秒,并点亮所有四个 LED。然后,两个 else if() 语句分别处理 215 毫秒至 250 毫秒 ➋ 之间的时间,点亮三个 LED,以及 250 毫秒至 300 毫秒 ➌ 之间的时间,点亮两个 LED。最后,一个 else 语句 ➍ 处理所有超过 300 毫秒的时间,并点亮一个 LED。
如果你需要更多的代码帮助,请查看我们在资源中提供的示例草图,地址为 www.nostarch.com/arduinoinventor/。
修改
修改这个项目的一种有趣方式是将其变成两人游戏。你可以添加第二个按钮,并重新利用你的 LED 来显示哪个玩家更快。在这个修改中,如果玩家 1 更快,绿色 LED 会亮起;如果玩家 2 更快,红色 LED 会亮起。
首先,添加第二个按钮。图 4-24 显示了面包板底部的附加按钮。请注意,它只是你为第一个按钮构建的上拉电阻/按钮组合电路的重复。
图 4-24: 为两人模式添加第二个按钮

注意
如果两个玩家恰好同时按下按钮,游戏会偏向玩家 1(绿色 LED)。虽然这种情况非常罕见,但如果两个按钮同时按下,你会如何修改代码,让两个 LED 都亮起呢?
两人游戏模式的完整代码可用,并附有修改的接线图,详见 www.nostarch.com/arduinoinventor/。
现在,去把你新的游戏带到城里吧。你比家里的人快吗?你比朋友们快吗?你认识的最快的人是谁?
第六章:5 A 彩色混合夜光灯
数字电子学和微控制器的奇妙之处在于它们很聪明。它们能够读取传感器并根据传感器告诉它们的信息做出决策。传感器是收集周围环境信息并将其转化为微控制器能够理解的形式的组件。
你可以使用传感器制作对各种刺激(如温度、声音和物体接近度)作出反应的项目,但在这个项目中,我们将从一个简单的夜光灯开始,它会对光线变化做出反应,如图 5-1 所示。
图 5-1: 完成的夜光灯项目

收集的材料
这个项目使用了一种新型 LED 和光敏电阻,这是一种根据检测到的光线强度变化而改变电阻值的传感器。我们鼓励你发挥创意,设计一个定制的灯罩,但如果你还不准备迎接这个挑战,也不必担心!本书的资源文件包括一个可以开始使用的灯罩设计。图 5-2 和图 5-3 展示了你在此项目中所需的零件和设备。
电子零件
注意
带星号()的零件不包含在标准的 SparkFun 发明者套件中,但可以在单独的附加套件中获得。*
• 一块 SparkFun RedBoard(DEV-13975)、Arduino Uno(DEV-11021)或其他兼容 Arduino 的板子
• 一根 USB Mini-B 电缆(CAB-11301,或你板子的 USB 电缆;未显示)
• 一块无焊面包板(PRT-12002)
• 一块迷你面包板(PRT-12043*;未显示)
• 一只 RGB LED,共阴极(COM-09264)
• 三个 330 Ω电阻(COM-08377,或者 20 个装的 COM-11507)
• 一只 10 kΩ电阻(COM-08374,或者 20 个装的 COM-11508)
• 一块光敏电阻(SEN-09088)
• 公对公跳线(PRT-11026)
• 短 4 英寸的公对公跳线(PRT-13870*)
• (可选)公对母跳线(PRT-09140*)
• (可选)一只 4 节 AA 电池架(PRT-09835*;未显示)
图 5-2: 夜光灯的组件

其他材料和工具
• 工艺刀
• 金属尺
• 胶水(热熔胶枪或工艺胶水)
• 一张卡纸(不是纸板),约 8.5 × 11 英寸
• 一张白色或半透明的羊皮纸,或者标准的复印纸,约 8.5 × 11 英寸
• 外壳模板(见图 5-20 第 144 页)
图 5-3: 夜光灯的推荐建筑材料

新组件
在这个项目中,你将使用两种新组件:一种将三种颜色集成在一个包中的 LED,以及一个光敏电阻。我们来看看这些组件是如何工作的。
RGB LED
如果你已经做过本书中的其他项目,那么你已经有了使用普通 LED 的经验。图 5-4 中显示的红色、绿色、蓝色(RGB) LED 工作方式非常相似。这个 LED 实际上是三只 LED 合在一个外壳中的:一只红色、一只绿色和一只蓝色。每个 LED 都有自己的正极(或阳极)引脚,但它们都共享一个负极(或阴极)引脚,称为公共阴极。
如果仔细观察图 5-4 中的 LED,你会注意到它的引脚长度各不相同。对于普通的 LED,短引脚是负极引脚,但对于 RGB LED,最长的引脚是负极引脚。这个元件的电路图通常像图 5-5 那样绘制。请注意,它显示了三个独立的 LED 被连接在一起,它们共享一个负极连接。
图 5-4: 带公共阴极引脚的 RGB LED

图 5-5: RGB LED 的电路图

要弄清楚这个特定 LED 哪个正极对应哪种颜色,可以将 LED 定位为图 5-5 中所示的样子。在这种方向下,最左边的引脚是红色正极引脚。接下来的引脚(最长的)是共享的负极引脚,最后两个引脚分别是绿色和蓝色的正极引脚。
记住哪个正极对应哪种颜色后,你可以像连接三个独立的 LED 一样将这个 LED 接入电路。只需要将你想使用的正极引脚连接到电源或 Arduino 输出引脚,串联一个限流电阻,再将公共阴极连接到地线。
RGB LED 很酷,因为你可以用它们创建大量的颜色。红色、绿色和蓝色是加色法中的三原色,LED 可以混合这些颜色来创造其他颜色的光。(这与原色颜料——红色、蓝色和黄色——不同,后者在你从小学美术课上可能记得,混合在一起可以在油漆中创造新的颜色。)图 5-6 中的加色环展示了三原色如何组合来创造彩虹中的任何颜色。
图 5-6: 加色环

使用你的 RGB LED,如果同时点亮蓝色 LED 和红色 LED,你会得到品红色光。将红色和绿色 LED 结合在一起,你会得到黄色。如果所有 LED 都亮起,你会得到白色光。这一概念是 LED 电视或显示器工作的基础:你屏幕上的每个像素本质上是一个 RGB LED。
光敏电阻
这个夜光灯在黑暗的房间里会打开,在光线充足的房间里会关闭。这意味着夜光灯需要判断房间是否黑暗。为此,它使用光传感器来监测周围的光照水平。有许多不同的光传感器可以选择,但我们使用了如图 5-7 所示的简单光敏电阻。这个组件有时也被称为光敏电阻(LDR)或光电池。另外,类似于许多其他类型的传感器,光敏电阻有时也被称为可变电阻传感器。
在这个项目中,光敏电阻的电阻值从约 80 Ω 到约 1,000,000 Ω(1 MΩ)变化,具体取决于其所接收的光照强度。当光敏电阻暴露在强光下时,它的电阻较低,而在黑暗中时,它的电阻较高。
图 5-7: 一只光敏电阻

要使用光敏电阻来测量亮度,必须将其放置在如图 5-8 所示的电压分压电路中。电压分压电路使用两个串联连接的电阻(即串联在电源电压 5 V 和地之间)来获得较小的电压。
图 5-8: 电压分压电路

这两个电阻上的总电压为 5 V,而R[1]和R[2]上的电压取决于两个电阻的阻值比。V[out]将是介于 5 V 和 0 V 之间的某个电压,因为电压在这两个电阻之间分配。V[out]与电阻值 R[1] 和 R[2] 之间的关系可以通过以下方程式来表示。

我们知道你在想什么:看起来像数学!嗯,确实是,而且数学是电子学中的一个重要部分,但它并不复杂。我们会慢慢来,确保每个人都能理解。这个小小的方程式在处理光敏电阻或任何其他类型的电阻传感器时特别有用。在电压分压电路中,如果你用光敏电阻替代R[1],就得到如图 5-9 所示的电路。
图 5-9: 一个带光敏电阻的电压分压电路

当周围光线变暗时,光敏电阻的电阻增大。现在,看看电压分压方程。当电阻R[1]增大时,分数的分母增大,使得整个分数变小。这意味着当光线变暗时,V[out]会变小。
通过这个电路,你可以通过将V[out]连接到 Arduino 左侧的一个模拟输入引脚(标有 A 的引脚)来准确读取光敏电阻上的光线强度。模拟信号是指那些可以在一定范围内变化的信号。到目前为止,你只使用了 Arduino 板右侧的数字引脚。与按键只有两种状态不同,光敏电阻可以根据光线的亮度和电压分压电路的影响,产生一系列的数值。这就是数字信号和模拟信号之间的区别。
这就是你需要了解的关于如何使用这个电压分压器电路的所有内容,但如果你想练习计算,参见第 130 页的“给我一些数学:电压分压器”。
给我一些数学:电压分压器
使用万用表,你可以在不同条件下测量光敏电阻的电阻值。(关于如何使用万用表的说明,请参见第 298 页的“用万用表测量电流”)。当我们用手电筒或手机照射光敏电阻时,测得的电阻大约为 100 Ω。当我们用手遮住光敏电阻时,测得的电阻大约为 200 kΩ。当固定电阻(R[2])设置为 10 kΩ时,我们预计在这两种情况下电压分压器的输出值会如下所示:

在 5V 的输入电压下,光敏电阻上的电压在不同的光照强度下从 0.24 V 变化到 4.95 V。在本章中,我们将向你展示如何使用 Arduino 读取这些电压。很酷吧?数学是有效的!
构建夜灯原型
让我们把 RGB LED 和电压分压器连接起来,构建夜灯电路。你将首先构建包含光敏电阻的电压分压器电路,然后添加 RGB LED。完成后,你的面包板应该像图 5-10 那样。我们还在图 5-11 中提供了一个电路图供参考。
图 5-10: 完成的原型电路

图 5-11: 完成的夜灯原型电路图

接线电压分压器
找到你的光敏电阻(它应该像图 5-7 中的那样)和一个 10 kΩ的电阻。回忆一下,10 kΩ电阻的颜色带为棕色、黑色和橙色,如图 5-12 所示。有关如何通过颜色带确定电阻值的详细信息,请参见第 308 页的“电阻与颜色带”。
图 5-12: 10 kΩ电阻(棕黑橙)

手头有了零件后,按照图 5-13 所示搭建电压分压电路。在搭建面包板电路时,连接电源(5V)和接地是一个很好的做法,因此首先完成这一步。找到面包板左侧的接地轨(–)和电源轨(+)。将 Arduino 的 5V 引脚连接到电源轨,将 Arduino 的 GND 引脚连接到接地轨。
图 5-13: 完成的电压分压器,使用光敏电阻

接下来,将光敏电阻插入面包板的下方,每条腿插入不同的排针中。将一端 10 kΩ电阻的一端插入与光敏电阻一条腿相同的排针中(连接两者),并将电阻的另一端插入一个单独的排针中。添加一根导线,将 5V 电源轨(+)连接到未连接电阻的光敏电阻腿。然后,添加另一根导线,将接地轨(–)连接到单独排针中的电阻腿。
最后,通过将一根导线从面包板与电阻和光敏电阻共用的排针连接到 Arduino 的模拟输入引脚 A0,将光敏电阻连接到 Arduino。这个导线通常被称为光敏电阻的输出电压线,也就是信号线。模拟输入引脚 A0-A5 都可以用来测量不同范围的电压。
注意,面包板电路看起来很像图 5-9 中的示意图。这是 Arduino 项目中最基本的传感器电路之一。许多其他模拟传感器,比如用于弯曲、温度和压力的传感器,也都是可变电阻。如果以后想尝试这些传感器,只需将光敏电阻替换为相应的传感器。
连接 RGB LED
记得项目 2 中的红绿灯电路吗?那个项目有三个 LED。RGB LED 实际上将这三个 LED 合并在一起。RGB LED 有四条腿,其中最长的一条是公共阴极(负极)腿。按照图 5-5 中的方向摆放 RGB LED,找到红色腿。将 RGB LED 插入面包板,使红色腿位于顶部,最长的腿排在第二位置,正如图 5-14 所示。
图 5-14: 将 RGB LED 添加到电压分压电路中

面包板的两半通过一条隔开的沟槽分开,分隔了各个排针。将 RGB LED 插入右侧,从红色引脚插入第 4 排开始。
接下来,找到三颗 330 Ω的电阻,它们的颜色带为橙色-橙色-棕色。使用这三颗电阻将红色、绿色和蓝色引脚连接过电路的沟槽,以便通向另一侧的开放行,如图 5-14 所示。电阻需要跨越沟槽,以确保电阻的两端不会直接短路。将一根线从 LED 的公共阴极(负极)引脚接到面包板左侧的地轨。最后,将 RGB LED 的三根引脚通过线缆连接到 Arduino:将 Arduino 的 11 号引脚连接到面包板上与红色引脚相连的电阻,将 Arduino 的 10 号引脚连接到面包板上与绿色引脚相连的电阻,将 Arduino 的 9 号引脚连接到面包板上与蓝色引脚相连的电阻。完成后,它应该与图 5-14 中的示意图相似。注意,红色、绿色和蓝色的线缆分别对应 LED 上的红色、绿色和蓝色正极引脚。
将 RGB LED 连接好后,你可以像控制单独的 LED 一样,通过 9、10、11 号引脚来控制每种颜色。打开 Arduino IDE,让我们来试试这个想法吧!
测试夜灯与基本颜色混合
在这个项目中,你将接触到一些新的概念,首先是如何使用 RGB LED 混合颜色。RGB LED 实际上是三颗 LED 组成的,你需要在代码中像对待三颗单独的 LED 那样来操作它。创建一个新的草图,并将默认代码替换为列表 5-1 中的setup()和loop()函数。
列表 5-1: 显示 RGB LED 青色的简单代码示例
void setup()
{
➊ pinMode(11, OUTPUT); //red
pinMode(10, OUTPUT); //green
pinMode(9, OUTPUT); //blue
}
void loop()
{
➋ digitalWrite(11, LOW); //red
➌ digitalWrite(10, HIGH); //green
➍ digitalWrite(9, HIGH); //blue
}
每个控制 RGB LED 的引脚都需要一个独立的pinMode()函数来将该引脚设置为OUTPUT ➊。之前的项目使用digitalWrite()函数通过单独的引脚控制 LED 的开关,你将使用相同的方法来控制 RGB LED。使用图 5-6 中的色轮,从其中选择你喜欢的颜色。我们选择了青色,即绿色和蓝色的组合。要生成青色,你需要通过digitalWrite()函数开启绿色 ➌ 和蓝色 ➍ 的 LED。为了确保红色 LED 熄灭,代码中还需要添加一个第三个digitalWrite()函数 ➋。
将此草图上传到你的 Arduino,如果电路连接正确且代码无误,你的 RGB LED 应该会发出柔和的青色光。如果颜色不同或完全不亮,请检查接线和 RGB LED 的方向。
注意,尽管你使用 RGB LED 来混合颜色,但你的代码仍然与本书中其他 LED 项目的代码非常相似。每颗 LED 都是通过单独的数字引脚控制的,使用pinMode()函数开启。你只是在同时使用多个digitalWrite()函数来控制颜色。
尝试:混合更多颜色!
尝试自己改变 LED 的颜色,这次加入红色。你能做出品红色吗?黄色呢?当你打开所有三种颜色时,你会得到什么颜色?可以参考颜色轮来看看。
编程夜光灯
在夜光灯电路中,光敏电阻器用作光传感器。光敏电阻器电压分压器连接到模拟输入引脚 A0。回想一下,Arduino 可以用来测量任何模拟输入引脚上的电压。你可以使用analogRead()函数让 Arduino 读取该引脚的传感器值。analogRead()函数读取应用到模拟输入引脚的电压,并返回一个介于 0 和 1,023 之间的值,电压范围从 0 V 到 5 V。例如,如果你将 2.5 V 施加到 A0,引脚的analogRead(A0)函数将返回一个大约为 512 的值,即大约是 1,023 的一半。
随着光线照射到光敏电阻器的强度变化,它的电阻会变化,由于电压分压电路的作用,模拟输入引脚上的电压也会发生变化。让我们看看如何在 Arduino 上编码。为了确定夜光灯是否应该开启,你需要读取光敏电阻器的电压,并将其与一个值进行比较,这个值可以表示房间是昏暗还是明亮。你应该已经将电路接好,RGB LED 和光敏电阻器连接到 A0 引脚。列表 5-2 显示了整个 Arduino 草图。你可以修改为列表 5-1 所做的草图,或者直接将此代码添加到一个全新的草图中。
列表 5-2: 完整的夜光灯代码
int calibrationValue;
int lightValue;
void setup()
{
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
➊ calibrationValue = analogRead(A0);
}
void loop()
{
➋ lightValue = analogRead(A0);
if(lightValue < calibrationValue - 50)
{
digitalWrite(11, LOW); //red
digitalWrite(10, HIGH); //green
digitalWrite(9, HIGH); //blue
}
➌ else
{
digitalWrite(11, LOW); //red
digitalWrite(10, LOW); //green
digitalWrite(9, LOW); //blue
}
}
将此代码上传到你的 Arduino 板上,并确保你所在的房间光线充足。当你用手遮住光敏电阻器时,RGB LED 应该会亮起,显示青色。如果它没有反应,试着用手将光敏电阻器包住,或者用书籍或杂志遮住它,确保它感应不到任何光线。现在,如果你移开手并将光敏电阻器暴露在光线下,RGB LED 应该会关闭。很酷吧!让我们来看看这如何工作。
准备检查光照强度
首先,草图创建了calibrationValue和lightValue全局变量,但没有给它们赋值。像列表 5-1 中的代码一样,setup()函数为 RGB LED 的每个引脚调用一次pinMode(),将引脚 9、10 和 11 设置为OUTPUT。接下来,草图从光敏电阻器➊读取一个初始的校准值,并将其存储在calibrationValue变量中。这个值将作为草图未来用来比较测得光照强度的基准值,以决定是否开启 LED。
现在,进入loop()函数。loop()函数会重复读取当前的光照强度并将其存储在lightValue➋变量中。每次loop()函数重复时,lightValue的值都会更新。
根据光线水平控制夜光灯
将初始光线水平存储在calibrationValue变量中,并将当前光线水平存储在lightValue变量中,Arduino 可以比较两者,并决定是否打开或关闭夜光灯。你可以通过使用if()语句来指示 Arduino 执行此操作,if()语句是一种控制草图中代码执行流程的结构。它允许 Arduino 根据一个表达式的真假来做出决策,表达式是一个数学语句,它只有两个结果:true或false。你可以在图 5-15 中看到if()语句的基本流程。
图 5-15: if()语句的结构流程

这个草图的if()语句检查表达式lightValue < calibrationValue - 50。<符号表示“小于”,所以这条语句的意思是,“lightValue是否小于calibrationValue减去 50?”如果该表达式为true,草图将执行if()语句下方花括号中的代码。
随着房间变暗,来自传感器电路的电压减小。这个表达式检查lightValue是否明显小于calibrationValue,如果房间变暗,这个表达式的值为true。如果是,草图将引脚 9 和 10 设置为HIGH,并将引脚 11 设置为LOW,以用青色点亮夜光灯。
当if()语句为false时,Arduino 跳过后续的代码。这段草图包含一个else ➌语句,只有在if()语句的表达式评估为false后,else语句才会执行。在else语句中,草图关闭所有三个引脚。
防止误报
如果草图仅需要检查光线水平是否发生变化,为什么要在表达式中从calibrationValue变量中减去 50 呢?将lightValue与小于calibrationValue的数字进行比较,可以增加草图的容忍度。如果你使用lightValue < calibrationValue的表达式,你的夜光灯会在最小的光线变化下闪烁(返回到页面 106 上的“逻辑比较运算符”以了解更多关于<符号的内容)。从校准值中减去 50 可以确保夜光灯在光线水平比校准(初始)测量值低 50 时开启。
重新校准夜光灯
在你美化夜灯之前,最后一个有用的信息是如何重置校准值,以便根据不同的光照水平重新校准。calibrationValue 的值在 setup() 函数中设置,因此它只运行一次。当你的 Arduino 通电时,有两种不同的方法可以重启你的草图。首先,你可以将 Arduino 关掉再重新开机,但这有点麻烦而且不太优雅。第二种方法稍微优雅一些。就像项目 4 一样,你只需按下图 5-16 所示的重置按钮即可重启草图。它的工作原理与计算机或游戏机上的重置按钮一样。每次按下该按钮,夜灯的校准值都会被重置。
图 5-16: 重置按钮,展现其点击时的光辉

每次你将项目搬到一个新房间或新的光照环境时,按下重置按钮重新校准光敏电阻。当你重新校准光敏电阻时,确保它读取的是房间的实际光照条件,并且没有被任何阴影遮挡。
使用 ANALOGWRITE() 创建更多颜色
你不仅限于目前看到的 RGB LED 的颜色。通过混合这些颜色的渐变,你可以创造出天蓝色、橙色、粉红色或其他成千上万种组合。但你不能通过简单地开关 RGB LED 上的标准颜色来创造这些颜色,因此你需要一种方法,只将红色稍微打开,并加入一点蓝色和绿色,来创造例如粉红色。
使用 PWM 创建模拟信号
要以这种方式使用 LED,你需要使用模拟值而不是数字值。在《电子学入门》一书中,我们讨论了模拟与数字的区别(见第 10 页)。数字值只能是开或关,就像普通的电灯开关一样。模拟信号有无限多个值,因此它像调光开关一样工作。
问题在于 Arduino 是一个数字设备,这意味着它只能开或关。为了让它输出一个介于开和关之间的值,你需要使用一种叫做脉宽调制(PWM)的技术,通过数字值模拟模拟信号。Arduino 通过极快地开关数字引脚,并根据信号处于 HIGH(开)与 LOW(关)的时间比例来创建一个看起来像模拟信号的输出。引脚处于高值的时间越长,信号的模拟值就越高。这有时也叫做改变占空比。图 5-17 展示了不同脉宽的占空比信号;75% 占空比的脉冲模拟值比 25% 占空比的脉冲高。
图 5-17: 展示不同脉宽的占空比信号

这很好,但并非所有 Arduino 引脚都支持 PWM。在标准 Arduino 板上,只有某些 GPIO 引脚——即引脚 3、5、6、9、10 和 11——支持 PWM。这些引脚在板上用波浪号(~)标注,在图 5-18 中突出显示。
图 5-18: 标准 RedBoard 上的 PWM 引脚

每当你需要控制某个具有变化值的事物时,例如 LED 的亮度、电机的速度或蜂鸣器的音调,你都需要使用 PWM 引脚来模拟模拟信号。在这里,我们将使用它来控制 RGB LED 每种颜色的亮度,从而混合颜色。对于这个项目,你的 RGB LED 红、绿、蓝引脚已经分别连接到 PWM 引脚 11、10 和 9,因此你不需要更改任何接线,只需修改代码。
使用 analogWrite()混合颜色
为了利用这些 PWM 功能,你需要使用analogWrite()函数,它将 PWM 值写入一个引脚。analogWrite()函数接受两个参数:你想控制的引脚编号和要写入的 PWM 值,范围始终是 0 到 255。0表示完全关闭,255表示完全开启。让我们写一个简单的草图来演示analogWrite()函数。
void setup()
{
➊ pinMode(9, OUTPUT);
}
void loop()
{
➋ analogWrite(9, 2);
}
首先,和其他引脚一样,你需要使用pinMode()函数指定如何使用 GPIO 引脚。你传递引脚编号,并且由于 LED 是输出设备,你传递OUTPUT,就像在过去的项目中一样 ➊。要设置 PWM 值,使用analogWrite()函数设置一个介于 0 和 255 之间的模拟值。在这个示例中,引脚 9(RGB LED 的蓝色阳极)被设置为模拟值2 ➋。将这个草图上传到 Arduino,你应该能看到一个微弱的蓝色 RGB LED。analogWrite()值为 2,使用 PWM 将此 LED 开启约 0.7%的时间,或 2/255 的时间。在继续之前,尝试更改 PWM 值并重新上传几次,感受不同值的强度。
现在你已经掌握了analogWrite()函数,尝试使用它来混合颜色。清单 5-3 创建了一种闪烁模式,使用了比之前更有趣的颜色。
清单 5-3: 多色闪烁
void setup()
{
➊ pinMode(11, OUTPUT); //red
pinMode(10, OUTPUT); //green
pinMode(9, OUTPUT); //blue
}
void loop()
{
➋ analogWrite(11, 153); //dark orchid purple
analogWrite(10, 50);
analogWrite(9, 204);
delay(1000);
➌ analogWrite(11, 155); //pale cerulean
analogWrite(10, 196);
analogWrite(9, 226);
delay(1000);
➍ analogWrite(11, 255); //cadmium yellow
analogWrite(10, 246);
analogWrite(9, 0);
delay(1000);
}
首先,使用pinMode()函数添加另外两个颜色引脚 ➊。然后,通过为三个引脚设置不同的模拟值来循环显示三种不同的颜色。这将设置每种特定颜色的亮度级别,改变最终混合中每种颜色的加入量。
第一组颜色会产生一种蓝紫色 ➋,第二组产生一种灰白色的浅蓝色 ➌,最后一组则产生明亮的黄色 ➍。尝试使用analogWrite()创建不同的颜色。
使用颜色选择器查找 RGB 值
你之前已经创建了一些非常具体的颜色,但很难预测哪些 RGB 值能生成某种颜色。一个简单的方法是使用网上的颜色选择器工具。有很多这样的工具,我们推荐www.colorpicker.com/。这个工具能为你从调色板中选取的颜色提供 RGB 值,如图 5-19 所示。
图 5-19: 来自www.colorpicker.com/的颜色选择器工具。

你要使用的三个数字标记为 R、G、B,分别代表红色、绿色和蓝色。忽略顶部的三个 H、S、B 框,它们是另一种常见的指定颜色的方法,称为HSB(色相、饱和度和亮度)。这是一种在许多颜色混合和图形设计应用中非常有用的技术,但当你能直接控制红色、绿色和蓝色这三种颜色时,它就没那么有用了。
自定义颜色夜灯代码
拥有 RGB 知识后,你现在可以轻松修改你的夜灯代码,通过将digitalWrite()函数替换为analogWrite()来加入你的自定义颜色。清单 5-4 显示了夜灯代码的变化,将颜色值设置为我们在图 5-19 中选择的青绿色。
清单 5-4: 使用analogWrite()替代digitalWrite()命令的最终夜灯代码
int calibrationValue;
int lightValue;
void setup()
{
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
calibrationValue = analogRead(A0);
}
void loop()
{
lightValue = analogRead(A0);
if(lightValue < calibrationValue - 50)
{
analogWrite(11, 66); //red
analogWrite(10, 166); //green
analogWrite(9, 199); //blue
}
else
{
analogWrite(11, 0); //red off
analogWrite(10, 0); //green off
analogWrite(9, 0); //blue off
}
}
到此为止,你的夜灯原型完成了!如果你不喜欢我们选择的青绿色,可以使用颜色选择器工具找到你喜欢的颜色,更新你的analogWrite()函数,使用它的 RGB 值,然后重新上传草图再继续进行。想要浏览一些有趣的颜色建议,可以访问99colors.net/color-names/。
现在代码已经可以运行了,是时候发挥创意,开始制作外壳和灯罩了。
制作夜灯外壳
我们建议使用卡纸而非纸板来制作此项目的外壳(因为卡纸切边更整齐且更容易操作),并使用羊皮纸或透明纸来制作灯罩。让我们开始动手吧。
卡纸制作
我们会展示一个基础的夜灯设计来帮助你入门,但我们鼓励你稍后通过自定义设计来发挥创意。或者,如果你有信心,也可以完全不使用我们的模板,自己从头设计夜灯外壳。
裁剪部件
这个项目有两个模板:一个是夜灯的结构模板,另一个是灯罩模板。灯罩可以由任何类似打印纸厚度的材料制作,但我们发现完全透明的材料(如羊皮纸)效果最好。如果你有打印机,可以在本书的资源文件中打开图 5-20 的模板,直接打印到你的材料上然后裁剪出来。
图 5-20: 夜光灯外壳模板(非全尺寸)

请注意,夜光灯外壳的底部有一个方形开口,用于电线的通过。我们还发现,去掉四个侧面板中的一个会更方便,这样可以更容易地将电线引回到较大的面包板上。是否包括第四个面板,将由你决定。
一旦你的模板部件复制到卡纸和透明材料上,开始切割它们。我们强烈推荐使用锋利的工艺刀和金属尺来确保项目的边缘干净,如图 5-21 所示。记住工艺刀使用安全:总是拉刀片(不要推),并进行多次切割。
图 5-21: 从卡纸上切割模板

组装部件
将所有部件摆放在你面前。你应该有六个结构部件和四个遮光片部件,正如图 5-22 所示。
图 5-22: 切割好的单独部件,准备组装

首先,拿起底部部件(中央有开口的部件)。将其平放在你面前,并将左边和顶部边缘向你折叠,以形成一个直角。将左边缘上的标签向内折叠,并用少量胶水将其固定在顶部边缘,正如图 5-23 所示。对底部部件的其他四个角重复此步骤,然后使用相同的方法组装顶部部件。
注意
根据你的卡纸厚度,你可能想用工艺刀轻轻划出折痕线。这将使角部更加锋利、干净。
图 5-23: 底部和顶部部件的组装

接下来,将四个侧面部件沿中线纵向折叠,形成一个漂亮的 90 度角。模板中有虚线可以指导你。一旦顶部和底部组装好并且侧面折叠完成,六个部件应该如图 5-24 所示。
图 5-24: 夜光灯外壳的组装部件

最后,拿起每个角部件并将其粘到底部,正如图 5-25 所示。
图 5-25: 将每个角部件粘到底部

当你完成粘合角落时,应该会有四个立起的角撑结构。我们发现,在粘合顶部部件之前先将遮光片粘好更为容易,所以只需在每个支撑结构的内边缘涂上一小点胶水,然后将遮光片按图示位置压入,正如图 5-26 所示。你可能只想使用三个遮光片,以便让电线能够通过。
图 5-26: 在加上顶部部件之前,先将遮光片粘好。

当你添加完所有面板后,添加顶部件。只需在每个角落上涂一点胶水,以固定顶部件,如图 5-27 所示。
图 5-27: 添加最终的顶部件

现在,你应该已经有了一个完成的夜灯外壳,像图 5-28 中的那样!
图 5-28: 最终的夜灯外壳

将电子元件放入内部
你有几个选择可以将电子元件转移到新的项目中:将面包板和 Arduino 底座放在夜灯下方,或者仅将 LED 移入夜灯内。我们选择了第二种方法。
首先,将 Arduino 从电脑上拔下,然后将 RGB LED 移到迷你面包板上。迷你面包板的工作原理与较大的面包板相同,只是没有电源轨,且较短。将跳线添加到迷你面包板上,以便将 LED 连接回原始电路,如图 5-29 所示。请注意,RGB LED 的四个引脚分别位于不同的行:一个是红色引脚,一个是地线引脚(最长的),一个是绿色引脚,一个是蓝色引脚。
图 5-29: 使用迷你面包板将 RGB LED 移入夜灯内

现在,将每根跳线的另一端连接到面包板上对应 RGB LED 引脚所在的行,如图 5-30 所示。
图 5-30: 将迷你面包板连接回主电路

如果你留了一边空着,只需像我们在图 5-31 中所做的那样,将跳线从灯罩后面引出。否则,仔细地将灯罩放在迷你面包板上,并用胶带将跳线固定在桌面上,以确保灯罩平稳,或者在灯罩底部的纸板上做几个缺口,以便跳线通过。
图 5-31: 将灯罩放置在迷你面包板上

让它发光!
如果你有外部电池包,现在可以放入四个 AA 电池,并将桶形插头插入 Arduino。电路板应该仍然是编程并运行的,因此只需关闭灯光。你应该得到一个柔和发光的夜灯,就像我们在图 5-32 中的那样。
图 5-32: 灯光熄灭!我们最终的发光夜灯项目。

进一步扩展
这个项目使用了许多新的技能和知识,但它仍然具有大量进一步修改的潜力。随着你在 Arduino 方面技能的提升,无论是设计还是代码方面,都有许多可以尝试的地方。
黑客
一个很好的草图技巧是,当夜光灯开启时,让颜色周期性地变化。你可以参考你的红绿灯项目代码,获得一些提示。一种方法是在if()语句中加入一些简单的闪烁代码,而不仅仅是使用digitalWrite()函数,来构建一个颜色动画。
你还可以尝试使用else if()命令为不同的光照读取值添加不同的颜色,而不是仅仅使用if()或else。这种方法的基本结构可能类似于列表 5-5。
列表 5-5: 三阶段夜光灯代码示例
if (lightValue < calibrationValue - 200)
{
//do if it is completely dark
digitalWrite(11, HIGH);
digitalWrite(10, LOW);
}
else if (lightValue < calibrationValue - 50)
{
//do if it is a little dim
digitalWrite(11, LOW);
digitalWrite(10, HIGH);
}
else
{
//do if it is bright
digitalWrite(11, LOW);
digitalWrite(10, LOW);
}
你可以看到草图使用了else if()来设置光照值的类别。然后,你需要为每个类别设置颜色。
修改
你的夜光灯设计完全由你决定,所以如果你愿意,可以创造一个全新的设计。你可以使用许多工具来设计一个外壳,然后通过自动化方式制作它,比如使用轻木激光切割、3D 打印(使用 ABS 或 HIPS 塑料等材料)甚至 CNC 铣削、雕刻或加工。计算机控制的制造将产生非常干净且精确的部件,从而成就一个更精致的产品。我们鼓励你探索这些可能性,创造出更持久、更精致的作品。如果你没有这些工具,可以尝试查找你所在城市的本地黑客空间或创客空间。通常它们会提供你可以使用的设施和工具。
我们提供了一些示例模板和项目创意,你可以在www.nostarch.com/arduinoinventor/上查看和改编这些内容。图 5-33 展示了如何通过一些有趣的图案来打破灯罩设计的示例。
图 5-33: 来自我们设计模板的有趣设计。

第七章:6 平衡梁
在这个项目中,你将使用旋转旋钮和舵机电机(能够进行精确运动的小型电机)构建一个桌面平衡梁游戏。游戏的目标是让小球在梁上来回滚动,而不掉下去。你将通过旋转旋钮来控制舵机的位置。舵机移动时,梁也会随之移动!准备好开始了吗?
图 6-1 显示了完成的项目。这是一个完全由纸板和一些家庭材料制成的简单机制。
图 6-1: 完成的平衡梁项目

收集材料
本项目的电路使用的零件相对较少,不过我们将介绍两种新的硬件:舵机电机和电位器。请查看图 6-2 至 6-4 中展示的电子零件和其他材料。
电子零件
• 一个 SparkFun RedBoard (DEV-13975)、Arduino Uno (DEV-11021) 或任何其他兼容的 Arduino 板
• 一根 USB Mini-B 电缆(CAB-11301 或你板子的 USB 电缆;未显示)
• 一个无焊面包板(PRT-12002)
• 一个 10 kΩ电位器(COM-09806)
• 一个微型舵机电机(ROB-09065)
• 公对公跳线(PRT-11026)
注意
本项目中使用的所有零件均为 SparkFun 发明者套件中的标准配件。
图 6-2: 平衡梁的组件

其他材料和工具
• 铅笔或马克笔
• 工艺刀
• 金属直尺
• 钳形钳
• 线切割钳
• 胶水(热熔胶枪或工艺胶水)
• 微型螺丝刀
• 剪刀(未显示)
• (可选)电钻以及 1/4 英寸、1/8 英寸和 1/16 英寸钻头
• 两张纸板(大约 8.5 × 11 英寸)
• 平衡梁模板(请参见图 6-16 在第 167 页)
• 一根竹签
• 一根小饮料吸管(竹签应该能够松松地插入吸管中)
• 一个乒乓球
• 一个中型回形针
图 6-3: 推荐的建筑材料

图 6-4: 推荐工具

新组件
在之前的项目中,你主要使用 Arduino 来控制 LED,但现在是时候拓展并探索其他组件了。本项目介绍了一种新的传感器,叫做电位器,以及电机,特别是舵机电机。
电位器
在这个项目中,你将使用电位器来控制平衡梁的运动。电位器是一种被称为可变电阻的传感器,这意味着它是一个电阻器,其值可以变化。
一个电位器通常有三个接脚或连接点,并由图 6-5 所示的符号表示。
图 6-5: 电位器的电路图

电位器有多种形状和尺寸,部分如图 6-6 所示。有些看起来像旋钮,有些是滑块,还有一些需要小螺丝刀来调节。无论它们的外观如何,它们的工作原理都是一样的。而且它们无处不在——在家里,你可能会在餐厅灯的调光开关、立体声音响的音量旋钮或 DVD 播放器等设备中找到它们。
图 6-6: 各种形状和尺寸的电位器。我们将使用左侧的那个。

一个电位器在图 6-7 中标记为 A 和 B 的两条对面脚之间具有固定的电阻值。电位器有各种电阻值,但在这个项目中,你将使用一个 10 kΩ的电位器。当你转动旋钮或移动滑块时,电位器的第三条脚(标记为 C),称为刮臂,会在电阻器上向上或向下移动,B 和 C 之间的电阻值会发生变化。正是这个电阻值被应用到电路中。
如果你顺时针转动旋钮,刮臂会向 A 移动,C 和 B 之间的电阻增加;如果你逆时针转动旋钮,刮臂会向 B 移动,电阻减小。图 6-7 展示了刮臂移动如何影响电阻。
图 6-7: 电位器的各种位置

如果你将 A 接到 5V,B 接到 GND,将 C 接到 Arduino 的模拟输入引脚,那么这个电路就开始类似于你在项目 5 中使用的电压分压器。当你转动旋钮时,你可以调节 C 端的电压,范围在 0V 和 5V 之间。这种设置有时也被称为可调电压分压器。
伺服电机
伺服电机(简称伺服)是一种特殊类型的电机,旨在将一个臂(或角度控制器)旋转到一个特定角度,这个角度你将在草图中确定。大多数伺服电机的旋转范围为 180 度,但有些可以旋转 360 度;这些称为连续旋转伺服电机。在这个项目中,你将使用一个标准的 180 度爱好者伺服电机,如图 6-8 所示。
图 6-8: 标准爱好者伺服电机

伺服电机广泛应用于成千上万种不同的产品,从模型车和飞机到你车上的速度计,再到制造它的机器人手臂。
黑色盒子里是什么?我们已经打开了一个,省得你自己动手——见图 6-9。
图 6-9: 伺服电机内部结构

在伺服电机内部,有三个主要部件:电机、齿轮传动和控制电路。当电压施加到电机上时,它会驱动齿轮传动,从而转动伺服电机的转轴。转轴的旋转位置由控制电路控制。齿轮传动的一部分是电位器,它随着电机的旋转而旋转。记住,电位器是一个简单的传感器,根据旋转的多少来改变电阻,当它作为可调电压分压器连接时,电压会随着电位器的旋转而变化。控制电路读取输入信号(此时来自 Arduino)和电位器值,并对比它们。当两个值相等时,电机停止并保持其位置。
一个伺服电机依赖于 PWM,这是我们在 “使用 analogWrite() 创建更多颜色” 中介绍的一个概念,见第 138 页。为了控制伺服电机的位置,Arduino 会发出每 20 毫秒脉冲一次的 PWM 信号。脉冲的宽度对应于伺服电机的特定旋转位置。图 6-10 通过显示伺服电机 0 度、90 度的中点和 180 度最大位置的最小 PWM 脉冲宽度来说明这一点。类似于闪烁 LED,你可以使用 Arduino 创建一个非常短的脉冲,持续 1 毫秒开,19 毫秒关,将伺服电机移动到 0 度的角度。
图 6-10: 标准伺服电机范围的 PWM 占空比

要将伺服电机的角度设置为 0 度,你可以使用如下代码:
void setup()
{
pinMode(9, OUTPUT);
}
void loop()
{
digitalWrite(9, HIGH);
delay(1);
digitalWrite(9, LOW)
delay(19);
}
这段代码将引脚 9 设置为 HIGH 持续 1 毫秒,然后立即将引脚设置为 LOW 持续 19 毫秒。一旦 19 毫秒过去,它需要再次将引脚设置为 HIGH 持续 1 毫秒,以维持定时周期。如果你的代码忙于管理像这样的定时,你就无法在不影响脉冲的定时和伺服控制的情况下添加任何其他内容。幸运的是,Arduino 有一个简便的技巧来简化伺服电机的控制方式:使用一个库。库是一个包含额外代码的文件,你可以将其与草图一起使用,以执行特定任务或更方便地使用某些部件。Servo 库处理了所有驱动伺服电机到特定角度所需的脉冲定时。
在这个项目中,你将使用 Arduino 根据传感器——你的电位器——的电压输出来移动一个平衡梁。代码将使用传感器的电压读数来设置伺服电机在给定旋转角度下的脉冲宽度,从而决定梁的角度。
好消息是,Arduino,特别是 Servo 库,已经为你完成了所有的繁重工作!了解脉冲宽度如何控制伺服电机的位置是很有意义的,但最终,软件会为你处理这些。
构建平衡梁原型
现在您了解了理论部分,接下来您将构建平衡梁的电路。您将从连接伺服电机开始,然后添加可变电阻器;图 6-11 展示了完整的电路。
请注意,伺服电机有一个三针母头。为了将其连接到电路,您需要使用母对母跳线。取三根短的母对母跳线,将它们连接到母针,如图 6-12 所示。为了方便区分各个引脚,可以使用与伺服电机线缆颜色相对应的跳线:黑色、红色和白色分别代表地线、电源线和信号线。现在,将伺服电机连接到 Arduino。
图 6-11: 平衡梁原型电路

图 6-12: 向伺服电机添加母对母跳线延长线

电路连接非常简单:将 Arduino 的 5 V 和 GND 连接到面包板左侧的电源轨道。将伺服电机的地线(黑色)连接到面包板的地线轨道,电源线(红色)连接到 5 V 轨道。将信号线直接连接到 Arduino 的 9 号引脚。完整的电路图请参见图 6-13。
图 6-13: 伺服电机连接到信号、电源和地线

最后,将伺服电机的旋钮安装到伺服电机的中心轴上。旋钮是伺服电机的不同形状的臂,与中心轴一起旋转,使得使用和安装物品更加方便。此时,选择伺服电机附带的任意一个旋钮,并将其按压安装到伺服电机的中心轴上,如图 6-14 所示。稍后您会安装特定的旋钮,但现在我们只需要让旋转更容易看到。
图 6-14: 按压安装伺服电机的旋钮(来自附带选项)

如果伺服电机开始移动或表现异常,只需将其黑色线缆从地线轨道中断开即可停止伺服电机。为了安全起见,在上传代码之前,最好保持黑色线缆断开。
现在,接入可变电阻器。面包板上有足够的空间,您可以将可变电阻器放置在任何位置,确保每个引脚都在自己的行中。将两侧的引脚连接到 5 V 和地线轨道,中央引脚直接连接到 Arduino 的模拟输入引脚 A0,如图 6-15 所示。
图 6-15: 完整的平衡梁电路

此时,您只连接了一个伺服电机和一个可变电阻器。为了赋予项目平衡的功能,您需要编写程序。
编程平衡梁
要在 Arduino 上使用伺服电机,你需要使用 Servo 库——正如前面提到的,这是一个预先编写的代码集合,扩展了你草图中代码的命令和功能。它为你提供了更多的功能和特性,简化了使用外部硬件与 Arduino 进行交互。例如,Servo 库包括将伺服电机连接到特定 Arduino 引脚的代码,将伺服电机移动到特定角度,甚至从引脚上断开伺服电机。
在你编写完整项目之前,你可以上传一个快速测试草图,检查你的伺服电机是否正常工作。
测试伺服电机
这是一个控制伺服电机的简单示例草图。通过选择 文件 ▸ 新建 来开始一个新的草图,然后在 Listing 6-1 中输入草图:
LISTING 6-1: 一个伺服电机的“Hello world”
➊ #include<**Servo**.h>
➋ **Servo** myServo;
void setup()
{
➌ myServo.attach(9);
}
void loop()
{
➍ myServo.write(90);
}
要使用 Servo 库,首先调用 #include<Servo.h> ➊,这告诉 Arduino 包含包含 Servo 库代码的Servo.h 文件。这将库的功能和定义添加到草图中。请注意,这是少数几个没有在行尾加分号的情况之一。在 Arduino 编程中,# 符号表示接下来的代码是预处理指令,这是一段特殊的代码,应该在草图的其余部分执行之前先执行。当你编译一个草图时,首先运行的是预处理器,它会查找所有以 # 符号开头并且没有分号结尾的行,并首先执行这些行。#include 指令告诉预处理器在编译草图中的代码之前包含命名文件中的所有代码。
你还可以使用下拉菜单通过选择 草图 ▸ 引入库... 来添加一个库,然后选择你想使用的库(在这个例子中是 Servo)。这将自动将 #include 语句添加到你的草图中。如果你不记得 #include 命令或库名称的确切语法,这个选项非常有用——例如,当你第一次使用某个库时。
该库允许你创建一种名为对象的数据结构。对象只是一个包含预定义变量和函数的容器。与对象关联的函数被称为方法。在这个草图中,Servo myServo; 这一行创建了一个名为 myServo 的新的 Servo 对象 ➋。
你可以给对象取任何你喜欢的名字,但我们建议使用具有描述性的名称,如 myServo,这样更容易识别。现在,你可以使用这个名字来引用 Servo 库中所有可用的伺服电机命令。例如,方法 myServo.attach() 告诉 Arduino 伺服电机连接到哪个引脚。如果你有多个伺服电机,每个电机都会有一个唯一的名称,这样你就可以独立控制它们。
举个例子,想象一个机器人臂,它在肩部、肘部和手腕处使用伺服电机。它的代码会创建三个 Servo 对象,分别命名为 shoulderServo、elbowServo 和 wristServo,这样你就可以精确地定位每个关节,并使它们的角度不同。每个 Servo 对象都有自己的一组方法,可以单独使用。
对于平衡梁,你只需要使用一个伺服电机。草图的设置告诉 Arduino,你在 9 号引脚上连接了一个伺服电机,方法是 myServo.attach(9) ➌。然后它会告诉 Arduino 将伺服电机移动到 90 度的位置,方法是 myServo.write(90) ➍。伺服库会在后台将旋转的角度转换为适当的脉冲宽度。这是 write() 方法内置的功能。
现在,将伺服电机的黑色线接入地线,并将代码上传到 Arduino,伺服电机将旋转至 90 度。此时将伺服电机连接好是安全的。
注意
尽管伺服电机的运动范围为 180 度,我们建议将 write() 的值保持在 10 至 170 度之间,特别是对于塑料齿轮的伺服电机。过度拉伸伺服电机的范围可能会造成无法修复的损害。
要再次移动伺服电机,只需将伺服电机运动范围内的另一个数字(10–170)传递给 write() 方法,并重新上传草图。稍微玩一下伺服电机,传递不同的数值。
好的,你现在知道如何让伺服电机只动一次了。现在,下面的代码让它开始真正地动起来。列表 6-2 将伺服电机的控制放入一个循环中,并重复动作。
列表 6-2: 伺服电机闪烁草图
#include<**Servo**.h>
**Servo** myServo;
void setup()
{
myServo.attach(9);
}
void loop()
{
myServo.write(10);
delay(1000);
myServo.write(170);
delay(1000);
}
这段代码是项目 1 中的闪烁草图的伺服电机版本。伺服电机先移动到 10 度,等待 1 秒钟,再移动到 170 度,等待 1 秒钟,然后重复这个过程。我们亲切地称之为“机器人行军”,因为当你有 20 多个人同时做这个动作时,它听起来像是一支机器人军队在行进,准备征服世界。
哇!你真的进展很快。伺服电机变得更有趣的是,当你能够自己控制伺服电机,而无需每次重新编程它时。现在,是时候让电位器参与进来了。
完成平衡梁草图
对于最终的草图,你将编程电位器来控制伺服电机的旋转。按照 列表 6-3 所示修改你的草图:
列表 6-3: 使用 map() 函数通过电位器控制伺服电机
#include<**Servo**.h>
**Servo** myServo;
➊ int potVal;
int anglePosition;
void setup()
{
myServo.attach(9);
}
void loop()
{
potVal = analogRead(A0);
➋ anglePosition = map(potVal, 0, 1023, 10, 170);
myServo.write(anglePosition);
delay(20);
}
这个草图读取电位器的值,将其转换为角度值,然后将该值写入伺服电机。这里有一些新的命令,我们将一步一步地讲解。
这段代码的顶部部分与前两个例子列表非常相似。它包含了 Servo 库并创建了一个名为myServo的Servo对象。它还声明了两个全局变量➊,分别是potVal和anglePosition。这些变量将用于分别存储电位器的原始值和计算得出的伺服电机角度位置。
在loop()函数中,变量potVal存储来自analogRead(A0)函数的原始模拟到数字转换值。当你旋转电位器上的旋钮时,擦拭脚的电压会在 0V 到 5V 之间变化。记住,analogRead()会将 0V 到 5V 的电压转换为 0 到 1,023 之间的数字。然而,0 到 1,023 的数值对于控制伺服电机并不十分有用。正如我们之前提到的,伺服电机需要保持在 10 到 170 度之间。
幸运的是,Arduino 有一个内置的map()函数,允许你将一个数值范围映射到另一个范围。变量anglePosition存储一个角度位置,这个位置是通过map()函数从potVal计算得出的➋。map()函数使用五个参数:map(input, fromLow, fromHigh, toLow, toHigh)。在这个例子中,它将potVal的值从 0 到 1,023 的范围映射到 10 到 170 的新范围。这是 Arduino 中一个非常方便的函数,使得数值范围的缩放和转换变得非常简单!
这个草图还增加了一个短暂的 20 毫秒延迟,给伺服电机足够的时间移动,然后再读取电位器的值。20 毫秒的延迟是伺服电机所需的最小延迟。你可能还记得,它是用来控制角度的 PWM 信号的时间周期。
一旦你更新了这个草图,将其上传到你的 Arduino。现在,当你旋转电位器时,伺服电机会随着它一起移动。非常酷!接下来,你将利用你刚获得的超能力,基于它构建一个平衡游戏。
制作平衡木
使用这种控制伺服电机的酷方法,我们认为创建一个桌面游戏会非常有趣。你将创建一个平衡木,并通过电位器和伺服电机来控制它。一个乒乓球会在平衡木上滚动,而你的目标是让乒乓球尽可能靠近平衡木的两端而不掉下来。
剪裁各部分
下载提供的模板,地址是 www.nostarch.com/arduinoinventor/(如图 6-16 所示),打印出来,然后将其描摹到你的纸板上。我们设计这个项目时,尽量将它缩小到尽可能小的纸板上。
图 6-16: 平衡木框架模板(非全尺寸)

使用工艺刀沿每个形状的周边线切割所有实线,以及电机支架的切割线。现在不要刻划任何部件;你将在后续过程中逐步进行。记住在切割时要注意安全。使用金属尺和锋利的工艺刀,如图 6-17 所示,并且要慢慢进行。使用钻机或工艺刀制作设计中六个不同的孔。如果使用钻机,你将需要一个 1/4 英寸的钻头来打入螺丝刀的通道孔,一个 1/8 英寸的钻头来打入轴孔,和一个 1/16 英寸的钻头来打入支架安装孔和两个电机安装孔。
图 6-17: 从模板中裁剪框架部件

完成切割后,你应该会得到六个像图 6-18 中所示的部件。
图 6-18: 所有纸板部件已裁剪

制作梁
取最长的一块,它将作为实际的梁,并小心地在其长度上沿虚线刻划。这将使你能够弯曲梁,使其托住球体。我们设计了这个模板,使得梁的长度为 11 英寸,等于标准 8.5 × 11 英寸纸张的长度。
接下来,准备支架安装。这是一个小的梯形纸片,宽约 2 1/4 英寸,高 1 英寸。你将使用这块纸片将伺服电机连接到梁上。沿着它刻划并将其弯成直角,如图 6-19 所示。
图 6-19: 准备支架安装

接下来,剪短吸管,使其长度为 1 3/4 英寸,并将其粘在梁的中心线上,如图 6-20 所示。
图 6-20: 将吸管粘在梁的中点

现在,将没有钻孔的支架安装半部粘在吸管旁边,如图 6-21 所示;确保带孔的那一半面朝向你,当吸管在右侧时。这个位置很重要,以便它能与伺服电机的安装臂配合。
图 6-21: 将支架安装到梁上

接下来,将梁的两侧弯起,形成一个托住球体的摇篮,如图 6-22 所示。
图 6-22: 弯曲梁的两侧以形成摇篮

使用较小的梯形纸片来固定梁的两端,并将梁固定在一起,以保持摇篮的形状。我们建议使用热熔胶枪,这样部件就能牢固地固定,正如在图 6-23 中所示。
图 6-23: 将端部件粘贴到梁上

接下来,使用剪线钳将竹签剪成大约 3 1/4 英寸长。我们建议使用竹签的钝端。将竹签插入饮料吸管中,形成平衡梁的轴心(如图 6-24 所示)。
图 6-24: 将剪好的竹签定位,使其两侧均匀突出

构建底座并安装伺服电机
现在你将开始制作平衡梁的底座。按照图 6-25 所示,打槽以便将底座部件弯曲成形。
图 6-25: 打槽底座部件的两侧

在打槽后,将两侧弯曲形成 U 形,如图 6-26 所示。对两个部件重复此操作。
图 6-26: 弯曲底座部件的两侧

在将底座粘合之前,你需要先安装伺服电机。将伺服电机从面包板电路中取出。模板中有一个小的方形切口,应该刚好适合超小型伺服电机。将伺服电机插入,使电机面朝内,如图 6-27 所示。
图 6-27: 插入伺服电机

你的伺服电机应附带三颗小螺丝,其中一颗较短,另外两颗较长。使用两颗较长的螺丝将伺服电机固定在位,如图 6-28 所示。如果没有螺丝,你也可以使用少量热熔胶来固定电机。
图 6-28: 使用两颗较长的螺丝将伺服电机固定到位

现在,选择一个约 0.5 英寸长的单面伺服电机齿轮。轻轻地将其推入伺服电机的末端,如图 6-29 所示。确保它已经牢固安装在伺服电机上,然后将伺服电机调整到 0 度。用手指轻轻地将伺服电机逆时针旋转,直到停止。你会听到伺服电机内的小齿轮转动。确保慢慢移动伺服电机;齿轮通常是塑料制的,可能会破裂。
图 6-29: 安装单面伺服电机齿轮

当伺服电机齿轮旋转到最大逆时针位置时,取下齿轮并重新安装,使其朝上,如图 6-30 所示。这将使连接连杆到梁上变得更容易。
图 6-30: 将伺服电机齿轮调整到 0 度位置

最后,使用伺服电机附带的最后一颗小螺丝将号角固定到位,以确保号角不会意外弹出。拧紧螺丝时,号角可能会旋转。没关系——这不会损坏任何部件,但你可能希望在拧紧螺丝时用手指固定号角,以防止号角旋转。如果你丢失了螺丝,也不用担心;你可以不使用螺丝,若号角滑出,只需重新安装即可。如果需要重新定位伺服电机臂,你需要拆下这颗螺丝,这也是为什么我们在底座模板的另一侧加了一个孔。
接下来,你需要一个联动装置,将伺服电机号角与梁连接起来。为此,你需要使用一把尖嘴钳将一只中等大小的回形针弯曲成型。图 6-31 展示了整个过程的步骤。
图 6-31: 切割、弯曲和塑形伺服电机联动的步骤

1. 使用钳子将回形针拉直,保持一端的小钩子。
2. 将回形针修剪至大约 2 英寸长。
3. 将回形针的一端从钩子处大约 1 1/2 英寸的位置弯开。
4. 进行最后的弯曲,形成一个大约 3/8 英寸深的小钩子。完成后,伺服电机联动的长度应约为 1 1/2 英寸(图 6-32)。考虑到我们模板的几何形状,这个长度刚好合适。如果你设计的是自己的外壳,可能需要稍微调整这个长度,以确保伺服电机号角能正确连接到梁上。
图 6-32: 回形针联动的最终弯曲

最终组装
现在是最后的组装步骤!首先,将两块底座粘合在一起。将两个方形卡片的两端粘合,先从与伺服电机相对的侧面开始(图 6-33 和 6-34)。这样你就可以腾出空间将手放入并连接伺服电机号角的联动装置。
图 6-33: 最好的粘接两件物品的方法是使用蛇形或 S 形胶水图案。

图 6-34: 首先固定底座的远端。

在伺服电机一侧保持开放,拿起弯曲回形针的原始钩端,将其钩入伺服电机号角的最后一个孔,如图 6-35 所示。
图 6-35: 将回形针挂过伺服电机号角的最后一个孔

四连杆联动与将伺服电机连接至完成任务
将伺服电机旋转运动转化为平衡梁上下运动的机制叫做四杆联动。我们设计了这个模板,使联动的长度大约为 1 1/2 英寸,并假设伺服电机的转盘长度为 1/2 英寸。我们使用这些测量值来计算伺服电机和梁的运动。如果你脑海中浮现出圆圈、弧线、支点以及大量复杂的几何形状,不用担心:我们已经完成了所有的复杂部分。下图展示了四杆联动的工作原理,其中联动和梁的支点被突出显示。

四杆联动是将物体的旋转运动(例如伺服电机的旋转)转换为不同运动(如梁的上下运动)的一种神奇方式。工程师和机器人专家经常使用这些机制和联动来让物体移动。
将联动装置的另一端通过电枢支架上的孔挂入,如图 6-36 所示。
图 6-36: 将回形针联动的另一端挂到电枢支架上

现在,将轴插入底座的一侧,小心地对齐另一侧,插入轴并通过匹配的孔,然后将底座另一端的标签粘合在一起(图 6-37 和 6-38)。
图 6-37: 将平衡梁安装到底座上

图 6-38: 完成的平衡梁项目

最后,将伺服电机重新连接到面包板电路中(图 6-39)。给 Arduino 供电,伺服电机应自动调整到正确的位置。旋转电位器并进行测试,确保联动和支点都按预期移动。如果没有,检查所有组件是否仍在原位,并确保没有掉落任何部件。
作为最后一步,我们建议添加一个额外的矩形底座支撑件。底座应大约为 2 × 3.75 英寸。将这块支撑件插入你的外壳底部,以增加额外的支撑。
图 6-39: 将伺服电机重新连接到面包板电路

到此为止,你的项目已经完成!现在,找一个乒乓球或弹珠,测试你的控制和精度技能。你现在有了一个游戏,当你应该做一些更有生产力的事情时,可以玩一玩。你能在不掉落的情况下将球来回滚动多少次?挑战一个朋友,看看谁更厉害!
进一步探索
这个项目是了解 Arduino 中伺服电机和库的一个很好的入门。这里有很大的潜力,我们希望分享一些起点,供你玩转伺服电机。
黑客
将电位器替换为来自项目 5 的光传感器电路。你需要添加一个 10 kΩ电阻,并调整你使用的缩放值。现在,把手上下移动在光传感器上方来控制球的运动。去挑战一下你的朋友吧!你是更擅长使用光传感器还是电位器呢?
修改
你可以为你的平衡梁添加一个“自动驾驶”模式,让它自动平衡球。为此,你需要在电路中添加一个开关。正如你在项目 5 中看到的,开关类似于按钮,能够在电路中接通或断开连接,但在开关中,连接会保持不变,直到再次切换。你将使用的开关称为单刀双掷(SPDT)开关,如图 6-40 所示。这是一个专业术语,意味着有一个公共引脚和两个可以连接的选项。当开关处于最左边位置时,它连接中心引脚和左边引脚;当开关处于最右边位置时,它连接中心引脚和右边引脚。
图 6-40: 单刀双掷开关

当正确接线时,这个开关将充当开关,允许你读取它是设置为 5V 还是接地。将开关放置在面包板上,确保每个引脚都有自己的接孔。我们将开关放置在面包板的顶部,如图 6-41 所示。与电位器相似,使用两根较短的跳线将开关的两个外侧引脚连接到面包板的 5V 和接地电源轨。使用第三根跳线将开关的中心引脚连接到 Arduino 的第 12 引脚。该电路的完整示意图见图 6-41。
图 6-41: 带有模式选择开关的最终电路

中心引脚是信号引脚,会根据开关的位置读取HIGH或LOW。你将使用这个信号引脚与一些基本逻辑配合,切换手动控制(使用电位器)和自动驾驶(使舵机自行前后移动)模式。
从本书的资源网站www.nostarch.com/arduinoinventor/下载并上传P6_AutoBalanceBeam.ino草图到你的 Arduino。查看草图中的注释,了解它是如何工作的。
记住,如果在切换自动驾驶模式时光束没有居中,球很可能会掉下来。可能需要尝试几次,但当你成功时,它看起来就像魔法一样!看看我们的视频,展示了这里运行的情况:www.nostarch.com/arduinoinventor/。
第八章:7 微型桌面温室
温室有各种形状和大小,从由塑料薄膜制成的小型室内温室到占地几千平方英尺的大型工业温室应有尽有。然而,并非每个人都想要一个全尺寸的温室,因此在本项目中,你将构建一个较小的模型,可以放在桌面上(见图 7-1)。
图 7-1: 微型桌面温室

在传统的温室中,透明玻璃或塑料窗格允许光能进入,来加热温室的内部,而温室被密封以困住温暖的空气,从而导致整体温度的升高。当然,危险在于温室可能会变得过于炎热。为了调节温度,许多温室配有风扇和自动通风口,当温室内温度过高时,自动开启顶部的窗户以通风。
你的温室也将配备一个自动通风口。你将构建一个温室控制器,监测温度,如果温度过高,窗户将打开,风扇将启动。
温室效应
温室足够温暖,可以全年种植蔬菜,因为它们能够捕捉并储存能量。地球的大气层类似于温室。太阳的热量由地球辐射出来,然后被大气层反射并捕获。这种独特的特性被称为温室效应,它负责保持我们星球的温度适宜并宜居。如果没有它,我们星球的温度将接近 0 华氏度(–18 摄氏度)!
温室效应的另一个常见例子是夏天中午的汽车。若车窗关闭,车内温度可以比外部温度高出 20 到 30 度。这就是为什么你绝对不能把宠物留在车内——尤其是在夏天!
收集材料
为了提升自动通风口,本项目使用了一种与第 6 项目中平衡梁使用的伺服电机类似的电机。我们还将在这个项目中介绍三种新零件:用于风扇的小型直流电机,晶体管用来控制电机,以及温度传感器用来检测温室内的温度。
当你收集零件时,你会发现晶体管和温度传感器非常相似——它们都是小巧的三脚设备,带有一个圆形的黑色塑料端,边缘是平的(见图 7-2)。为了区分它们,可以将平边对准光源,你应该能看到一些印刷标记;温度传感器上应标有字母TMP。收集好零件,见图 7-3 和 7-4,然后我们开始吧!
图 7-2: TMP36 温度传感器(左)和 2N2222 晶体管(右)

电子元件
• 一个 SparkFun RedBoard(DEV-13975)、Arduino Uno(DEV-11021)或任何其他兼容 Arduino 的板子
• 一根 USB Mini-B 电缆(CAB-11301 或你板子的 USB 电缆)
• 一个无焊面包板(PRT-12002)
• 一个 330Ω电阻(COM-08377,或 COM-11507,包含 20 个包装)
• 一个二极管(COM-08588)
• 一个 NPN 晶体管—2N2222 或 BC337(COM-13689)
• 一个 TMP36 温度传感器(SEN-10988)
• 一个爱好电机(ROB-11696)
注意
带星号()的零件不包含在标准 SparkFun 发明者套件中,但可以在单独的附加套件中找到。*
• 一个亚微型伺服电机(ROB-09065)
• 男对男跳线(PRT-11026)
• 男对母跳线(PRT-09140*)
图 7-3: 微型桌面温室的组件

其他材料和工具
• 铅笔(未显示)
• 工艺刀
• 金属尺
• 尺子
• 尖嘴钳
• 线剪
• 胶水(热熔胶枪或工艺胶水)
• 遮蔽胶带(未显示)
• 纸板(约一张 11 × 17 英寸或三张 8.5 × 11 英寸的纸)
• 外壳模板(请参见图 7-18 和第 208 页)
• 一张透明胶片(8.5 × 11 英寸,未显示)
• 1 个中型回形针(未显示)
图 7-4: 推荐的工具和材料

新组件
首先,让我们看一下新的组件,从温度传感器开始。
TMP36 温度传感器
你已经知道如何测量光照强度。通过这个小巧的传感器,你还可以测量温度。TMP36 是最容易使用的温度传感器之一。传感器本身被封装在一个小型塑料外壳中,形状像一个带平边的圆柱体,并且只有三个引脚。(记得将平边朝向光源,以便识别字母TMP,这样就不会将其与晶体管混淆。如果显示的是 2N2222 或其他内容,那就是错误的部件。)
正确连接电源后,TMP36 传感器将产生与其所感应的温度成正比的电压。类似于你在项目 5 中测量光照强度或在项目 6 中测量电位器位置的方式,你可以使用analogRead()来测量该传感器上的电压。我们将在本项目中展示如何将该电压转换为温度读数。
标准爱好电机
为了让空气流动穿过温室,你将使用一个小型爱好者电机来制作风扇,如图 7-5 所示。这是最简单类型的电机。当你将它的两根电线连接到电源时,电机会旋转,反向连接时,电机会朝相反方向旋转。不同于你在项目 6 中使用的伺服电机,这个爱好者电机会连续旋转。爱好者电机工作电压在 3V 到 6V 之间,所以非常适合 Arduino 项目。
图 7-5: 标准直流爱好电机

NPN 晶体管
晶体管的发明使得创造各种数字设备成为可能。例如,Arduino 上的微控制器实际上由数百万个晶体管组成。晶体管属于一类叫做半导体的元件。半导体是一种设备,它有时表现得像导体,允许电流流动,而有时像绝缘体一样,阻止电流流动。
本项目通过提升 Arduino 的电流输出,使用晶体管像开关一样工作。爱好者电机大约需要 200-300 mA 的电流,但 Arduino 的OUTPUT引脚最多只能提供约 40 mA 的电流。通过一个简单的晶体管电路,我们将向你展示如何使用低电流的 Arduino 引脚触发晶体管开关,像开关一样打开或关闭。
采用系统化方法
为了组织起见,你将把这个项目分成三个独立的部分或子系统来构建。这种被称为系统化方法的技术,工程师用来将一个复杂的项目分解成几个可以单独构建和测试的可管理部分。三个不同部分的主要组件是温度传感器、伺服电机(用于自动通风)和直流电机(用于风扇)。这三部分的原理图如图 7-6 所示,整个项目的接线图如图 7-7 所示。
图 7-6: 电路的原理图

图 7-7: 电路的接线图

构建温度监控器
首先,让我们来看一下温室控制系统中用于测量温度的部分。市面上有许多不同的温度传感器。你可能会遇到几种常见类型,比如热敏电阻,它根据温度变化电阻值,和热电偶,它输出一个非常小的电压(低于 10 mV),并需要一个放大器电路来使用。TMP36 是一种第三类传感器,它简单地输出一个电压,经过校准,在 25 摄氏度时为 0.75 V。随后,电压会根据周围环境的温度线性变化。这意味着随着温度变化,电压也会相应变化,如图 7-8 所示。
图 7-8: TMP36 传感器的线性温度与电压响应

使用 TMP36 测量温度
TMP36 是最容易使用的温度传感器之一。该传感器被封装在一个小塑料外壳中,外形像一个带有平边的圆柱体,并且只有三个引脚。
如前所述,TMP36 与晶体管非常相似,而晶体管也包含在 SparkFun 发明家套件中,因此通过在光源下倾斜部件并检查其平面边缘,查看是否有TMP字母。如果是2N2222或其他字母,则表示该部件不正确。
TMP36 提供的电压输出与周围环境的摄氏温度直接相关。既然你已经知道如何使用 analogRead() 命令测量电压,那么这个传感器就容易使用了。
外侧引脚用于接地和电源连接,中间的引脚是传感器的电压信号。要使用 TMP36,只需将一个引脚连接到 5 V,一个引脚连接到地线,感应引脚连接到 Arduino 模拟引脚以读取温度。请注意传感器的连接方式。当平面朝左时,顶部引脚应连接到 5 V,底部引脚应连接到地线。在 25 摄氏度时,感应引脚的电压读数将为 0.750 V(750 毫伏)。随着温度变化,该引脚的电压将按每摄氏度 0.010 V(10 毫伏)的速率变化。现在,这些数字听起来可能有些复杂,但我们将向你展示如何使用这些信息在代码中获取实际的温度读数,并将其转换为华氏度。不过,在此之前,我们先进行接线。
连接温度传感器
图 7-9 显示了温度监测电路的单独接线。你大部分的元件将位于面包板的右侧,因此需要将 Arduino 板上的 5 V 和 GND 通过跳线直接连接到面包板右侧的电源轨。接着,将 TMP36 传感器插入面包板的下半部分,确保传感器的平面朝左,如 图 7-9 所示。
图 7-9: 显示只有温度传感器的简化接线图

接下来,使用两根短跳线将 TMP36 传感器的顶部引脚连接到 5 V,将底部引脚连接到地线,确保平面朝左。中间的引脚是传感器的输出电压。将这根引脚通过跳线连接到 Arduino 板的 A0 引脚,就完成了!
现在让我们看看一个代码示例,了解如何从这个传感器获取温度读数。
编程温度传感器
TMP36 传感器根据环境温度产生电压输出。TMP36 的数据手册提供了将电压读数转换为温度的几个参考点:它显示电压变化率为每摄氏度 0.010 V,在 25 摄氏度时,传感器的电压为 0.750 V。利用这些信息,如果你测量传感器的输出电压,你可以在代码中将其转换为温度读数。
你可能还记得 项目 5 中提到的,analogRead() 函数将电压读取为整数,其中 5 V 对应 1023,0 V 对应 0。为了理解这个数值,你需要将其转换为电压,再将电压转换为摄氏度,最后将其转换为华氏度。为了让代码更简洁,首先你需要编写一个自定义函数,将原始的 analogRead() 数字转换为电压。
创建自定义转换函数
示例 7-1 展示了一个自定义函数的例子,该函数会将原始的analogRead()值转换并返回转换后的电压值。
示例 7-1: 自定义函数volts()将原始模拟值转换为电压
➊ float ➋volts(➌int rawValue)
{
➍ const float AREF = 5.0;
➎ float calculatedVolts;
➏ calculatedVolts = rawValue * AREF / 1023;
➐ return ➑calculatedVolts;
}
在项目 3 中,我们展示了如何编写自己的自定义函数,以简化代码并使loop()更易于阅读。在这些示例中,函数的数据类型始终设置为void,因为这些函数没有返回值。在这种情况下,你希望函数能够返回从analogRead()转换为电压的结果,因此需要指定数据类型。使用数据类型float ➊,因为你希望此函数返回尽可能精确的小数位数。命名该函数为volts ➋,以使其尽可能简洁又具有描述性,然后定义传递给此函数的参数 ➌,在这个例子中,它就是来自analogRead()的原始值。
将原始的analogRead()值转换为电压所需的数学计算非常简单,因为正如我们之前提到的,我们已经知道analogRead()函数在输入 5 V 时返回1023,在输入 0 V 时返回0。这意味着analogRead()的值1023等于 5 V。自定义的volts()函数利用这个比率将原始的analogRead()测量值转换为电压。
注意
通常的做法是使用全大写字母来表示常量或常量名称。
首先,声明一个作为引用使用的变量,命名为AREF ➍,并用它来定义参考电压,即 5.0 V。由于你将在整个代码中使用它,因此使用const关键字将其设置为常量。
接下来,你将定义一个变量来存储转换结果,命名为calculatedVolts ➎。注意,这个变量的数据类型也设置为float。你需要确保进行的数学运算在整数之外的部分也足够精确。为了计算电压,只需将rawCount乘以AREF(5.0 V)与 1023 的比率 ➏。
return指令 ➐ 是我们在之前的项目中没有使用过的命令。当程序执行到return指令时,它会退出自定义的volts()函数,并返回到调用该函数的代码位置。当你在return指令后面放置一个值时,函数会返回并报告该值。return的数据类型必须与函数的数据类型匹配。到目前为止,我们使用的所有函数都没有包含return,因为这些函数的定义数据类型是void,并且没有返回值。在这里,return指令后跟着变量calculatedVolts ➑,这告诉程序返回calculatedVolts的值到调用它的代码位置。
请注意,return指令也可以与具有void数据类型的函数一起使用,以指示草图退出该函数并返回。在这种情况下,return后面没有值(参见 Listing 7-2)。
LISTING 7-2: 一个带有void数据类型和return指令的自定义函数
void blink()
{
digitalWrite(13, HIGH);
delay(500);
digitalWrite(13, LOW);
delay(500);
return;
}
测试该函数
让我们用一个示例草图来测试新的volts()函数。在 Listing 7-1 的代码中,向setup()和loop()添加几行,读取 A0 引脚上的电压,并将其打印到串口监视器。完整的代码示例见 Listing 7-3。
LISTING 7-3: 测试模拟到电压的转换
//Example sketch – reads analog input from A0 and prints the
//raw analog value and the voltage
int rawSensorValue;
float rawVolts;
void setup()
{
**Serial**.begin(9600); //initializes the serial communication
**Serial**.print("raw");
**Serial**.print("\t"); //tab character
**Serial**.print("volts");
**Serial**.println(); //new line character
}
void loop()
{
rawSensorValue = analogRead(A0); //read in sensor value
rawVolts = volts(rawSensorValue); //convert sensor value
//to volts
**Serial**.print(rawSensorValue); //print raw sensor value
**Serial**.print("\t");
**Serial**.print(rawVolts); //print raw voltage reading
**Serial**.println(); //new line character
}
/***********************************************************/
float volts(int rawCount)
{
float AREF = 5.0;
float calculatedVolts;
calculatedVolts = rawCount * AREF / 1023;
return calculatedVolts;
}
将你的 Arduino 板通过 USB 电缆连接到计算机,上传此示例代码,然后打开串口监视器。你应该会看到文本在屏幕上滚动,就像在 Figure 7-10 中那样。根据这个结果,模拟值 156 等于 0.76V。你可以使用以下计算来检查输出电压:156 × (5.0 / 1,023) = 0.762 V。当数学运算结果正确时,总是很棒!
FIGURE 7-10: 串口监视器输出的原始传感器值和电压

如果在loop()中没有额外的delay()命令,Arduino 每秒大约读取传感器 80 到 90 次,并将信息发送回计算机。这就是为什么你会得到一连串的读数,而不是单个读数。如果你希望减慢草图打印读数到屏幕的速度,只需在loop()的末尾、最后一个Serial.println();之后添加delay(1000);。这将使循环速度变为每秒一个读数,从而不会像之前那样快速流动。你将在稍后的代码中实现这一点。
还有一步需要做,让草图显示温度而不是电压。
将电压转换为温度
现在你需要一个公式来将电压转换为温度。你知道 TMP36 输出的电压会根据它感应到的温度线性变化,这意味着随着温度变化,电压也会按比例变化。因此,为了创建一个公式,你将使用斜率-截距方程:
y = mx + b
这个方程描述了一条直线,或者更一般地,描述了两个变量 x 和 y 之间的关系,其中 m 是描述变化率的斜率,b 是 y 截距,表示该直线与 y 轴的交点。在这种情况下,两个变量 x 和 y 分别是电压和温度,你将使用已知的变量 x(电压)来计算未知的变量 y(温度)。
如前所述,温度传感器的数据手册指出,电压变化的速率是每摄氏度 0.010 V,或者每 100 摄氏度 1 V。这个速率是直线的斜率,因此在方程中成为 m。如果你代入变量,方程将如下所示:

TMP36 的数据表还提供了一个参考点,用于将电压映射到摄氏度:在 25 摄氏度时,传感器的电压为 0.750 V。将这些数值代入公式中以求解 y 轴截距 b:

现在你有了 m 和 b 的数值。下面是最终的公式:

这是一种可以应用于任何具有与电压成线性关系的传感器的技术。这也是一个很好的提醒,表明数学是重要且有用的。不过,如果这些内容让你感到困惑,别担心——你只需要知道的是,要找到传感器的温度,你只需将电压读数代入这个公式即可。
现在,在代码中使用这个公式将 rawVolts 读数转换为温度。你将从列表 7-3 中的示例代码开始,并添加列表 7-4 中的新代码,以显示来自传感器的温度读数。
列表 7-4: 将电压转换为温度
//Example sketch – calculates the temperature from the TMP36
//sensor and prints it to the Serial Monitor
int rawSensorValue;
float rawVolts;
➊ float tempC;
float tempF;
void setup()
{
Serial.begin(9600); //initializes the serial communication
Serial.print("raw");
Serial.print("\t"); //tab character
Serial.print("volts");
➋ **Serial**.print("\t");
**Serial**.print("degC");
**Serial**.print("\t");
**Serial**.print("degF");
Serial.println();
}
void loop()
{
rawSensorValue = analogRead(A0); //read in sensor value
rawVolts = volts(rawSensorValue); //convert sensor value
//to volts
➌ tempC = 100 * rawVolts – 50; //convert volts to deg. C
➍ tempF = 1.8 * tempC + 32; //convert deg. C to deg. F
--*snip*--
Serial.print(rawVolts); //print raw voltage reading
➎ **Serial**.print("\t");
**Serial**.print(tempC);
**Serial**.print("\t");
**Serial**.print(tempF);
Serial.println(); //new line character
➏ delay(1000);
}
/***********************************************************/
float volts(int rawCount)
--*snip*--
首先,在全局命名空间顶部添加两行代码来声明用于存储摄氏度温度 (tempC) 和华氏度温度 (tempF) 的变量 ➊。接下来,在 setup() 中添加几行代码,使用适当的列标题来显示打印到串口监视器上的读数,并用制表符分隔,它由控制字符 \t 表示 ➋。现在,你可以使用斜截式方程来计算摄氏度温度 ➌。最后,你还会添加一行代码,将温度从摄氏度转换为华氏度 ➍。
此外,为了提供更多反馈,添加一些额外的 Serial.print() 行,确保将新变量 ➎ 打印到串口监视器上。注意,最后一行 Serial.print() 实际上是一个 Serial.println() 命令,它插入了换行符并将光标移动到下一行。这将确保每个新的读数从新的一行开始。最后,添加 1 秒的延迟 ➏ 以减慢循环速度,确保每秒只采样一次,给你足够的时间查看文本。
上传更新后的代码到你的 Arduino,并打开串口监视器。你应该会看到四列数据滚动在屏幕上,如图 7-11 所示。正如新列标题所示,这些数据分别代表原始数据、电压、摄氏度温度和华氏度温度。如果你用手指挤压温度传感器,你会注意到温度上升。恭喜你,你已经完成了一个工作温度监视器,这是这个项目的第一大部分!
图 7-11: 串口监视器显示来自 TMP36 的温度

构建伺服电机自动通风装置
你将使用像项目 6 中使用的伺服电机来打开和关闭温室的窗户。伺服电机是一种简单的电动机,使用三根线分别连接控制信号(黄色或白色)、电源(红色)和地线(黑色),如图 7-12 所示。
图 7-12: 将伺服电机添加到电路中

大多数标准伺服电机都有一个三针母连接器。将三根公对公跳线连接到这三根引脚上,以扩展这些连接,如图 7-13 所示。如果可能,最好将跳线的颜色与伺服连接器上的引线颜色匹配。然后,将黄色(或白色)信号线连接到 Arduino 板上的 9 号引脚。将红色线连接到 5V,将黑色线连接到面包板右侧的 GND。
图 7-13: 插入公对公跳线将伺服电机连接到面包板

编程自动通风窗
添加清单 7-5 中的伺服代码。
清单 7-5: 添加伺服控制
➊ #include<**Servo**.h>
➋ **Servo** myServo;
//Example sketch – calculates the temperature from the TMP36
//sensor and prints it to the Serial Monitor
int rawSensorValue;
float rawVolts;
float tempC;
float tempF;
➌ int setPoint = 85;
int returnPoint = 83;
void setup()
{
myServo.attach(➍9, 1000, 2000); //initializes myServo object
Serial.begin(9600); //initializes the serial communication
--*snip*--
}
void loop()
{
--*snip*--
Serial.println(); //new line character
➎ if(tempF > setPoint)
{
myServo.write(180);
}
else if(tempF < returnPoint)
{
myServo.write(0);
}
delay(1000);
}
/***********************************************************/
float volts(int rawCount)
--*snip*--
回想一下项目 6,要使用伺服电机,你需要包含伺服库 ➊ 并创建一个名为myServo的伺服对象 ➋。接下来,创建两个变量 ➌ 来定义控制系统的设定点。这些设定点是窗户自动打开和关闭的温度。注意,setPoint(打开窗户)比returnPoint(关闭窗户)高 2 度,这意味着窗户不会在这 2 度之间打开或关闭。这种控制技术被称为滞后,对于温度可能会有轻微波动的系统非常有用,它能防止窗户因温度变化微小而频繁地开关。
最后,你需要通过告诉 Arduino 伺服电机连接到 9 号引脚来初始化伺服电机 ➍。你可能会注意到,myServo.attach()命令有两个额外的数字,而不是你在项目 6 中使用的一个数字。要理解原因,请查看第 201 页的“伺服电机的工作原理”。
最后,在控制逻辑中,你将使用嵌套的if()–else if()控制语句。将 8 行代码添加到➎,位于delay()之前,用于在温度高于 85 华氏度(setPoint)时将伺服电机移动到 180 度位置,在温度降到 83 华氏度以下(returnPoint)时将伺服电机返回到 0 度位置。这将打开和关闭自动通风窗。
从清单 7-5 上传更新后的代码到你的 Arduino,并打开串行监视器。通过用手指夹住传感器或将手掌围绕传感器吹气,使其温度超过 85 度,来试试。观察串行监视器,看看温度是否上升。一旦温度达到 85 度,你应该能听到伺服电机的声音,它会转到指定位置。现在,让温度传感器静置并冷却。一旦温度降到returnPoint以下,你应该再次听到伺服电机的声音,它会返回到 0 度位置。挺酷吧?
现在你将构建最终组件:风扇。
构建风扇电机
风扇将通过一个小型直流爱好电机旋转,这是一种标准的圆柱形设备,具有两根导线和一个中心轴,当你给它施加电压时,轴会旋转。你一直使用的伺服电机内部有齿轮,可以让它在非常精确的运动范围内移动。回想一下,它的运动范围大约只有 180 度。这里你将使用直流电机,而不是伺服电机,因为你需要风扇叶片在你持续施加电力时不断旋转。这使得它非常适合做风扇功能。你将使用的直流电机设计用于 3V 到 6V 之间的电压,但当它旋转时,电流大约在 200-300 毫安之间。而 Arduino 的OUTPUT引脚仅能驱动大约 40 毫安的电流,因此为了给直流电机提供足够的电流,你将构建一个额外的电路,称为晶体管放大电路——更技术性地说,是共射极放大器。
伺服电机的工作原理
伺服电机是一种根据来自微控制器的信号将电机移至特定位置的整洁设备。标准伺服电机具有大约 180 度的固定运动范围。
所有伺服电机都有三根线:一根白色(或有时是黄色或橙色)的信号线、一根红色的电源线和一根黑色的接地线。信号是一种每 20 毫秒发送一次的独特脉冲,频率为 50 赫兹。为了编码一个位置,微控制器会通过改变脉冲的宽度来指示电机将要转动的角度或位置(有关脉冲宽度调制的更多详情,请参见项目 5)。对于大多数标准伺服电机,1,000 微秒(1,000 μs)的脉冲表示 0 度位置,2,000 微秒的脉冲表示完全的 180 度位置,因此 1,500 微秒的脉冲表示伺服电机的中点或 90 度位置。
Arduino 的Servo.h库将脉冲宽度映射到电机的位置,但该库对伺服电机的位置和脉冲宽度有稍微不同的定义,0 度位置的脉冲为 544 μs,180 度位置的脉冲为 2400 μs。这使得该库能够与扩展范围的伺服电机一起工作,但超出了大多数标准伺服电机的限制。因此,当你使用像myServo.write(0);这样的命令时,电机会接收到一个 544 μs 的脉冲,并尝试超出其物理限制。如果发生这种情况,电机会抖动、嗡嗡作响并发热,因为它无法移动到与 544 μs 脉冲对应的位置。
为了抵消这个问题,你可以在代码中为伺服电机设置限制。如同在项目 6 中一样,使用命令myServo.attach(9);来初始化 Arduino 的 9 号引脚上的伺服电机。你也可以添加参数来设置电机的上下脉冲宽度限制,像这样:myServo.attach(9, 1000, 2000);。通过这个初始化,命令myServo.write(0);将使电机移动到 0 度角的位置,并且不会发生抖动、嗡嗡声或发热。
如果你想了解更多关于伺服电机如何工作的内容,可以查看 SparkFun 上的教程,网址是www.sparkfun.com/tutorials/283/。
一个晶体管有三个引脚:集电极(C)、基极(B)和发射极(E)。任何流入基极的电流都会在集电极引脚上得到放大。基极就像一个控制门,允许电流从集电极流向发射极。你施加在基极上的压力越大,门就会开得越宽,更多的电流可以从集电极流向发射极。晶体管的奇妙之处在于,你只需在基极上施加少量电流,就能让大量电流从集电极流向发射极。如果你给基极提供过多的电流,可能会烧毁晶体管,所以就像你使用电阻限制 LED 电流一样,你会在电路中使用一个 330 Ω的电阻来限制流向基极的电流。
晶体管有许多不同的应用,如放大器和其他设备,但在这里你只需将其用作一个可控开关。尽管 Arduino 引脚只能提供少量电流,当你向晶体管的基极发送HIGH信号时,晶体管会“打开”。这允许电流在集电极和发射极之间流动。它本质上将这些引脚连接在一起,就像关闭一个开关。
晶体管的发射极一侧连接到接地轨道。注意电机的一侧连接到 5V 轨道,电机的另一侧连接到晶体管的集电极。当晶体管被打开时,这会将集电极连接到晶体管的发射极,从而关闭电机和地面之间的开关,使电机转动。工程师称之为“在截止模式和饱和模式之间操作晶体管”。电机的电源直接来自电源轨道。这意味着你可以使用 Arduino OUTPUT引脚提供的有限电流来控制需要更大电流的设备。
你将根据图 7-14 所示,在面包板的顶部部分搭建晶体管电路。
图 7-14: 添加风扇电机晶体管控制电路

在你的工具包中找到晶体管。正如图 7-15 所示,晶体管看起来与温度传感器非常相似,但如果你仔细观察,你应该能看到外壳平面上的 2N2222 或 BC337。这是一个NPN 晶体管,它将作为你用 Arduino 控制的电机开关。
图 7-15: 本项目中使用的 NPN 晶体管——2N2222(左)和替代部件 BC337(右)

将晶体管保持平面朝左,并将其插入面包板的顶部部分,使顶部引脚大约在六行下方,如图 7-14 所示。在这个位置,顶部引脚是集电极,中间是基极,底部是发射极(见图 7-15)。
将一个 330 Ω的电阻连接到基极引脚,并将其拉伸穿过面包板中的沟槽,如图 7-14 所示。然后,将该电阻的另一端连接到 Arduino 的 11 号引脚,这样 11 号引脚就通过 330 Ω的电阻连接到基极引脚。这是来自 Arduino 的低电流控制信号,用来开关晶体管。当晶体管开启时,电流将通过电机,电机将转动。
将一根小跳线从晶体管的发射极(下端引脚)连接到地线。最后,将电机的一根线连接到晶体管的集电极(上端引脚)。电机会根据你使用的电线不同而顺时针或逆时针转动,但在这个情况下,电机的转动方向并不重要,你可以选择任意一根电机线。将电机的另一根线连接到电源轨,然后使用跳线将面包板的电源轨连接到 Arduino 的 5V 端。当在基极引脚上检测到微小的电流信号时,集电极和发射极之间的连接被闭合。这时,电流从 5V 通过电机到 GND,完成了一个电路路径,从而使电机开始转动。图 7-16 展示了电机的电路。
图 7-16: 用 Arduino 引脚驱动电机的晶体管电路

该电路的最后一个部分是一个保护二极管,有时也称为反向二极管,它能够保护晶体管免受电机可能造成的损坏。在电机内部,有许多线圈,这些线圈通过产生电磁场,推动和拉动电机中的永久磁铁—这就是导致轴转动的原因。线圈是电子学中非常有趣的元件。它们产生的磁场实际上是一种储存能量的形式,当电路关闭时,这种储存的能量会反弹并导致电压尖峰,进而损坏晶体管。反向二极管为这些电压尖峰提供了一个泄放的通道,从而避免电压通过晶体管。它有时也被称为缓冲电路。
为了正确连接这个元件,需要注意的是二极管是有极性的,其方向是有区别的。二极管的本体在一端有一条线或带,如图 7-17 所示。确保带有标记的那一侧连接到电机的正极(5V)。
图 7-17: 用于晶体管电路中的反向二极管,旁边放有一个硬币以作大小对比

将二极管添加到电机中,确保带有标记的一侧连接到电机的 5V 电线,如图 7-16 中的电路图所示。将二极管的另一脚连接到电机的另一根线—即连接到晶体管集电极引脚的那根。这意味着二极管的两脚与电机的两根线共享相同的连接。当两个设备这样连接时,我们称它们是并联连接的。
现在你应该已经完成了一个完整的电路,其中包括图 7-6 中的三个子电路。接下来你需要再添加几行代码来控制电机。
编程风扇电机
控制电机的代码非常简单,就像你用来控制 LED 开关的代码一样。将清单 7-6 中的代码添加到你当前的草图中。
清单 7-6: 完整的微型桌面温室控制代码
#include<Servo.h>
Servo myServo;
--*snip*--
void setup()
{
➊ pinMode(11, OUTPUT);
myServo.attach(9, 1000, 2000);
Serial.begin(9600); //initializes the serial communication
--*snip*--
}
void loop()
{
--*snip*--
Serial.println(); //new line character
if(tempF > setPoint)
{
myServo.write(180);
➋ digitalWrite(11, HIGH); //turn the fan on
}
else if(tempF < returnPoint)
{
myServo.write(0);
➌ digitalWrite(11, LOW); //turn the fan off
}
delay(1000);
}
/***********************************************************/
float volts(int rawCount)
{
const float AREF = 5.0;
float calculatedVolts;
calculatedVolts = rawCount * AREF / 1023;
return calculatedVolts;
}
只有几行新代码。第一行是在setup()中。这一行设置了 11 号引脚,该引脚通过晶体管连接到电机,设为OUTPUT ➊。接下来是if()-else if()条件语句块。在这里,你添加了两条命令来打开电机 ➋ 和关闭电机 ➌。记住,电机实际上是温室的风扇。通过这些额外的代码,风扇将在窗户打开时启动,窗户关闭时停止。
添加完这些代码行后,将最新版本上传到你的 Arduino,打开串口监视器,重新测试一次。尝试通过将温度传感器夹在手指间或使用呼吸加热它,看看会发生什么。当温度读数接近 85 华氏度时,舵机电机应该开始工作,爱好电机也应该启动。你可能会注意到,一旦电机启动,温度读数就会完全失常。这里涉及的内容较多,但我们有一个快速的解决方法。
隔离电机效应
当电机开启时,由于电机的额外电流负载,Arduino 的电压会下降到大约 4.1-4.5 伏。你可能会发现,电机启动后,温度读数开始不规则变化,而且电机可能会反复开关几次,直到温度读数稳定下来。我们之前提到过,当使用analogRead()时,1023等于 5 伏,但这只是部分正确。完整的事实是,1023等于源电压的大小,所以如果源电压降到 4.1 伏,那么1023现在就等于 4.1 伏。这会影响 Arduino 获取准确测量值的能力。
为了纠正这个问题,在loop()的开头,即第一个大括号后,添加两行代码,告诉 Arduino 在读取温度传感器之前关闭电机:
digitalWrite(11, LOW); //turn off the motor before
//reading sensor
delay(1); //short 1 ms delay before
//reading sensor
现在,Arduino 会在读取温度传感器电压之前,关闭电机 1 毫秒。这会隔离电机的电压下降与analogRead()之间的干扰,而且不需要添加过多的代码。
添加这两行代码后,将新草图上传到你的 Arduino,再次尝试加热传感器。现在,它应该更加稳定地工作了。电路已搭建好,代码运行流畅,接下来是构建实际的温室结构。
构建微型桌面温室外壳
我们提供了一个使用纸板制作的温室外壳模板,但你可以使用任何你喜欢的材料。事实上,宜家出售一种叫做 SOCKER 的小型温室,你可以很容易地修改它来配合这个项目。
对于微型桌面温室,完成后的尺寸大约为底座 4.5 × 4.5 英寸,最高点为 6 英寸。在* www.nostarch.com/arduinoinventor/*提供的资源中,我们有两个模板选项:一个是分成三张 8.5 × 11 英寸的纸板(如图 7-18 所示),另一个是单张 11 × 17 英寸的纸板。
图 7-18: 微型桌面温室纸板外壳模板(非全尺寸)

小心地从纸板上剪下模板的各个部分。这里有四个独特的部分:五边形的侧面、前后方的正方形墙壁、屋顶窗户和风扇电机支架。取一块正方形的侧面和一块五边形的前后墙面,将它们并排放置,透明面朝上。使用一条窄胶带将这两块固定在一起,如图 7-19 所示。重复此过程,直到所有四个侧面墙壁都固定好,但不要把最后两面墙壁贴合;将封闭部分平放,直到加上窗户。
图 7-19: 使用窄条胶带将各个部分固定在一起。

现在,你需要将六块透明薄膜切割成稍大于每个墙壁和窗户开口的大小。根据我们提供的模板,你需要四块尺寸为 4.25 × 4.25 英寸的正方形和两块尺寸为 4.25 × 2.5 英寸的正方形。你应该能够从一张透明薄膜中切出这六块;建议先将它们描绘出来,这样可以最大限度地利用薄膜。此时,你只需要固定侧墙的窗户;屋顶窗户将在最后加上。使用一条胶水或小片胶带,将这些窗户固定在温室的内侧,与胶带同一侧,如图 7-20 所示。
图 7-20: 小心地将透明薄膜窗对齐并粘贴到纸板上。

一旦这些透明窗片到位,再沿最后一个裸露的边缘粘上一条胶带,将其连接起来,这样你就有了一个正方形的底座和一个像小温室一样的结构,如图 7-21 所示。温室的顶部和底部应该仍然是开放的,可能还会感觉有些不稳定,但一旦你加上屋顶,整个结构就会稳定下来。
图 7-21: 温室四个侧面完成

添加自动通风窗伺服电机
在我们的模板上,我们尽可能靠近窗户的枢轴点处为伺服电机做了一个小切口,以最大限度地增加窗户在伺服电机角度变化时的开度。断开伺服电机与电路的连接,如果伺服电机端没有安装伺服头,给伺服电机加一个。我们推荐使用单面伺服头,因为它能更清楚地显示伺服电机的指向。用手指轻轻顺时针旋转伺服电机,直到它停止,设定电机在 180 度的位置。这个位置是窗户完全打开时伺服头的位置。卸下并重新安装伺服头,使其朝上,方向与伺服电机背面出来的电线相反,如图 7-22 所示。
图 7-22: 伺服电机角度为 180 度——顺时针旋转

现在,将伺服电机从温室内部的模板孔中穿过,使电线朝下,伺服头朝上(见图 7-23)。
图 7-23: 将伺服电机放入温室

伺服电机的接头应该与纸板平齐。你可以使用随伺服电机附带的螺丝以及我们模板上的两个小螺孔来固定伺服电机,或者使用一些热熔胶将其固定在合适位置。
制作纸夹连杆
如同在项目 6 中一样,你需要一个连杆将伺服头与窗户连接。取一个中号的回形针,拉直所有部分,除了末端的小钩子。然后,拿一个尺子,在离小钩子末端约 1 1/8 英寸的地方加上一个锋利的 90 度弯曲,弯曲方向远离钩子,如图 7-24 所示。
图 7-24: 弯曲纸夹连杆

添加屋顶
屋顶部分是一个矩形的纸板。按照图 7-25 所示,剪掉窗户,并在屋顶的中心线处做压痕。压痕边缘将作为窗户翻板的铰链,允许窗户开合。
图 7-25: 屋顶部分

将温室定位,使伺服电机在左侧。屋顶的一半将用胶水固定,另一半将形成一个可开合的窗户翻板。使用少量热熔胶,将屋顶的一半结构固定到温室上。只粘合屋顶的三条边(即六条边中的一半),这样仍然可以有一个可以打开的翻板。确保打开的一侧与伺服头运动的方向一致,如图 7-26 所示。
图 7-26: 固定屋顶。确保只粘合屋顶的一半,这样另一半才能打开。

如图 7-27 所示,将回形针连杆挂到舵机角的最后一个孔上,圆形的钩子连接到舵机上。确保对面的弯曲部分指向舵机电动机的方向。这将挂在屋顶部件的框架上。
图 7-27: 舵机角和回形针连杆

保持窗户翻盖打开,旋转回形针,直到您可以将其插入框架的侧面,通过纸板本身。如果回形针不够长,您可以重新弯曲它,或者将舵机角的角度稍微向上调整,以延长连杆的长度。将温室结构稍微抬起并从下方重新调整舵机角可能会有帮助。一旦您调整好舵机角,使其能够接触到位置,就可以将回形针的一端插入窗框的侧面,如图 7-28 所示。
图 7-28: 舵机臂连杆与窗翻盖连接

将从另一侧突出部分的回形针弯曲成钩形,以防止连杆掉出来(见图 7-29),然后将剩余部分剪掉。现在小心地前后移动舵机;您应该能够打开和关闭温室的盖子!
图 7-29: 舵机连杆中钩子的弯曲

机制完成后,您可以将透明窗户粘贴或固定到温室的顶部。四个角落涂上一小点胶水即可将窗板固定住。窗板应该放在温室屋顶的外面,以便为连杆的开启和关闭留出空间。接下来,您将制作一个盒子来容纳电动机和风扇。
制作风扇电动机盒
电动机将作为风扇来移动空气,以便在温室温度过高时帮助通风。为了防止电动机在旋转和振动时移动,我们设计了一个小纸板盒来固定它。模板设计为一个五面盒子,并且有一个小孔,以允许电动机电线通过,见图 7-30。
从一块硬纸板上剪下这个模板,并小心地在虚线处打折,使其可以折叠成一个盒子。使用胶带或热熔胶固定盒子的四个边,以确保它能紧密地容纳电动机(见图 7-31)。
要制作风扇叶片,您需要将一小块卡纸粘到电动机的末端。将风扇叶片剪裁成不超过 1.25 英寸宽。为了帮助风扇更好地移动空气,按图 7-32 所示折叠卡纸的边缘。
图 7-30: 风扇电动机盒

图 7-31: 完全组装的电动机盒,电动机位于其中

图 7-32: 最终风扇叶片

这样可以确保风扇不会碰到植物或温室内部的其他物体。使用少量热胶,将风扇叶片固定到电机上,如图 7-33 和图 7-34 所示。
图 7-33: 将风扇叶片固定到电机上

图 7-34: 风扇电机组件完成

连接它
现在你拥有了所有需要的组件来搭建你的迷你桌面温室,是时候安装电子设备了。将温度传感器从面包板上取下,使用三根公对母跳线来扩展传感器的连接,如图 7-35 所示。注意你移动的电线,并使用延长线重新连接它们。当你将温度传感器的平面朝向自己,针脚朝左时,顶端的针脚是电源,中间的针脚是信号,底部的针脚是接地。我们使用红色、黄色和黑色电线分别表示电源、信号和接地连接。
图 7-35: 使用公对母跳线扩展温度传感器

你需要将温度传感器放置在温室内部。使用一小块遮蔽胶带将温度传感器直接固定在植物上,然后将其放入新的温室中,如图 7-36 所示。现在你可以将电线从温室的一侧引出,或者你也可以打个小孔,将电线穿过。
图 7-36: 将温度传感器直接固定到你的植物上

同样,将风扇电机组件移动到温室的角落附近。电机的电线应该足够长,能直接连接到面包板,如果需要,你可以添加额外的公对母延长线来简化接线。
现在,你应该仍然有足够的空间让一小株植物舒适地休息在这个新建的小温室中。是时候把你的新异国植物放进去,安置到你全新的温室里!为了测试我们的自动通风系统如何有效地调节温度,我们用一些非常大的泛光灯模拟了室内阳光,来加热空气。
图 7-37 展示了我们在迷你桌面温室中的一项测试。
图 7-37: 测试迷你桌面温室

进一步探索
这个项目有很多提升的机会。
黑客
目前,你的温室还挺小的。为了给更多的植物腾出空间,找一个像复印纸盒子那样大的纸箱,剪几个窗户,用透明膜覆盖窗户,然后将电子设备移到这个更大、更好的温室里。或者,看看 IKEA 卖的塑料温室。你可以在哪里安装伺服电机,以便你能开关这个温室的窗户?
修改
当前的设定温度是 85 华氏度,虽然这是一个适合我们测试的温度,因为我们可以通过自己的体温轻松地提高温度,但实际上对于大多数植物来说,这个温度还是偏低。查找一下你植物的理想生长温度,并修改你的代码以使用这个新的设定温度。
你还可以修改温室采样温度的频率,通过延迟来调整。1 秒钟的延迟非常短。如果温度波动,盖子将在几秒钟内开关多次,这样会变得很烦人。将延迟时间更改为 5 分钟左右,即 30,000 毫秒。
第九章:8 画图机器人,机器人艺术家
作为向 Logo 海龟项目的致敬,在这个项目中我们将制作一个画图机器人:一个你可以编程来移动和绘制的机器人。Logo 是由 Daniel G. Bobrow、Wally Feurzeig、Seymour Papert 和 Cynthia Solomon 于 1960 年代末期创立的编程语言。后来它被改编成支持一个带绘图笔的机器人,称为海龟(见图 8-1)。
图 8-1: Logo 海龟的早期版本

海龟被连接到计算机,以接收 Logo 语言中的移动命令,例如 fd 10 来驱动前进 10 步。当海龟移动时,它会用附带的笔绘制图形。这些 Logo 海龟是一个早期的教育系统,旨在通过视觉化的方式教授编程概念。
你将制作自己的 Arduino 控制的海龟,画图机器人(图 8-2),它的灵感来自 Seymour Papert 及其团队的工作。
图 8-2: 完成的画图机器人

所需材料
你的机器人将有两个轮子,每个轮子都有一个电机,通过一个新的组件称为 H-桥 由 Arduino 控制。H-桥是一个小型模块化电路板,类似于你在上一个项目中使用的晶体管电路,只不过它可以让你控制电机的速度和方向。这将给你的机器人提供最大的灵活性和控制能力。收集好你的部件(如图 8-3 和图 8-4 所示),让我们开始吧!
电子元件
• 一个 SparkFun RedBoard(DEV-13975),Arduino Uno(DEV-11021),或任何其他兼容的 Arduino 板
• 一条 USB Mini-B 数据线(CAB-11301 或你板子的 USB 数据线)
• 一个无焊接面包板(PRT-12002)
• 两个齿轮驱动的业余电机(ROB-13302*)
• 一个 TB6612FNG H-桥电机驱动器(ROB-09457* 未焊接或 ROB-13845* 已焊接)
• 两个适合齿轮驱动电机的橡胶轮(ROB-13259*)
• 公对公跳线(PRT-11026)
• 公对母跳线(PRT-09140*)
• 一个 4 AA 电池座(PRT-09835*)
注意
带星号()的部件不包含在标准 SparkFun 发明者工具包中,但可在单独的附加工具包中购买。*
图 8-3: 画图机器人的组件和材料

其他材料和工具
• 铅笔
• 手工刀
• 金属直尺
• 胶水(热熔胶枪或手工胶水)
• (可选)电钻和 3/16 英寸钻头
• (可选)烙铁
• 纸板(约 12 英寸见方)或纸箱
• 乒乓球
• 外壳模板(见 图 8-12,第 235 页)
图 8-4: 推荐工具

新组件
在这个项目中,你将使用两个新组件:H-桥电机驱动器和齿轮驱动的业余电机。让我们来看看这些组件是如何工作的。
H-桥电机驱动集成电路
在项目 7 中,你使用了一个晶体管电路来控制单个电机与 Arduino,这使得你能够控制电机的速度,但无法控制其旋转方向。在本项目中,你将使用一个名为H 桥电机驱动器的新组件,它将使你能够同时控制电机的速度和旋转方向。
H 桥电机驱动器是一个集成电路(IC)芯片,由大约十几个晶体管通过内部连接一起,封装在一个小型塑料外壳中。IC 芯片是一个预先接线的电路,它被集成到一个单一的封装中,旨在简化复杂项目的构建。市面上有许多不同类型的 IC,其中一个例子是 Arduino Uno 的“大脑”,即 ATMega328 芯片。在本项目中,H 桥电机驱动器 IC 使你能够通过连接电源和少数信号线到 Arduino,控制电机的速度和旋转方向。
你可能记得在项目 7 中,晶体管仅仅是一个可以电子控制的开关。标准的 H 桥电机驱动器由四到五个晶体管(或开关)按 H 形结构连接,如图 8-5 所示。(图中的电机不包括在 H 桥 IC 内,你需要自己添加它。)通过控制四个主要开关(标记为 A–D)中哪些是开关,你可以控制电流通过电机的流向。第五个开关(E)控制电机旋转的速度。
图 8-5: 具有方向和速度控制的 H 桥电路

记住电流是从正极流向负极的。如果你关闭 A 和 D 开关,电流将从左到右流经电机,使电机朝一个方向旋转。如果你关闭 B 和 C 开关,电流将从右到左流经电机,使其朝相反方向旋转。
E 开关通过 PWM(参见“使用 PWM 创建模拟信号”第 139 页)迅速开关。PWM 信号的占空比将决定电机的旋转速度。在你的机器人中,将有两个电机,每个电机都有自己的 H 桥电路,你将为每个电机连接一个车轮,从而控制其旋转速度和方向。
本项目中使用的 H 桥电机驱动器是东芝 TB6612FNG,如图 8-6 所示。它以一个带孔的分立板形式出现,孔间距为 0.100 英寸——非常适合插入面包板。
图 8-6: TB6612FNG H 桥电机驱动器分立板(未焊接引脚)

注意
该芯片还具有一个待机引脚,可以用来将电机置于待机模式以节省电力,但你不需要这个功能,因此将禁用它。
东芝 TB6612FNG 实际上是一个双H 桥集成电路。这意味着它在一个小封装中内置了两个完整的 H 桥电路,允许你使用一个电路板控制机器人上的两个电机。H 桥将两个电机区分为 A 和 B,如你在电路板底部的图 8-6 中所见。要控制每个 H 桥电路,你需要使用三根信号线:两根用于方向控制,一根用于速度控制。
你可以选择购买带有焊接针脚的电路板,也可以选择没有焊接针脚的版本。如果你想省去焊接的麻烦,确保你购买的是已预焊接针脚的电路板(ROB-13845)。如果你买的是没有预焊接针脚的电路板(ROB-09457),也没问题,但你需要将男针焊接到针脚上;有关焊接说明,请参阅第 302 页中的“如何焊接”部分。在开始构建这个项目之前,无论如何,你应该确保手头的电路板看起来像图 8-7 所示。
图 8-7: DEV-12211 H 桥电机驱动器,已焊接针脚

齿轮驱动爱好者电机
我们在项目 7 中使用的基本爱好者电机非常适合用于简单的机制,如旋转风扇,但它并不提供很大的扭矩(旋转力)。在这个项目中,我们需要用电机来移动整个项目,因此我们需要使用齿轮电机——一个连接到齿轮箱的电机(见图 8-8)。
图 8-8: 一个齿轮驱动爱好者电机由一个基本电机和一个齿轮箱组成

齿轮箱本质上将机械旋转转换为扭矩。这个齿轮箱具有 48:1 的减速比,这意味着电机的 48 次旋转等于输出轴的 1 次旋转。这将电机的转速降低大约 1/48 倍,同时扭矩增加 48 倍。基本上,输出速度变慢,但扭矩大大增加。
构建 DRAWBOT 原型
现在,让我们连接电路,看看它是如何工作的。你现在只需要连接一个电机来测试 H 桥电机驱动器,因此你只需使用双 H 桥板的一半。图 8-9 显示了电路板和 Arduino 的连接方式。电路板横向分为两部分,顶部控制电机 A,底部控制电机 B,尽管电源引脚是两个电机共用的。从 Arduino 连接 5V 和 GND 到面包板的电源轨,并确保添加一根跳线,将面包板上的两个 5V 轨连接起来,这样你就可以使用任意一个轨道供电;这样可以避免交叉太多的电线,并保持电路板整洁。
图 8-9: H 桥测试电路

从 H-桥的左上角开始,连接 5V 到顶部的两个引脚,VM 和 VCC。VM 控制电机的电源,VCC 控制芯片的电源。接下来,使用跳线将芯片的一个 GND 引脚连接到面包板的 GND 轨道。H-桥上有三个地引脚,如图 8-7 所示,你可以选择任意一个。
接下来,你将连接电机。电机有两根电线:红色和黑色。电线的方向实际上并不重要,但为了保持一致,连接红色电线到标记为 A01 的引脚,黑色电线到标记为 A02 的引脚。
剩余的引脚位于左侧,用于控制第二个电机和另一个 GND 引脚,所以暂时留着它们。H-桥扩展板顶部右侧的引脚用于 Motor A 的信号线连接。最上面的引脚标记为 PWMA,控制电机的速度。将其连接到 Arduino 的 11 引脚。(记住,3、5、6、9、10 和 11 引脚都支持 PWM 功能,可以与 analogWrite() 函数一起使用。)
接下来的两个引脚,标记为 AIN2 和 AIN1,用于控制 Motor A 的方向和驱动,你可以通过将这些引脚设置为不同的高低组合来控制。表 8-1 显示了这些组合。将 AIN2 连接到 Arduino 的 12 引脚,将 AIN1 连接到 Arduino 的 11 引脚。
表 8-1: H-桥电机控制器功能
| AIN1 | AIN2 | 功能 |
|---|---|---|
| 高 | 低 | 顺时针 |
| 低 | 高 | 逆时针 |
| 高 | 高 | 电子刹车(见注释) |
| 低 | 低 | 停止/滑行 |
注释
将两个引脚设置为高电平将启用 电子刹车。电机的两根电线基本上被短接在一起,导致电机的旋转突然停止。相反,将两个引脚都设置为低电平将只停止电机的主动旋转,意味着车轮会滑行至停,而不是突然停下。
最后,你需要禁用 STBY 引脚。如前所述,这个 H-桥 IC 有一个待机引脚,允许你将芯片置于低功耗睡眠模式,这在对功耗有要求的应用中非常有用。对于这个项目,你不需要这个功能,所以你将禁用它。这个芯片设计时将 STBY 作为低电平有效输入。这意味着当该引脚为低电平时,它进入待机模式。要禁用待机,你需要将此引脚直接连接到电源轨上的 5V。
编程 DRAWBOT
让我们从一个简单的测试开始。这个简单的示例将使电机顺时针缓慢旋转 1 秒钟,改变方向后快速逆时针旋转 1 秒钟,然后停止 1 秒钟,之后重新开始。打开 Arduino IDE,将清单 8-1 中的代码复制到你的窗口中。完成后,点击 上传,然后看看发生了什么!
清单 8-1: H-桥电机控制器的速度和方向控制示例
➊ const byte AIN1 = 13;
const byte AIN2 = 12;
const byte PWMA = 11;
void setup()
{
pinMode(AIN1, OUTPUT);
pinMode(AIN2, OUTPUT);
pinMode(PWMA, OUTPUT);
}
void loop()
{
//set direction to clockwise
➋ digitalWrite(AIN1, HIGH);
digitalWrite(AIN2, LOW);
➌ analogWrite(PWMA, 50);
delay(1000);
//set direction to counterclockwise
➍ digitalWrite(AIN1, LOW);
digitalWrite(AIN2, HIGH);
analogWrite(PWMA, 255);
delay(1000);
//brake
➎ digitalWrite(AIN1, HIGH);
digitalWrite(AIN2, HIGH);
delay(1000);
}
该草图以一种新的数据类型开始:const byte ➊。关键字const用于声明一个常量,它类似于变量,但其值在代码中不能再被改变。因此,常量对于声明那些在代码中始终保持不变的东西非常有用,比如引脚号或配置。在这种情况下,这些常量定义了控制 H 桥的引脚号。由于引脚号是介于 0 和 13 之间的数字,因此可以将这些常量定义为数据类型byte。
注意
大多数情况下,使用常量或变量并不会有太大区别,但常量在 Arduino 上使用的内存更少,因此在可以使用常量的情况下,最好采用它们,并且你可能会在网上看到其他人的示例中使用常量。此外,虽然这不是一个硬性规定,但常量的命名通常使用全大写字母。
接下来,你将引脚设置为输出,然后使用两个digitalWrite()函数 ➋ 设置你希望电机旋转的方向,这两个函数作用于AIN1和AIN2引脚。第一个循环块将AIN1设置为HIGH,AIN2设置为LOW,这会让电机顺时针旋转。为了设置速度,你使用analogWrite()函数 ➌ 作用于PWMA引脚。你可能还记得项目 5,你可以使用analogWrite()将模拟引脚设置为从0到255的 PWM 值;这里给定的值50是相对较慢的。电机会旋转 1 秒钟,因为有delay(1000),接下来的循环块则通过两个digitalWrite()函数 ➍ 改变电机的方向。这里,草图简单地交换了哪个引脚是HIGH,哪个是LOW,并通过analogWrite()将速度设置为255,再加上另一个delay(1000),使电机旋转 1 秒钟。
草图的最后部分将AIN1和AIN2都设置为HIGH ➎,并添加另一个delay(1000)。这应用了电子刹车,并在循环再次开始之前停止电机 1 秒钟,然后重复该模式。
以这段代码为例,现在你只需三行代码就可以控制电机的速度和方向!但我们可以让它更简单。让我们通过使用自定义函数来清理代码。
创建一个自定义函数
目前,每次你想控制电机时,都会使用三行代码:两行控制方向,一行设置速度。在这一部分,你将创建一个自定义函数,它只需一个数字来同时确定旋转的方向和速度。这个数字可以是-255到255之间的任何值,如果数字为正,则电机顺时针旋转;如果为负,则电机逆时针旋转。将 Listing 8-2 中的代码添加到草图的末尾。
LISTING 8-2: 设置电机 A 的速度的自定义函数
void ➊setMotorA(➋int motorSpeed)
{
➌ if (motorSpeed > 0)
{
digitalWrite(AIN1, HIGH);
digitalWrite(AIN2, LOW);
}
➍ else if (motorSpeed < 0)
{
digitalWrite(AIN1, LOW);
digitalWrite(AIN2, HIGH);
}
➎ else
{
digitalWrite(AIN1, HIGH);
digitalWrite(AIN2, HIGH);
}
➏ analogWrite(PWMA, abs(motorSpeed));
}
将函数命名为setMotorA() ➊。该函数使用一个数字作为单一参数motorSpeed ➋来设置电机的速度。首先,一个简单的if()语句通过检查motorSpeed是否大于零来判断数字是正数还是负数。如果motorSpeed为正数 ➌,if()语句设置方向引脚,使电机顺时针旋转。如果它是负数 ➍,else if()语句设置方向引脚,使电机逆时针旋转。如果既不是正数也不是负数(即它是0),一个最终的else语句 ➎将两个方向引脚设置为HIGH,以应用刹车并停止电机。
➏行使用abs()数学函数来求motorSpeed的绝对值。analogWrite()函数设置电机的速度,但它仅适用于0到255之间的值。abs()函数确保使用motorSpeed的正部分,即绝对值,来设置速度。
清理代码
现在,让我们通过这个新函数来清理loop()函数。你可以在列表 8-3 中看到,loop()的代码变得更简洁,阅读起来也更容易。将这些更改应用到你的草图中的loop(),然后上传到你的开发板上。它的表现应该和之前一样。如果你想将电机设置为不同的速度或方向,只需要一行代码!
列表 8-3: 使用自定义函数setMotorA()的简化版loop()
void loop()
{
//set direction to clockwise
setMotorA(100);
delay(1000);
//set direction to counterclockwise
setMotorA(-255);
delay(1000);
//stop
setMotorA(0);
delay(1000);
}
这段代码设置了一个setMotorA()的值和一个延迟,用于调整每次速度和方向的变化。现在你已经有了你的 Drawbot 的基础!接下来,你将接线第二个电机。
接线第二个电机
DrawBot 需要第二个电机,这样它才能在两个轮子上快速移动。图 8-10 展示了第二个电机的接线方式。将电机 B 插入在突破板左侧,位于第一个电机连接处下方,红线连接到 B02,黑线连接到 B01。接着,将信号控制线添加到 H 桥突破板,位于右侧的 STBY 引脚下方。将 H 桥的 PWMB 引脚连接到 Arduino 的引脚 10 用于速度控制,将 BIN1 和 BIN2 引脚分别连接到 Arduino 的引脚 8 和 9 用于方向控制。
图 8-10: 电机驱动器和两个电机的接线图

现在,你需要添加控制第二个电机的代码,具体内容参见列表 8-4。
列表 8-4: 为电机 B 添加常量和pinMode()函数
const byte AIN1 = 13;
const byte AIN2 = 12;
const byte PWMA = 11;
➊ const byte BIN1 = 8;
const byte BIN2 = 9;
const byte PWMB = 10;
void setup()
{
pinMode(AIN1, OUTPUT);
pinMode(AIN2, OUTPUT);
pinMode(PWMA, OUTPUT);
➋ pinMode(BIN1, OUTPUT);
pinMode(BIN2, OUTPUT);
pinMode(PWMB, OUTPUT);
}
这段代码为电机 B 添加了三个额外的常量 ➊,用于信号控制引脚,并在setup()中将这些引脚设置为OUTPUT ➋。
接下来,你将再次编写一个自定义函数来控制电机 B。这个代码和setMotorA()函数非常相似,你可以通过高亮显示setMotorA()代码,复制(CTRL-C)并粘贴(CTRL-V)到setMotorA()函数下方,然后将A改为B,节省时间。这是程序员常用的一种技巧,可以节省大量时间。你只需要小心确保将所有A都改为B,否则代码将无法正常工作(清单 8-5)。
清单 8-5: 电机 B 的自定义函数
void setMotorB(int motorSpeed)
{
if (motorSpeed > 0)
{
digitalWrite(BIN1, HIGH);
digitalWrite(BIN2, LOW);
}
else if (motorSpeed < 0)
{
digitalWrite(BIN1, LOW);
digitalWrite(BIN2, HIGH);
}
else
{
digitalWrite(BIN1, HIGH);
digitalWrite(BIN2, HIGH);
}
analogWrite(PWMB, abs(motorSpeed));
}
现在,草图需要为setMotorA()和setMotorB()设置motorSpeed值。让我们把这些添加进去,测试电机一起工作。
驱动两个电机
为了让你的绘图机器人前进,你需要右电机顺时针旋转,左电机逆时针旋转。这个看起来可能有些违反直觉,但从侧面看看机器人底盘。图 8-11 展示了机器人框架的两侧,并且用箭头标出了前进方向。
图 8-11: 机器人从左右两侧的侧视图。为了前进,右轮必须顺时针旋转,左轮必须逆时针旋转。

在机器人的右侧,电轮需要顺时针旋转才能让机器人前进,而在机器人的左侧,电轮则需要逆时针旋转。注意每个轴的旋转方向。如果需要,可以将一块遮蔽胶带贴在电机旋转端,这样你就能看到轴的旋转方向。
现在,为了让机器人后退,只需反转那些方向。将setMotorB()的自定义函数代码添加到你的草图中后,调整你的loop()使其像清单 8-6 那样,然后上传代码,观察电机旋转!
清单 8-6: 测试两个电机的新loop()代码
void loop()
{
//drive forward medium speed for one second
setMotorA(100);
setMotorB(-100);
delay(1000)
//drive backward quickly for one second
setMotorA(-255);
setMotorB(255);
delay(1000);
//stop for one second
setMotorA(0);
setMotorB(0);
delay(1000);
}
你应该看到电机 A(右侧)顺时针旋转,而电机 B(左侧)逆时针旋转,之后 1 秒钟它们会互换。如果你发现两个电机在同一方向旋转,请交换其中一个电机的红黑线连接。
只需几行代码,你就可以让你的绘图机器人前进、右转、左转、后退,并且摇摆不定!
现在是时候为你的绘图机器人构建一个框架或底盘了。因为你编写的代码都在草图的loop()部分,所以你的电机会继续旋转、停止、旋转、停止。为了在构建底盘时停止电机的旋转,暂时断开 USB 线与电脑的连接。
构建绘图机器人底盘
如果你使用的是带面包板支架和 Arduino 基板的 SIK,那么你需要将 Drawbot 的底盘做得至少和基板一样大。基板的尺寸是 6 英寸 × 4.25 英寸。你可以使用一块纸板或薄胶合板制作底盘。根据我们的设计,我们将底盘做成了一个 6 × 8 英寸的矩形,如图 8-12 所示。你可以从 www.nostarch.com/arduinoinventor/ 下载这个模板的 PDF 文件。
图 8-12: Drawbot 底盘的底部视图(非全尺寸)

使用胶带或热熔胶枪,将电机固定在底盘的底部,按照图 8-13 所示的方向安装,电机的轮毂靠近后部,电机的较长端朝向前部。(热熔胶是一种很好的半永久性固定方法,因为你可以用工艺刀轻松刮掉它,移除零件,如果以后想重用它。)在将电机固定到底盘时,你可能需要暂时断开电机与面包板电路的连接,所以记得在粘好后重新连接它们。如果需要重新接线,可以参考图 8-10。
图 8-13: 使用热熔胶将电机安装到底盘

我们的基础模板上有一个孔,是为了安装笔或记号笔设计的。毕竟,Drawbot 需要能够绘图!为了让笔更稳固,我们将两块较小的纸板粘在一起,做成了一个更高的笔架。将这些纸板粘到孔的正上方,如图 8-14 所示。
图 8-14: 将笔架粘到底盘上

最后,将车轮安装到电机上。你会注意到电机的轴上有两个平面边缘(图 8-15)。确保将这些平面边缘与车轮轴孔的平面边缘对齐。安装可能会有点紧。推车轮时,要牢牢握住整个电机,以免不小心把电机从底盘上撕下来。
图 8-15: 电机轴的侧面图。将平面边缘与车轮开口的平面对齐。

Drawbot 将使用两个电机车轮作为动力和转向,同时在另一端使用滑动滑轮来保持平衡。这种方法叫做差动转向。这个特定的 Drawbot 被设计为前驱系统,后部使用乒乓球作为滑动滑轮。随着 Drawbot 的移动,乒乓球会在地面上滑动。将乒乓球按图 8-16 所示粘好,尽量将其居中,以确保最佳平衡。
注意
如果你想自己做车轮,可以参考图 8-15 中轴孔的形状和尺寸。
图 8-16: 将乒乓球作为滑行滑轮安装

现在,将面包板支架、Arduino 和电池包放置在底盘顶部,如图 8-17 所示。使用少许胶带或一些胶水点,确保它们不会移动。
图 8-17: 面包板支架、Arduino 和电池组安装在纸板底盘上

测试和故障排除
重新连接 USB 线到你的 Arduino 板,或者插入电池包,看看发生了什么。你可能需要握住电缆,以免它缠绕。机器人应该会慢慢向前移动 1 秒钟,快速反向移动 1 秒钟,然后停止 1 秒钟。因为你所有的代码都在loop()中,所以 Drawbot 会不断重复这个动作,直到你断开电源。在调试代码时,你可能想将 Drawbot 的后端垫在几本书上,以避免车轮触碰到桌面。
现在,如果你的机器人行为不像你预期的那样,你需要进行一些故障排除来找出问题所在。首先,确定问题。有两个常见的问题我们在这个机器人上见过:移动方向错误和转圈。如果它先向后移动而不是向前移动,交换连接到 H 桥的两个电机的红线和黑线。如果你的机器人转圈而不是向前或向后移动,尝试交换 Motor A 或 Motor B 的电机线——不要同时交换两者,否则你会遇到同样的问题。现在,你应该有一个可以前进然后后退、停下并重复这个动作的 Drawbot。
在继续之前,测试一下 Drawbot 在 1 秒钟内移动的距离,并记录下来。你会给它加上标记笔开始画线,你可能需要调整速度和时间,以便让 Drawbot 更容易控制。如果你在一个小桌子上工作,较慢的速度和较短的时间可能是最好的选择。
转弯并制作图案:机器人方舞
现在你已经掌握了让机器人前进和后退的操作,看看你能用你的新创作做出什么有趣的图案。在将标记笔放到你的 Drawbot 上之前,试着转弯。
要让机器人向右转,两个电机都需要逆时针旋转,要让机器人向左转,它们需要顺时针旋转。看看你能不能让你的 Drawbot 跳个小方舞!画一个方形的基本步骤如下:
• 向前移动。
• 转动 90 度。
• 向前移动。
• 转动 90 度。
• 向前移动。
• 转动 90 度。
• 向前移动。
• 转动 90 度。
图 8-18 展示了所需的步骤。你会注意到,你会重复相同的步骤四次。你已经知道可以使用loop()来执行重复的动作,但循环是无限重复的,而你希望你的绘图机器人在四次转动后停止。幸运的是,有一种编程技巧非常适合重复某部分代码指定次数:那就是for()循环。
图 8-18: 简单广场舞的步骤

for() 循环以命令for开始,后跟一对圆括号。在圆括号内有三个部分。参见列表 8-7。
列表 8-7: for() 循环
for(➊int count = 0; ➋count < 4; ➌count++)
{
➍ //insert code here that you want to repeat
}
第一部分 ➊ 是计数器变量的声明和初始化,用于跟踪循环的重复次数。你将这个变量声明为整数,命名为count,并初始化为0。你可以随意命名这个变量,只要在接下来的两个部分使用相同的变量名即可。接下来的部分是条件语句 ➋,它控制for()循环是否继续执行或停止。在这里,只要条件语句count < 4为true,循环就会继续。由于count初始化为0,所以第一次循环时该条件为true,循环将继续。第三部分是增量语句 ➌,它告诉for()循环在每次重复后如何处理计数器变量。这里,count++是count = count + 1的简写,表示每次循环时将计数器变量增加 1。最后一部分 ➍ 是你希望重复或循环执行的代码,就像你放在花括号中的任何代码一样。
所以,总的来说,for() 循环的参数表示循环应继续运行,直到运行四次为止,此时count将增加到4,条件语句将变为false,循环退出。for() 循环是一种非常方便的方式,可以清理代码并将指令重复特定次数。
现在,使用这个新技能编写你的广场舞代码。在你的草图中,将loop()替换为列表 8-8 中的loop()。其他部分保持不变。
列表 8-8: 广场舞代码
void loop()
{
➊ for(int count = 0; count < 4; count++)
{
//drive forward
➋ setMotorA(100);
setMotorB(-100);
➌ delay(500);
//turn right
➍ setMotorA(-100);
setMotorB(-100);
delay(250);
}
➎ delay(1000);
}
快速操作变量的简写
通常,你可能希望在代码中增加、减少或修改一个变量的值。最常见的用途是每次循环重复时将变量的值增加 1,你可以使用变量名 = 变量名 + 1来实现。但是,也有一些简写的方法可以修改变量的值,如下表所示。
| 简写代码 | 完整代码 | 描述 |
|---|---|---|
变量名++; |
变量名 = 变量名 + 1; |
增加 1 |
变量名 += 2; |
变量名 = 变量名 + 2; |
增加 2 |
变量名 += n; |
变量名 = 变量名 + *n*; |
增加 n |
variableName--; |
variableName = variableName - 1; |
递减 1 |
variableName -= 2; |
variableName = variableName - 2; |
递减 2 |
variableName -= n; |
variableName + variableName - *n*; |
递减 n |
variableName *= n; |
variableName = variableName * *n*; |
乘以 n |
variableName /= n; |
variableName = variableName / *n*; |
除以 n |
为了绘制一个正方形,草图使用 for() 循环 ➊ 重复这四个步骤。机器人首先前进 ➋ 半秒钟 ➌。你需要确保它不会走得太远,避免在地板上画得一团乱。接下来,要让机器人转弯,草图将两个电机设置为逆时针旋转 ➍。为了完成每个正方形,你添加了一个短暂的 1 秒延迟 ➎。注意,延迟是在 for() 循环的大括号之后。Drawbot 会重复这四个步骤——前进并转弯四次——然后等待 1 秒后,整个 loop() 循环再重复。这将给你时间手动移动 Drawbot 或重新调整位置,如果需要的话。我们办公室里的这些数值效果很好,但你可能需要根据自己的 Drawbot 微调速度设置和时间。调整你的草图,直到你得到一个方形的移动模式。如果不完美也没关系——只要继续测试转角的速度。这就是你要创造的艺术的一部分!
我们已经在清单 8-9 中包括了完整的方形舞步 Drawbot 草图。
清单 8-9: 完整的 Drawbot 方形舞步代码
const byte AIN1 = 13;
const byte AIN2 = 12;
const byte PWMA = 11;
const byte BIN1 = 8;
const byte BIN2 = 9;
const byte PWMB = 10;
void setup()
{
pinMode(AIN1, OUTPUT);
pinMode(AIN2, OUTPUT);
pinMode(PWMA, OUTPUT);
pinMode(BIN1, OUTPUT);
pinMode(BIN2, OUTPUT);
pinMode(PWMB, OUTPUT);
}
void loop()
{
for(int count = 0; count < 4; count++)
{
//drive forward
setMotorA(100);
setMotorB(-100);
delay(500);
//turn right
setMotorA(-100);
setMotorB(-100);
delay(250);
}
delay(1000);
}
void setMotorA(int motorSpeed)
{
if (motorSpeed > 0)
{
digitalWrite(AIN1, HIGH);
digitalWrite(AIN2, LOW);
}
else if (motorSpeed < 0)
{
digitalWrite(AIN1, LOW);
digitalWrite(AIN2, HIGH);
}
else
{
digitalWrite(AIN1, LOW);
digitalWrite(AIN2, LOW);
}
analogWrite(PWMA, abs(motorSpeed));
}
void setMotorB(int motorSpeed)
{
if (motorSpeed > 0)
{
digitalWrite(BIN1, HIGH);
digitalWrite(BIN2, LOW);
}
else if (motorSpeed < 0)
{
digitalWrite(BIN1, LOW);
digitalWrite(BIN2, HIGH);
}
else
{
digitalWrite(BIN1, LOW);
digitalWrite(BIN2, LOW);
}
analogWrite(PWMB, abs(motorSpeed));
}
正如我们之前提到的,底盘模板的前端有一个凹槽,用来放置标记笔。我们建议使用可洗的或干擦标记笔。找一块大海报纸或干擦板,铺在地板上。务必小心,不要让它在地板上乱画!这可能会惹上麻烦。(相信我们,我们以前犯过这个错误,真是后悔不已。)
将你的 Drawbot 放在绘图表面上。用一块胶带固定标记笔,确保它与绘图表面接触良好。手动移动 Drawbot,在绘图表面上测试标记笔的位置。现在,插入 USB 电缆连接到电脑,看看会发生什么。如果它看起来会跑出绘图表面,直接掉到地板上,赶紧抓住 Drawbot。
为了让操作更有趣,改变标记笔的颜色或修改代码来绘制不同大小的正方形。看看你能否让你的 Drawbot 绘制螺旋和星形!图 8-19 展示了我们在办公室中 Drawbot 绘制的一些有趣图案。
图 8-19: Drawbot 在行动——确保使用一张大纸,以免它把地板弄得满是画!

如果你想给你的 Drawbot 增加一些风格,试着翻翻你的手工材料,或者找一些废弃的纸板,看看你能想到什么。我们在办公室把一个旧的椒盐饼罐放在了我们的 Drawbot 上。现在它被称为椒盐饼机器人(Pretzel Bot),它四处走动并派发免费的椒盐饼(见图 8-20)。
图 8-20: 椒盐饼机器人!Arduino 和面包板被隐藏在盒子里。

进一步探索
Drawbot 是基础机器人学的入门。最简单的机器人实际上只是一个控制器和两个电动机,这正是你在这里拥有的。我们将为你提供一些想法,带你迈向下一个层次。
黑客
预编程的动作很有趣,但每次你想要改变模式时,都需要重新上传草图。不过,通过对代码做一些更改,你可以在 Drawbot 仍在运行时,通过串口监视器控制它。
到目前为止,你只使用串口监视器来读取 Arduino 在项目运行时发送回计算机的数据,比如传感器数据,但串口监视器也可以向 Arduino 发送数据。打开串口监视器窗口,你会看到顶部有一个小文本框,并且有一个标记为发送的按钮,如图 8-21 所示。这个框允许你向 Arduino 发送数据,这样你就可以从串口监视器控制它。
图 8-21: 串口监视器窗口

P8_DrawbotSerial.ino 草图(可从 www.nostarch.com/arduinoinventor/ 下载)使用串口监视器发送三个数字来控制电动机 A、电动机 B 和驱动时间。
看看代码。这个新的草图声明了三个变量来存储电动机 A 的速度、电动机 B 的速度和延迟时间,这三个数字就是你发送的内容。你使用Serial.begin(9600);初始化与 Arduino 的通信,通过串口监视器发送和接收数据。Arduino 读取你输入到串口监视器的数据,并将任何整数赋值给三个变量 speedA、speedB 和 delayTime,然后这些值会用于熟悉的 setMotorA()、setMotorB() 和 delay() 函数。更多完整的解释可以参考代码中的注释。
将草图上传到你的 Drawbot,打开串口监视器,输入 100 -100 500,然后按下 ENTER 或点击发送(参见图 8-22)。Drawbot 应该向前移动半秒钟后停止。现在,你可以精细调整你的 Drawbot 程序,而不必每次都重新上传代码!代码将运行一次,除非你给它额外的命令,否则不会重复。当你输入六个数字时,如100 -100 500 -100 -100 250,会发生什么呢?试试看能不能编排出一个由一系列数字表示的舞蹈程序。
图 8-22: 编排机器舞蹈

修改
你还可以让你的绘图机器人编程绘制其他哪些形状?运用你学到的 for() 循环,看看能否修改代码让它绘制三角形或星形。你需要进行一些实验,调整时机和速度,使其刚刚好。当你一次只移动一个轮子时,会发生什么呢?
奖励
有一个奖励脚本(在 www.nostarch.com/arduinoinventor/)可以让你使用更简单的命令控制海龟,例如 fd 10 和 bk 10 来向前或向后移动 10 步。下载 P8_BonusTurtle.ino 并将其加载到你的 IDE 中。然后打开串口监视器,输入以下命令:fd 10 向前移动 10 步,bk 10 向后移动 10 步,rt 90 向右转 90 度,lt 90 向左转 90 度。看看你能否用这些指令让你的新海龟跳一个方形舞!
第十章:9 拖车计时器
在项目 4 中,你制作了一个反应计时器来测量你按下按钮的速度。在这个项目中,你将基于那里的技巧,制作一个用于 Hot Wheels 启发的赛道的计时器(见图 9-1)。我们将向你展示如何在一个小型便携式 LCD 屏幕上显示完赛时间,这样你就可以将项目从电脑上分离出来。我们还将展示如何修改这个项目,增加第二条赛道和指示灯(LED)来显示哪辆车获胜。你准备好了吗?
图 9-1: 完成的拖车计时器

所需材料
本项目使用的许多部件你可能已经熟悉(见图 9-2 和 9-3)。我们只会介绍一个新部件:16 × 2 字符 LCD,你将用它直接显示比赛时间,而不是在电脑屏幕的串行监视器中显示。
注意
在“深入了解”第 273 页,我们将展示如何修改你的拖车计时器,使你可以竞速两辆车并显示获胜时间。标准的 SparkFun 发明者套件包括一个光敏电阻(SEN-09088),但你需要两个来完成这个最终的修改。幸运的是,光敏电阻是一个相当便宜的元件,所以你可以购买另一个,或者和拥有发明者套件的朋友合作,做出双人版本。
电子元件
• 一个 SparkFun RedBoard (DEV-13975),Arduino Uno (DEV-11021),或任何其他兼容的 Arduino 板
• 一根 USB Mini-B 电缆 (CAB-11301 或你的板载 USB 电缆)
• 一个无焊接面包板 (PRT-12002)
• 一个 10 kΩ 电阻,如果你想做双人版本,则需要两个 (COM-08374,或者 COM-11508,一包 20 个)
• 一个光敏电阻(SEN-09088),如果你想做双人版本则需要两个*
• 一个按键开关 (COM-10302)
• 一个 10 kΩ 可调电阻 (COM-09806)
• 一个 16 × 2 字符 LCD (LCD-00255)
• 一个微型伺服电机 (ROB-09065)
• 公对公跳线 (PRT-11026)
• 公对母跳线 (PRT-09140*)
注意
标记有星号()的部件不包含在标准的 SparkFun 发明者套件中,但可以在单独的附加包中购买。*
图 9-2: 拖车计时器的组件

其他材料和工具
• 工艺刀
• 金属尺
• 钳子
• 线切割钳
• 纸胶带
• 胶水(热熔胶枪或手工胶水)
• 硬纸板(约 8.5 × 11 英寸),一个小纸盒,或厚卡纸
• 竹签
• 外壳模板(见图 9-15,第 266 页)
• Hot Wheels 或其他小型玩具车进行竞速(未显示)
• (可选)玩具车赛道(未显示)
图 9-3: 推荐的工具和材料

在之前的项目中,我们使用串口监视器将 Arduino 发送的信息显示到计算机上。在这个项目中,我们将教你如何将 LCD 直接添加到你的项目中,这是一个值得学习的技能。LCD 需要许多连接线,但不用担心——我们将一步一步地教你。当你掌握了这个部分的使用后,你可以将其添加到你过去的一些项目中,使它们完全便携!
新组件:16 × 2 字符 LCD
LCD是液晶显示的缩写。液晶技术发明已有 40 多年,广泛应用于数字手表、闹钟、投影仪、电视、计算机显示器等设备中。
你将在这个项目中使用的 LCD 是一个简单的单色显示屏,意味着它只能显示一种颜色。显示屏下方有一层液晶。液晶是一种独特的化学物质,当施加微小电流时,它会从透明变为不透明。结合背光或反射镜,液晶可以用于构建非常简单的显示器。光线的通过或阻挡取决于电流施加在液晶的哪个区域——这意味着如果你能控制电流,就能制作出图形。
16 × 2 字符 LCD 显示最多 32 个字符的信息,每个字符被分解为一个 5 × 8 像素矩阵。每个单独的像素可以根据施加的电流改变透明度,由 Arduino 控制。例如,当图 9-4 中的黄色像素变为不透明时,字母A将在 LCD 屏幕上显示。
图 9-4: 5 × 8 像素矩阵上表示的大写字母A

单个字符包含 40 个独立像素,每个像素都由 Arduino 控制,这意味着有 1280 条不同的控制线!幸运的是,这个项目中使用的 LCD 配备了日立公司生产的特殊并行接口 LCD 驱动 IC——HD44780。这个芯片允许你仅通过六条来自 Arduino 的控制线在屏幕上显示几乎任何字符。图 9-5 显示了 LCD 上的引脚。
图 9-5: 一个简单的 16 × 2 字符 LCD

注意
LCD可以使用最多八个引脚来传输数据(d0–d7),但我们将要使用的 LCD 仅使用四个引脚,这些引脚标记为 d4–d7。
LCD 共有 16 个引脚,但本项目只使用引脚 1–6 和 11–16。引脚从左到右编号为 1 至 16(顶部的引脚)。表 9-1 描述了 LCD 上每个引脚的功能。在一些数据表中,你可能会看到标签上有一条线,例如引脚 5 上的
标签。这个线条表示该特性是低电平有效,意味着该引脚在低电压时被激活。因此,在这种情况下,当你想写入 LCD 时,引脚 5 需要设置为LOW。我们将在“连接数据和控制电路”一节中详细讨论这一点,见第 255 页。
表 9-1: 16 × 2 字符 LCD 引脚描述
| 引脚 | 描述 |
|---|---|
| 1 | 地(GND) |
| 2 | LCD 电源(5V) |
| 3 | 对比度调整(0–5V) |
| 4 | 寄存器选择(RS) |
| 5 | ![]() |
| 6 | 启用 |
| 7–10 | 数据线 d0–d3(未使用) |
| 11–14 | 数据线 d4–d7(每次传输 4 位数据) |
| 15 | 背光电源(5V) |
| 16 | 背光接地(GND) |
与其单独控制每个字符的 40 个像素,HD44780 驱动芯片通过四个数据线和两个控制线来解释 Arduino 发送的数据,并将其转换为显示的字符。为了进一步简化接口,Arduino 社区编写了一个 LCD 库,用于向 LCD 写入代码。我们将在代码中查看这一点。
拖拉赛计时器操作
在开始连接电子元件之前,让我们讨论一下草图的功能。我们设计这个赛车计时器时,当按下按钮时,舵机向上移动,打开起始门,允许赛车沿赛道下坡。同时,Arduino 记录起始时间,并等待看到赛车何时到达赛道底部的光敏电阻处,那里使用的是项目 5 中的相同光传感器电路。你将把光传感器嵌入赛道的中心,这样当赛车经过时,它会投下一个阴影,Arduino 可以检测到。当 Arduino 检测到阴影时,它将记录停止时间,并计算总时间,即停止时间减去起始时间。如果这听起来像是项目 4 中的反应计时器,那是因为它确实是!
构建 LCD 电路
你将从构建 LCD 电路开始。LCD 共有 16 个引脚,但你只会使用其中的 12 个。图 9-6 展示了 LCD 电路连接的原理图。
图 9-6: LCD 电路连接的原理图

LCD 有 16 个引脚,占用面包板的 16 行,因此在进行此项目时需要小心布置。你将把 LCD 安装在面包板右侧的前 16 行。确保将 Arduino 的电源和地线连接到面包板左侧的电源和地线轨道。
请注意,LCD 上的引脚没有标注。在我们引导你完成接线时,我们将按顺序提到 LCD 上的引脚,从底部的 1 号引脚开始。
为 LCD 供电
LCD 有两个独立的电源,一个用于背光,另一个用于控制逻辑。你需要将这两者分开接线。
将 LCD 的 1 号引脚连接到 GND,LCD 的 2 号引脚连接到 5 V,使用面包板上的电源轨道。这为 LCD 的控制电路和 HD44780 LCD 驱动芯片提供电源。接下来,将 LCD 的 15 号引脚连接到 5 V,LCD 的 16 号引脚连接到 GND,再次使用电源轨道。这两个连接为 LCD 内置的背光提供电源(参见 图 9-7)。
图 9-7: 连接 LCD 的电源和背光。

控制对比度
你可以调整 LCD 屏幕的对比度。为此,你可以使用电位器控制 LCD 3 号引脚上的电压,电位器与平衡梁中的电压分压电路类似(参见 项目 6)。回想一下,电位器与可变电阻是相同的:它有三个引脚,当你旋转旋钮时,中间引脚与任一端引脚之间的电阻发生变化。如果你将电位器的顶部和底部引脚连接到 5 V 和 GND,你就得到一个可变电压分压器,其中中心引脚上的电压会根据旋钮的旋转程度在 5 V 和 GND 之间变化(参见 图 9-8)。
图 9-8: 电位器示意图,作为可变电压分压器连接

将电位器添加到面包板上,位于 LCD 下方。将电位器的外部引脚连接到 5 V 和 GND,将中间引脚连接到 LCD 的 3 号引脚,用于对比度控制。
现在,你只需要添加 LCD 的数据和控制线路。
连接数据和控制线路
你需要另外七根电缆来连接 LCD,包括四条数据线和三条控制线。LCD 的引脚 5 是读/写功能,允许 Arduino 读取和向显示器写入数据。你只需使用这个读/写连接来向 LCD 发送数据,或者写入设备,所以你可以将它连接到地(GND),也就是“将引脚接地”。如果你查看表 9-1,你会注意到标签上有一条线覆盖着写入(Write)。如前所述,这种符号通常在数据表和文档中使用,表示低电平信号将激活该功能。低输入等同于地,因此请添加一根电缆将 LCD 的引脚 5 连接到 GND,如图 9-9 所示。
图 9-9: 将 LCD 的引脚 5 连接到 GND 进行
控制。

最后的六根电缆将连接 LCD 和 Arduino。LCD 的引脚 11 到 14 是 Arduino 用来向 LCD 发送信息的四条数据线。将这些引脚连接到 Arduino 的引脚 10、11、12 和 13,如图 9-10 所示。电缆应直接从 Arduino 板连接到 LCD,避免交叉。
最后两条连接是引脚 6 的使能(Enable)和引脚 4 的寄存器选择(Register Select)。使能引脚用于向 LCD 发出数据传输信号,寄存器选择引脚决定数据是表示要显示的字符还是指令,如清除屏幕或移动光标;这能让你更好地控制屏幕上显示的内容。将 Arduino 的引脚 9 连接到 LCD 的引脚 6,将 Arduino 的引脚 8 连接到 LCD 的引脚 4,如图 9-10 所示。
图 9-10: LCD 电路的最终接线

表 9-2 显示了 LCD 屏幕的连接,帮助你确保所有连接正确。
表 9-2: LCD 引脚连接
| LCD 引脚 | 连接 |
|---|---|
| 16 | 地 (GND) |
| 15 | 5V |
| 14 | Arduino 引脚 13 |
| 13 | Arduino 引脚 12 |
| 12 | Arduino 引脚 11 |
| 11 | Arduino 引脚 10 |
| 10 | 不适用 |
| 9 | 不适用 |
| 8 | 不适用 |
| 7 | 不适用 |
| 6 | Arduino 引脚 9 |
| 5 | 地 (GND) |
| 4 | Arduino 引脚 8 |
| 3 | 电位器的中间引脚 |
| 2 | 5V |
| 1 | 地 (GND) |
测试 LCD
在接线之前,先测试一下,确保电路到目前为止按预期工作。将 Arduino 连接到计算机。
一旦供电,背光应该会亮起。尝试旋转电位器旋钮。即使 LCD 上没有显示任何内容,你也应该能看到屏幕对比度的变化,从最暗到最高时显示 32 个明亮的矩形。
如果你没有看到此内容,请仔细检查接线。确保所有电源连接到 LCD 的接线与表 9-2 中的内容一致。
一旦你让 LCD 工作,复制 Listing 9-1 中的代码到 Arduino,并上传到你的设备。这段简单的示例应该会在第一行显示文本 SparkFun Arduino,在第二行显示一个运行的计数器。
LISTING 9-1: 用于在 LCD 上显示文本和运行的 millis() 计数器的测试代码
➊ #include<**LiquidCrystal**.h>
➋ **LiquidCrystal** lcd(8, 9, 10, 11, 12, 13);
void setup()
{
➌ lcd.begin(16, 2); //initializes interface to LCD
➍ lcd.clear();
➎ lcd.print("SparkFun Arduino");
}
void loop()
{
➏ lcd.setCursor(0, 1); //move cursor to the 2nd line
//(col 0, row 1)
➐ lcd.print(millis()/1000); //print the number of
//seconds elapsed
}
让我们来看看这个示例中发生了什么。首先,它包含了 Arduino 社区创建的 LiquidCrystal.h 库 ➊,这个库简化了六个不同的控制和数据线。这将使你更容易向 LCD 发送指令。
接下来,这段代码创建了一个名为 lcd 的对象,使用了 LiquidCrystal 库 ➋。注意,这次在创建对象时,你传递了一组参数,对应 LCD 的引脚:寄存器选择、使能引脚和四个数据引脚。这是你配置每个功能对应的引脚的地方。在某些文档中,你可能会看到这个命令写作 LiquidCrystal lcd(RS, Enable, d4, d5, d6, d7)。
你可能会想知道为什么我们不使用 LCD 的四个引脚。这个 LCD 可以通过四个或八个数据线传输数据。根据 LCD 的数据手册,当你使用四个数据线时,你需要使用 LCD 上方的四个引脚——标记为 d4、d5、d6 和 d7 的引脚。虽然这种方式传输数据到 LCD 需要 Arduino 花费两倍的时间,但它有助于保持电路尽可能简单,记住,Arduino 的时钟频率是 16 MHz。这可是非常快的!
LiquidCrystal 库有大约 20 个不同的命令,可以简化对这个 LCD 的控制。在这个示例中,我们会向你展示一些基本命令,允许你配置屏幕大小、清空屏幕、显示信息和移动光标。
setup() 部分的代码包含了一些只会在 Arduino 启动时运行一次的指令。其中第一个是 lcd.begin(16, 2); ➌,它将 LCD 设置为 16 × 2 字符 LCD,使得库可以正确地换行并从一行移到下一行。
下一条指令,lcd.clear(); ➍,允许你在显示新文本之前清空屏幕。它还会将光标的位置重置到屏幕第一行的第一个字符。如果没有这条指令,LCD 会保留上次显示的内容。
然后,命令 lcd.print("SparkFun Arduino"); ➎ 会在 LCD 上显示文本 SparkFun Arduino。由于 lcd.clear() 指令刚刚清空了屏幕,这段文本将出现在显示的第一行。这段文本正好是 16 个字符长,应该填满 LCD 的第一行。这个命令类似于 Serial.print(),但使用 lcd.print() 时,你不需要连接到计算机或者打开串口监视器,就能看到来自设备的文本和信息。
loop() 每次重复时都会用新信息刷新屏幕。首先,它使用 lcd.setCursor(0, 1); ➏ 将光标移动到 LCD 的第二行,以免覆盖第一行的 SparkFun Arduino 文本。setCursor() 方法中的两个数字指示字符的位置(0)和行数(1)。像很多编程环境一样,Arduino 的计数是从 0 开始的,而不是从 1 开始。
注意
LiquidCrystal 库只适用于字符型 LCD 显示屏。图形 LCD 屏幕也有提供,但它们使用一个不同的库,叫做 OpenGLCD,它允许你显示图形,如线条、矩形和圆形,以及文本。
最后,草图使用另一个 lcd.print() 指令打印一个计数器 ➐。这个计数器使用 millis() 函数,它报告自 Arduino 通电以来的毫秒数。将这个值除以 1,000,就可以得到秒数的计数器。我们将在比赛计时器中使用类似的技巧。
现在,你能弄清楚如何更改文本,使得第一行显示你的名字吗?如何将时间显示改为以分钟为单位而不是秒数?多试试这个代码示例,直到你熟悉如何在 LCD 上显示数据。只需要 Arduino 的六个 GPIO 引脚,你就能将 LCD 显示器添加到任何项目中!
本示例演示了 Arduino LiquidCrystal 库中最常用的指令,但如果你想查看其他可以使用的命令,请参考 www.arduino.cc/en/Reference/LiquidCrystal/。
现在你已经使 LCD 电路工作,接下来是添加按钮、伺服电机和光传感器电路。
添加其余的电子元件
拖拉赛计时器将使用你在之前的项目中已经组装好的几个部件:一个用于开始比赛的按钮,一个用于控制汽车起跑门的伺服电机,和一个用于检测汽车到达赛道终点的光敏电阻。图 9-11 展示了这三个附加组件的原理图。
图 9-11: 拖拉赛计时器中附加组件的原理图

将一个按钮放置在面包板上,使其两腿分别位于中心分隔线的两侧,并将一侧的两个引脚连接到 Arduino 的引脚 5 和 GND。部件将填满面包板的大部分空间,因此请密切注意面包板上的行以及各个组件的连接方式。为了节省空间,本项目使用了没有外部上拉电阻的按钮,这与 项目 4 中反应计时器的做法不同。相反,我们将在代码中启用上拉电阻。
接下来,连接将打开起跑闸门的伺服电机。使用三根公对公跳线,将信号线(黄色或白色)连接到 Arduino 的 4 号引脚,将红线连接到 5 V 电源轨,将黑线连接到 GND 电源轨。
最后,添加光传感器电路并连接一个电压分压电路。将光敏电阻的一端连接到 5 V 电源轨,另一端通过一个 10 kΩ 的下拉电阻连接到 GND 电源轨。将同时连接光敏电阻和 10 kΩ 上拉电阻的这一行连接到 Arduino 的 A0 引脚。这个电路应该与 项目 5 中的夜光电路相似。
这个电路包含很多组件,所以请慢慢来,并仔细核对你的接线是否与图 9-12 中的图示一致。
图 9-12: 拖拉赛计时器的完整电子电路,包括起始按钮和闸门

编程拖拉赛计时器
现在让我们将所有内容整合在一起。启动一个新的草图,并输入清单 9-2 中的代码,或者从 www.nostarch.com/arduinoinventor/ 下载该代码。这个示例将结合我们在过去项目中单独使用过的几个概念和思想。
清单 9-2: 拖拉赛计时器草图
➊ #include<**LiquidCrystal**.h>
#include<**Servo**.h>
**LiquidCrystal** lcd(8, 9, 10, 11, 12, 13);
➋ **Servo** startingGate;
➌ const byte buttonPin = 5;
const byte servoPin = 4;
const byte finishSensor1Pin = A0;
const int darkThreshold = 500;
➍ int finishSensor1;
boolean finishFlag = false;
long startTime;
long stopTime;
float raceTime;
void setup()
{
➎ pinMode(buttonPin, INPUT_PULLUP);
startingGate.attach(servoPin, 1000, 2000);
startingGate.write(0);
➏ lcd.begin(16, 2);
lcd.clear();
lcd.print("Drag Race Timer");
lcd.setCursor(0, 1);
lcd.print("Push to start!");
➐ while (digitalRead(buttonPin) == HIGH)
{
}
lcd.clear();
lcd.print("Go!");
startingGate.write(180);
startTime = millis();
}
void loop()
{
➑ finishSensor1 = analogRead(finishSensor1Pin);
➒ if ((finishFlag == false) && (finishSensor1 < darkThreshold))
{
finishFlag = true;
stopTime = millis();
raceTime = stopTime - startTime;
lcd.clear();
lcd.print("Finish Time:");
lcd.setCursor(0, 1);
➐ lcd.print(raceTime / 1000, 3);
}
}
让我们来看一下这一切是如何工作的。首先,草图通过 #include 指令 ➊ 引入了两个库,LiquidCrystal.h 和 Servo.h。接下来,它初始化了一个名为 lcd 的 LiquidCrystal 对象,类似于清单 9-1,以及一个名为 startingGate 的 Servo 对象 ➋。
然后,草图声明了一组常量,用于连接按钮、电动机和光敏电阻电路的引脚 ➌。这样一来,在你进行更改和修改时,如果需要将某根线移到 Arduino 的不同引脚,只需在代码中更改一个数字即可。最后一个常量是一个名为 darkThreshold 的阈值,用于设置检测汽车是否遮挡光传感器时的光照水平。这里将其设置为 500,大约处于 0–1023 范围的中间,但你可能需要根据自己房间的环境调整这个值。
接下来,草图声明了一些变量 ➍。finishSensor1 变量用于存储光敏电阻传感器的原始值。下一个变量 finishFlag 是一个状态变量,用于跟踪草图当前的状态。finishFlag 变量初始化为 false,用于表示比赛是否结束(就像在一级方程式比赛中挥动的终点旗帜,标志着比赛的胜者)。我们稍后将在代码中根据传感器的输入值来设置它。接下来的三个变量用于通过 Arduino 内置的 millis() 定时器来计算比赛时间。
现在,代码的setup()部分通过声明引脚模式为INPUT_PULLUP➎来设置按钮引脚,使用内建的上拉电阻。这一技巧消除了在项目 4 中使用外部上拉电阻的需要。
接下来,草图初始化舵机并将其默认位置设置为0。这将是起跑门下降时的位置。
草图然后将一些信息显示到 LCD➏上,告知用户如何开始比赛。这几行代码设置 LCD,清空屏幕,并显示两行文本。请小心,文本每行限制在 16 个字符以内;超过 16 个字符,文本会超出屏幕显示到右侧。接着,代码通过使用while()循环技术➐等待按钮按下,这种技术在反应计时器中使用,它会阻止草图继续,直到按钮被按下。当按钮被按下时,digitalRead(buttonPin)会读取为LOW,然后代码将舵机移动到上升位置并设置startTime变量。
在loop()中,草图读取光传感器并将当前读数存储到变量finishSensor1➑。传感器将被嵌入在坡道的尽头。当汽车越过终点线时,它将滚过传感器,遮挡大部分光线,类似于项目 5 中的夜灯草图,草图会将传感器的值与darkThreshold值进行比较。
注意
你的传感器需要放在一个光线适中的区域,以便传感器被照亮和被遮挡时的对比度足够大,能够引起电压的下降。请注意,天花板灯光可能会在你身体的阴影投射到传感器时导致虚假检测。如果你想确保传感器工作良好,可以拿一盏小台灯,把它放在传感器上方。
记住,在汽车通过传感器的时间里,loop()可能会重复几次。因为我们只想捕捉到汽车越过终点线的第一个时刻,草图使用了一个复合if()语句➒来捕捉finishFlag变量为false 并且 终点传感器被遮挡的时刻(即其值小于darkThreshold)。&&表示逻辑与(参见复合逻辑运算符在第 264 页)。请特别注意if()语句中使用的括号数量——它们表示运算顺序以及逻辑如何运作。
现在,在if()语句内部,finishFlag状态变量切换为true。因为finishFlag状态变量现在设置为true,所以复合if()语句只会捕捉到汽车越过传感器的第一个时刻。
草图接着记录停止时间并计算经过的比赛时间。最后,草图将比赛时间打印到 LCD 上。
raceTime 变量被声明为 float(浮动点数变量),以便它可以存储带有小数的数字。默认情况下,lcd.print() 方法会显示浮动点数值的两位小数精度,但你可以向 lcd.print() 方法添加第二个参数来指定更多或更少的小数位数。在 ➓ 中,草图通过将毫秒数除以 1,000 来计算已过去的秒数。指令 lcd.print(raceTime / 1000, 3); 中的额外 3 告诉 Arduino 显示小数点后三位数,因此时间将精确到毫秒。不要忘记代码中的最后两个大括号。请仔细检查确保代码与 清单 9-2 相符,并将草图上传到设备。
复合逻辑运算符
在第四章中,我们介绍了用于比较两个值的简单逻辑比较运算符。回想一下,逻辑比较或表达式的结果只能是 true 或 false。在编程中,有时需要同时比较多个条件;例如,当你需要在一个变量为 false 且传感器值小于阈值时才执行某段代码:((finishFlag == false) && (finishSensor1 < darkThreshold))。这里,注意到逻辑比较被括号分组在复合 AND (&&) 的两侧。
两个或多个逻辑比较的组合称为 复合逻辑表达式。表达式是从左到右 评估(或读取)的。为了保持所有内容在一起并遵守正确的运算顺序,使用括号分隔各个表达式是一个好主意。用来组合逻辑表达式的两种主要运算符是 AND 和 OR,如下表所示。
| 符号 | 复合运算符 | 描述 |
|---|---|---|
(*表达式 A*) && (*表达式 B*) |
AND | *表达式 A* 和 *表达式 B* 必须都为 true。 |
(*表达式 A*) || (*表达式 B*) |
OR | *表达式 A* 或 *表达式 B* 必须为 true。 |
快速测试
如果你的一切连接正确且代码上传成功,你将听到伺服电机移动到 0 度位置,并看到 LCD 上显示的信息,如 图 9-13 所示。如果文本出现乱码或其他错误,请仔细检查 LCD、按钮和光传感器的连接。
图 9-13: 比赛开始时的 LCD 显示文本

按下按钮,看看会发生什么。伺服电机应该移动,显示屏应该变更为 “Go!” 的信息。现在,用手指遮住光敏电阻。LCD 屏幕应该显示自按下按钮并遮住光敏电阻后的经过时间(见 图 9-14)。
当电子元件都正常工作时,是时候搭建起跑门和赛道了。如果传感器没有按预期工作,可以尝试更改darkThreshold值。如果传感器太敏感或立即触发,减小darkThreshold值。如果覆盖传感器时它没有反应,可以尝试增大该值。做完这些更改后,重新上传代码并再次测试。
图 9-14: LCD 显示屏与经过的时间

构建拖拉赛道
拖拉赛道包括一个起跑塔和一个旋转门,控制汽车进入赛道。对于赛道,你可以使用一段玩具赛车赛道,也可以从卡纸上自制。塔的模板如图 9-15 所示。你可以从* www.nostarch.com/arduinoinventor/*下载这个模板的 PDF 文件。
图 9-15: 起跑门的纸板模板(非实际尺寸)

搭建起跑塔
小心地从纸板上剪下模板(见图 9-16)。模板一侧有一个孔用于安装伺服器,另一侧有一个孔用于安装起跑门的竹签轴心。其他部件包括支撑梁和起跑门。
图 9-16: 按模板描线并小心地用锋利的工艺刀剪下部件。

在切好部件后,首先将伺服器安装在图 9-15 中标记的开口处。从支撑梁外侧插入伺服器,使伺服器舵轮朝向车子。你可以使用随伺服器附带的小螺丝,或者少量胶水将伺服器固定到位,如图 9-17 所示。暂时不要安装伺服器舵轮,下一步会将其安装到起跑门上。
图 9-17: 使用热胶固定伺服器

现在,将两根支撑梁粘合到位。下部支撑梁将插入每个侧面切口中的槽口。上部支撑梁应准确地嵌入每个侧面顶部的缺口中。使用少量胶水将这些部件固定到位。完成后,你应该拥有一个类似于图 9-18 所示的起始支撑塔。
图 9-18: 添加支撑梁

组装起跑门
要搭建起跑门,你需要一块 2.5 × 1 英寸的纸板和一根短小的竹签或细长的咖啡搅拌棒。这将作为起跑门的轴心。首先,在起跑门的边缘涂上一小滴胶水,并将伺服器舵轮固定,使其中心悬挂在边缘之外,如图 9-19 所示。
图 9-19: 将伺服器舵盘粘贴到起始门的边缘

将竹签剪至 3.5 英寸长。在边缘涂上一条胶水,并将车轴与伺服器的舵盘对齐,如图 9-20 所示。
图 9-20: 将车轴粘贴到起始门上

插入你的 Arduino 并按下重置按钮来重置伺服器的位置。记住,代码开始时伺服器处于 0 度位置;这是下垂位置,起始门将车子固定在原地。要将起始门放入支撑塔,首先将车轴插入与伺服器相对的侧面孔中,如图 9-21 所示。记住,当门打开时,它会顺时针旋转。
图 9-21: 将车轴插入起始门的侧面孔中

完成的起始塔和门如图 9-22 所示。
图 9-22: 完成的起始塔

现在你需要一个轨道。你可以使用标准的 Hot Wheels 轨道,这会适合安装到下方支撑架上,或者自己制作轨道。如果你想使用 Hot Wheels 轨道,可以跳到 “添加光敏电阻” 的部分,参见第 270 页。
自己制作轨道
要制作自己的轨道,你需要至少一张 3.5 × 11 英寸的卡纸。你可以制作几段轨道并将它们粘贴在一起,形成更长的轨道,但在我们的例子中,我们只使用一段轨道。
你需要将轨道的两侧边缘折叠。每一侧,都要量测并标记离边缘一英寸四分之一的位置。现在,沿着这些线折叠,使轨道的每一侧都有一个四分之一英寸的凸缘。(使用金属尺子或桌边来帮助折叠卡纸,通常能够得到一条整齐的折痕。)这个凸缘将防止你的汽车飞出轨道,并且增加一点结构强度。你的轨道应该类似于图 9-23 所示的样子。
现在,使用一个小型手持打孔器,在轨道末端大约半英寸的位置打一个孔,用来安装光敏电阻。如果你没有打孔器,工艺刀或尖锐的铅笔也可以使用。只要小心切割纸张,并且使用切割垫来保护桌面。孔的大小需要刚好能容纳光敏电阻的头部。
图 9-23: 完成的轨道,两个边缘已经折叠起来

添加光敏电阻
无论你使用的是自制赛道还是标准玩具赛道,下一步是将光敏电阻添加到坡道底部。光敏电阻将充当你的终点线传感器。(标准 Hot Wheels 赛道的末端有一个小孔,直径略小于光敏电阻传感器的直径。幸运的是,塑料赛道足够柔韧,你可以直接将光敏电阻的头部按压通过这个孔。)
将光敏电阻从面包板上取下,并将引脚弯曲成直角,使其看起来像图 9-24 所示。
图 9-24: 将光敏电阻的头部弯曲成直角。

将光敏电阻的头部插入赛道上的孔中。确保光敏电阻没有突出太多,否则小车会卡住它;小车应该能够不受干扰地滚过传感器。将电阻的引脚弯曲成直角后,你应该能够像图 9-25 所示,安全地将它们固定在赛道的底部。
图 9-25: 将光敏电阻固定到赛道上

使用一对母对公跳线将光敏电阻重新连接到面包板。如果你将赛道延伸得太远,无法到达面包板,可以根据需要添加更多的母对公跳线来延长连接线。
想要感受极速吗?计算平均速度
使用这个项目,你可以准确测量小车从坡道上滚下来并穿过终点线所需的时间,但你不知道小车的速度——还是知道的?
好吧,你已经知道小车到达赛道底部所需的总时间,并且知道赛道的长度。通过这两条信息,你可以估算小车的运动速度。我们之所以称之为估算,是因为这实际上是一个平均速度,而不是小车到达坡道底部传感器时的确切速度。如果你观察小车滚下坡道,你会看到它从坡道顶部开始时没有移动,然后慢慢移动,接着在下坡过程中继续加速。
平均速度定义为单位时间内行驶的距离。因此,要找到平均速度,你需要测量赛道的长度,并将该值除以经过的时间。

我们的赛道从起点门到终点传感器的长度大约是 8.5 英寸,在我们上一次测试中,时间为 0.581 秒。如果将这两个数字相除,我们得到的平均速度是 14.6 英寸每秒。

记住,这是小车的平均速度。对于我们的简单直坡设置,这大致是小车在坡道中部的运动速度,由于小车在坡道顶部时并未移动,这意味着在坡道底部时,小车的速度是这个速度的两倍。你的车移动得有多快?
测试与故障排除
最后,将赛道的末端没有光敏电阻的部分放置在起始塔旁边,使坡道大约延伸出塔的长度,约等于玩具车的长度(见图 9-26)。这将是起始位置。
图 9-26: 完成的拖车计时器,车已准备好发车

如果您还没有这样做,将 Arduino 重新连接到您的计算机或电源上。按下重置按钮,确保草图重新启动,然后找出您最喜欢的火柴盒车或 Hot Wheels 车,并将其放在起跑门后面。按下起跑按钮,观看您的车起跑!
到达终点花了多长时间?在我们的赛道上,我们的玩具车用了超过 0.5 秒的时间。尝试几种不同的车,或者邀请一些朋友来看谁的车更快。如果你在车上粘上一些硬币,它会更快吗?进行实验,看看不同的东西如何影响你的赛车时间。
进一步扩展
在这个项目中,我们向您介绍了如何使用 LCD 直接显示来自 Arduino 草图的信息。以下是一些如何进一步拓展此项目的想法。
改装
与自己比赛的乐趣是有限的。让我们看看如何添加第二个赛道,并让两辆车互相比赛。(见图 9-27)对于这个改装,您需要一个额外的光敏电阻,这个电阻在标准的 SparkFun Inventor’s Kit 中没有包含。可以找一个也有此工具包的朋友,单独购买一个,或者在本书的附加零件包中找到一个。
首先,您需要创建一个独立的终点传感器电路。我们能够在面包板的底部附近插入一个光敏电阻和一个下拉电阻电路,如图 9-28 所示。通过一个 10 kΩ下拉电阻将第二个光敏电阻电路连接到 Arduino 的 2 号引脚,并将另一个引脚连接到 5V 电源轨。
将光敏电阻放置在第二个轨道上,并添加公对母跳线将其连接到面包板上的电路。将第二个轨道放置在第一个轨道旁边,放置在起始塔上。现在,是时候上传一些新的代码,以便同时使用这两个传感器。您只需添加几行额外的代码,允许两辆车进行比赛。从www.nostarch.com/arduinoinventor/下载P9_TwoCarDragRaceTimer.ino文件并打开它。
让我们来看看此代码的新增部分。首先,代码为第二个光敏电阻终点传感器添加了一个新常量和变量,finishSensor2Pin和finishSensor2。
然后,它通过复合if()语句检查哪个传感器首先被跨越。如果车#1 首先到达,finishSensor1将为0,而finishSensor2仍为1。在这个if()语句内部,指令将获胜信息显示到 LCD 屏幕上,并将状态变量finishFlag设置为true。
else-if() 语句检查车 #2 是否首先穿越终点线;在这种情况下,finishSensor2 将为 0,而 finishSensor1 仍然为 1。如果两辆车确实同时穿越终点线,这段代码什么也不做。看看你能否想出如何在平局时添加一个平局功能。
代码中有很多注释来帮助解释更多内容。现在,上传代码到你的开发板并开始竞赛!谁的车最快?
图 9-27: 拖曳赛计时器,带有两条赛道

图 9-28: 添加第二个光敏电阻,用于同时竞赛两辆车

修改
现在你已经了解了如何使用 LCD 电路,试着回去并给你已经完成的项目添加一个 LCD。在任何你用串行监视器显示信息的项目中,比如第 4 项目的反应计时器或第 7 项目的小型桌面温室,你都可以将串行监视器替换为 LCD。
你需要检查电路连接和使用的引脚配置;你需要从你的 Arduino 获取六个 GPIO 引脚来控制 LCD。如果你想看到一个使用 LCD 的反应计时器项目示例,可以查看我们在 InventorSpace 上创建的教程,* invent.sparkfun.com/cwists/preview/1145-sik-lcd-reaction-timer/ *。
第十一章:10 个微型电子钢琴
在这个项目中,你将使用一种特殊的触摸传感器和一个压电蜂鸣器来制作一个 Arduino 钢琴(见图 10-1)。无论你是否具备音乐才能,这都将是一个有趣的项目!
图 10-1: 完成的微型电子钢琴

所需材料
本项目中只有几个新部件。一个是软电位器(SoftPot),它将作为你的键盘,另一个是压电蜂鸣器,它将提供声音。你需要的材料如图 10-2 和图 10-3 所示。准备好你的材料,我们开始吧。
电子部件
• 一个 SparkFun RedBoard(DEV-13975),Arduino Uno(DEV-11021),或任何兼容 Arduino 的开发板
• 一根 USB Mini-B 电缆(CAB-11301,或你的开发板 USB 电缆)
• 一个无焊面包板(PRT-12002)
• 一个 10 kΩ电阻(COM-08374,或 20 个装的 COM-11508)
• 一个 50 毫米 SoftPot 膜电位器(SEN-08680)
• 一个压电蜂鸣器(COM-07950)
• 公对公跳线(PRT-11026)
注意
本项目中使用的所有部件均为 SparkFun 发明者套件中的标准部件。
图 10-2: 微型电子钢琴的组件

其他材料和工具
• 铅笔
• 工艺刀
• 金属尺
• (可选)电烙铁
• 遮蔽胶带
• 硬纸板或卡纸(约 4 × 5 英寸)或一个小纸盒
图 10-3: 额外的材料

新组件
如我们所述,本项目只引入两个新部件。第一个是 SoftPot 触摸传感器:一种特殊类型的电位器,类似于项目 6 中使用的,但它会对压力做出反应。第二个新部件是压电蜂鸣器。让我们更详细地了解这两个部件。
SoftPot 膜电位器
在项目 6 中,我们介绍了一种简单的电位器,带有旋钮,可以用来控制平衡梁。SoftPot,如图 10-4 所示,工作原理相似,但它对压力做出反应,而不是旋转旋钮。
图 10-4: 50 毫米 SoftPot

SoftPot 是一种薄而柔韧的传感器,可以检测沿其长度施加压力的位置。当你按压时,中间引脚和最接近的端引脚之间的电阻会在 0Ω和 10kΩ之间变化,具体取决于检测到的压力大小。SoftPot 传感器有一层薄膜,将中间引脚连接与外侧引脚分开。该传感器非常精确,几乎具有无限的分辨率。在工业应用中,SoftPot 常用于识别滑动部件、机器人手臂以及其他进行精确运动的部件的位置。
在这个项目中,你将使用这个传感器作为钢琴键盘。你将把条形的长度分成八个部分或“键”,用来演奏不同的音符。SoftPot 实际上与之前项目中使用的旋钮电位器相同。在电路图和示意图中,你可能会看到与图 10-5 相同的符号。
图 10-5: SoftPot 的电路图与常规电位器的电路图完全相同。

压电蜂鸣器
压电蜂鸣器(图 10-6)类似于扬声器,当你对两个引脚施加电压时,会产生可听见的“咔嗒”声;这些“咔嗒”声发生得非常快,每秒几百次甚至几千次,其频率产生了音调。在典型的压电蜂鸣器内部,有一个特殊的晶体,叫做压电元件,当施加电压时它会发生形变。晶体与一个圆形金属片连接,当晶体形变时,金属片会震动小圆柱内的空气,从而产生“咔嗒”声。
图 10-6: 压电蜂鸣器

注意
包含在 SparkFun 发明者套件中的蜂鸣器实际上使用的是一个小型磁性线圈,而不是压电元件,但它的工作原理是相同的。我们仍然称其为压电蜂鸣器。
你已经知道如何使用 Arduino 以不同的速度闪烁 LED 了。现在,如果你以每秒数百次的速率“闪烁”蜂鸣器,我们就可以将这些“咔嗒”声转换为音调!用于表示蜂鸣器、扬声器和类似元件的电气符号有几种不同的形式。我们在本章中使用的符号如图 10-7 所示。
图 10-7: 蜂鸣器的电气符号

构建电路
该电路只使用了两个电子元件:蜂鸣器和 SoftPot,分别如图 10-8 和图 10-9 所示。将蜂鸣器插入面包板时,你可能会注意到其引脚比面包板的孔稍微靠得更近。只需轻轻弯曲引脚,使其与孔对齐,然后插入蜂鸣器。引脚应该与三个孔的间距一致。
SoftPot 有三个引脚,类似于你在第六章中使用的电位器。但与常规电位器不同,SoftPot 需要一个下拉电阻,以确保当没有输入或没有施加压力时,SoftPot 默认为 0 的状态。这将防止在没有按压传感器时蜂鸣器发出噪音。
图 10-8: 微型电子钢琴电路的示意图

图 10-9: 微型电子钢琴电路在面包板上的原型

按照图示组装电路。电路相当简单。首先,将 5V 和 GND 连接到面包板左侧的电源和接地轨道。接着,把蜂鸣器大约放置在面包板顶部 10 行的位置。将蜂鸣器的一端连接到 Arduino 的引脚 9,另一端连接到接地轨道。然后,将 SoftPot 插入面包板的底部。将 SoftPot 的顶部引脚连接到 5V 电源轨道,底部引脚连接到接地轨道,中间引脚连接到 Arduino 的模拟输入引脚 A0。记住,SoftPot 需要一个下拉电阻连接在滑动引脚(中间引脚)和接地轨道之间。最后,在中间引脚和接地轨道之间添加一个 10 kΩ的电阻。
一旦你完成了搭建,就可以尝试几个代码示例了。
编程迷你电子钢琴
首先,你将通过让蜂鸣器发出噪音和可识别的音调来测试构建,然后将这些噪音和音调映射到 SoftPot 上。一旦你了解了如何让代码发出声音,我们将向你展示如何添加功能,使你可以像玩一个小钢琴一样演奏多达八个不同的音符。
你已经看到 Arduino 能够生成非常快速的脉冲并响应瞬间的按钮按压。连接好电路后,你将利用这些功能来发出一些有趣的声音,并测试蜂鸣器的频率响应。
测试蜂鸣器
Arduino 有一些命令,使得播放音符变得非常简单。控制 Arduino 发出声音的有两个函数:tone(),它让蜂鸣器播放你指定的频率,以及noTone(),它告诉蜂鸣器停止发声,以便你控制音符的时长。以下是如何使用这两个函数的说明。
//creates a tone of *frequency* on *pin* for a *duration* in ms
tone(*pin*, *frequency*, *duration*);
//stops the playing of a tone on the given pin
noTone(*pin*);
tone()函数需要三个参数:蜂鸣器连接的引脚号、要播放的频率和播放音符的持续时间。调用时,tone()会在指定的引脚上生成一个你提供的频率的方波,持续指定的时间。这个方波触发了连接到该引脚的蜂鸣器内部的圆盘振动,从而发出该频率的声音。开始后,tone()会持续指定的时间,或者直到你调用另一个不同频率的tone()命令,或者调用noTone()命令停止 Arduino 播放声音。
将清单 10-1 中的代码复制到 Arduino IDE 中,或从* www.nostarch.com/arduinoinventor/*下载草图。在这个示例中,你将使用串口监视器来选择频率,蜂鸣器将在半秒钟内播放该音调。
清单 10-1: 串口音调测试代码
//Serial Tone Test Example
//Upload this example and then open the Serial Monitor
int freq;
void setup()
{
➊ pinMode(9, OUTPUT);
**Serial**.begin(9600);
**Serial**.println("Type in a frequency to play.");
}
void loop()
{
➋ if(**Serial**.available() > 0) //wait for a serial
//input string
{
➌ freq = **Serial**.parseInt(); //parse out integer value
**Serial**.print("Playing note: "); //user feedback
**Serial**.println(freq);
➍ tone(9, freq, 500); //play the note for 500 ms
➎ delay(500); //delay for the note duration
}
else
{
noTone(9);
}
}
让我们来看一下这一切是如何工作的。在setup()中,草图设置了蜂鸣器 GPIO 引脚的pinMode ➊,初始化了 Arduino 上的串口通信,并打印出一条简短的消息,指示玩家该做什么。
接下来,在loop()中,if(Serial.available() > 0)语句➋检查是否有数据通过串口通信发送过来,代码如下:Serial.available()返回从串口监视器接收到的字节数,而if()语句将该值与零进行比较;如果字节数大于零,则意味着数据已接收,脚本会读取这些数据➌。Serial.parseInt()函数将数据转换为整数,并将其存储在变量freq中。
代码使用tone()函数➍播放存储在freq中的频率 500 毫秒,然后有一个短暂的delay() ➎。delay()确保音符播放完 500 毫秒后才会开始下一个音符。tone()命令是一个非阻塞函数:它执行完后会继续执行下一条指令,而不需要等待。
在将此草图上传到你的设备后,打开串口监视器(CTRL-SHIFT-M 或 工具 ▸ 串口监视器)。然后,点击行结束符下拉列表(图 10-10),并选择无行结束符。当你在数字设备之间发送数据时,有时会使用一个不可见的行结束符(EOL)字符来表示消息的结束。最常用的两个 EOL 字符是换行符(NL)和回车符(CR)。虽然该字符可能看起来是不可见的,但对于 Arduino 来说,这个字符仍然会被读取为一个值。选择此选项可以确保它不会发送任何额外的字符。
现在,在最上方的框中,你可以输入任意数字并按回车,蜂鸣器会播放该音符半秒钟(500 毫秒)。人类的听觉范围大约是 20 Hz 到 20,000 Hz,所以可以尝试不同的频率,但要保持在这个范围内。
图 10-10: 打开串口监视器,将该选项更改为无行结束符。

Arduino 上的串口通信有一个 64 字节长的缓冲区。缓冲区就像是数据进入设备时的等待队列,在这种情况下,它允许你通过串口监视器一次发送多个音符。例如,你可以通过输入以下数字来播放《小星星》这首歌,数字之间用逗号分隔且不包含空格。最后一个逗号也很重要。Serial.parseInt()命令会寻找由非数字字符(如逗号)分隔的数字字符。Arduino 需要看到最后一个符号(最终的逗号),以解析出最后一个音符。
262,262,392,392,440,440,392,392,349,349,330,330,294,294,262,262,
确保不要在这里输入空格。每个字符等于 1 字节数据,如果你试图一次发送超过最大 64 字节的数据,最后几项数据将会被截断。
最后,蜂鸣器的频率被优化为 2,047 Hz。你可能会注意到,当你尝试这个频率时,蜂鸣器非常、非常响,可能会让人觉得烦人,所以请注意:你周围的人可能不太喜欢 2,047 Hz 频率下蜂鸣器发出的高频声音!
创建特定音符
如果你想尝试一些更传统的方式,可以将实际的音乐音符与频率关联起来。许多乐器以中央 C 为参考点,中央 C 的频率大约为 262 Hz。表 10-1 显示了 C 大调音阶一个八度的频率简表。
表 10-1: C 大调音阶的音符和频率
| 注意 | 大约频率 |
|---|---|
| C | 262 |
| D | 294 |
| E | 330 |
| F | 349 |
| G | 392 |
| A | 440 |
| B | 494 |
| C | 524 |
尝试实验看看能不能演奏一首歌。为了帮助你入门,“小星星”从音符 CC、GG、AA、GG、FF、EE、DD、CC 开始。你可能会注意到,Arduino 发出的声音不是最有旋律的。这是因为 Arduino 只能将引脚设置为 HIGH(开)或 LOW(关)。这会生成一个方波,产生刺耳的音调。你可以使用电容器做一些技巧来创建噪音过滤器,从而柔化这个声音,但那是另一本书的内容!
使用 SoftPot 生成声音
用频率制作音乐很有趣,但一次又一次地输入它们非常繁琐。相反,你可以使用 SoftPot 作为一个传感器,它的值会对应蜂鸣器的频率。
SoftPot 通过一个简单的电压分压器连接,这样对软条的压力就会转化为从 0 到 5 V 的电压。记住,Arduino 可以通过 analogRead() 函数将 0 到 5 V 的模拟电压转换为 0 到 1023 的数值。使用 清单 10-2 中的简单草图,你可以通过按压 SoftPot 向蜂鸣器发送频率。当 SoftPot 没有被按压时,由于下拉电阻的作用,值默认为 0,因此不会发出声音。
启动一个新的 Arduino 草图,复制清单 10-2 中的代码示例,并将其上传到您的设备。
清单 10-2: 发声器示例代码
//Noisemaker Example
//upload this example, open the Serial Monitor,
//and then squeeze the SoftPot
➊ int sensorValue;
void setup()
{
pinMode(9, OUTPUT);
**Serial**.begin(9600);
}
void loop()
{
➋ sensorValue = analogRead(A0);
➌ if (sensorValue > 0) //if there's a press on sensor,
//play note
{
➍ **Serial**.print("Raw sensor reading: ");
**Serial**.println(sensorValue);
➎ tone(9, sensorValue, ➏50);
delay(➐50);
}
➑ else
{
noTone(9);
}
}
让我们来看看这个草图是怎么工作的。你首先声明一个变量来存储原始的传感器读取值 ➊。在循环中,analogRead() 函数读取 A0 引脚上的电压 ➋,并将该值赋给变量 sensorValue。当没有施加压力时,该值将为 0 V。你使用 if() 语句 ➌ 来检查输入值是否大于 0,如果是,草图就会打印出原始的传感器值 ➍,并使用 tone() 命令 ➎ 播放 sensorValue 中存储的值。如果草图在播放音符时卡住了,尝试将 if() 语句中的 0 改为更大的数字,比如 10 或 20 ➌。这个技术叫做 设置死区范围。有些传感器并不总是返回零值,因此这个死区范围设定了一个值范围,程序仍然可以将其视为零。每个传感器可能略有不同。
请注意,在这个示例中,我们改变了tone()播放的持续时间➏和delay()的时间➐,将其设置为50毫秒。这将允许你更快速地更改音符。否则,你只能播放半秒长的音符!最后,你需要确保 Arduino 只在按下 SoftPot 时才播放音调。为此,你可以使用else()语句来检测 SoftPot 是否未被按下➑,并使用noTone()命令来关闭蜂鸣器。
现在,不再使用从串口监视器发送的频率,而是 Arduino 使用 SoftPot 的原始传感器值。要进行尝试,只需用拇指和食指夹住 SoftPot,在传感器的不同点施加压力。当你上下滑动手指时,可以产生从 1 赫兹到 1,023 赫兹的音调。你听到了什么?听起来像外星人降临吗?相当酷吧——现在你有了自己的特效生成器!你能演奏出类似歌曲的东西吗?如果不能,看看下一个示例。
演奏一首歌
现在你已经了解了 Arduino 如何发出声音,接下来是将 SoftPot 的传感器读取值映射到真实音符的过程,这样你就可以演奏真正的音乐了。你将把传感器分成八个不同的部分(或键),并将这些部分映射到一个索引,用来播放音符。
将清单 10-3 中的草图复制到 Arduino IDE 中,并将其上传到你的设备。
清单 10-3: 微型电子钢琴草图
//Tiny Electric Piano Example Code
➊ int frequencies[] = {262, 294, 330, 349, 392, 440, 494, 524};
int sensorValue;
➋ byte note;
void setup()
{
pinMode(9, OUTPUT);
**Serial**.begin(9600);
}
void loop()
{
sensorValue = analogRead(A0);
if (sensorValue > 0) //if it's a note, play it!
{
//map the key pressed to a note
➌ note = map(sensorValue, 0, 1023, 0, 8);
➍ note = constrain(note, 0, 7);
**Serial**.print(sensorValue);
**Serial**.print("\t");
**Serial**.println(➎frequencies[note]);
tone(9, ➏frequencies[note], 50);
delay(50);
}
else
{
noTone(9);
}
}
看一下代码。首先,你声明了一个数据结构,称为数组。数组是一种表示多个值的变量,而不仅仅是一个单一的值。数组可以是任何标准数据类型,包括字节、整数、长整型和浮点型,并且你需要在数组名称之前声明数据类型。
声明一个数组类似于声明一个变量,只不过数组名称后面跟着两个方括号[ ]。当你初始化数组时,你需要在两个大括号{ }内定义列表,并使用逗号分隔每个值:
*dataType arrayName*[] = {*val0*, *val1, val2, val3, val4*... };
清单 10-3 声明了一个名为frequencies[]的整数(int)数组,用于存储八个音符频率的值➊。你可以通过方括号中的索引号来访问数组中的值。第一个值被称为frequencies[0],第二个值是frequencies[1],以此类推。请注意,和往常一样,索引从 0 开始,而不是从 1 开始,正如图 10-11 所示。
图 10-11: frequencies[]数组元素

然后,草图声明了一个名为note的索引变量➋。数组只有八个值,因此你可以将该变量声明为byte,因为它比整数占用更少的内存空间。接下来,草图使用map()函数➌将原始的sensorValue读取值从 0–1,023 范围映射到 0–8 的范围。
map()函数是将一个值范围转换到另一个值范围的一个好工具。你传入输入值、输入的范围和目标范围,它会将你的输入值缩放到目标范围,如下所示:
map(*inValue*, *inMin*, *inMax*, *outMin*, *outMax*);
程序将这个缩放后的值赋给变量note。这个值将作为索引来引用数组。但是,虽然map()函数将 0–1,023 的范围映射到 0–8 的范围,但frequencies[]数组实际上只有八个值,且索引从 0 到 7。
我们这样做是因为map()在从一个范围映射到另一个范围时会向下舍入,因此,为了获得八个均匀间隔的值,我们实际上需要提供九个值。只有当输入等于 1,023 时,才会产生值 8。由于值 8(第九个值)不是数组的有效索引,我们使用另一个命令constrain() ➍来修正这个问题。此函数将值限制在 0 到 7 的范围内。任何低于 0 的值都被限制为最小值 0,而任何大于 7 的值都被限制为最大值 7。constrain()函数通常与map()一起使用,用于缩放和限制值。
constrain()函数的使用方法如下:
constrain(*value*, *minValue*, *maxValue*);
最后,程序将当前触发音符的频率输出到串行监视器 ➎,并使用tone()函数 ➏来播放这个音符。
测试时,按压或挤压 SoftPot。当你上下移动手指沿着传感器的长度时,你会听到不同的音符。试试你能弹奏出什么歌曲。
现在,让我们将这个原型制作成一个完成的钢琴!
制作钢琴
为了将原型转换成一个更实用的钢琴,你只需要将其应用到一个平面表面上,并标出八个键位。
不幸的是,这个传感器的引脚既不够长也不够粗,无法直接插入公对母跳线,因此,如果你想将其从面包板上取下,我们建议你从发明者套件中焊接三根公对公跳线到 SoftPot 的两端,如图 10-12 所示。或者,你可以使用公对母跳线,但需要使用一把尖嘴钳压紧两端。如果你这么做,将传感器引脚插入一组公对母跳线并压碎传感器引脚周围的塑料外壳。确保导线与传感器的引脚接触。
图 10-12: 将导线焊接到 SoftPot

SoftPot 具有粘性背面,设计用于平放在硬表面上,因此,连接好导线后,将传感器粘贴到任何便携的硬表面上(如图 10-13 所示),比如一小块纸板,甚至是面包板支架本身。
图 10-13: SoftPot 安装选项

SoftPot 传感器长 50 毫米(约 2 英寸)。你需要将传感器的长度划分成八个键位,也就是说每个键大约应有四分之一英寸宽。使用遮蔽胶带或纸张,按照图 10-14 的示例标出八个四分之一英寸宽的键。
图 10-14: 标记键位

接下来,将遮蔽胶带或纸张放在 SoftPot 上,以便为演奏音符提供指导。完成的键盘应类似于图 10-15。如果你愿意,你可以在键上标注与代码中的频率匹配的音符,帮助你从乐谱中演奏。
图 10-15: 完成的微型电动钢琴键盘

深入探索
在这个项目中,我们介绍了压电蜂鸣器和 SoftPot。现在你已经知道如何发出一些有趣的声音,下面是一些扩展此项目的想法。
破解
尝试修改代码,看看能创造出哪些有趣的音调。例如,如果你想用不同的调或音阶演奏一首歌,你可以改变数组中的频率。表 10-2 显示了可以用来改变钢琴调性的频率。C 大调和 G 大调是音乐中最常用的两种音阶。你可以在线查找一些乐谱,或者随便试试。你能用你的新 SoftPot 钢琴演奏《小星星》吗?
表 10-2: 选定的三重八度音符大致频率
| 音符 | 大致频率 | 音符 | 大致频率 | 音符 | 大致频率 |
|---|---|---|---|---|---|
| C[3] | 131 | C[4] | 262 | C[5] | 524 |
| C♯[3]/D♭[3] | 139 | C♯[4]/D♭[4] | 277 | C♯[5]/D♭[5] | 554 |
| D[3] | 147 | D[4] | 294 | D[5] | 587 |
| D♯[3]/E♭[3] | 156 | D♯[4]/E♭[4] | 311 | D♯[5]/E♭[5] | 622 |
| E[3] | 165 | E[4] | 330 | E[5] | 659 |
| F[3] | 175 | F[4] | 349 | F[5] | 698 |
| F♯[3]/G♭[3] | 185 | F♯[4]/G♭[4] | 370 | F♯[5]/G♭[5] | 740 |
| G[3] | 196 | G[4] | 392 | G[5] | 784 |
| G♯[3]/A♭[3] | 208 | G♯[4]/A♭[4] | 415 | G♯[5]/A♭[5] | 831 |
| A[3] | 220 | A[4] | 440 | A[5] | 880 |
| A♯[3]/B♭[3] | 233 | A♯[4]/B♭[4] | 466 | A♯[5]/B♭[5] | 932 |
| B[3] | 247 | B[4] | 494 | B[5] | 988 |
修改
为了让事情更加有趣,将一个失真踏板添加到这个钢琴项目中。这个按钮将切换音阶,给你 8 个音符的价格换来 16 个音符。按照图 10-16 所示,将按钮连接到 Arduino 的第 2 引脚。
这个草图只需要为踏板添加几行额外的代码,每次按下按钮时,乘数变量将循环变化。这被称为状态机,因为每次按下按钮时,变量的状态都会改变。这个修改的代码在本书的资源中,位于www.nostarch.com/arduinoinventor/,文件名为P10_TinyPiano_v2.ino。
图 10-16: 为八度控制添加按钮到引脚 2

新的代码行声明了一个状态变量 octaveMultiplier,然后添加了一个 pinMode() 命令来设置引脚 2 为 INPUT。当按钮被按下时,状态变量 octaveMultiplier 会递增,从而改变频率,使得音符升高。
尝试一下。当您按下按钮时,音符应该都会升高一个八度。现在,您可以使用这个简单的 Arduino 乐器演奏最多 16 个音符!
附加项目:二进制小号
SoftPot 是一个不错的设计,但作为额外奖励,我们构建了一个 Arduino 乐器,使用实际的按钮作为键,而不是 SoftPot。这个最后的项目叫做二进制小号。它使用三个按钮来指定播放的音符,第四个按钮用于播放音符,就像吹小号一样。通过三个按钮,您可以使用表 10-3 中显示的按键组合来指定最多八种不同的组合。
表 10-3: 二进制小号按钮序列
| 按钮 1 | 按钮 2 | 按钮 3 | 播放的音符 |
|---|---|---|---|
| 上 | 上 | 上 | C (262 Hz) |
| 上 | 上 | 下 | D (294 Hz) |
| 上 | 下 | 上 | E (330 Hz) |
| 上 | 下 | 下 | F (349 Hz) |
| 下 | 上 | 上 | G (392 Hz) |
| 下 | 上 | 下 | A (440 Hz) |
| 下 | 下 | 上 | B (494 Hz) |
| 下 | 下 | 下 | C (524 Hz) |
您可能会认出这个模式是一个二进制序列。它按照 000、001、010、011、100、101、110 和 111 的顺序计数。
为了在面包板上腾出位置放置四个按钮,将蜂鸣器稍微上移一点,然后按照图 10-17 所示添加这些按钮。
图 10-17: 二进制小号接线图

此修改的完整代码可以在书籍资源中的 www.nostarch.com/arduinoinventor/ 网站上找到,文件名为 P10_TinyBinaryTrumpet.ino。使用二进制小号演奏音符需要一些时间来适应,但表 10-3 中的按键序列应该能帮助您快速掌握。
结果是,使用四个按钮,您实际上可以演奏最多 16 个不同的音符。您能找出如何修改示例以实现这一点吗?查看书籍资源以了解如何操作。
无论您是使用小型电子钢琴还是二进制小号,我们都希望这对您未来从事音乐创作的职业有所帮助。现在,去找到一个观众,展示您的最新技能吧!
附录
更多电子技术知识
本附录提供了使用万用表和焊接的操作指南,并且是读取电阻器色带的实用参考。
使用万用表测量电气
万用表是诊断和排除电路故障的必备工具。顾名思义,它是一种能够测量与电气相关的多种内容的仪表——即电流、连续性、电阻和电压。让我们来看一下如何使用万用表。在本教程中,我们将使用 SparkFun VC830L(TOL-12966;如图 A-1 所示),但这些方法应适用于大多数万用表。
万用表的部件
万用表有三个主要部件,在图 A-1 中标出。
图 A-1: 一款典型的万用表

显示屏通常可以显示四位数字和一个负号。选择旋钮允许用户将万用表设置为读取不同的内容,如毫安(mA)电流、电压(V)和电阻(Ω)。选择旋钮外部的数字表示每个设置的最大值或范围。
在某些万用表上,显示屏不会显示单位。在这种情况下,假定显示的值具有与设置相同的单位,因此如果你将范围设置为 200 Ω,显示的数字将以Ω为单位。如果你将范围设置为 2 kΩ、20 kΩ或 200 kΩ,则显示的值将以 kΩ为单位。
大多数万用表配有两个探头,插入设备前面的三个端口中的两个。这三个端口分别标有 COM、mAVΩ和 10A。COM 代表公共端,几乎总是应连接到地、负极或电路的负端。mAVΩ端口用于测量电流(最多 200 mA)、电压(V)和电阻(Ω)。10A 端口是用于测量大于 200 mA 电流的特殊连接端口。
大多数探头的末端有香蕉型连接器,可以插入万用表,使得不同类型的探头可以使用。对于大多数测量,将红色探头插入 mAVΩ端口,将黑色探头插入 COM 端口。
测量连续性

测量连续性可能是排除故障和调试电路中最重要的功能。这项功能使我们能够测试导电性,并追踪电气连接是否已接通。将万用表设置为连续性模式,标有二极管符号并且有传播波(像声音从扬声器发出)围绕它,尽管这在不同的万用表之间可能会有所不同。
将两个探头端点接触在一起,你应该能听到响铃声——这就是为什么检查连续性有时被称为“响铃”电路。你可以使用这种方法测试无焊面包板上哪些孔是连接的,哪些不是。探头的尖端通常太大,不能直接插入面包板,但你可以将两根导线插入面包板的同一排,并将探头的两端触摸到每根导线的末端。你应该能听到响铃声,表示这两根导线通过排连接在一起。你也可以使用这种方法追踪电路。因为你通常看不见所有导线的走向,这是一种快速的方式来测试两个点是否电连接。当你检查连续性时,连接探头的哪一侧并不重要,因为你只是检查一侧是否与另一侧电连接。
测量电阻

连续性设置仅在电阻较低时发出响声,但要获得电阻的实际值,你需要使用电阻设置。将旋钮调至标有欧米伽符号(Ω)的电阻设置,它代表欧姆,即测量电阻的单位。确保你测量的电阻器或元件没有通电或连接到电路中。电阻器像许多电气元件一样有两个端点。要测量电阻,只需将探头的两端触摸到电阻器的两端。与连续性测量一样,连接红色和黑色探头的位置不重要。
有几种可能的电阻范围设置可供选择。这些设置表示你可以测量的最大值。如果你想高精度地测量一个小电阻,可以将万用表设置得很低——例如设置为 200 Ω。
如果你尝试测量超过范围的电阻,万用表会显示[1\. ],并且不会显示零。如果电阻大于你选择的范围,尝试将范围调高一点,再次进行测量。
试试看!如果你测量 330 Ω电阻(橙色-橙色-棕色),在每个设置下你记录的值是多少?所有电阻都有公差带;大多数电阻的公差通常为 5%。你的测量误差是多少百分比?是否在公差范围内?
测量光敏电阻的电阻。将你的手或其他不透明物体放在光敏电阻上方,并在不同高度下测量电阻。
测量电压

电压是两点之间电势的测量,有时也被称为电势差。类似于电阻设置,测量电压的不同设置指定了最大值。
你会注意到有两个范围符号,一个是两个直线,另一个是一个弯曲的线。两个直线表示直流电(DC)测量,这是电子学中最常用的测量方式。弯曲的线表示交流电(AC),就是你家墙壁里的电。确保旋钮调到了正确的设置——你可能需要选择直流电(DC)。20 V 档位是本书项目的最佳选择,因为在 Arduino 上所有的电压都限制在 5 V 以内。
现在,试着测量 Arduino 板上的电压。使用 USB 电缆将你的 Arduino 板连接到计算机以供电。要测量电压,将黑色探针连接到 GND(地)。然后,用红色探针测试各个点或引脚的电压(相对于 GND)。5 V 引脚显示的是什么?3.3 V 引脚呢?
测量电流

电流是电荷在电路中流动的速率,单位是安培(amps)。为了捕捉电荷的流动速率并测量电流,你需要断开电路并将表计连接到你想要测量电流的位置。将旋钮调节到你预期测量的电流范围。如果你测量的电流可能超过 200 mA,转动选择旋钮至 10A 档,并将红色探针插入万用表机身上标有 10A 的端口。如果不确定,这个范围是最安全的开始选择,可以避免损坏表计。
例如,要测量一个简单的 LED 和电阻电路中的电流,你可以在 LED 和电阻之间将电路切入(见图 A-2)。电流必须通过万用表。因为这是一个串联电路,你可以在这条路径上的任何位置测量电流:在 LED 之前、LED 之后,或者电阻之后。
图 A-2: 将万用表插入电路测量电流

在测量电流时,要非常小心,不要超过万用表的限制——你应该能从万用表的用户手册中找到其支持的电流范围。如果超过电流限制,可能会导致万用表的保险丝烧断。(如果保险丝烧断不必担心——更换保险丝并不贵。更换新保险丝时,你可能需要用螺丝刀打开万用表的后盖。)标准的 mAVΩ端口通常能处理最高 200 mA 的电流。
如何进行焊接
焊接是制作电子原型项目时最基础的技能之一。它涉及将一种特殊金属——焊锡——熔化并将其夹在两个组件之间,使它们更持久地连接在一起(见图 A-3)。
图 A-3: 焊接

焊料,如图 A-4 所示,是一种熔点较低的金属合金。现代焊料的熔点大约在 180 摄氏度或 356 华氏度左右,差不多是烘焙饼干时需要的温度。大多数用于电子设备的焊料内部含有助焊剂,一种清洁液体。焊料融化时,助焊剂帮助清洁焊接表面,并促进焊料的流动。
图 A-4: 一卷焊料

要进行焊接,你需要使用烙铁。大多数烙铁的大小大约和一根中等大小的胡萝卜差不多,分为两个主要部分:手柄和加热端(见图 A-5)。
图 A-5: 一款典型的烙铁

焊接铁有许多样式和类型。较便宜的烙铁大约价格为 10 美元,通常有一个固定温度设置,但我们建议选择带有温控旋钮的烙铁。烙铁的最佳温度大约是 650 华氏度。如果温度太高,尖端会迅速氧化并变脏。如果温度不够高,则无法融化焊料。可调温的烙铁能帮助你控制这一点,所以值得花些额外的钱购买。
使用烙铁时要非常小心:当你打开烙铁时,加热端会迅速加热到足以融化金属的温度。那真的很热!始终从手柄处握住烙铁——即使烙铁关掉了,也绝不能从加热端握住。
你还应该用一块纸板、一块切割垫或废木块来保护你工作时的桌面表面。而且,在开始焊接之前,你应该始终佩戴护眼装备。小块的焊料和助焊剂有时会飞溅出来。为了保护你的宝贵眼睛,最好保持安全!
加热烙铁
使用烙铁时,首先插上电源并让它加热。根据烙铁的类型,这可能需要 30 秒到几分钟的时间。在烙铁加热时,请确保它放在支架上,以免加热端接触到桌面或工作台。
当烙铁变热时,焊料应该能轻松融化,所以可以通过将焊料的一端触碰到烙铁的尖端侧面来进行测试。这是烙铁最热的部分,被称为甜点(见图 A-6),你应该使用这一部分来加热元件。如果焊料立刻融化,说明你的烙铁已经足够热,能够进行焊接。
图 A-6: 烙铁尖端的侧面比尖端的最前端要热得多。

完善你的焊接技巧
与你可能认为的不同,焊接时并不直接将铁头接触到焊料以将其融化。诀窍是将铁头放在你打算焊接的元件上约 2 到 3 秒钟。然后将焊料直接涂抹到加热的接头上,焊料就会融化。焊料总是会朝着热源流动,并停留在元件的最热部分。如果你将焊料直接加到铁头上,它可能会聚集在铁头上,而不会流到你想焊接的部位。如果发生这种情况,只需清洁铁头并重新尝试。像握住铅笔一样握住铁头,用你的主手从手柄处握住它。另一只手则握住一段焊料。小心不要将焊料握得离你正在融化的端部太近,因为热量可能会沿着焊料传导到你的手指。
将铁头的甜点部位触碰到你打算焊接的元件上。确保铁头的甜点部位同时接触到两个需要焊接的部件,以便它们均匀加热,如图 A-3 所示。数三秒钟:一千零一、一千零二、一千零三。
接下来,在将铁头保持在元件上的同时,将焊料的末端送入接头。记住,焊料会朝着热源流动。
当你送入足够的焊料使接头填满时,移开焊料,但再保持铁头在接头上 1 秒钟。这会让焊料流动并定型。然后将铁头从接头上移开,放回支架上。
一个好的焊接接头应该是平滑且略带光泽的。如果你正在焊接到电路板上,接头通常会呈现出小火山或巧克力糖果的形状。焊接需要练习,因此如果你的焊接接头看起来不干净或不平滑,可以尝试重新加热接头,让焊料再次流动并定型,或者再加一点焊料。图 A-7 展示了一些常见的焊接错误及其解决方案。
图 A-7: 常见的焊接错误及解决方案

清洁铁头
保持铁头清洁是获得良好焊接接头的秘诀之一。我们建议在每次使用前加热铁头,并使用铜刷或湿海绵擦去可能积累的多余焊料和氧化物。
如果铁头脏了且你无法擦去多余的积垢,可以使用铁头清洁剂(TOL-13246);这是一种由温和酸和焊料混合而成的物质。使用时,加热铁头,将热铁头的尖端放入清洁剂中,让清洁剂溶解氧化物和积垢,约 10 到 15 秒。然后,用海绵擦拭铁头。如果需要,可以重复此过程。铁头应该是光亮的。
焊接技巧
图 A-8 展示了更多使用焊接铁头的提示和建议。
图 A-8: 焊接提示和建议

注意
某些类型的焊锡含有铅。我们强烈建议在进行任何焊接后,无论使用哪种类型的焊锡,都要洗手。
使用烙铁是一项任何制造者工具箱中都应具备的技能。当你准备将原型项目做得更持久、更耐用时,焊接能确保线路和组件之间的连接不会断开。
额外的焊接工具
这里有一些额外的工具,我们推荐你使用,以帮助你每次都能制作完美的焊点。这些工具帮助固定你的零件、清理焊点并去除多余的焊锡。
第三只手
第三只手 基本上是一种夹具,用来固定你正在焊接的零件,是焊接时最好的助手之一。第三只手有很多种版本,但大多数只是几只鳄鱼夹固定在一个重型支架上,帮助你在用手持焊锡和烙铁时固定零件。许多基本款甚至还配有放大镜和小型烙铁支架,如图 A-9 所示。
图 A-9: 第三只手焊接支架

助焊剂笔
做出一个好的焊点的窍门之一是确保所有表面干净,你可以使用助焊剂,一种稍带酸性的清洁液,通常由树脂制成。助焊剂笔(见图 A-10)的使用方法像涂料标记笔;你只需将笔尖按在你正在焊接的焊点上,直到有少量液体流到电路板上。然后将烙铁直接放在焊点上,插入焊锡,借助助焊剂,焊锡会更快速地融化,并更好地与组件结合。
图 A-10: 一支水溶性助焊剂笔

助焊剂对焊接有很大的帮助,但它有一定的腐蚀性,因此确保尽量减少与皮肤的接触,并在使用后立即洗手。
焊锡吸取带
在焊接时,你有时会发现自己加了太多焊锡,或者将焊锡弄到了不该去的地方。桌面上有两种工具可以帮助你去除多余的焊锡。第一种是焊锡吸取带,这是一种细密编织的铜网,形状像带子,如图 A-11 所示。
使用焊锡吸取带时,将吸取带放在你希望去除焊锡的焊点上,将加热后的烙铁放在吸取带上,既加热吸取带,也加热下方的焊点,焊锡融化后会被吸取带吸走,进入铜网。完成!
图 A-11: 焊锡吸取带的特写

在移除吸取带和烙铁时,确保将烙铁保持在吸取带上。如果你过早地将烙铁拿开,吸取带会被焊接到电路板上。如果发生这种情况,只需重新加热焊点以去除吸取带。
焊锡吸尘器
另一个可以去除多余焊锡的工具称为吸锡器或吸锡泵。这个巧妙的工具使用柱塞(类似于注射器)创建真空,并通过按下按钮释放。
要使用吸锡器,首先按下柱塞以预装工具。接下来,加热您希望移除的焊点,直到它完全熔化为液态。将吸锡器的尖端靠在焊锡上(同时保持熔化状态),最后按下释放按钮吸走多余的焊锡。
如果不起作用,请再试一次。有时在您希望从中去除焊锡的区域添加少量焊锡会有所帮助。
电阻器和色环
电阻器的值有很广泛的范围,但是如何通过看这个小组件来判断它的值呢?上面没有数字或文字!
电阻器使用色环系统来显示它们的值。图 A-12 展示了色环系统的工作原理。
大多数电阻器有四到五个彩色环。电阻器的最后一个环指定了容差或制造商允许的变化程度。大多数情况下,您的电阻器将有一个金色的容差环,为 5%。这意味着制造商允许该电阻器的值波动多达 500Ω仍然被视为 10 kΩ 电阻器。例如,具有 5%容差的 10 kΩ 电阻器的电阻值可以波动到 500Ω,并且仍然被视为 10 kΩ 电阻器。
当您从左向右阅读带有容差环的电阻器时(通常是金色或银色环朝向右侧),剩余的环指定了电阻值。在四环电阻器上,前两个环指定基数,第三个环是乘数。在五环电阻器上,前三个环指定基数,第四个环是乘数。
例如,10 kΩ 电阻器的前三个色环是棕色、黑色和橙色。根据图 A-12 中的图表,棕色代表 1,黑色代表 0,因此棕黑表示基数是 10。第三个环是橙色,指定了一个 10³ 的乘数,总计为 10,000。最后一个环指定了容差,在这个例子中是 5%(金色)。
图 A-12: 电阻器颜色编码速查表

以防以后需要查找电阻器颜色编码,你可能想折角此页作为参考。没关系,我们不会告诉图书管理员。
第十二章:后记
我们希望你喜欢本书中的项目制作过程。我们在构思不同的项目创意时非常开心,项目中融合了纸板、纸张、乒乓球和其他常见材料,以及基本的电子元件。但本书中的项目仅仅是一个开始。我们希望这些项目能激发你的灵感,并帮助你释放你内心的发明家!我们鼓励你将每章中的组件和概念结合起来,创造属于你自己的Franken-duino项目!如果你有任何想法或建议想要与我们分享,请通过* ArduinoInventorsGuide@sparkfun.com *给我们留言。
祝你好运,祝你愉快地创作!
附加资源
Arduino 背后的社区非常庞大。Arduino IDE 每月被下载近 60 万次。尽管 Google 几乎能找到互联网上的任何信息,但这里有一些我们经常使用并推荐的其他资源。
Arduino 学习社区 www.arduino.cc/en/Guide/HomePage
Arduino 语言参考 www.arduino.cc/en/Reference/HomePage
SparkFun 教育教程 learn.sparkfun.com/
Adafruit 学习 Arduino 社区 learn.adafruit.com/category/learn-arduino
鸣谢
我们感激 Arduino 社区、SparkFun 的每一位成员以及 No Starch Press 的编辑们给予我们的巨大支持。本书中我们所汇编的创意和项目归功于这些人,以及过去几年里无数次的白板头脑风暴、电子邮件和旁聊。我们还要感谢我们的家人——Zondra、Bear、Bridge、Mariela、Mia 和 Maeva——在测试、原型制作和写作过程中给予的支持。谢谢你们!
关于 SparkFun 系列
SparkFun 系列是 No Starch Press 与 SparkFun Electronics 之间的合作,后者是一个在线零售商,销售各种零件,帮助你实现自己的电子项目。系列中的每本书都由 SparkFun 的经验丰富的制作人员撰写,并由 No Starch Press 的编辑们编辑。最终的结果?就是你现在正在阅读的这本书。
SparkFun 处理指南
SparkFun 处理指南展示了如何制作与周围世界互动的数字艺术作品。通过使用 Processing 编程语言和一点想象力,你可以将详细的像素艺术扩展到宏大的比例,录制和采样音频来创建自己的音效板,并创建反映声音、光线、温度和时间变化的可视化效果。

资源
访问 www.nostarch.com/arduinoinventor/ 获取项目模板、草图、更新、勘误表和其他信息。
更多实用的书籍来自
NO STARCH PRESS

THE SPARKFUN GUIDE TO PROCESSING
使用代码创建互动艺术
作者 DEREK RUNBERG
2015 年 8 月,312 页,$29.95
ISBN 978-1-59327-612-6
全彩

儿童电子学
玩转简单电路并实验电力!
作者 ØYVIND NYDAL DAHL
2016 年 7 月,328 页,$24.95
ISBN 978-1-59327-725-3
全彩

ARDUINO 游乐场
为经验丰富的创客提供的极客项目
作者 WARREN ANDREWS
2017 年 3 月,344 页,$29.95
ISBN 978-1-59327-744-4

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

THE MAKER’S GUIDE TO THE ZOMBIE APOCALYPSE
用简单电路、Arduino 和树莓派来保护你的基地
作者 SIMON MONK
2015 年 10 月,296 页,$24.95
ISBN 978-1-59327-667-6

ARDUINO 工作坊
一本包含 65 个项目的动手入门书
作者 JOHN BOXALL
2013 年 5 月,392 页,$29.95
ISBN 978-1-59327-448-1
1.800.420.7240 或 1.415.863.9900 | sales@nostarch.com | www.nostarch.com
开始 DIY 电子项目
要求
ARDUINO UNO 或 SPARKFUN REDBOARD
Arduino 微控制器使学习电子学变得容易,但可能很难知道从哪里开始。这本书中的 10 个项目将教你如何与超级智能的 Arduino 和一些零件一起构建、编码和发明。
首先,你将通过一本介绍电路工作原理、如何读取接线图,以及如何使用无焊接面包板构建和测试项目的入门书籍掌握基础知识。接下来,你将学习如何使用电动机、LED、传感器等硬件让你的设备动起来、发出声音、闪烁,并与世界互动,在此过程中完成以下 10 个项目:
• 第一个经典的 Arduino 项目:让 LED 闪烁
• 一款迷你交通灯
• 一款显示动画图案和形状的 LED 屏幕
• 一款快节奏的按键游戏,测试你的反应能力
• 一款光敏、变色的夜灯
• 一款具有挑战性的平衡球游戏
• 一款带自动风扇和通风口的温度感应迷你温室
• 一款可以控制的电动机器人
• 一款为玩具车设计的计时器
• 一款可以真正弹奏的迷你电子钢琴!
每个项目都将教会你真实的编码技巧,让你能够控制你的发明,例如如何使用变量存储温度读数、如何用函数启动计时器或旋转电动机,以及如何使用循环做出决策。你还会发现一些技巧和窍门,帮助你为每个小工具增添自己的创意,并进一步提升。
关于 Sparkfun 电子公司
SparkFun Electronics 是一个在线零售商店,销售用于 DIY 项目的电子零件。它为公众提供课程,同时通过其教育部为教育工作者提供资源、教程和专业发展。
![]() |
极致极客娱乐^™ www.nostarch.com |
|---|
*“我躺平。”
本书采用了耐用的装订方式,不会自动合上。*




浙公网安备 33010602011771号