闪电编程课——第四课

278 阅读29分钟

Arduino编程入门

闪电编程,助你闪电入门!⚡️⚡️⚡️

本节课的大纲:

  • Arduino介绍及互动装置艺术
  • Arduino中C++编程语言的入门
  • Arduino的核心概念及术语
  • Arduino入门项目练习

本节课学完后能收获什么:

  • 明白Arduino是什么、能做什么
  • 基本掌握第二种编程语言:C++
  • 能够看懂Arduino硬件项目的组成部分
  • 能够动手完成一个Arduino硬件项目的编码和连线

课前准备:

一、Arduino 介绍:从虚拟代码到实体艺术的桥梁

1.1 Arduino是什么?

Arduino是一款开源的电子原型平台,由一个可编程的电路板(硬件)和配套的开发环境(软件)组成。Arduino的设计初衷是让非专业人士也能快速上手,因此它非常适合初学者、艺术家、设计师。 它包含:

  • 一块电路板(最常用的是 Arduino Uno
  • 一个用来编程的软件(Arduino IDE)
  • 一套简单的编程语言(我们今天要学的 C++)。

image.png

与传统编程不同,Arduino能感知物理世界(通过传感器)并与之互动(通过灯光、电机等)。

就像艺术家的"电子物理画笔"——通过编写Arduino代码,可以让LED灯跳舞、让雕塑对触摸做出反应,甚至创造会"呼吸"的灯光装置(Arduino for Artists)。

1.2 为什么艺术家需要Arduino?

我们先来看学校的课程安排:

  • UAL-cc:第一学期的两门必修课,一门是p5.js,另一门就是Arduino(Physical )。
  • 金匠-ca:第一学期必修课是p5.js,重要选修课是Arduino。

对于艺术生来说,Arduino是创作互动装置艺术的理想工具。

传统的装置艺术是静态的,观众只能被动观赏。而加入了Arduino的装置艺术变成了活的、会呼吸的艺术品:

  1. 互动性:作品能够感知观众的存在,并做出实时响应
  2. 动态性:光线、声音、运动可以随时间变化
  3. 沉浸感:观众成为作品的一部分,而不仅仅是旁观者

比如以下的互动装置艺术案例:

  • 案例1:一个由数百个LED灯组成的雕塑,当有人靠近时,灯光像流水一样避开,形成互动的光流。
  • 案例2:一排小风扇,根据实时网络上的情绪数据(比如“快乐”关键词的数量)改变转速,将风吹动悬挂的丝带,形成“数据风铃”。
  • 案例3:一个机械臂握着画笔,根据传感器采集的环境声音大小,在画布上画出不同力度和长度的线条。

许多震撼的当代艺术作品都运用了Arduino技术:

image.png

image.png

image.png

1.3 动手练习:认识Wokwi模拟器

由于大家手头没有物理设备(Arduino开发板),我们本节课使用Wokwi模拟器进行教学。Wokwi是一个在线、免费、无需安装且支持多种板卡的Arduino模拟器。

操作步骤:

  1. 访问Wokwi模拟器环境 New Arduino Uno Project - Wokwi Simulator
  2. 尝试在左侧代码编辑区粘贴以下代码:
void setup() {
  Seria1.begin(9680);//初始化串口通信,设置波特率为9688
}

void loop() {
    Serial.println("欢迎来到创意编程与耳动装置设计课程!");
    delay(2000);//暂停2800毫秒(即2秒),控制输出频率
}

  1. 点击播放键"Start Simulation",观察界面变化和输出。

image.png

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. 你的姓名或艺名
  2. 一句描述你艺术追求或风格的短句
  3. 一个代表你当前创作状态的词语(如“探索中”、“爆发期”、“沉淀期”)

要求每行输出之间间隔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常用数据类型详解:

  1. int(整数) : -32,768 到 32,767

    • 用途:计数、引脚编号、简单数值
  2. long(长整数) : 约-21亿 到 21亿

    • 用途:时间计算(毫秒)、大数值
  3. float(浮点数) : 小数,如3.14

    • 用途:温度、距离、角度等需要小数的场合
  4. boolean(布尔) : true 或 false

    • 用途:开关状态、条件判断
  5. char(字符) : 单个字符,如'A'

    • 用途:按键输入、简单标识
  6. 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项目通常包括以下几个部分:

  1. Arduino板:这是项目的核心,负责执行代码并控制外部设备。Arduino板通常包含一个微控制器、输入/输出引脚、电源接口以及用于编程的USB接口。
  2. 传感器(输入):用于检测环境中的变化,如温度、光线、运动等。
  3. 执行器(输出):如LED、电机、蜂鸣器等,用于执行具体的动作。
  4. 连接线:用于将传感器和执行器连接到Arduino板上。

我们要学习的是 Arduino UNO板,这是 Arduino系列中最受欢迎、使用最多的开发板。

image.png

认识Arduino-Uno开发板:

实体Arduino-uno开发板的接口说明:

image.png

下图为Wokwi所模拟的Arduino-Uno板子:

image.png

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程序的结构

image.png

Arduino程序有两个主要部分:setup()loop()。这是每个Arduino程序都必须包含的基本结构。

  • setup()函数在程序开始时只运行一次,通常用于初始化设置。
  • loop()函数在setup()执行完后会不断重复执行,直到断电。

类比: P5.js的模版代码,setup()和Draw()

示例-wokwi模拟器操作

Wokwi - Online ESP32, STM32, Arduino Simulator

运行程序:(现实中相当于把程序烧录到板子上)

image.png

添加元件:

image.png

image.png

连线: 点击元件两端,按esc取消。

image.png


第一个Arduino项目:点亮LED灯

让我们从一个简单的项目开始:点亮一个LED。理解Arduino的基本工作原理。(所有同学进行一次全流程操作。)

所需材料

  • Arduino Uno板
  • 1个LED
  • 1个220欧姆电阻(Register)
  • 连接线

电路连接

image.png

  1. 将LED的长脚(正极)连接到Arduino的数字引脚13。
  2. 将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秒
}

练习

  1. 修改上面的代码,使LED以不同的时间间隔闪烁。
  2. 尝试添加第二个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操作步骤
  1. 添加元件

    • 在项目一的基础上,点击左侧“+”号,搜索并添加“Pushbutton”元件
  2. 电路连接(使用内置上拉电阻方案,电路最简):

    • 将按钮的一个引脚连接到Arduino的数字引脚“D2”
    • 将按钮的另一个引脚连接到Arduino的“GND”引脚
    • (无需像LED一样外接电阻,代码中启用内置上拉电阻)

image.png

  1. 代码编写
// 定义引脚常量
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);
}
  1. 运行模拟
    • 点击“Start Simulation”按钮
    • 点击画布上的按钮,观察LED是否在按下时点亮,松开时熄灭
    • 打开“Serial Monitor”,查看按钮状态变化时的调试信息

扩展挑战

在代码中添加一个全局变量 int pressCount = 0;,用于记录按钮被按下的总次数。每次检测到按钮从“未按下”变为“按下”的状态变化时(即上升沿或下降沿检测),将计数器加1,并通过串口输出“第X次按钮按下”。

提示:需要记录前一次按钮状态,与当前状态比较以检测变化。


3.4 项目三:调光控制台灯 —— 学习模拟输入与PWM输出

项目目标

通过旋转电位器(旋钮)无级调节LED亮度,理解模拟信号读取(analogRead)、脉宽调制输出(analogWrite)、数值映射(map函数)的概念,以及PWM(脉冲宽度调制)技术在模拟亮度控制中的应用。

Wokwi操作步骤
  1. 添加元件

    • 保留项目二中的LED和电阻(但需更改连接引脚),去掉按钮
    • 点击左侧“+”号,搜索并添加“Potentiometer”元件(电位器,类似台灯旋钮)
  2. 电路连接

    • 将电位器的左侧引脚连接到Arduino的“5V”引脚
    • 将电位器的右侧引脚连接到Arduino的“GND”引脚
    • 将电位器的中间引脚(滑动端)连接到Arduino的模拟输入引脚“A0”
    • 将LED的阳极(通过330Ω电阻)连接到Arduino的数字引脚“D9”(注意:D9是支持PWM的引脚,标记有~符号)
    • 将LED的阴极连接到“GND”引脚

image.png

  1. 代码编写
// 定义引脚常量
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);
}
  1. 运行模拟
    • 点击“Start Simulation”按钮
    • 旋转画布上的电位器旋钮,观察LED亮度是否随之平滑变化
    • 打开“Serial Monitor”,查看电位器值和亮度值的实时变化
技术原理说明——模拟信号

在Arduino控制器中,编号前带有“A”的引脚被专门设计为模拟输入引脚。这些引脚能够被Arduino读取,以获取其上的模拟值,也就是读取其上的电压大小。

模拟输入功能拥有10位精度,意味着它可以将0至5伏的电压范围转换为0至1023的整数表示。而要读取模拟值,我们通常使用analogRead(pin)函数,例如analogRead(A0)即表示读取A0引脚上的模拟值。

与模拟输入相对的是模拟输出功能,它通过analogWrite()函数实现。尽管这个函数名为“写模拟值”,但它实际上并非直接输出模拟信号,而是利用PWM(脉冲宽度调制)技术来模拟输出不同的电压。

image.png

  • 模拟输入: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创意编程的基础知识和实践技能:

  1. C++语言基础:理解了库、变量类型、数组、函数、判断、循环等核心语法概念,并通过独立于硬件的纯代码练习巩固了这些知识,为后续硬件编程打下了坚实的语法基础。
  2. Arduino核心概念:熟悉了Arduino程序的基本结构(setup和loop),理解了数字信号与模拟信号的区别,掌握了输入设备与输出设备的连接与控制方法,了解了电压、电流、电阻等基础电路概念在实际项目中的应用。
  3. Wokwi模拟器操作:能够熟练使用Wokwi在线平台进行电路搭建、代码编写、模拟运行和调试输出,具备了在无实体硬件条件下进行原型设计和验证的能力。
  4. 项目实践经验:通过四个由浅入深的实战项目(基础闪烁、按钮控制、旋钮调光、光感控制),学生将理论知识应用于实际问题解决,培养了计算思维和工程实践能力。

附加资源

作业:环境光感应装置 —— 综合应用模拟输入与条件控制

背景知识
  1. 光敏电阻(LDR) :一种对光线敏感的电阻元件,其阻值会随光照强度变化 —— 光照越强,阻值越小;光照越弱,阻值越大。通过检测其阻值变化可间接获取环境光强度。
  2. 分压电路:由于 Arduino 无法直接测量电阻值,需通过分压电路将电阻变化转换为电压变化。本项目中,光敏电阻与 10KΩ 固定电阻串联,接在 5V 电源与地之间,中间节点连接到模拟输入引脚,通过读取该节点电压(转换为 0-1023 的数字值)反映光照强度。
  3. 模拟输入与数字输出:Arduino 的模拟输入引脚(如 A1)可将外部电压(0-5V)转换为 0-1023 的数字信号;数字输出引脚(如 D13)可输出高电平(HIGH,约 5V)或低电平(LOW,约 0V),用于控制 LED 等元件的开关。
  4. 函数封装与条件控制:将特定功能(如判断环境是否黑暗)封装为函数可提高代码复用性;通过 if-else 条件判断,可根据传感器数据实现设备的自动响应逻辑。
项目目标

通过光敏电阻检测环境光线强度,当环境变暗时自动点亮 LED,模拟智能照明或感应式艺术装置。综合应用以下知识:

  • 模拟输入信号的读取(光敏电阻数据采集)
  • 分压电路的搭建与原理理解
  • 函数封装(将判断逻辑模块化)
  • 条件控制语句(根据光线强度控制 LED 状态)
分步指导说明
一、电路搭建(Wokwi 操作)
  1. 添加元件

    • 保留基础 LED 电路:LED(阳极接电阻,电阻另一端接 D13,阴极接 GND)
    • 新增元件:搜索并添加 “Photoresistor sensor”(光敏电阻)、“Resistor”(阻值选择 10KΩ)
  2. 连接分压电路(光敏电阻部分)

    • 光敏电阻的 VCC 引脚 → Arduino 的 5V 引脚
    • 光敏电阻的 AO 引脚 → 同时连接到 Arduino 的 A1 引脚和 10KΩ 电阻的一端
    • 10KΩ 电阻的另一端 → Arduino 的 GND 引脚
    • 光敏电阻的 GND 引脚 → Arduino 的 GND 引脚
二、代码编写
  1. 定义引脚与变量

    • 用 const 关键字定义 LED 和光敏电阻的连接引脚(LED_PIN=13,LDR_PIN=A1)
    • 定义光线阈值变量(lightThreshold),用于判断环境明暗(初始值可设为 300,后续可调试调整)
  2. 初始化设置(setup 函数)

    • 用 pinMode () 将 LED 引脚设置为输出模式
    • 用 Serial.begin (9600) 初始化串口通信,便于输出调试信息
  3. 封装判断函数

    • 定义 isDarkEnvironment 函数,接收光线值参数,返回布尔值(光线值 < 阈值时返回 true,代表环境暗)
  4. 主循环逻辑(loop 函数)

    • 用 analogRead (LDR_PIN) 读取光敏电阻的模拟值(0-1023)
    • 调用 isDarkEnvironment 函数判断环境状态,通过 digitalWrite () 控制 LED 亮灭
    • 用 Serial.print/println 输出当前光线值和系统状态(如 “LED 已点亮”)
    • 用 delay (500) 控制循环间隔,避免信息输出过于频繁
三、运行与调试
  1. 点击 Wokwi 的 “Start Simulation” 启动模拟
  2. 调试光线阈值:点击光敏电阻,在属性面板中调整 “illuminance”(光照强度,单位 lux),观察 LED 状态变化,根据实际情况修改 lightThreshold 值
  3. 打开 “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 混合出不同颜色(如微暗显示蓝色,中等暗显示紫色,全黑显示红色),模拟不同场景氛围。