Arduino编程入门
闪电编程,助你闪电入门!⚡️⚡️⚡️
本节课的大纲:
- Arduino介绍及互动装置艺术
- Arduino中C++编程语言的入门
- Arduino的核心概念及术语
- Arduino入门项目练习
本节课学完后能收获什么:
- 明白Arduino是什么、能做什么
- 基本掌握第二种编程语言:C++
- 能够看懂Arduino硬件项目的组成部分
- 能够动手完成一个Arduino硬件项目的编码和连线
课前准备:
-
在线Arduino模拟器环境:New Arduino Uno Project
-
Wokwi官方API文档: Wokwi Docs
一、Arduino 介绍:从虚拟代码到实体艺术的桥梁
1.1 Arduino是什么?
Arduino是一款开源的电子原型平台,由一个可编程的电路板(硬件)和配套的开发环境(软件)组成。Arduino的设计初衷是让非专业人士也能快速上手,因此它非常适合初学者、艺术家、设计师。 它包含:
- 一块电路板(最常用的是
Arduino Uno) - 一个用来编程的软件(Arduino IDE)
- 一套简单的编程语言(我们今天要学的
C++)。
与传统编程不同,Arduino能感知物理世界(通过传感器)并与之互动(通过灯光、电机等)。
就像艺术家的"电子物理画笔"——通过编写Arduino代码,可以让LED灯跳舞、让雕塑对触摸做出反应,甚至创造会"呼吸"的灯光装置(Arduino for Artists)。
1.2 为什么艺术家需要Arduino?
我们先来看学校的课程安排:
- UAL-cc:第一学期的两门必修课,一门是p5.js,另一门就是Arduino(Physical )。
- 金匠-ca:第一学期必修课是p5.js,重要选修课是Arduino。
对于艺术生来说,Arduino是创作互动装置艺术的理想工具。
传统的装置艺术是静态的,观众只能被动观赏。而加入了Arduino的装置艺术变成了活的、会呼吸的艺术品:
互动性:作品能够感知观众的存在,并做出实时响应动态性:光线、声音、运动可以随时间变化沉浸感:观众成为作品的一部分,而不仅仅是旁观者
比如以下的互动装置艺术案例:
- 案例1:一个由数百个LED灯组成的雕塑,当有人靠近时,灯光像流水一样避开,形成互动的光流。
- 案例2:一排小风扇,根据实时网络上的情绪数据(比如“快乐”关键词的数量)改变转速,将风吹动悬挂的丝带,形成“数据风铃”。
- 案例3:一个机械臂握着画笔,根据传感器采集的环境声音大小,在画布上画出不同力度和长度的线条。
许多震撼的当代艺术作品都运用了Arduino技术:
- "Psychoplash" :由20个充气模块组成的互动装置,使用Arduino UNO R4 WiFi控制灯光和运动,根据观众移动产生"呼吸"效果(Creating Art with the Arduino UNO R4 WiFi)
- "Bubble Concert" :通过吹泡泡棒触发不同音符的协作音乐装置,使用温度传感器检测吹气动作(Bubble Concert – Interactive Art with Processing and Arduino)
- 机械镜子:艺术家Dan Rozin用Arduino驱动机械元件作为"像素"创造动态镜像(Arduino for Artists - Matthew McClain)
1.3 动手练习:认识Wokwi模拟器
由于大家手头没有物理设备(Arduino开发板),我们本节课使用Wokwi模拟器进行教学。Wokwi是一个在线、免费、无需安装且支持多种板卡的Arduino模拟器。
操作步骤:
- 访问Wokwi模拟器环境 New Arduino Uno Project - Wokwi Simulator
- 尝试在左侧代码编辑区粘贴以下代码:
void setup() {
Seria1.begin(9680);//初始化串口通信,设置波特率为9688
}
void loop() {
Serial.println("欢迎来到创意编程与耳动装置设计课程!");
delay(2000);//暂停2800毫秒(即2秒),控制输出频率
}
- 点击播放键"Start Simulation",观察界面变化和输出。
Wokwi的界面主要分为三个区域:
- 代码编辑区:在这里编写Arduino代码。
- 电路模拟区:在这里可以像搭积木一样,从元件库中拖拽出Arduino、LED、电阻、按钮等,并用虚拟导线将它们连接起来。
- 运行与调试区:点击绿色的“播放”按钮来启动模拟,观察电路的反应。这里还有一个“串口监视器”,可以帮助我们看到程序输出的信息。
二、Arduino 中的 C++ 编程语言入门
Arduino的编程是基于C/C++语言的,但为了方便初学者,Arduino官方做了很多简化和封装。
本节我们将介绍Arduino编程中的一些基础知识:库、变量类型、数组、函数、判断和循环。这些内容在JavaScript中也有对应的概念,我们会适当对比,帮助大家理解。
大家已经有了 JavaScript 的基础,编程的逻辑是相通的,切换编程语言就像是换了一只画笔。本节课,我们把编程的“画笔”从 JavaScript 换成 Arduino 的 C++,让CodeArt在真实的物理空间中得到展现。
说明:
在进入Arduino硬件编程之前,我们首先将C++作为一门独立的编程语言进行学习。让大家能够专注于语言本身的逻辑结构,避免在初学阶段被硬件连接、电路原理等额外复杂度干扰。
本部分所有示例和练习均不涉及任何硬件引脚控制或Arduino特定函数,仅使用标准C++语法结构和Serial串口输出功能(可在Wokwi模拟器的串口监视器中查看结果)。
2.1 库(Libraries)—— 代码的预制模块
库的引入和使用
在C++中,我们经常需要使用一些预先写好的功能,这些功能被打包在“库”里。#include指令就是在项目中导入一个工具库。
库 是一组预先编写好并经过测试的函数和数据结构的集合,开发者可以通过包含(include)指令将其引入自己的程序中,无需从零开始实现。
在Arduino开发环境中,最核心的库是 <Arduino.h>,它定义了所有与硬件交互的函数,如pinMode、digitalWrite、Serial等。
如何使用库?
在Arduino中,使用库通常需要两步:
首先在程序开头用#include <库名.h>语句引入库,然后就可以在代码中使用该库提供的函数和对象了。
例如,引入Servo库后,我们可以创建一个Servo对象并调用它的方法:
#include <Servo.h> // 引入Servo舵机库
Servo myServo; // 创建一个Servo对象
案例代码:输出欢迎信息
复制以下代码到wokwi编辑器中:
New Arduino Uno Project - Wokwi Simulator
#include <Arduino.h> // 包含Arduino核心库,使Serial对象可用
void setup() {
Serial.begin(9600); // 初始化串口通信,设置波特率为9600
}
void loop() {
Serial.println("欢迎来到创意编程与互动装置设计课程!");
delay(2000); // 暂停2000毫秒(即2秒),控制输出频率
}
代码说明
Serial.begin(9600)是用于初始化串口,简单理解为计算机和Arduino通信的接口。“9600” 代表每秒传输9600个二进制位。Serial.println()用于打印信息到串口监视器。
运行效果:在Wokwi模拟器中点击运行此代码后,自动打开串口监视器,即可看到每两秒输出一行欢迎信息。
练习任务1:输出个人艺术宣言
请修改上述代码,使其依次输出三行内容:
- 你的姓名或艺名
- 一句描述你艺术追求或风格的短句
- 一个代表你当前创作状态的词语(如“探索中”、“爆发期”、“沉淀期”)
要求每行输出之间间隔1秒。
参考答案示例:
#include <Arduino.h>
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println("林墨");
delay(1000);
Serial.println("用代码与光线重构空间感知");
delay(1000);
Serial.println("探索中");
delay(1000);
}
2.2 变量类型(Data Types)—— 数据的静态声明
变量类型声明
C++ 是【强类型】语言,而 JS 是【弱类型】的。
在C++中,变量类型是显式声明的,每个变量都必须明确说明它是什么类型,变量类型决定了变量占用的内存大小和可存储的值范围。
JavaScript vs C++变量声明:
JavaScript - 不需要指定类型
let age = 25;
let name = "Arduino";
let temperature = 36.5;
let isHappy = true;
C++ - 必须指定类型
int age = 25; // 整数
String name = "Arduino"; // 字符串
float temperature = 36.5; // 小数
bool isHappy = true; // 布尔值
Arduino常用数据类型详解:
-
int(整数) : -32,768 到 32,767
- 用途:计数、引脚编号、简单数值
-
long(长整数) : 约-21亿 到 21亿
- 用途:时间计算(毫秒)、大数值
-
float(浮点数) : 小数,如3.14
- 用途:温度、距离、角度等需要小数的场合
-
boolean(布尔) : true 或 false
- 用途:开关状态、条件判断
-
char(字符) : 单个字符,如'A'
- 用途:按键输入、简单标识
-
String(字符串) : 文本,如"Hello"
- 用途:显示信息、串口通信
类比理解: 把变量类型想象成不同的容器:
- int像是装鸡蛋的盒子(只能装整个的)
- float像是量杯(可以装半杯、1/3杯)
- boolean像是开关(只有开/关两种状态)
- String像是便利贴(可以写字)
案例代码:计算装置作品尺寸
#include <Arduino.h>
void setup() {
Serial.begin(9600);
float width = 120.5; // 作品宽度,单位:厘米
float height = 90.0; // 作品高度,单位:厘米
float depth = 30.0; // 作品深度,单位:厘米
float volume = width * height * depth;
Serial.print("装置作品宽度: ");
Serial.println(width);
Serial.print("装置作品高度: ");
Serial.println(height);
Serial.print("装置作品深度: ");
Serial.println(depth);
Serial.print("装置作品体积: ");
Serial.println(volume);
}
void loop() {
// 此处留空,因为所有计算和输出已在setup中完成。只执行一次。
}
代码说明:
Serial.print() 和 Serial.println()的区别:打印的输出是否换行
运行说明:
程序运行后,串口监视器将输出作品的三维尺寸及计算得到的体积值。
练习任务2:计算色彩混合比例
请声明三个浮点型变量,分别代表红、蓝、黄三种基础色的混合比例(建议值:red = 0.4, blue = 0.35, yellow = 0.25)。
计算三者之和,并输出“色彩混合比例总和:X”,验证其是否等于1.0。
如果总和不等于1.0,请额外输出一条提示信息“混合比例未归一化!”。
参考答案示例:
#include <Arduino.h>
void setup() {
Serial.begin(9600);
float red = 0.4;
float blue = 0.35;
float yellow = 0.25;
float total = red + blue + yellow;
Serial.print("色彩混合比例总和: ");
Serial.println(total);
if (total != 1.0) {
Serial.println("⚠️ 混合比例未归一化");
}
}
void loop() {}
2.3 数组(Arrays)—— 同类数据的有序集合
概念讲解
数组是用于存储一组相同类型数据的容器。在声明数组时,必须指定其元素类型和大小(即能容纳的元素个数)。数组元素通过索引访问,索引从0开始。
- JavaScript中数组是动态的:
let colorPalette = ["crimson", "cobalt", "ochre"]; - C++中数组是静态的:
String colorPalette[3] = {"crimson", "cobalt", "ochre"};
C++中,数组的大小在声明后不可改变。
C++数组的声明和使用:
int ledPins[3] = {26, 27, 28}; // 声明并初始化数组
String colors[3] = {"red", "green", "blue"}; // 声明并初始化数组
JavaScript数组更加灵活,可以存储不同类型的数据:
let ledPins = [26, 27, 28]; // 声明并初始化数组
let colors = ["red", "green", "blue", 255]; // 可以存储不同类型的数据
案例代码:遍历并输出调色盘颜色
#include <Arduino.h>
void setup() {
Serial.begin(9600);
String palette[5] = {"朱红", "群青", "藤黄", "石绿", "钛白"};
Serial.println("我的数字调色盘包含以下五种传统中国画颜料:");
for (int i = 0; i < 5; i++) {
Serial.print(i + 1);
Serial.print(". ");
Serial.println(palette[i]);
}
}
void loop() {
}
运行说明:程序将按顺序输出调色盘中的五种颜色名称,并附带编号。
练习任务3:倒序输出情绪色彩序列
请创建一个包含五种情绪对应色彩的字符串数组
(例如:["愤怒-红", "忧郁-蓝", "喜悦-黄", "平静-绿", "神秘-紫"])
然后使用for循环从最后一个元素开始,倒序输出所有元素。
参考答案示例:
#include <Arduino.h>
void setup() {
Serial.begin(9600);
String emotionColors[5] = {"愤怒-红", "忧郁-蓝", "喜悦-黄", "平静-绿", "神秘-紫"};
Serial.println("情绪色彩倒序列表:");
for (int i = 4; i >= 0; i--) {
Serial.println(emotionColors[i]);
}
}
void loop() {}
2.4 函数(Functions)—— 代码的模块化封装
概念讲解
函数是将一段具有特定功能的代码封装起来,赋予其一个名称,以便在程序的不同位置重复调用。
在C++中,定义函数时必须明确指定其返回值类型;如果函数不返回任何值,则使用 void 作为返回类型。
- JavaScript函数定义:
function calculateArea(width, height) { return width * height; } - C++函数定义:
float calculateArea(float width, float height) { return width * height; }
JavaScript对比:C++中函数需要明确声明返回类型和参数类型。
// JavaScript中的函数
function add(a, b) {
return a + b;
}
// 或箭头函数
const multiply = (a, b) => a * b;
// C++中的函数
int add(int a, int b) {
return a + b;
}
// 无返回值函数
void blinkLED(int times) {
}
练习任务4:创建作品评价生成函数
请编写一个名为 generateReview 的函数,该函数接收一个字符串参数 artistName,无返回值(void)
功能是在串口输出一句格式为“[艺术家姓名]的作品在本次展览中获得了高度评价!”的评论。
在setup函数中,调用该函数三次,分别传入三位不同艺术家的名字。
参考答案示例:
#include <Arduino.h>
void generateReview(String artistName) {
Serial.print(artistName);
Serial.println("的作品在本次展览中获得了高度评价!");
}
void setup() {
Serial.begin(9600);
generateReview("草间弥生");
generateReview("奥拉维尔·埃利亚松");
generateReview("teamLab");
}
void loop() {}
2.5 判断(Conditionals)—— 程序的分支逻辑
概念讲解
判断结构(if...else)允许程序根据不同的条件执行不同的代码块。
判断结构的核心在于条件表达式,其结果必须为布尔值(true或false)。
常用的比较运算符包括:==(等于)、!=(不等于)、>(大于)、<(小于)、>=(大于等于)、<=(小于等于);逻辑运算符包括:&&(与)、||(或)、!(非)。
if/else判断语句在JS和C++中几乎完全相同:
// JavaScript中的判断
let score = 85;
// C++中的判断
int score = 85;
if (score >= 90) {
Serial.println("A");
} else if (score >= 80) {
Serial.println("B");
} else {
Serial.println("C");
}
练习任务5:创作状态判断器
请声明一个整型变量 creativeEnergy,表示当前创作能量值(范围1-10,10为最高)。编写判断逻辑:
- 如果 creativeEnergy >= 8,输出“当前创作状态:极高”
- 如果 creativeEnergy >= 5,输出“当前创作状态:良好”
- 如果 creativeEnergy >= 2,输出“当前创作状态:一般”
- 否则,输出“当前创作状态:枯竭”
参考答案示例:
#include <Arduino.h>
void setup() {
Serial.begin(9600);
int creativeEnergy = 6;
Serial.print("创作能量值: ");
Serial.println(creativeEnergy);
if (creativeEnergy >= 8) {
Serial.println("当前创作状态:灵感迸发,效率极高");
} else if (creativeEnergy >= 5) {
Serial.println("当前创作状态:稳步进行,状态良好");
} else if (creativeEnergy >= 2) {
Serial.println("当前创作状态:略有倦怠,建议休息");
} else {
Serial.println("当前创作状态:能量枯竭,急需充电");
}
}
void loop() {}
2.6 循环(Loops)—— 自动化的重复执行
概念讲解
循环结构允许程序重复执行一段代码,直到满足特定条件为止。
C++中常用的循环包括for循环(适用于已知循环次数)和while循环(适用于条件控制循环)。
- for循环语法:
for (初始化; 条件; 迭代) { 循环体 } - while循环语法:
while (条件) { 循环体 }
for和while循环在JS和C++中语法相同。
// JavaScript中的循环
for (let i = 0; i < 5; i++) {
console.log("Count: " + i);
}
// C++中的循环
for (int i = 0; i < 5; i++) {
Serial.print("Count: ");
Serial.println(i);
}
案例代码:展览倒计时
#include <Arduino.h>
void setup() {
Serial.begin(9600);
Serial.println("新媒体艺术双年展开幕倒计时:");
for (int i = 10; i >= 1; i--) {
Serial.print(i);
Serial.println("...");
delay(1000); // 每秒倒数一次
}
Serial.println("展览正式开幕!欢迎各位观众!");
}
void loop() {}
运行说明:程序模拟展览开幕前的10秒倒计时,每秒输出一个数字,最后输出开幕信息。
三、Arduino核心概念与开发板实战
Arduino 的基本组成部分
一个典型的Arduino项目通常包括以下几个部分:
- Arduino板:这是项目的核心,负责执行代码并控制外部设备。Arduino板通常包含一个微控制器、输入/输出引脚、电源接口以及用于编程的USB接口。
- 传感器(输入):用于检测环境中的变化,如温度、光线、运动等。
- 执行器(输出):如LED、电机、蜂鸣器等,用于执行具体的动作。
- 连接线:用于将传感器和执行器连接到Arduino板上。
我们要学习的是 Arduino UNO板,这是 Arduino系列中最受欢迎、使用最多的开发板。
认识Arduino-Uno开发板:
实体Arduino-uno开发板的接口说明:
下图为Wokwi所模拟的Arduino-Uno板子:
1)14个数字接口:数字接口从图上可以看到总共有14个(0~13号),0号和1号口属于串口通信用的,一般我们不去占用;在2到13号口中带有“~”符号的接口,代表它不仅可以输出高电平和低电平信号,也可以输出调制的模拟信号,不带“~”符号的接口就只能输出5V高电平或者0V低电平。
数字信号:非黑即白
- 只有两种状态:HIGH(5V)或LOW(0V)
- 适用:开关、LED开关、简单通信
模拟信号:有灰度层次
- 连续变化的值:0V到5V之间的任意值
- 适用:传感器读数、音频、渐变效果
2)6个模拟接口:Arduino下方A0,A1......,A5这几个带A的接口就是模拟接口。模拟接口只能读取不能输出模拟量,基本上用模拟接口接收传感器信号。
3)USB接口:有两个功能 a. 用来给Arduino供电 b. 和电脑串口通信。
4)外部电源接口:也是用来给Arduino供电的,和USB的区别在于你可以把电池通过这个接口插上供电。
5)工作状态灯:如果L和On两个灯亮,则说明Arduino在工作。
6)TX,RX灯:这两个灯和串口通信有关,TX闪说明Arduino正通过串口通信发送信息,RX闪说明Arduino正通过串口通信接受信息。
Arduino程序的结构
Arduino程序有两个主要部分:setup()和loop()。这是每个Arduino程序都必须包含的基本结构。
setup()函数在程序开始时只运行一次,通常用于初始化设置。loop()函数在setup()执行完后会不断重复执行,直到断电。
类比: P5.js的模版代码,setup()和Draw()
示例-wokwi模拟器操作
Wokwi - Online ESP32, STM32, Arduino Simulator
运行程序:(现实中相当于把程序烧录到板子上)
添加元件:
连线: 点击元件两端,按esc取消。
第一个Arduino项目:点亮LED灯
让我们从一个简单的项目开始:点亮一个LED。理解Arduino的基本工作原理。(所有同学进行一次全流程操作。)
所需材料
- Arduino Uno板
- 1个LED
- 1个220欧姆电阻(Register)
- 连接线
电路连接
- 将LED的长脚(正极)连接到Arduino的数字引脚13。
- 将LED的短脚(负极)通过220欧姆电阻连接到Arduino的GND(地)引脚。
代码示例
void setup() {
// 将数字引脚13设置为输出模式
pinMode(13, OUTPUT);
}
void loop() {
// 点亮LED
digitalWrite(13, HIGH);
// 等待1秒
delay(1000);
// 熄灭LED
digitalWrite(13, LOW);
// 等待1秒
delay(1000);
}
代码解释
setup()函数在Arduino启动时运行一次,用于初始化设置。在这里,我们将数字引脚13设置为输出模式。loop()函数会不断重复执行。在这个例子中,我们通过digitalWrite()函数控制高低电平,从而控制LED的亮灭,并使用delay()函数来控制时间间隔。
运行结果: LED会每隔1秒钟闪烁一次。
代码改进
- 13 太多,代表什么意思?
// 定义常量,提高代码可读性和可维护性
const int LED_PIN = 13;
void setup() {
// 在setup函数中初始化引脚模式
pinMode(LED_PIN, OUTPUT); // 设置D13引脚为输出模式
}
void loop() {
// 在loop函数中实现LED闪烁逻辑
digitalWrite(LED_PIN, HIGH); // 输出高电平,点亮LED
delay(1000); // 延时1000毫秒(1秒)
digitalWrite(LED_PIN, LOW); // 输出低电平,熄灭LED
delay(1000); // 延时1秒
}
练习
- 修改上面的代码,使LED以不同的时间间隔闪烁。
- 尝试添加第二个LED,并编写代码使两个LED交替闪烁。
示例代码
// 定义两个LED的引脚常量
const int LED_PIN1 = 13; // 第一个LED连接的引脚
const int LED_PIN2 = 12; // 第二个LED连接的引脚
void setup() {
// 初始化两个LED引脚为输出模式
pinMode(LED_PIN1, OUTPUT);
pinMode(LED_PIN2, OUTPUT);
}
void loop() {
// 点亮第一个LED,熄灭第二个LED
digitalWrite(LED_PIN1, HIGH);
digitalWrite(LED_PIN2, LOW);
delay(1000); // 保持1秒
// 熄灭第一个LED,点亮第二个LED
digitalWrite(LED_PIN1, LOW);
digitalWrite(LED_PIN2, HIGH);
delay(1000); // 保持1秒
}
3.3 项目二:按钮开关台灯 —— 掌握数字输入与条件判断
项目目标
通过按钮控制LED的亮灭状态,学习数字引脚输入模式设置(pinMode)、数字输入读取(digitalRead)、条件判断(if语句)的应用,以及Arduino内置上拉电阻的使用方法。
Wokwi操作步骤
-
添加元件:
- 在项目一的基础上,点击左侧“+”号,搜索并添加“Pushbutton”元件
-
电路连接(使用内置上拉电阻方案,电路最简):
- 将按钮的一个引脚连接到Arduino的数字引脚“D2”
- 将按钮的另一个引脚连接到Arduino的“GND”引脚
- (无需像LED一样外接电阻,代码中启用内置上拉电阻)
- 代码编写:
// 定义引脚常量
const int BUTTON_PIN = 2;
const int LED_PIN = 13;
void setup() {
// 设置按钮引脚为输入模式,并启用内置上拉电阻
pinMode(BUTTON_PIN, INPUT_PULLUP);
// 设置LED引脚为输出模式
pinMode(LED_PIN, OUTPUT);
// 初始化串口通信,用于调试输出
Serial.begin(9600);
Serial.println("按钮控制LED项目已启动");
}
void loop() {
// 读取按钮状态
int buttonState = digitalRead(BUTTON_PIN);
// 根据按钮状态控制LED
if (buttonState == LOW) {
// 按钮被按下
digitalWrite(LED_PIN, HIGH); // 点亮LED
Serial.println("按钮被按下,LED已点亮");
} else {
// 按钮未被按下
digitalWrite(LED_PIN, LOW); // 熄灭LED
}
// 添加短延时,防止按钮抖动并降低处理器占用
delay(50);
}
- 运行模拟:
- 点击“Start Simulation”按钮
- 点击画布上的按钮,观察LED是否在按下时点亮,松开时熄灭
- 打开“Serial Monitor”,查看按钮状态变化时的调试信息
扩展挑战
在代码中添加一个全局变量 int pressCount = 0;,用于记录按钮被按下的总次数。每次检测到按钮从“未按下”变为“按下”的状态变化时(即上升沿或下降沿检测),将计数器加1,并通过串口输出“第X次按钮按下”。
提示:需要记录前一次按钮状态,与当前状态比较以检测变化。
3.4 项目三:调光控制台灯 —— 学习模拟输入与PWM输出
项目目标
通过旋转电位器(旋钮)无级调节LED亮度,理解模拟信号读取(analogRead)、脉宽调制输出(analogWrite)、数值映射(map函数)的概念,以及PWM(脉冲宽度调制)技术在模拟亮度控制中的应用。
Wokwi操作步骤
-
添加元件:
- 保留项目二中的LED和电阻(但需更改连接引脚),去掉按钮
- 点击左侧“+”号,搜索并添加“Potentiometer”元件(电位器,类似台灯旋钮)
-
电路连接:
- 将电位器的左侧引脚连接到Arduino的“5V”引脚
- 将电位器的右侧引脚连接到Arduino的“GND”引脚
- 将电位器的中间引脚(滑动端)连接到Arduino的模拟输入引脚“A0”
- 将LED的阳极(通过330Ω电阻)连接到Arduino的数字引脚“D9”(注意:D9是支持PWM的引脚,标记有~符号)
- 将LED的阴极连接到“GND”引脚
- 代码编写:
// 定义引脚常量
const int POT_PIN = A0; // 电位器连接模拟引脚A0
const int LED_PIN = 9; // LED连接PWM数字引脚D9
void setup() {
// 设置LED引脚为输出模式(PWM引脚无需特殊设置)
pinMode(LED_PIN, OUTPUT);
// 模拟输入引脚默认为输入,无需设置pinMode
Serial.begin(9600);
Serial.println("旋钮调光控制项目已启动");
}
void loop() {
// 读取电位器模拟值(范围0-1023)
int potValue = analogRead(POT_PIN);
// 将0-1023的范围映射到0-255(PWM输出范围)
int brightness = map(potValue, 0, 1023, 0, 255);
// 输出PWM信号控制LED亮度
analogWrite(LED_PIN, brightness);
// 通过串口输出当前电位器值和映射后的亮度值,用于调试
Serial.print("电位器值: ");
Serial.print(potValue);
Serial.print(" | 映射亮度: ");
Serial.println(brightness);
// 添加短延时,使亮度变化更平滑
delay(20);
}
- 运行模拟:
- 点击“Start Simulation”按钮
- 旋转画布上的电位器旋钮,观察LED亮度是否随之平滑变化
- 打开“Serial Monitor”,查看电位器值和亮度值的实时变化
技术原理说明——模拟信号
在Arduino控制器中,编号前带有“A”的引脚被专门设计为模拟输入引脚。这些引脚能够被Arduino读取,以获取其上的模拟值,也就是读取其上的电压大小。
模拟输入功能拥有10位精度,意味着它可以将0至5伏的电压范围转换为0至1023的整数表示。而要读取模拟值,我们通常使用analogRead(pin)函数,例如analogRead(A0)即表示读取A0引脚上的模拟值。
与模拟输入相对的是模拟输出功能,它通过analogWrite()函数实现。尽管这个函数名为“写模拟值”,但它实际上并非直接输出模拟信号,而是利用PWM(脉冲宽度调制)技术来模拟输出不同的电压。
- 模拟输入:analogRead函数读取的是引脚上的模拟电压值,Arduino Uno将其转换为0-1023的数字(10位分辨率),0对应0V,1023对应5V。
- PWM模拟输出:analogWrite函数并非输出真正的模拟电压,而是输出脉冲宽度调制信号。通过快速切换高低电平,并调整高电平在一个周期内所占的比例(占空比),来模拟不同的平均电压,从而实现LED亮度的无级调节。占空比0%对应完全熄灭,100%对应全亮。
- 数值映射:map函数用于将一个范围内的数值线性映射到另一个范围,是硬件控制中常用的转换工具。
扩展挑战
修改代码,实现“呼吸灯”效果:LED亮度自动从0渐变到255,再从255渐变回0,循环往复。同时,使用电位器控制呼吸的快慢——电位器值越大,呼吸频率越快(即渐变过程所用时间越短)。
提示:使用for循环实现渐变,用map函数将电位器值映射为delay时间。
课程总结
通过本节课的学习,能基本掌握Arduino创意编程的基础知识和实践技能:
- C++语言基础:理解了库、变量类型、数组、函数、判断、循环等核心语法概念,并通过独立于硬件的纯代码练习巩固了这些知识,为后续硬件编程打下了坚实的语法基础。
- Arduino核心概念:熟悉了Arduino程序的基本结构(setup和loop),理解了数字信号与模拟信号的区别,掌握了输入设备与输出设备的连接与控制方法,了解了电压、电流、电阻等基础电路概念在实际项目中的应用。
- Wokwi模拟器操作:能够熟练使用Wokwi在线平台进行电路搭建、代码编写、模拟运行和调试输出,具备了在无实体硬件条件下进行原型设计和验证的能力。
- 项目实践经验:通过四个由浅入深的实战项目(基础闪烁、按钮控制、旋钮调光、光感控制),学生将理论知识应用于实际问题解决,培养了计算思维和工程实践能力。
附加资源
作业:环境光感应装置 —— 综合应用模拟输入与条件控制
背景知识
- 光敏电阻(LDR) :一种对光线敏感的电阻元件,其阻值会随光照强度变化 —— 光照越强,阻值越小;光照越弱,阻值越大。通过检测其阻值变化可间接获取环境光强度。
- 分压电路:由于 Arduino 无法直接测量电阻值,需通过分压电路将电阻变化转换为电压变化。本项目中,光敏电阻与 10KΩ 固定电阻串联,接在 5V 电源与地之间,中间节点连接到模拟输入引脚,通过读取该节点电压(转换为 0-1023 的数字值)反映光照强度。
- 模拟输入与数字输出:Arduino 的模拟输入引脚(如 A1)可将外部电压(0-5V)转换为 0-1023 的数字信号;数字输出引脚(如 D13)可输出高电平(HIGH,约 5V)或低电平(LOW,约 0V),用于控制 LED 等元件的开关。
- 函数封装与条件控制:将特定功能(如判断环境是否黑暗)封装为函数可提高代码复用性;通过 if-else 条件判断,可根据传感器数据实现设备的自动响应逻辑。
项目目标
通过光敏电阻检测环境光线强度,当环境变暗时自动点亮 LED,模拟智能照明或感应式艺术装置。综合应用以下知识:
- 模拟输入信号的读取(光敏电阻数据采集)
- 分压电路的搭建与原理理解
- 函数封装(将判断逻辑模块化)
- 条件控制语句(根据光线强度控制 LED 状态)
分步指导说明
一、电路搭建(Wokwi 操作)
-
添加元件
- 保留基础 LED 电路:LED(阳极接电阻,电阻另一端接 D13,阴极接 GND)
- 新增元件:搜索并添加 “Photoresistor sensor”(光敏电阻)、“Resistor”(阻值选择 10KΩ)
-
连接分压电路(光敏电阻部分)
- 光敏电阻的 VCC 引脚 → Arduino 的 5V 引脚
- 光敏电阻的 AO 引脚 → 同时连接到 Arduino 的 A1 引脚和 10KΩ 电阻的一端
- 10KΩ 电阻的另一端 → Arduino 的 GND 引脚
- 光敏电阻的 GND 引脚 → Arduino 的 GND 引脚
二、代码编写
-
定义引脚与变量
- 用 const 关键字定义 LED 和光敏电阻的连接引脚(LED_PIN=13,LDR_PIN=A1)
- 定义光线阈值变量(lightThreshold),用于判断环境明暗(初始值可设为 300,后续可调试调整)
-
初始化设置(setup 函数)
- 用 pinMode () 将 LED 引脚设置为输出模式
- 用 Serial.begin (9600) 初始化串口通信,便于输出调试信息
-
封装判断函数
- 定义 isDarkEnvironment 函数,接收光线值参数,返回布尔值(光线值 < 阈值时返回 true,代表环境暗)
-
主循环逻辑(loop 函数)
- 用 analogRead (LDR_PIN) 读取光敏电阻的模拟值(0-1023)
- 调用 isDarkEnvironment 函数判断环境状态,通过 digitalWrite () 控制 LED 亮灭
- 用 Serial.print/println 输出当前光线值和系统状态(如 “LED 已点亮”)
- 用 delay (500) 控制循环间隔,避免信息输出过于频繁
三、运行与调试
- 点击 Wokwi 的 “Start Simulation” 启动模拟
- 调试光线阈值:点击光敏电阻,在属性面板中调整 “illuminance”(光照强度,单位 lux),观察 LED 状态变化,根据实际情况修改 lightThreshold 值
- 打开 “Serial Monitor”,查看光线值和系统响应信息,验证逻辑是否正确
参考代码
cpp
运行
// 定义引脚常量:明确硬件连接关系,便于后期修改
const int LDR_PIN = A1; // 光敏电阻连接到模拟输入引脚A1
const int LED_PIN = 13; // LED连接到数字输出引脚13
// 光线阈值:环境明暗的判断标准,可根据实际场景调整
int lightThreshold = 300;
void setup() {
// 初始化LED引脚为输出模式,使其可被控制亮灭
pinMode(LED_PIN, OUTPUT);
// 初始化串口通信(波特率9600),用于输出调试信息
Serial.begin(9600);
Serial.println("环境光感应装置已启动");
Serial.println("当光线值低于阈值时,LED将自动点亮");
}
// 函数功能:判断当前环境是否黑暗
// 参数:lightValue - 光敏电阻的模拟读数(0-1023)
// 返回值:true表示环境暗,false表示环境亮
bool isDarkEnvironment(int lightValue) {
// 光线越暗,光敏电阻阻值越大,A1引脚的电压越低,读数越小
return lightValue < lightThreshold;
}
void loop() {
// 读取光敏电阻的模拟值(范围0-1023)
int lightValue = analogRead(LDR_PIN);
// 根据环境明暗状态控制LED
if (isDarkEnvironment(lightValue)) {
digitalWrite(LED_PIN, HIGH); // 环境暗,点亮LED
Serial.println("环境光线较暗,LED已自动点亮");
} else {
digitalWrite(LED_PIN, LOW); // 环境亮,熄灭LED
Serial.println("环境光线充足,LED保持熄灭");
}
// 输出当前光线值,方便调试阈值
Serial.print("当前光线值: ");
Serial.println(lightValue);
// 延时500毫秒,降低数据输出频率
delay(500);
}
创意扩展(可选)
将单色 LED 替换为共阴极 RGB LED(搜索 “LED RGB Common Cathode”),实现更丰富的光照响应:
- 用 3 个 PWM 引脚(如 D5、D6、D7)分别连接 RGB 三个通道
- 设计多级阈值(如微暗:光线值 300-500;中等暗:100-300;全黑:<100)
- 根据不同光线范围,通过 analogWrite () 输出不同 PWM 值,控制 RGB 混合出不同颜色(如微暗显示蓝色,中等暗显示紫色,全黑显示红色),模拟不同场景氛围。