学习 Arduino 编程
本文旨在使您熟悉基本的 Arduino 编程,并向您展示如何编写与现实世界中的物体交互的程序。(强制性声明:我上次真正学习电子学是在高中,因此本文更侧重于编程方面,而不是电子方面。)
物理计算在开始讨论这个非常酷的东西 Arduino(意大利语,意为“好朋友”)之前,请允许我说几句关于物理计算这个引人入胜的主题。物理计算有多种定义方式,但核心思想似乎是相同的:物理计算关注于开发通过硬件和软件的结合与主机计算机之外的世界交互的软件——可以这么说,它是感知世界的。这种感知能力使这些应用程序能够感知外部事件并以预定义的方式对其做出响应。这是通过使用传感器和执行器(我将在下面描述)来实现的。
执行器和传感器Arduino 通过执行器和传感器与世界互动。传感器是向您的应用程序描述世界的电子元件。传感器工作的一种常见方式是,它们的电气特性会随着其运行条件的变化而变化(以数学上已知的方式)。例如,当入射光强度发生变化时,光敏电阻的电阻会发生变化。热敏电阻是另一种此类传感器的示例,其电阻会随着工作温度的变化而变化。柔性传感器是另一种类型的传感器,其电阻根据弯曲或“弯曲”的程度而变化。这些变化可以作为电信号在 Arduino 的输入引脚上读取。根据传感器的类型,信号可以是数字信号(开或关)或模拟信号(连续的值流)。本文的后半部分将介绍如何使用模拟传感器。
另一方面,执行器是用于对外部事件做出反应的电子元件。例如,当天黑时,应打开灯。因此,传感器和执行器用于实现互补的目标:一个感知,另一个反应。执行器的示例包括螺线管和伺服电机。在本文的后面,我将解释如何使用 Arduino 控制伺服电机。
ArduinoArduino 是一个开源电子原型平台,由两个主要部分组成:Arduino 板(硬件)和 Arduino IDE(软件)。Arduino IDE 用于编写将与您的 Arduino 和连接到它的设备交互的程序。在 Arduino 世界中,这样的程序被称为草图(sketch),它起源于其母语 Processing(请参阅“资源”部分)。
Arduino 板是一个小型的微控制器电路板。在撰写本文时,存在许多 Arduino 板:Arduino UNO、Nano、Mega、Mini、Pro 等(有关完整列表,请参阅“资源”部分)。Arduino UNO(图 1)是基本 Arduino 板的最新版本,您需要其中一个才能阅读本文(有关 UNO 的详细规格,请参阅“资源”部分)。

图 1. Arduino UNO(由 http://arduino.cc/en/Main/ArduinoBoardUno 提供)
除了 UNO 之外,您还需要以下硬件才能完成本文的学习
-
面包板,用于搭建电路。
-
一些 LED。
-
电阻:330 欧姆(至少与 LED 数量相同),10 k 欧姆电阻。
-
连续旋转伺服电机(SpringRC SM-S4303R 连续旋转伺服电机:http://www.robotgear.com.au/Product.aspx/Details/482)。
-
柔性传感器。
-
线性电位器。
-
连接线。
开始使用 Arduino 的一个极好的方法是 Sparkfun 的 Arduino 入门套件。这个入门套件包含您完成本文学习所需的所有硬件以及更多硬件(伺服电机除外)。
如果您还没有打开 Arduino 并将其插入 USB 端口,请插入。对于本文的目的而言,通过 USB 连接供电就足够了。如果您连接更多设备,则需要连接外部电源。
您将使用一种与 C 非常相似且基于 Processing 的语言对 Arduino 进行编程。您可以从 Arduino 项目网站下载 Arduino IDE。
Arduino IDE您可能会猜到,IDE 始终是前端。真正的部分是编译器、链接器和库,它们需要存在才能与基于 AVR 微控制器的 Arduino 进行通信和编程。根据您的 Linux 发行版,软件包的确切名称会有所不同,因此我在此仅按名称列出软件
-
用于 AVR 的 GNU C 和 C++ 编译器。
-
AVR binutils。
-
AVR libc。
-
avrdude(一个用于将代码上传到微控制器板的程序)。
-
rxtx(用于串行通信)。
安装这些软件包后,启动您的 Arduino IDE。花一点时间探索 IDE。用于编译(验证)和上传草图的按钮非常重要。
您的计算机和 Arduino 之间的通信将通过随 Arduino 板一起包装的 USB 电缆进行。一旦您将 USB 电缆插入计算机(另一端插入 Arduino 板),它应该会在 Arduino IDE 的“工具”→“串口”下显示为 /dev/ttyACMx。如果您有多个 USB 串行设备通信,请注意选择正确的设备。您需要正确设置用户权限才能访问串口(有关特定于发行版的说明,请参阅“资源”部分)。
闪烁 LED对于第一个草图,让我们先闪烁一个 LED,然后将其扩展为交替闪烁多个 LED。在软件部分之前,让我们首先设置电路以将 LED 连接到 Arduino。完成的电路应如图 2 所示。

图 2. 单个 LED 闪烁的电路
接下来,在您的 Arduino IDE 中,打开“示例”→“Basics”→“Blink”中的草图,该草图应如清单 1 所示。
清单 1. 简单的 LED 闪烁草图
/*
Blink
Turns on an LED on for one second,
then off for one second, repeatedly.
This example code is in the public domain.
*/
void setup() {
// initialize the digital pin as an output.
// Pin 13 has an LED connected on most Arduino boards:
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH); // set the LED on
delay(1000); // wait for a second
digitalWrite(13, LOW); // set the LED off
delay(1000); // wait for a second
}
从该草图和电路图中可以看出,LED 连接到 Arduino 的数字引脚 13。一旦您验证(编译)草图并将其上传到 Arduino 板,您应该会看到 LED 闪烁。
因为这是您的第一个草图,所以请花一些时间了解 Arduino 草图的总体框架。如果您熟悉 C 或 C++,您会注意到此草图中有两个函数:setup() 和 loop()。您在 setup() 中编写的代码用于初始化,并在草图上传到板后执行一次。loop() 中的代码会重复执行,只要 Arduino 供电。即使您关闭 Arduino 电源并重新打开电源,草图仍然存在,直到您用另一个草图覆盖它。Arduino 草图应保存在扩展名为 pde 的文件中,并存储在同名目录中。
对于下一个草图,让我们连接多个 LED 并交替闪烁它们,以创建炫酷的涟漪效果。(您可能想要连接三个以上不同颜色的 LED。)此草图的电路如图 3 所示。

图 3. 多个 LED 闪烁的电路
清单 2. 多个 LED 闪烁草图
/* Multiple LED Blinking program
Amit Saha
*/
// constants won't change. Used here to
// set pin numbers:
const int numPins = 3;
const int ledPin [] = {11,12,13}; // the number of LED pins
int interval = 100; // interval at which to blink (milliseconds)
void setup() {
// Iterate over each of the pins and set them as output
for(int i=0;i<numPins;i++)
pinMode(ledPin[i], OUTPUT);
}
/* Loop until death */
void loop()
{
for(int i=0;i<numPins;i++)
{
digitalWrite(ledPin[i],HIGH);
delay(interval);
digitalWrite(ledPin[i],LOW);
delay(interval);
}
}
一旦您将此草图上传到您的 Arduino UNO 板,您的 LED 应该会呈现一场色彩缤纷的表演。
模拟传感器在第一个草图中,您通过写入 Arduino 的数字引脚来打开和关闭 LED。如果您想要介于两者之间的东西呢?例如,淡入和淡出?向您介绍一种叫做电位器的小型设备。要设置电路,请将电位器的中心引脚连接到模拟引脚 0,并将另外两个引脚分别连接到 +5V 电源和接地。LED 应按照第一个草图中的方式连接。
清单 3. 使用线性电位器淡入/淡出 LED
/* Using potentiometer to fade on/off an LED
* Original code notice below:
* ------------------------------
* Demonstrates analog input by reading an analog sensor on
* analog pin 0 and turning on and off a light emitting
* diode (LED) connected to digital pin 13.
* The amount of time the LED will be on and off
* depends on the value obtained by analogREAD().
* Created by David Cuartielles
* Modified 16 Jun 2009
* By Tom Igoe
* http://arduino.cc/en/Tutorial/AnalogInput
*/
int sensorPin = 0;
int ledPin = 13;
int sensorValue = 0;
void setup() {
//declare the ledPin as an OUTPUT:
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
sensorValue = analogRead(sensorPin);//
digitalWrite(ledPin, HIGH);
delay(sensorValue);
digitalWrite(ledPin, LOW);
delay(sensorValue);
}
在清单 3 中,您可以看到来自电位器的读数用于控制延迟时间,因此产生了淡入和淡出的效果。
接下来,让我们使用一种叫做柔性传感器的电子元件来(您猜对了)控制 LED。基本上,让我们使用柔性传感器代替早期电路中的电位器。柔性传感器是一种模拟传感器,其电阻随弯曲角度而变化。您会看到,当您向任一侧弯曲时,电阻会发生变化——一侧增加,另一侧减少。Arduino 草图如清单 4 所示。
清单 4. 使用柔性传感器淡入/淡出 LED
/* Flex sensor + LED
/* Analog Input
* Demonstrates analog input by reading an analog sensor
* on analog pin 0 and turning on and off a light emitting
* diode (LED) connected to digital pin 13.
* The amount of time the LED will be on and off depends
* on the value obtained by analogREAD().
* Created by David Cuartielles
* Modified 16 Jun 2009
* By Tom Igoe
* http://arduino.cc/en/Tutorial/AnalogInput
* Modified 16 July, 2011
* By Amit Saha
* Current code was tested with a Flex sensor
*/
int sensorPin = 0; /*Flex sensor pin */
int ledPin = 13; /* LED pin*/
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
int loop=1;
float sensorValues=0.0,delaytime;
for(loop=1;loop <=10 ;loop++)
{
sensorValues = sensorValues + analogRead(sensorPin);
}
//Use the average as a delay value
delaytime = sensorValues/10;
digitalWrite(ledPin, HIGH);
delay(delaytime);
digitalWrite(ledPin, LOW);
delay(delaytime);
}
柔性传感器有两个引脚:一端应连接到 +5V 输入,另一端应连接到模拟引脚 0,然后再通过一个 10 k 欧姆电阻接地。
串行通信到目前为止的所有草图都使用了 Arduino 库调用来读取和写入 Arduino 引脚。您还没有直接使用主机计算机和 Arduino 板之间的串行通信。首先,我将描述如何编写基本的客户端/服务器风格程序。
“服务器”程序是一个驻留在 Arduino 板上的草图,等待串行数据可用(在本例中为整数),并通过将数字加 1 发送回“客户端”。
清单 5. 服务器草图示例
/* Serial communication demo: +1*/
/* Server program
Amit Saha*/
int number;
void setup()
{
// Open the serial connection, 9600 baud
Serial.begin(9600);
}
void loop()
{
// Get the data "packet"
// Wait for some data to arrive
if (Serial.available()>1) {
//operation=Serial.read();
number=Serial.read();
Serial.println(number + 1);
}
}
程序的客户端侧使用 Processing 及其串行库编写(清单 6)。
清单 6. 串行客户端草图示例
/*Client for Serial communication*/
/* Amit Saha */
import processing.serial.*;
Serial myPort; // The serial port
// initial variables:
int i = 1; // counter
char inData;
void setup () {
size(400, 350); // window size
// List all the available serial ports
println(Serial.list());
// Pick up the first port, since I usually have
// just the Arduino connected.
// Make sure the correct port is selected here.
myPort = new Serial(this, Serial.list()[0], 9600);
myPort.clear();
// set initial background:
background(255); }
void draw () {
myPort.write(4);
//myPort.write(5);
if (myPort.available() > 0) {
inData = (char)myPort.read(); // Typecast it to the corresponding
// character for the
// ASCII value
serialEvent();
}
}
void serialEvent () {
System.out.println(inData);
}
要运行客户端,请下载 Processing IDE 并使用清单 5 中的代码创建一个新草图。在将服务器草图(清单 5)上传到 Arduino 后运行此代码。
控制伺服电机现在,让我们使用您的 Arduino 代码让物体动起来。对于此示例,让我们控制伺服电机的运动——即启动和停止伺服电机并控制其速度和旋转。三根线从伺服电机伸出:控制线(白色/黄色)、电源线(红色)和地线(黑色/棕色)。首先,设置电路,使控制线连接到 Arduino 的数字引脚 2,红线连接到 Arduino 的 5V 输入,黑线连接到地。现在,将草图(清单 7)上传到您的 Arduino。您还可以选择将 LED 连接到数字引脚 13(以与您之前连接单个 LED 相同的方式),这将根据伺服电机是否旋转而打开或关闭。
清单 7. 伺服电机控制草图
/*
* servo1: servo1.pde
* Servo control from the Serial port
*
* Slower, faster, Center and Stop a Servo with an LED Blinky
* Created: 1 June, 2011, Amit Saha (http://echorand.me)
* Adapted from http://principialabs.com/arduino-serial-servo-control/
*/
/** Adjust these values for your servo and setup, if necessary **/
int servoPin = 2; // control pin for servo motor
int minPulse = 500; // minimum servo position
int maxPulse = 3000; // maximum servo position
int turnRate = 10; // servo turn rate increment (larger value,
faster rate)
int refreshTime = 20; // time (ms) between pulses (50Hz)
int OFF=0; // This variable will be used to get/set the status of the servo
/** The Arduino will calculate these values for you **/
int centerServo; // center servo position
int pulseWidth; // servo pulse width
int moveServo; // raw user input
long lastPulse = 0; // recorded time (ms) of the last pulse
/* LED setup*/
int ledPin=13;
void setup() {
pinMode(ledPin, OUTPUT); // LED Blink
pinMode(servoPin, OUTPUT); // Set servo pin as an output pin
centerServo = maxPulse - ((maxPulse - minPulse)/2);
pulseWidth = 0;
Serial.begin(9600);
Serial.println(" Arduino Serial Servo Control");
Serial.println("Keys:'(s)lower' or '(f)aster', spacebar to center
and o to stop");
Serial.println();
moveServo = 60;
}
void loop() {
// wait for serial input
if (Serial.available() > 0) {
// read the incoming byte:
moveServo = Serial.read();
// ASCII 's' is 115, ASCII 'f' is 102, 'o' is 111, 'spacebar' is 32
if (moveServo == 115) { pulseWidth = pulseWidth - turnRate; OFF=0;}
//slower
if (moveServo == 102) { pulseWidth = pulseWidth + turnRate;OFF=0; }
//faster
if (moveServo == 32) { pulseWidth = centerServo; OFF=0;} //center
if (moveServo == 111) { OFF= 1;} //STOP
// limit the servo pulse at min and max
if (pulseWidth > maxPulse) { pulseWidth = maxPulse; }
if (pulseWidth < minPulse) { pulseWidth = minPulse; }
}
// pulse the servo every 20 ms (refreshTime) with current pulseWidth
// this will hold the servo's position if unchanged, or move it if
// changed
if (OFF == 0)
{
/* Turn ON the LED*/
digitalWrite(ledPin,HIGH);
if (millis() - lastPulse >= refreshTime) {
digitalWrite(servoPin, HIGH); // start the pulse
delayMicroseconds(pulseWidth); // pulse width
digitalWrite(servoPin, LOW); // stop the pulse
lastPulse = millis(); // save the time of the last pulse
}
}
else
{
/* Turn OFF the LED*/
digitalWrite(ledPin,LOW);
//Stop the servo
digitalWrite(servoPin, LOW);
}
}
草图上传完成后,使用 screen
打开一个串行通信通道(随意用您喜欢的终端通信程序替换它)。键入 screen /dev/tttyACM0 9600
,您应该会看到“伺服电机提示符”为您服务
Arduino Serial Servo Control
Keys:'(s)lower' or '(f)aster', spacebar to center and o to stop
按下伺服电机的按键应产生所需的行为。如果您查看伺服电机机制的代码清单,您将看到控制速度的关键基本上是向伺服电机发送 HIGH 和 LOW 信号之间的延迟时间(变量 pulseWidth)。在这里,我们使用称为脉冲宽度调制的重要技术来模拟模拟行为,您可以在其他地方阅读有关该技术的更多信息。
如果您已经成功运行此伺服电机示例,您可能还想查看 Arduino IDE 中“文件”→“示例”→“Servo”下用于伺服电机的其他示例草图。
幕后一瞥本文即将结束,我希望您玩得很开心。但是,如果您像我一样,您可能已经开始想知道幕后发生了什么——从 Processing 草图到在 Arduino 板上执行的字节的整个过程。
幕后的实际工作由用于 AVR 微控制器的 GNU C/C++ 编译器、链接器和库完成。如果您在编译草图时按住 Shift 键,您将看到 - avr-g++
、avr-g++
、avr-ar
和 avr-objcopy
命令被调用。首先,您的 Arduino 草图被转换为合适的 C++ 文件(扩展名为 .cpp),然后对其进行编译、链接,最后转换为上传到 Arduino 板的 hex 文件。您可以在 /tmp/build*.tmp 目录中看到所有这些中间文件。了解此构建过程使您可以通过编写适当的 Makefile 来绕过 IDE 进行 Arduino 开发。有关示例,请参阅“资源”部分中列出的“命令行 Arduino 开发”文章。
我已经描述了一些可以使用 Arduino 完成的简单但很酷的事情,但本文只是触及了表面。有许多优秀的书籍列出了大量您可以构建的 Arduino 项目,以供娱乐和盈利。当然,除了所有优秀的在线资源外,还有这些。在纯粹从网络上的各种博客文章和基于试错的学习方法探索 Arduino 的过程中,我发现了 Arduino 生态系统中的许多优秀项目。请务必查看本文的“资源”部分,了解一些最有趣的 Arduino 书籍、文章和项目。
另请注意,电线很好,但仅当您想将自己限制在桌面甚至房间的范围内时才适用。如果您希望您的 Arduino 在外面,与您的主机分离,您需要探索无线通信方式。向 XBee 模块问好,它允许与 ZigBee 通信标准进行通信。
在结束之前,您可能会遇到串行通信不稳定的问题。特别是在长时间试验串行通信期间,我发现串口会保持锁定状态,或者根本无法从主机计算机访问。我的建议是耐心等待。拔下并重新插入几次,然后尝试手动杀死锁定文件。现在,您应该可以正常使用了。
致谢感谢出色的 Arduino 社区成员提供的文档和网络上众多其他博主。Arduino 电路图是使用 Fritzing (http://fritzing.org) 绘制的。
资源本文作者代码:https://bitbucket.org/amitksaha/articles_code/src/6bed469945fd/arduino_article
Arduino 管道:http://www.concurrency.cc/book
PureData(音频处理和可视化):http://puredata.info
Firefly Experiments(弥合 Grasshopper、Arduino 微控制器、互联网及其他之间的差距):http://www.fireflyexperiments.com
Arduino:http://arduino.cc
Processing 语言:http://processing.org
Arduino 硬件(I/O 板):http://www.arduino.cc/en/Main/Hardware
Arduino Uno:http://arduino.cc/en/Main/ArduinoBoardUno
Sparkfun 的 Arduino 入门套件:http://www.sparkfun.com/products/10174
在 Linux 上安装 Arduino(适用于不同的发行版):http://www.arduino.cc/playground/Learning/Linux
命令行 Arduino 开发:http://shallowsky.com/software/arduino/arduino-cmdline.html
Massimo Banzi 的Arduino 入门,O'Reilly Media/Make:http://shop.oreilly.com/product/9780596155520.do
Joshua Noble 的交互式编程:设计师 Processing、Arduino 和 openFrameworks 指南,O'Reilly:http://shop.oreilly.com/product/9780596154158.do
Michael Margolis 的Arduino 食谱,O'Reilly:http://shop.oreilly.com/product/0636920022244.do
Arduino 语言参考:http://www.arduino.cc/en/Reference/HomePage
Tom Igoe 的物理计算页面:http://www.tigoe.net/pcomp
Arduino 实验者指南:http://ardx.org/src/guide/2/ARDX-EG-ADAF-WEB.pdf
Arduino 教程 (tronixstuff):http://tronixstuff.wordpress.com
Principia Labs,Arduino 串行伺服电机控制:http://principialabs.com/arduino-serial-servo-control
Make 的 Arduino 网站:http://blog.makezine.com/arduino
Lady Ada 上的 Arduino 教程:http://www.ladyada.net/learn/arduino