
# Arduino_C_教程

---

## 1. Arduino IDE 的下载、安装和使用方法

参考链接：[https://www.keyesrobot.cn/projects/Arduino/zh-cn/latest/](https://www.keyesrobot.cn/projects/Arduino/zh-cn/latest/)

<span style="color: rgb(255, 76, 65);">注意：</span><span style="background:#ff0;color:#000">本教程使用的是Arduino IDE版本：2.1.1 。</span>

---

## 2. 添加 Arduino 库文件

打开Arduino IDE，点击“**<u>项目</u>**”，选择“**<u>导入库</u>**”，最后选择“**<u>添加.ZIP库...</u>**”。

![](media/ABCDE11.png)

根据库文件的路径打开本项目教程所用的库文件夹，选中库文件夹中 .zip格式的库文件压缩包（以“**Arduino_TFT_display**”库文件压缩包为例），然后单击“**打开**”，这样Arduino_TFT_display库文件成功加入。

![Img](./media/ABCDE12.png)


然后参照上述方法依次将剩余的库文件（红色框中的）全部添加完。

![Img](./media/ABCDE13.png)

---

## 3. 单个传感器/模块项目课程

上面操作完后，打开套件工具包，我们可以看到套件中有很多传感器/模块，有主板和连接线等。这里，我们将多种传感器/模块利用自带连接线单独连接在主板。然后上传对应的实验代码，单独测试各个传感器/模块的功能。我们下面的课程是先从简单到复杂学习单个模块/传感器的原理，后面再学习一些模块/传感器的扩展应用以巩固加深我们对该套件的理解。

**<span style="color: rgb(255, 76, 65);">特别注意：</span>** 实验时，模块/传感器连接线材时，必须按照教程里的接线方法及位置，电源与信号脚不能错接，否则会损坏模块/传感器。


### 第01课 Hello World

**1. 项目介绍**

对于Arduino的初学者，先从一些简单的开始学习吧！在这个项目中，你只需要一个主板，USB线和计算机就可以完成“Hello World!”项目。它不仅是主板和计算机的通信测试，也是Arduino的初级项目。这也是一个入门实验，让你进入计算机的编程世界。

---

**2. 实验组件**

| ![img](media/KS5016.png) | ![img](media/USB.jpg) |
| :----------------------: | :-------------------: |
|主板 x1|       USB线 x1        |

---

**3. 模块接线图**

![img](media/011301.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''<u>**HelloWorld.ino**</u>"。

```c++
/*
 * 名称   : Hello World
 * 功能   : 输入字母“R”，串口显示“Hello World”。
 * 作者   : http://www.keyes-robot.com/ 
*/
char val;   // 定义变量val 

void setup(){
Serial.begin(9600);   // 设置波特率为9600
}

void loop(){
  if (Serial.available() > 0) {
    val=Serial.read();   // 读取指令或字符从PC到Arduino，并赋值给"val"的值
    if(val=='R') {   // 检查输入的字母是否为“R”,如果是
       Serial.println("Hello World!");  //显示“Hello World !”
    }
  }
}
```

将主板通过USB线连接到计算机后，开始上传代码。为了避免将代码上传至主板时出现错误，必须选择与计算机连接正确的板型Arduino Uno和串口端口。

先点击“**<u>工具</u>**”→“**<u>开发板</u>**”，可以查看到Arduino AVR Boards板型，选择对应的主板型号Arduino Uno。

![img](media/011401.png)

再点击“<u>**工具**</u>”→“**<u>端口</u>**”，选择对应的串口端口。

**<span style="background:#ff0;color:#000">
注意：将主板通过USB线连接到计算机后才能看到对应的串口端口。 </span>**

![img](media/0114011.png)

单击![img](media/wps17.jpg)将代码上传到你所选定的主板上。

![img](media/011402.png)

代码上传成功。

![img](media/011403.png)

---

**5. 实验结果**

代码上传成功后，单击串口监视器图标![img](media/06.jpg)进入串口监视器，设置波特率为 **<u>9600</u>**，接着在文本框输入字母“**<u>R</u>**”，按下回车键(Enter 键)，能看到串口监视器打印“**<u>Hello World!</u>**”。

![011501](media/011501.png)

---

**6. 代码说明**

| 代码                | 说明                                                         |
| ------------------- | ------------------------------------------------------------ |
| char val            | 定义一个变量val                                              |
| Serial.begin(9600)  | 设置波特率为9600                                             |
| Serial.available( ) | 获取串口上可读取的数据的字节数，该数据已经到达并存储在接收缓存（共有64字节）中。Serial.available() > 0表示串口接收到了数据，可以读取。 |
| Serial.read( )      | 读取写入的串行数据。                                         |
| if( ){ }            | 如果“（ ）”里的条件满足，则执行“{ }”里的程序。               |
| Serial.println( )   | 换行输出数据。从串行端口输出数据，跟随一个回车和一个换行符。 |

--- 

### 第02课 交通灯

**1. 项目介绍**

交通灯，也就是马路上十字路口的红绿灯，在我们的日常生活中很常见。交通灯是由红、黄、绿三种颜色组成的，根据一定的时间规律循环交替亮起或熄灭。每个人都应该遵守交通规则，这可以避免许多交通事故。

想学习交通灯的原理吗？我们可以用红、黄、绿3个LED外接电路来模拟马路上的交通灯。因此我们特别设计了这款交通灯模块，模块上的红、黄、绿3个LED灯模拟交通灯。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流：40mA

最大功率：0.2W

工作温度：-10°C ~ +50°C

控制信号：数字信号

尺寸：47.6mm x 23.8mm x 18mm

接口：2.54mm间距，5pin防反接口

---

**3. 模块原理图**

![031301](media/031301.png)

由原理图可以得知，控制这个模块就好比分别控制3个独立的LED灯(我们这个灯可直接由单片机IO口驱动)，给对应颜色LED灯高电平就亮起对应的颜色。比如，我们给信号“R”输出高电平，也就是5V，则红色LED点亮。

---

**4. 实验组件**

| ![img](media/KS5016.png) | ![img](media/KE4008.png) | ![img](media/5pin.png)       | ![img](media/USB.jpg) |
| ------------------------ | ------------------------ | ---------------------------- | ---------------------------- | 
| 主板 x1      | 交通灯模块 x1     | 5P线(反向) x1 | USB线 x1             |

---

**5. 模块接线图**

![img](media/031501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Traffic_Light.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*
 * 名称   : Traffic_Light
 * 功能   : 模拟交通灯
 * 作者   : http://www.keyes-robot.com/ 
*/
int redPin = A1;      // 红色LED连接A1
int yellowPin = A0;   // 黄色LED连接到A0
int greenPin = 7;    // 绿色LED连接D7

void setup() {
  //LED接口设置为输出模式
  pinMode(greenPin, OUTPUT);
  pinMode(yellowPin, OUTPUT);
  pinMode(redPin, OUTPUT);
}

void loop() {
  digitalWrite(greenPin, HIGH);   //点亮绿色LED
  delay(5000);   //延迟5秒
  digitalWrite(greenPin, LOW);   //关闭绿色LED
  for (int i = 1; i <= 3; i = i + 1) {    //循环三次
    digitalWrite(yellowPin, HIGH);   //点亮黄色LED
    delay(500);   //延迟0.5秒
    digitalWrite(yellowPin, LOW);   //关闭黄色LED
    delay(500);   //延迟0.5秒
  }
  digitalWrite(redPin, HIGH);   //点亮红色LED
  delay(5000);  //延迟5秒
  digitalWrite(redPin, LOW);   //关闭红色LED
}
```

将主板通过USB线连接到计算机后，开始上传代码。为了避免将代码上传至主板时出现错误，必须选择与计算机连接正确的板型Arduino Uno和串口端口。

先点击“**<u>工具</u>**”→“**<u>开发板</u>**”，可以查看到Arduino AVR Boards板型，选择对应的主板型号Arduino Uno。

再点击“<u>**工具**</u>”→“**<u>端口</u>**”，选择对应的串口端口。

**<span style="background:#ff0;color:#000">
注意：将主板通过USB线连接到计算机后才能看到对应的串口端口。 </span>**

单击![img](media/wps17.jpg)将代码上传到主板上，等待代码上传成功后查看实验结果。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，你会看到模块上绿色LED亮5秒然后熄灭，黄色LED闪烁3秒然后熄灭，红色LED亮5秒然后熄灭。模块按此顺序循环亮灭。

![img](media/IMG_145218.png)

![img](media/IMG_145240.png)

![img](media/IMG_145248.png)

---

**8. 代码说明**

| 代码                       | 说明                                                       |
| -------------------------- | ---------------------------------------------------------- |
| pinMode(ledPin, OUTPUT)    | 设置引脚的模式。OUTPUT为输出模式，INPUT为输入模式 。         |
| digitalWrite(ledPin, HIGH) | 设置引脚的输出电压为高\低电平。HIGH为高电平，LOW为低电平。 |
| delay(1000)                | 将程序的执行暂停一段时间，也就是延时。单位是毫秒。          |

---

### 第03课 发出激光

**1. 项目介绍**

激光模块上的激光头发出的激光与常见的光不同。一方面，激光的单色性好。另一方面，激光发射器内部特定的结构，使得激光能够被聚集成单束光，朝着同一方向射出，亮度高，方向性好。

正是由于这些特性，激光被广泛用于对特定材料进行切割、焊接、表面处理等等。激光的能量非常高，玩具激光笔照射人眼可能导致眩光，长时间可能导致视网膜损害，我国也禁止用激光照射航行的飞机。因此，**请注意不要用激光发射器对准人眼。**

---

**2. 模块参数**

工作电压：DC 3.3V~5V

工作电流：21mA

最大功率：0.105W

控制信号：数字信号

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 10mm

接口：间距为2.54 mm 3pin防反接口

---

**3. 模块原理图**

![](media/041301.png)

激光头模块主要由激光头组成，激光头由发光管芯、聚光透镜、铜可调套筒三部分组成。

从激光模块的电路原理图我们可以知道，它是用三极管驱动的。激光头的 1 脚始终上拉到VCC，在信号端 S 处输入一个高电平数字信号，NPN三极管Q1导通，激光头的 2 脚被下拉到GND，此时传感器开始工作。在信号端 S 处输入低电平时NPN三极管Q1不导通，传感器停止工作。

---

**4. 实验组件**

| ![img](media/KS5016.png) |![img](media/KE4043.png) |![img](media/3pin.png)       | ![img](media/USB.jpg) | 
| ------------------------ | ------------------------ | ---------------------------- | ---------------------------- | 
|主板 x1 | 激光模块 x1    | 3P线(反向) x1 | USB线 x1   |
 
 

---

**5. 模块接线图**

![](media/041501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Laser.ino**"。

**注意：为了避免上传代码不成功，请不要在上传代码前连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*
 * 名称   : Laser sensor
 * 功能   : 激光灯闪光
 * 作者   : http://www.keyes-robot.com/ 
*/
int laserPin = 3;                //定义激光引脚为D3
void setup() {
  pinMode(laserPin, OUTPUT);     //将激光引脚定义为输出模式
}

void loop() {
  digitalWrite(laserPin, HIGH);     //打开激光
  delay(2000);                      //延迟2秒
  digitalWrite(laserPin, LOW);      //关闭激光
  delay(2000);                      //延迟2秒
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，能看到模块上激光管发射红色激光信号2秒，然后关闭发射2秒，循环交替。

![img](media/IMG_150024.png)

---

**8. 代码说明**

可以参照第02课的代码说明，这里就不多做介绍了。

---

### 第04课 自锁按键

**1. 项目介绍**

自锁按键模块主要是由一个自锁按键组成，上电后，按下白色按键，模块信号端输出低电平，将白色按键按起，模块信号端输出高电平。第02课我们学习了怎么让单片机的引脚输出一个高电平或者低电平，这节课程我们就来学习怎么读取引脚的电平。

自锁按键模块的白色按键按下，单片机读取到低电平，将白色按键按起读取到高电平。通过读取传感器上S端的高低电平，判断自锁按键是按下还是按起，并且在串口监视器上显示测试结果。

---

**2. 模块参数**

工作电压：DC 3.3V~5V 

工作温度：-10°C ~ +50°C

控制信号：数字信号

尺寸：32mm x 23.8mm x 15.6mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/071301.png)

按键有四个引脚，其中1和3是相连的，2和4是相连的，在我们未按下按键时，1、3与2、4是断开的，信号端S读取的是被1KΩ的上拉电阻R1所拉高的高电平，而当我们按下按键时，1、3和2、4连通。信号端S连接到了GND，此时读取到的电平为低电平，即按下按键，传感器信号端为低电平，模块上的LED灯亮起；按键按起时，信号端为高电平，模块上的LED灯灭。

---

**4. 实验组件**

| ![img](media/KS5016.png) | ![img](media/KE4045.png) |![img](media/3pin.png)       | ![img](media/USB.jpg) | 
| ------------------------ | ------------------------ | ---------------------------- |---------------------------- |
| 主板 x1  | 自锁按键模块 x1  | 3P线(反向) x1 | USB线 x1 |

  

---

**5. 模块接线图**

![img](media/071501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Self-locking_button.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*
 * 名称   : Self-locking button
 * 功能   : 读自锁按键的值
 * 作者   : http://www.keyes-robot.com/ 
*/
int val = 0;            //用于存储自锁按键的键值
int button = 3;         //将自锁按键的引脚连接到D3
void setup() {
  Serial.begin(9600);      //启动串口监视器，设置波特率为9600
  pinMode(button, INPUT);  //设置自锁按键的引脚为输入模式
}

void loop() {
  val = digitalRead(button);  //读取自锁按键的值并将其赋值给变量val
  Serial.print(val);          //打印变量vald的值
  if (val == 0) {             //按下自锁按键时读取到低电平，并打印出相关的信息
    Serial.print("        ");  
    Serial.println("Press the botton"); //打印自锁按键的释放信息
    delay(100);
  }

  else {               //按起自锁按键
    Serial.print("        ");
    Serial.println("Press the botton again");
    delay(100);
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

当按下模块上的白色按键时，按键值value为0，串口监视器打印出“**0     Press the button**”；再次按下白色按键（即白色按键被按起）时，按键值value为1，串口监视器打印出“**1   Press the botton again**”字符。

![img](media/IMG_151225.png)

![img](media/IMG_151244.png)

![img](media/071701.png)

---

**8. 代码说明**

| 代码                   | 说明                                                         |
| ---------------------- | ------------------------------------------------------------ |
| pinMode(button, INPUT) | 由“ int button = 3; ”知道，定义自锁按键管脚为D3。“INPUT”设置为输入模式。通过pinMode()配置为INPUT必须使用上拉或下拉电阻（我们的模块已经使用上拉电阻R1）。该电阻的目的是在开关断开时将引脚拉至已知状态。通常选择一个4.7K/10K欧姆的电阻，因为它的阻值足够低，可以可靠地防止输入悬空。同时，该阻值也要足够高，以使开关闭合时不会消耗太多电流。如果使用下拉电阻，则当开关断开时，输入引脚将为低电平；当开关闭合时，输入引脚将为高电平。如果使用上拉电阻，则当开关断开时，输入引脚将为高电平；当开关闭合时，输入引脚将为低电平。 |
| if( ){ } else{ }       | 如果（ ）里的表达式为真，则执行 if { }块内的代码。如果（ ）里表达式为假 ，则执行 else { }块内的代码。 |
| digitalRead(button)    | 读取按键的数字电平，高HIGH或者低LOW。如果该引脚未连接任何东西，则digitalRead( )可以返回HIGH或LOW（并且可以随机更改）。主板上的输入引脚A0、A1、A2、A3、A4、A5可以用作数字引脚，分别对应数字引脚D14、D15、D16、D17、D18、D19。|
| Serial.begin(9600)     | 初始化串口通信，并设置波特率为9600。                         |

---

### 第05课 电容触摸传感器

**1. 项目介绍**

电容触摸模块主要由1个触摸检测芯片 TTP223-BA6 构成。模块上提供一个触摸按键，功能是用可变面积的按键取代传统按键。当我们上电之后，传感器需要约0.5秒的稳定时间，此时间段内不要触摸按键，此时所有功能都被禁止，始终进行自校准，校准周期约为4秒。

---

**2. 模块参数**

工作电压：DC 3.3V~5V

工作电流：3mA

最大功率：0.015W

输出信号：数字信号

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 9mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![](media/081301.png)

TTP223N-BA6 的输出通过 AHLB（4）引脚选择高电平或低电平有效。通过 TOG（6）引脚选择直接模式或触发模式。

| TOG  | AHLB | 引脚Q的功能           |
| ---- | ---- | --------------------- |
| 0    | 0    | 直接模式，高电平有效  |
| 0    | 1    | 直接模式，低电平有效  |
| 1    | 0    | 触发模式，上电状态为0 |
| 1    | 1    | 触发模式，上电状态为1 |

从原理图我们可以知道 TOG 脚和 AHLB 脚是悬空的，此时输出为直接模式，高电平有效。

当我们用手指触摸模块上的感应区时，信号端 S 输出高电平（上一课学习的自锁按键模块与之相反，当自锁按键感应到按下输出低电平），板载红色LED点亮，我们通过读取模块上 S 端的高低电平，判断电容触摸模块上的感应区是否感应到触摸。

<span style="color: rgb(255, 76, 65);">注意：</span>我们上电之后，传感器需要约0.5sec的稳定时间，此时间段内不要对键进行触摸，此时所有功能都被禁止，始终进行自校准，当键没被触摸时，重校准周期约为4.0sec。

---

**4. 实验组件**

| ![img](media/KS5016.png) |![img](media/KE4013.png) |![img](media/3pin.png) | ![img](media/USB.jpg) |
| ------------------------ | ------------------------ | ---------------------------- |---------------------------- |
|主板 x1|  电容触摸传感器 x1 | 3P线(反向) x1 | USB线 x1 |
 

---

**5. 模块接线图**

![](media/081501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Touch_sensor.ino**"。

**注意：为了避免上传代码不成功，请不要在上传代码前连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*
 * 名称   : Touch sensor
 * 功能   : 读取电容触摸传感器的值
 * 作者   : http://www.keyes-robot.com/ 
*/
int val = 0;
int touch = 3;            //定义触摸引脚D3 
void setup() {
  Serial.begin(9600);     //波特率为9600
  pinMode(touch, INPUT);  //设置触摸引脚为输入模式
}

void loop() {
  val = digitalRead(touch); //读取触摸引脚的值
  Serial.print(val);        //打印触摸引脚的值
  if (val == 1) {  //按下为高电平
    Serial.print("        ");
    Serial.println("Touch the button");
    delay(100);
  }
  else {          //释放为低电平
    Serial.print("        ");
    Serial.println("Loosen the button");
    delay(100);
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

![](media/135790.png)

当电容触摸传感器模块上的感应区感应到触摸时，板载红色LED点亮，value 值为 1，串口监视器打印出“**1  Touch the button**”；当没有感应到触摸时，板载红色LED熄灭，value 值为 0，串口监视器打印出“**0 Loosen the button**”。

![](media/IMG_151728.png)

![](media/IMG_151742.png)

![](media/081703.png)

---

**8. 代码说明**

可以参照第04课的代码说明，这里就不多做介绍了。

---

### 第06课 检测障碍物

**1. 项目介绍**

避障传感器主要由一对红外线发射与接收管元件组成。实验中，我们通过读取传感器上S端高低电平，判断是否存在障碍物。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 6.8mA

最大功率: 0.034W

输出信号: 数字信号

最大感应距离: 17.3cm

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 11mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/091301.jpg)

NE555时基电路提供给发射管TX发射出一定频率的红外信号，红外信号会随着传送距离的加大逐渐衰减，如果遇到障碍物，就会形成红外反射。当检测方向RX遇到反射回来的信号比较弱时，接收检测引脚输出高电平，说明障碍物比较远；当反射回来的信号比较强，接收检测引脚输出低电平，说明障碍物比较近，此时指示灯亮起。传感器上有两个电位器，一个用于调节发送功率，一个用于调节接收频率，通过调节两个电位器，我们可以调节它的有效距离。

---

**4. 实验组件**

| ![img](media/KS5016.png) |![img](media/KE4019.png) | ![img](media/3pin.png) | 
| ------------------------ | ------------------------ | ---------------------------- |
|主板 x1|  避障传感器 x1 | 3P线(反向) x1 |
|![img](media/USB.jpg) | ![img](media/ABC13.png)|     |
| USB线 x1| 一字螺丝刀 x1 |    |

---

**5. 模块接线图**

![img](media/091501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**obstacle_avoidance_sensor.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*
 * 名称   : Obstacle avoidance sensor
 * 功能   : 读取避障传感器的值
 * 作者   : http://www.keyes-robot.com/ 
*/
int val = 0;
void setup() {
  Serial.begin(9600);   //设置波特率为9600
  pinMode(3, INPUT);    //设置引脚D3为输入模式
}

void loop() {
  val = digitalRead(3);  //读取数字电平
  Serial.print(val);     //打印读取的电平信号
  if (val == 0) {  //障碍物检测
    Serial.print("        ");
    Serial.println("There are obstacles");
    delay(100);
  }
  else {  //未发现障碍物
    Serial.print("        ");
    Serial.println("All going well");
    delay(100);
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，接着开始调节传感器模块上的两个电位器感应距离。避障传感器上有两个电位器，分别是接收频率调节电位器和发射功率调节电位器，如下图所示。

![img](media/091701.jpg)

先调节发射功率调节电位器，先用一字螺丝刀将电位器顺时针拧到尽头，然后逆时针慢慢往回调，当调节到SLED灯亮起时，微调使传感器上SLED灯介于亮与不亮之间的**不亮**状态。

接着设置接收频率调节电位器，同样用一字螺丝刀将电位器顺时针拧到尽头，然后逆时针慢慢往回调，当SLED灯亮起时，微调使传感器上SLED灯介于亮与不亮之间的**不亮**状态，此时能检测障碍物的距离最长。

打开串口监视器，设置波特率为 **<u>9600</u>**。当传感器检测到障碍物时，value 值为 **0**，SLED 灯亮，串口监视器打印出 “**0   There are obstacles**” ；没有检测到障碍物时，value 值为 **1**，SLED 灯灭，串口监视器打印出 “**1   All going well**” 。

![img](media/IMG_151916.png)

![img](media/IMG_151943.png)

![img](media/091702.png)

---

**8. 代码说明**

可以参照第04课的代码说明，这里就不多做介绍了。

---

### 第07课 光折断计数

**1. 项目介绍**

光折断模块主要由 1 个 ITR-9608 光电开关组成，它属于对射光电开关传感器。

在本实验中，我们通过设置代码，模拟出流水线上利用类似传感器实现对产品进行计数的功能。

---

**2. 模块参数**

工作电压：DC 3.3V~5V

工作电流：24mA

最大功率：0.12W

输出信号：数字信号

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 13mm

接口：2.54 mm间距，3pin防反接口

---

**3. 模块原理图**

![](media/111301.jpg)

光折断模块上的光电开关是利用被检测物体对光束的遮挡或反射，由同步回路选通电路，从而检测遮挡物体的有无。所有能反射光线的物体都可以被检测。光电开关将输入的电流在发射器上转换为光信号并射出，然后接收器根据接收到的光线强弱或有无，对目标物体进行检测。

当用不透明物体放置在传感器凹槽时，C 脚与 VCC 连通，传感器信号端 S 为高电平，自带红色 LED熄灭；传感器凹槽没有任何东西时，传感器信号端被 R2 拉低为低电平，自带红色LED亮起。

---

**4. 实验组件**

| ![](media/KS5016.png) | ![img](media/KE4014.png) |![img](media/3pin.png) | ![img](media/USB.jpg) | 
| --------------------- | ------------------------ | ---------------------------- |---------------------------- |
|主板 x1     |  光折断模块 x1 | 3P线(反向) x1 | USB线 x1  |     



---

**5. 模块接线图**

![](media/111501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Photo_Interrupt.ino**"。

**注意：为了避免上传代码不成功，请不要在上传代码前连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*
 * 名称   : Photo_Interrupt
 * 功能   : 光折断传感器计数
 * 作者   : http://www.keyes-robot.com/ 
*/
int PushCounter = 0;  //count变量的初始值为0
int State = 0;        //存储传感器当前的输出状态
int lastState = 0;    //存储传感器最后的输出状态
void setup() {
  Serial.begin(9600); //设置波特率为9600
  pinMode(3, INPUT);  //将光折断传感器引脚D3设置为输入模式
}

void loop() {
  State = digitalRead(3);   //读取当前状态
  if (State != lastState) { //如果状态与上次读取的不同
    if (State == 1) {       //遮挡光线时
      PushCounter = PushCounter + 1; //计数 + 1
      Serial.println(PushCounter);   //打印计数 
    }
  }
  lastState = State;  //更新状态
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

串口监视器打印出 PushCounter 的数据，物体每穿过传感器凹槽一次，PushCounter 数据加 1。

![](media/IMG_151445.png)

![](media/IMG_151549.png)

![](media/111703.png)

---

**8. 代码说明**

通过以下表格，我们可以了解此课程代码的逻辑设置，这个编程技巧我们在后面还会用到。

| 初始状态                              |         |
| :------------------------------------ | :------ |
| State（传感信号端数值）               | 设置为0 |
| lastState（传感器信号端上一循环数值） | 设置为0 |
| PushCounter（累计通过物体数目）       | 设置为0 |

| 状态                       |                                                              |                                                  |
| -------------------------- | ------------------------------------------------------------ | ------------------------------------------------ |
| 当物体开始穿过传感器凹槽时 | lastState为0，State检测到变为1，两个数据不相等，lastState变为1。 | PushCounter设置为PushCounter加1打印PushCounter值 |
| 当物体离开传感器凹槽时     | lastState为1，State检测到变为0，两个数据不相等，lastState变为0。 | PushCounter不变不打印PushCounter值               |
| 当物体再次穿过传感器凹槽时 | lastState为0，State检测到变为1，两个数据不相等，lastState变为1。 | PushCounter设置为PushCounter加1打印PushCounter值 |
| 当物体再次离开传感器凹槽时 | lastState为1，State检测到变为0，两个数据不相等，lastState变为0。 | PushCounter不变不打印PushCounter值               |


---

### 第08课 碰撞传感器

**1. 项目介绍**

碰撞传感器主要用的是轻触开关，而上一课我们学习的自锁按键模块用的是按键开关。碰撞传感器常用于3D打印机内做限位开关。

---

**2. 模块参数**

工作电压：DC 3.3V~5V

工作电流：20mA

最大功率：0.1W

输出信号：数字信号

尺寸：39.5mm x 23.5mm x 9.2mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/131301.png)

碰撞传感器主要由 1 个轻触开关组成。当物体碰到轻触开关弹片，下压时，2 脚和 3 脚导通，传感器信号端 S 被下拉为低电平，模块上自带的红色 LED 点亮；当没有物体碰撞轻触开关时，2 脚和 3 脚不导通，3 脚被 4.7 K的电阻 R1 上拉为高电平，即传感器信号端S为高电平，此时自带红色 LED 熄灭。碰撞传感器的原理与倾斜模块的电路原理几乎一样，不同之处在于导通方式。

---

**4. 实验组件**

| ![](media/KS5016.png) |![](media/KE4023.png) |![img](media/3pin.png)       | ![img](media/USB.jpg) |   
| --------------------- | --------------------- | ---------------------------- |---------------------------- |
|主板 x1| 碰撞传感器 x1 | 3P线(反向) x1 | USB线 x1  |  
 
 

---

**5. 模块接线图**

![](media/131501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**collision_sensor.ino**"。

**<span style="background:#ff0;color:#000">
注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。 </span>**

```c++
/*
 * 名称   : collision sensor
 * 功能   : 读取碰撞传感器的值
 * 作者   : http://www.keyes-robot.com/ 
*/
int val = 0;
void setup() {
  Serial.begin(9600);  //波特率设置为9600
  pinMode(3, INPUT);   //设置碰撞传感器的引脚D3为输入模式
}

void loop() {
  val = digitalRead(3);  //读取碰撞传感器的值
  Serial.print(val);      //打印碰撞传感器的值
  if (val == 0) {   //碰撞时的值为0
    Serial.print("        ");
    Serial.println("The end of this!");
    delay(100);
  }
  else {    //无碰撞时的值为1
    Serial.print("        ");
    Serial.println("All going well");
    delay(100);
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

将传感器的上弹片下压时，value值为0，模块上LED点亮，串口监视器打印出“**0  The end of this!**” ；当松开弹片时，value值为1，模块上LED熄灭，串口监视器打印出“**1  All going well!**”。

![](media/IMG_151845.png)

![](media/IMG_151821.png)

![](media/131701.png)

---

**8. 代码说明**

可以参照第04课的代码说明，这里就不多做介绍了。

---

### 第09课 附近有人吗

**1. 项目介绍**

人体红外热释传感器主要由一个RE200B-P传感器元件组成。它是一款基于热释电效应的人体热释运动传感器，能检测到人体或动物身上发出的红外线，配合菲涅尔透镜能使传感器探测范围更远更广。

实验中，通过读取模块上S端高低电平，判断附近是否有人在运动；并且在串口监视器上显示测试结果。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 3.6mA

最大功率: 0.018W

静态电流: <50 uA

输出信号: 数字信号

触发方式: L不可重复触发/H重复触发

视野角度：Y = 90°，X = 110°（视野角度为理论数值）

最大检测距离: ≤5米

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 7.4mm

接口：2.54mm间距为，3pin防反接口

---

**3. 模块原理图**

![](media/161301.jpg)

这个模块的原理图可能较前面的模块稍复杂，我们一部分一部分来看。先看电压转换部分，作用是将5V输入电压转换为3.3V输入电压。因为我们模块上用到的热释电红外传感器的工作电压是3.3V，不能直接用5V电压供电使用。有了这个电压转换部分，3.3V输入电压和5V输入电压都适用于此热释电红外传感器。

当红外热释传感器没有检测到红外信号时，红外热释传感器的1脚输出低电平，此时模块上的LED两端有电压差，有电流流过，LED被点亮，MOS管Q1导通（Q1是NPN MOS管，型号为2N7002。由于红外热释传感器的1脚输出低电平，所以Q1的源极Vs=0，而Q1的栅极Vg=3.3V，于是Q1的栅极G和Q1的源极S之间的电压 Vgs = 3.3V 大于Q1的阈值电压 2.5V，Q1导通。），信号端S检测到低电平。

当红外热释传感器检测到红外信号时，红外热释传感器的1脚输出高电平，此时模块上的LED熄灭，MOS管Q1不导通，则信号端S检测到被10K上拉电阻R5拉高的高电平。

---

**4. 实验组件**

| ![img](media/KS5016.png) |![img](media/KE4018.png)| ![img](media/3pin.png)  | ![img](media/USB.jpg) |   
| ------------------------ | --------------------------- | ---------------------------- |---------------------------- |
|主板 x1 | 人体红外热释传感器 x1 | 3P线(反向) x1 | USB线 x1   |    

---

**5. 模块接线图**

![img](media/161501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**PIR_motion.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*
 * 名称   : PIR motion
 * 功能   : 读取人体红外传感器的数值
 * 作者   : http://www.keyes-robot.com/ 
*/
int val = 0;
int pirPin = 3;   //PIR运动传感器的引脚定义为D3
void setup() {
  Serial.begin(9600);   //波特率设置为9600
  pinMode(pirPin, INPUT);    //将传感器设置为输入模式
}

void loop() {
  val = digitalRead(pirPin);    //读取传感器值
  Serial.print(val);    //打印传感器值
  if (val == 1) {    //附近有人移动，输出高电平
    Serial.print("        ");
    Serial.println("Some body is in this area!");
    delay(100);
  }
  else {    //如果附近没有人移动，输出低电平
    Serial.print("        ");
    Serial.println("No one!");
    delay(100);
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

当传感器检测到附近有人在运动时，value值为1，模块上LED熄灭，串口监视器显示“**1   Somebody is in this area!**”；没有检测到附近有人在运动时，value值为0，模块上LED点亮，串口监视器显示“**0   No one!**”。

![img](media/IMG_151352.png)

![img](media/IMG_151315.png)

![img](media/161701.png)

---

**8. 代码说明**

可以参照第04课的代码说明，这里就不多做介绍了。

---

### 第10课 播放声音

**1. 项目介绍**

蜂鸣器是一种一体化结构的电子讯响器，采用直流电压供电，广泛应用于计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。蜂鸣器主要分为有源蜂鸣器和无源蜂鸣器。在这个实验中，我们来学习尝试控制有源蜂鸣器发出声音。有源蜂鸣器元件内部自带震荡电路，使用时，我们只需要给无源蜂鸣器元件足够的电压，无源蜂鸣器就会自动响起。

---

**2. 模块参数**

工作电压：DC 3.3 ~ 5V 

工作电流：22.5mA

最大功率：0.12W

频率：2300HZ

控制信号：数字信号

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 12.3mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/171301.jpg)

从原理图我们可以得知，蜂鸣器的1脚通过串联一个电阻R2连接到电压正极；蜂鸣器的2脚连接到NPN三极管Q1的C极，集电极；Q1的B极，也就是基极通过串联一个电阻R1连接到S信号端；发射集接到GND。

当三极管Q1导通时，蜂鸣器的2脚连通GND，有源蜂鸣器便会工作。那么如何让三极管Q1导通呢？**NPN三极管的导通条件是基极（B）电压比发射极（E）电压高 0.3V 以上，只需要基极（B）被上拉至高电平即可。** 虽然三极管Q1的基极（B）有一个下拉电阻R3导致其不导通，但是R3电阻的阻值大，使其为弱下拉电阻。三极管Q1的基极（B）还连接了一个阻值小的强上拉电阻R1，只要我们用单片机IO口给S信号端输入高电平，强上拉电阻R1会将三极管Q1的基极（B）强上拉为高电平，三极管Q1就会导通，有源蜂鸣器就会工作。

---

**4. 实验组件**

| ![img](media/KS5016.png) |![img](media/KE4010.png) |![img](media/3pin.png)       | ![img](media/USB.jpg) |    
| ------------------------ | ------------------------ | ---------------------------- |---------------------------- |  
| 主板 x1|  有源蜂鸣器模块 x1  |3P线(反向) x1 | USB线 x1  |      

---

**5. 模块接线图**

![img](media/171501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Active_buzzer.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*
 * 名称   : Active buzzer
 * 功能   : 有源蜂鸣器产生声音
 * 作者   : http://www.keyes-robot.com/ 
*/
int buzzer = 3;   //定义蜂鸣器接收器引脚为D3
void setup() {
  pinMode(buzzer, OUTPUT);    //设置输出模式
}

void loop() {
  digitalWrite(buzzer, HIGH); //发声
  delay(1000);
  digitalWrite(buzzer, LOW);  //停止发声
  delay(1000);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，模块上有源蜂鸣器响起1秒，停1秒，循环交替。

![img](media/IMG_153247.png)

---

**8. 代码说明**

| 代码                       | 说明              |
| -------------------------- | ----------------- |
| digitalWrite(buzzer, HIGH) | D3口输出高电平 |
| digitalWrite(buzzer, LOW)  | D3口输出低电平 |


---

### 第11课 演奏音乐

**1. 项目介绍**

在之前的项目中，我们研究了有源蜂鸣器，它只能发出一种声音，可能会让你觉得很单调。这个项目将学习另一种蜂鸣器，无源蜂鸣器。与有源蜂鸣器不同，无源蜂鸣器可以发出不同频率的声音。在这个项目中，我们将使用控制板控制无源蜂鸣器演奏歌曲。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 50mA

最大功率: 0.25W

控制信号：数字信号

方波大小：大约2KHz

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 12.3mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/181301.jpg)

这是一个无源蜂鸣器，上一课我们介绍过，有源蜂鸣器自带振荡源，是有正负之分的，只需要提供5V电压信号就会发声，发出的声音音调单一、频率固定。而无源蜂鸣器内部不带震荡电路，是没有正负之分的，类似于喇叭，只要加载不同的频率的电信号就可以实现发声，根据不同的频率所发出的声音也是不一样的。改变单片机引脚输出的工作频率，从而改变蜂鸣器鸣叫的音调。还可以改变输出电平的占空比，从而改变蜂鸣器输出声音的大小。

---

**4. 实验组件**

| ![img](media/KS5016.png) |![img](media/KE4067.png)    | ![img](media/3pin.png)       | ![img](media/USB.jpg) |
| ------------------------ | --------------------------- | ---------------------------- |---------------------------- | 
|主板 x1 |无源蜂鸣器模块 x1 |3P线(反向) x1 | USB线 x1 |

---

**5. 模块接线图**

![img](media/181501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Passive_buzzer.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*
 * 名称   : Passive Buzzer
 * 功能   : 无源蜂鸣器播放音乐
 * 作者   : http://www.keyes-robot.com/ 
*/
#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978
#define REST 0
int tempo=114;   // 改变这个可使歌曲变慢或变快
int buzzer = 3;  // 将此更改为你想使用的任何一个引脚
// 乐曲的音符后面跟着持续时间.
// A 4表示四分音符，8表示十八分音符，16表示十六分音符，以此类推
// !!负数用来表示带点的注释
// 所以-4意味着一个带点的四分音符，也就是说，四分之一加上十八分之一
int melody[] = {
  NOTE_E4,4,  NOTE_E4,4,  NOTE_F4,4,  NOTE_G4,4,//1
  NOTE_G4,4,  NOTE_F4,4,  NOTE_E4,4,  NOTE_D4,4,
  NOTE_C4,4,  NOTE_C4,4,  NOTE_D4,4,  NOTE_E4,4,
  NOTE_E4,-4, NOTE_D4,8,  NOTE_D4,2,
  NOTE_E4,4,  NOTE_E4,4,  NOTE_F4,4,  NOTE_G4,4,//4
  NOTE_G4,4,  NOTE_F4,4,  NOTE_E4,4,  NOTE_D4,4,
  NOTE_C4,4,  NOTE_C4,4,  NOTE_D4,4,  NOTE_E4,4,
  NOTE_D4,-4,  NOTE_C4,8,  NOTE_C4,2,
  NOTE_D4,4,  NOTE_D4,4,  NOTE_E4,4,  NOTE_C4,4,//8
  NOTE_D4,4,  NOTE_E4,8,  NOTE_F4,8,  NOTE_E4,4, NOTE_C4,4,
  NOTE_D4,4,  NOTE_E4,8,  NOTE_F4,8,  NOTE_E4,4, NOTE_D4,4,
  NOTE_C4,4,  NOTE_D4,4,  NOTE_G3,2,
  NOTE_E4,4,  NOTE_E4,4,  NOTE_F4,4,  NOTE_G4,4,//12
  NOTE_G4,4,  NOTE_F4,4,  NOTE_E4,4,  NOTE_D4,4,
  NOTE_C4,4,  NOTE_C4,4,  NOTE_D4,4,  NOTE_E4,4,
  NOTE_D4,-4,  NOTE_C4,8,  NOTE_C4,2
};
// 给出字节数的类型，每个int值由两个字节(16位)组成
// 每个音符有两个值(音高和持续时间)，所以每个音符有四个字节
int notes=sizeof(melody)/sizeof(melody[0])/2; 
// 这计算了整个音符的持续时间，单位是ms (60s/节拍)*4拍
int wholenote = (60000 * 4) / tempo;
int divider = 0, noteDuration = 0;
void setup() {
  // 重复旋律的音符
  // 记住，数组是音符数的两倍(音符+持续时间)
  for (int thisNote = 0; thisNote < notes * 2; thisNote = thisNote + 2) {
    // 计算每个音的持续时间
    divider = melody[thisNote + 1];
    if (divider > 0) {
    noteDuration = (wholenote) / divider; // 常规提示，继续
    } else if (divider < 0) {
      // 虚线注释的持续时间为负
      noteDuration = (wholenote) / abs(divider);
      noteDuration *= 5.; // 给打点音符增加一半的持续时间
    }
    // 只在90%的时间里演奏这个音符，留下10%作为暂停
    tone(buzzer, melody[thisNote], noteDuration*0.9);
  // 等待特定的时间后再演奏下一个音符.
    delay(noteDuration);
    noTone(buzzer);  // 下一个音节前停止波形产生前的下一个说明.
  }
}
void loop() {
//如果你想永远重复这首歌，在这里复制粘贴setup()中的代码.
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，无源蜂鸣器模块播放一首音乐。

![img](media/IMG_153143.png)

---

**8. 代码说明**

| 代码                                                      | 说明                                                         |
| --------------------------------------------------------- | ------------------------------------------------------------ |
| int tempo=114;                            | 改变这个可使歌曲变慢或变快。                                   |
| int melody[]                                              | 创建音乐旋律列表。                                           |
| int noteDurations[]                                       | 创建音调持续时间列表。                                       |
|int wholenote = (60000 * 4) / tempo; | 计算了整个音符的持续时间，单位是ms (60s/节拍)*4拍。        |
| sizeof(melody)                                     | sizeof是一个操作符（operator）。其作用是返回一个对象或类型所占的内存字节数。 |

---

### 第12课 风扇转动

**1. 项目介绍**

130电机模块上的HR1124S是应用于直流电机方案的单通道H桥驱动器芯片。HR1124S的H桥驱动部分采用低导通电阻的PMOS和NMOS功率管。低导通电阻保证芯片低的功率损耗，使得芯片安全工作更长时间。此外HR1124S拥有低待机电流，低静态工作电流，这些性能使HR1124S易用于玩具方案。

实验中，我们可通过输出到两个信号端IN+和IN-的电压方向来控制电机的转动方向，让电机转动起来。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

最大输入电流：1A

电机驱动电压：5V

电机最大驱动电流：1A（输入DC 5V）

电机最大驱动电流：0.4A（输入DC 3.3V）

控制信号: 数字信号

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 24.5mm

接口：2.54mm间距，4pin防反接口

---

**3. 模块原理图**

![img](media/191301.jpg)

HR1124S芯片的作用是助于驱动电机。而电机所需电流较大，无法用三极管驱动更无法直接用IO口驱动。让电机转动起来的方法很简单，给电机两端添加电压即可。不同电压方向电机转向也不相同，额度电压内，电压越大，电机转动得越快；反之电压越低，电机转动得越慢，甚至无法转动。所以我们可以用PWM口来控制电机的转速，这一课我们先学习用高低电平来控制电机。


---

**4. 实验组件**

| ![img](media/KS5016.png) |![img](media/KE4038.png) |![img](media/4pin.png) |
| ------------------------ | ------------------------ | ---------------------------- |
|主板 x1|  130电机模块 x1     |4P线(反向) x1 |
| ![img](media/USB.jpg)    | ![](media/WS4.png)|![](media/6.png)|
|  USB线 x1   | 电池盒 x1   | AA电池 x6 |

<span style="color: rgb(255, 76, 65);">**注意：电机与风扇叶是分开装的，需要组合到一起。**</span>

**5. 模块接线图**

<span style="color: rgb(0, 209, 0);">**注意：请勿用手握住风扇叶，请将风扇叶对着空旷的地方，以免受伤。**</span>

![img](media/191501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**130_Motor.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后外接电源供电，上电后，观察实验结果。**

```c++
/*
 * 名称   : 130DC Fan motor
 * 功能   : 电机顺、逆旋转
 * 作者   : http://www.keyes-robot.com/ 
*/
//定义电机的两个引脚接口，分别为D4和D5
int INA = 4;   //INA对应IN+
int INB = 5;  //INB对应IN-
void setup() {
  //将电机引脚设置为输出
  pinMode(INA, OUTPUT);
  pinMode(INB, OUTPUT);
}

void loop() {
  //逆时针方向转
  digitalWrite(INA, HIGH);
  digitalWrite(INB, LOW);
  delay(2000);
  //停止
  digitalWrite(INA, LOW);
  digitalWrite(INB, LOW);
  delay(2000);
  //顺时针方向转
  digitalWrite(INA, LOW);
  digitalWrite(INB, HIGH);
  delay(2000);
  //停止
  digitalWrite(INA, LOW);
  digitalWrite(INB, LOW);
  delay(2000);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

**注意：请勿用手握住风扇叶，请将风扇叶对着空旷的地方，以免受伤。**

<span style="color: rgb(255, 76, 65);">注意：</span> 由于风扇在转动时，所需的电流比其他传感器要大，会引起电路中电压电流波动，特别是风扇进行正反转时，电压电流波动过大，导致主板的电压电流过低，会导致复位。


代码上传成功后，拔下USB线断电，按照接线图正确接好模块，给主板外接电源供电（将装有电池的电池盒接到主板上）。上电后风扇逆时针转动2秒；停止2秒；顺时针转动2秒；停止2秒；循环交替。

![img](media/IMG_154609.png) 

---

**8. 代码说明**

将管脚设置为D4、D5，当D4输出为低电平即INA(IN+)输入低电平，D5输出为高电平即INB(IN-)输入高电平时（输入与输出是相对的，这个实验中对于我们单片机的引脚来说，单片机输出高低电平，模块就为输入了，即从单片机输出到模块；例如按键模块则相反，是模块输出到单片机），电机顺时针旋转；当D4输出为高电平，D5输出为低电平时，电机逆时针旋转；当两个管脚都设置为低电平时，电机停止转动。

---

### 第13课 读取电位器的值

**1. 项目介绍**

旋转电位器是一个模拟传感器。前面我们学习过的传感器，都是数字传感器。例如我们前面学习的人体红外热释传感器，当传感器检测到附近有人在运动时，我们读取到高电平（5V），当未检测到附近有人在运动时，我们读取到低电平（0V），而在0~5V中间的电压值，我们数字IO口无法读取到，当然人体红外热释传感器也只能输出高低电平。而模拟传感器就可以通过我们主板上的模拟口读取中间的电压值，那么，在本实验中我们使用旋转电位器通过模拟口来读取中间的电压值（即：模拟值）。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 20 mA

工作功率: 0.1 W

输出信号: 模拟信号

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 28.4mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/201301.png)

旋转电位器原理是靠电刷在电阻体上滑动，在电路中获取与输入电压形成一定关系地输出电压。旋转电位器传感器选用了一个10K可调电阻。通过旋转电位器上的旋杆，我们可以改变电阻大小，信号端S检测到电压变化（0 ~ 5V），而这个电压变化是一个连续变化的模拟量，也就是在0~5V内可以取任意值，我们必须先对这个模拟量进行ADC采集，来测量连续的这些模拟量。A/D 是模拟量到数字量的转换，依靠的是模数转换器(Analog to Digital Converter)，简称ADC。我们的主板已经集成了ADC采集，可以直接使用。

我们的主板默认分辨率为10位，即ADC位数是10位。一个n位的ADC表示这个ADC共有2的n次方个刻度，10位的ADC，输出的是从0～1023一共1024个数字量，也就是2的10次方个数据刻度，每个刻度就是5V/1024≈0.0049V，这也叫分辨率。

ADC：ADC是一种电子集成电路，用于将模拟信号(如电压)转换为由1和0表示的数字信号。我们在主板上的ADC的范围是默认的10位（ADC的位数表示将模拟量转换成数字量后所用的二进制位数），其可存储数字量范围为：0 ~ 2^10即0 ~ 1023。假设它的参考电压是5V，也就是说把参考电压分成1024份，最小分辨率为5V/1024，模拟值的范围对应于ADC值。因此，ADC拥有的比特越多，模拟的分区就越密集，最终转换的精度也就越高。

![img](media/201302.png)

纵坐标数字0 : 0V ~ 5.0/1024 V 范围内的模拟量（横坐标）;

纵坐标数字1 : 5.0/1024 V ~ 2 * 5.0/1024 V 范围内的模拟量（横坐标）;

......

模拟将被相应地划分。换算公式如下：

![img](media/201303.png)

DAC：这一过程的可逆需要DAC，数字到模拟转换器。数字I/O端口可以输出高电平和低电平(0或1)，但不能输出中间电压值，这就是DAC有用的地方。主板上8位精度的DAC可以将VCC(这里是5.0V)分成2^8=256个部分。例如，当数字量为1时，输出电压值为5.0V/256 * 1=0.0195V，当数字量为128时，输出电压值为5.0V/256 * 128=2.5V，DAC的精度越高，输出电压值的精度就越高。

换算公式如下：

![img](media/201304.png)

---

**4. 实验组件**

| ![img](media/KS5016.png) | ![img](media/KE4064.png)  |![img](media/3pin.png)  | ![img](media/USB.jpg) |    
| ------------------------ | ------------------------- | ---------------------------- | ---------------------------- | 
| 主板 x1| 旋转电位器 x1 |3P线(反向) x1 | USB线 x1  |    

---

**5. 模块接线图**

![img](media/201501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Potentiometer.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : Potentiometer
 * 功能   : 读取电位器的模拟值
 * 作者   : http://www.keyes-robot.com/ 
*/
int val = 0;   //设置value为0
int PIN_ANALOG_IN = A2;   //电位器传感器的引脚定义为A2

void setup() {
  Serial.begin(9600);   //波特率设置为9600
  pinMode(PIN_ANALOG_IN, INPUT);    //将传感器的引脚设置为输入模式
}

void loop() {
  val = analogRead(PIN_ANALOG_IN);    //读取传感器的模拟信号
  Serial.print("potentiometer:  ");    //打印字符串potentiometer:
  Serial.println(val);    //打印且显示模拟信号
  delay(200);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

滑动电位器手柄时，串口监视器打印出此时电位器的模拟值。

![img](media/IMG_160244.png)

![img](media/201701.png)

---

**8. 代码说明**

| 代码                                      | 说明                                                         |
| ----------------------------------------- | ------------------------------------------------------------ |
| pinMode(PIN_ANALOG_IN, INPUT); | 由“ int PIN_ANALOG_IN = A2; ”知道，定义旋转电位器的模拟管脚为A2，“INPUT”设置为输入模式。 |
| analogRead(PIN_ANALOG_IN)                 | 从指定的模拟引脚读取模拟值。为了兼容性，默认analogRead()分辨率为 10 位。详细了解请参考链接：[https://vimsky.com/examples/usage/arduino-language-functions-analog-io-analogread-ar.html](https://vimsky.com/examples/usage/arduino-language-functions-analog-io-analogread-ar.html) 。这个函数是从指定的模拟引脚PIN_ANALOG_IN读取 电位器的模拟信号，模拟信号的范围：0~1023。 
| val = analogRead(PIN_ANALOG_IN);  |将电位器的模拟信号赋值于变量val。|

---

### 第14课 检测水量

**1. 项目介绍**

水滴传感器是一个模拟输入模块，也叫雨水、雨量传感器。可用于各种天气状况的监测，检测是否下雨及雨量的大小，转成模拟信号输出，并广泛应用于汽车自动刮水系统和智能天窗系统等。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 1.5mA

最大功率: 0.075W

输出信号: 模拟信号

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 9.3mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/211301.jpg)

水滴传感器通过电路板上裸露的印刷平行线检测水量的大小。水量越多，就会有更多的导线被联通，随着导电的接触面积增大，雨滴感应区 2 脚输出的电压就会逐步上升。信号端 S 检测到的模拟值就越大。除了可以检测水量的大小，它还可以检测空气中的水蒸气。

---

**4. 实验组件**

| ![img](media/KS5016.png)|![img](media/KE4048.png) |![img](media/3pin.png)       | ![img](media/USB.jpg) |   
| ------------------------ | ------------------------ | ---------------------------- | ---------------------------- |
|主板 x1 | 水滴传感器 x1 | 3P线(反向) x1 | USB线 x1   |     
 

---

**5. 模块接线图**

![img](media/211501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Steam_sensor.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : Steam sensor
 * 功能   : 读取水滴传感器的模拟值检测水量
 * 作者   : http://www.keyes-robot.com/ 
*/
int val = 0;   //设置value为0
int PIN_ANALOG_IN = A2;   //水滴传感器的引脚定义为A2

void setup() {
  Serial.begin(9600);   //波特率设置为9600
  pinMode(PIN_ANALOG_IN, INPUT);    //将传感器的引脚设置为输入模式
}

void loop() {
  val = analogRead(PIN_ANALOG_IN);    //读取传感器的模拟信号
  Serial.print("water volume:  ");    //打印字符串water volume:
  Serial.println(val);    //打印且显示水量的模拟信号
  delay(200);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

![img](media/211701.png)

在水滴传感器的感应区滴几滴水（**小心用水，注意不要滴到感应区以外的其他任何地方，包括主板**），串口监视器打印出此时水滴传感器的模拟值。水量变化，模拟值也会发生变化。水量越多，输出的模拟越大。

![img](media/IMG_160418.png)

![img](media/211702.png)

---

**8. 代码说明**

可以参照第13课的代码说明，这里就不多做介绍了。

---

### 第15课 检测声量

**1. 项目介绍**

声音传感器是可以检测声音大小的。实验中，我们利用这个传感器测试当前环境中的声音对应的模拟值。声音越大，模拟值越大；并在串口监视器上显示测试结果。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 15mA

最大功率: 0.075W

输出信号: 模拟信号

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 10.3mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/221301.png)

声音传感器主要由一个高感度麦克风元件和LM386音频功率放大器芯片组成。高感度麦克风元件用于检测外界的声音。利用LM386音频功率放大器芯片设计对高感度麦克风检测到的声音进行放大的电路，最大倍数为200倍。使用时我们可以通过旋转传感器上电位器，调节声音的放大倍数。顺时针调节电位器到尽头，放大倍数最大。

---

**4. 实验组件**

| ![img](media/KS5016.png)  |![img](media/KE4027.png) |![img](media/3pin.png)  | 
| ------------------------ | ------------------------ | ---------------------------- |
|主板 x1 |  声音传感器 x1 | 3P线(反向) x1 |
| ![img](media/USB.jpg) | ![img](media/ABC13.png) |       | 
|USB线 x1 |一字螺丝刀 x1 |      |

---

**5. 模块接线图**

![img](media/221501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**MicroPhone.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : MicroPhone
 * 功能   : 读取声音传感器的模拟值检测声量
 * 作者   : http://www.keyes-robot.com/ 
*/
int val = 0;   //设置value为0
int PIN_ANALOG_IN = A2;   //声音传感器的引脚定义为A2

void setup() {
  Serial.begin(9600);   //波特率设置为9600
  pinMode(PIN_ANALOG_IN, INPUT);    //将传感器的引脚设置为输入模式
}

void loop() {
  val = analogRead(PIN_ANALOG_IN);    //读取传感器的模拟信号
  Serial.print("sound volume:  ");    //打印字符串sound volume:
  Serial.println(val);    //打印且显示声音强度的模拟信号
  delay(200);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

![img](media/830a.png)

串口监视器打印出声音传感器接收到的声音对应的模拟值。对准MIC头大声说话，可以看到接收到的声音对应的模拟值变大。（**注意：如果声音变化对应的模拟值没有变化并且一直都是数字0，需要用一字螺丝刀顺时针旋转电位器来调节。**）

![img](media/IMG_160325.png)

![img](media/221701.png)

---

**8. 代码说明**

可以参照第13课的代码说明，这里就不多做介绍了。 

---

### 第16课 检测光照强度

**1. 项目介绍**

光敏电阻传感器是一个常用的光敏电阻传感器，它主要由一个光敏电阻元件组成。光敏电阻元件的阻值随着光照强度的变化而变化，此传感器就是利用光敏电阻元件这一特性，设计电路将阻值变化转换为电压变化。光敏电阻传感器可以模拟人对环境光线的强度的判断，方便做出与人友好互动的应用。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 0.2mA

最大功率: 0.001W

输出信号: 模拟信号

工作温度：-10°C ~ +50°C

尺寸：32 x 23.8 x 7.4 mm

接口：2.54 mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/231301.png)

当没有光照射时，电阻大小为0.2 MΩ，光敏电阻的信号端（2脚）检测的电压接近0。随着光照强度增大，光线传感器的电阻值越来越小，所以信号端能检测到的电压越来越大。

---

**4. 实验组件**

| ![img](media/KS5016.png)  |![img](media/KE4026.png) |![img](media/3pin.png)       | ![img](media/USB.jpg) |    
| ------------------------ | ------------------------ | ---------------------------- |---------------------------- |
| 主板 x1  | 光敏电阻传感器 x1  |3P线(反向) x1 | USB线 x1  |     

---

**5. 模块接线图**

![img](media/231501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Photoresistor.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : Photoresistor
 * 功能   : 读取光敏传感器的模拟值检测光照强度
 * 作者   : http://www.keyes-robot.com/ 
*/
int val = 0;   //设置value为0
int PIN_ANALOG_IN = A2;   //光敏传感器的引脚定义为A2

void setup() {
  Serial.begin(9600);   //波特率设置为9600
  pinMode(PIN_ANALOG_IN, INPUT);    //将传感器的引脚设置为输入模式
}

void loop() {
  val = analogRead(PIN_ANALOG_IN);    //读取传感器的模拟信号
  Serial.print("light_intensity:  ");    //打印字符串light_intensity:
  Serial.println(val);    //打印且显示光线强度的模拟信号
  delay(200);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

串口监视器打印出光敏传感器的模拟值。光照越强，可以看到模拟值越大。

![img](media/IMG_160353.png)

![img](media/231701.png)

---

**8. 代码说明**

可以参照第13课的代码说明，这里就不多做介绍了。

---

### 第17课 检测温度

**1. 项目介绍**

NTC-MF52AT模拟温度传感器的原理与光敏电阻传感器类似，只是感应的器件不同。将传感器信号端接到主板的模拟口，可以读出对应的模拟值，电压值和温度值。我们可以利用模拟值，输出电压值，通过特定公式，计算出当前环境的温度值。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 20mA

最大功率: 0.1W

输出信号: 模拟信号

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 7.4mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/241301.png)

NTC-MF52AT模拟温度传感器主要由NTC-MF52AT热敏电阻元件组成。NTC-MF52AT热敏电阻元件能够感知周边环境温度的变化，随着温度的升高，热敏电阻的阻值降低，4.7K电阻两端的电压上升，从而引起信号端S的电压变化。

**NTC 热敏电阻温度计算公式：Rt = R * EXP( B * (1/T1-1/T2) ) 。**

其中，T1和T2指的是K度，即开尔文温度。K度=273.15(绝对温度)+摄氏度。

Rt 是热敏电阻在周围温度为T1（当前温度）时的电阻值。

R是热敏电阻在周围温度为T2常温（常温取25℃）时的标称阻值。参考规格书可知我们用的NTC-MF52AT模拟温度传感器在 25℃ 下热敏电阻的零功率电阻值为10KΩ ± 5%（即R=10K），T2=(273.15+25) 。

B值是热敏电阻的重要参数，为材料常数，在25℃下测得。参考规格书可知B值为 3950±1%。

EXP() 是e^()，e的n次方。

通过转换可以得到温度T1与电阻Rt的关系：T1=1 / (ln(Rt/R) /B+1/T2) ，这里可以将ln换算成log，即T1=1/ ( log(Rt/R)/B + 1/T2 ) 。

那么我们唯一需要知道的就是Rt的值。回到上面的原理图，设热敏电阻两端电压为VRt，固定的 R1电阻两端的电压为VR，由电阻分压知识VR/VRt = R1/Rt可以知道：Rt = R1 * (5.0-VR)/VR 。而我们实际得到的VR是转换后的模拟值，需要转换成电压值，即VR = AnalogValue / 1023 * 5.0。

**注意**：计算出来的温度是开尔文温度，因此需要减去K值，对应的摄氏温度 t = T1 - 273.15，同时加上0.5的误差矫正。

---

**4. 实验组件**

| ![img](media/KS5016.png)  |![img](media/KE4025.png)  | ![img](media/3pin.png)       | ![img](media/USB.jpg) |    
| ------------------------ | --------------------------- | ---------------------------- |---------------------------- |
| 主板 x1| NTC-MF52AT模拟温度传感器 x1 | 3P线(反向) x1 | USB线 x1   |  
 
---

**5. 模块接线图**

![img](media/241501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Temperature_sensor.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : Temperature sensor
 * 功能   : 模拟温度传感器检测温度
 * 作者   : http://www.keyes-robot.com/ 
*/
#define PIN_ANALOG_IN  A2 // 定义模拟温度传感器的引脚为A2

float Rt=0;      //NTC 热敏电阻
float R=10000;   //具有固定电阻值的10K电阻
float T2=273.15+25; //转换成开尔文温度
float B=3950;    //B值是热敏电阻的一个重要参数
float K=273.15;  //开氏度 (K°)
float VR=0;

void setup() {
  Serial.begin(9600);
}

void loop() {
  int AnalogValue = analogRead(PIN_ANALOG_IN);   //读A2引脚的模拟值
  VR = (float)(AnalogValue / 1023.0 * 5.0);  //转换成电压值
  Rt = (5.0 - VR) / VR * 4700;    //计算NTC热敏电阻
  float temp = 1/(1/T2+log(Rt/R)/B)-K+0.5;//计算温度
  Serial.print("Analog value:");
  Serial.print(AnalogValue);
  Serial.print("  |  Voltage:");
  Serial.print(VR);
  Serial.print("V");
  Serial.print("  |  Temperature:");
  Serial.print(temp);
  Serial.println("℃");
  delay(1000);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

串口监视器打印出模拟温度传感器(热敏传感器)当前所处环境下的模拟值、电压值和温度值。

![img](media/IMG_161346.png)

![img](media/241701.png)

---

**8. 代码说明**

| 代码                                    | 说明                                                         |
| --------------------------------------- | ------------------------------------------------------------ |
| VR = (float)(AnalogValue / 1023 * 5.0)   | 将R1电阻两端转换后的模拟值转换成电压值，数据类型为单精度浮点型。 |
| Rt = (5.0 - VR) / VR * 4700             | 计算热敏电阻在当前温度下的电阻值。                           |
| float temp = 1/(1/T2+log(Rt/R)/B)-K+0.5 | 计算当前环境的温度，数据类型为单精度浮点型。                 |


---

### 第18课 测压力大小

**1. 项目介绍**

薄膜压力传感器是基于新型纳米压敏材料辅以舒适杨式模量的超薄薄膜衬底一次性贴片而成的，兼具防水和压敏双重功能。

通过采集模块上S端模拟信号，判断压力大小。模拟值越小，压力越大；并在串口监视器上显示测试结果。

---

**2. 模块参数**

工作电压: DC 3.3V~5V

工作电流: 0.5mA

最大功率: 0.0025W

量程: 0~10KG

响应点: 150g

重复性: ＜±9.7%（60%负载）

一致性: ±10%

耐久性: ＞100万次

初始电阻: 大于10MΩ(无负载)

响应时间: ＜1ms

恢复时间: ＜15ms

输出信号: 模拟信号

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 7.4mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/251301.png)

当传感器感知到外界压力时，传感器的电阻值发生变化。薄膜压力传感器使用LM321运算放大器芯片将传感器感知到的压力变化的压力信号转换成相应变化强度的电信号输出。这样就可以通过检测电压信号变化得知压力变化情况。

---

**4. 实验组件**

| ![img](media/KS5016.png)  |![img](media/KE4069.png) |![img](media/3pin.png)    | ![img](media/USB.jpg) |      
| ------------------------ | ------------------------ | ---------------------------- | ---------------------------- |
|主板 x1   | 薄膜压力传感器 x1  |3P线(反向) x1 | USB线 x1  | 
   
---

**5. 模块接线图**

![img](media/251501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Film_pressure_sensor.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : Film pressure sensor
 * 功能   : 读取薄膜压力传感器的模拟值检测压力大小
 * 作者   : http://www.keyes-robot.com/ 
*/
int val = 0;   //设置value为0
int PIN_ANALOG_IN = A2;   //薄膜压力传感器的引脚定义为A2

void setup() {
  Serial.begin(9600);   //波特率设置为9600
  pinMode(PIN_ANALOG_IN, INPUT);    //将传感器的引脚设置为输入模式
}

void loop() {
  val = analogRead(PIN_ANALOG_IN);    //读取传感器的模拟信号
  Serial.print("pressure value:  ");    //打印字符串pressure value:
  Serial.println(val);    //打印且显示压力大小的模拟信号
  delay(200);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。为了使实验数据最精准，请将薄膜压力传感器尽量平放。

串口监视器打印出薄膜压力传感器的模拟值。用手按压薄膜时，随着压力的增大，可以看到模拟值逐渐变小。

![img](media/IMG_160507.png)

![img](media/251701.png)

---

**8. 代码说明**

可以参照第13课的代码说明，这里就不多做介绍了。


---

### 第19课 火焰传感器

**1. 项目介绍**

火焰传感器对火焰光谱特别灵敏，且灵敏度可调。性能稳定，是救火机器人的必备部件。火焰传感器上有一个远红外火焰探头，起着非常重要的作用，它可以用作机器人的眼睛来寻找火源或足球。利用它可以制作灭火机器人、足球机器人等。

该传感器有两个信号输出端，分别可输出数字信号与模拟信号。实验中，我们分别读取模块传感器数字信号与模拟信号，将测试结果在串口监视器上显示。

---

**2. 模块参数**

工作电压: DC 3.3V~5V

工作电流: 1.2mA

最大功率: 0.006W

输出信号：模拟信号和数字信号

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 9.2mm

接口：2.54mm间距，4pin防反接口

---

**3. 模块原理图**

![](media/261301.png)

红外火焰传感器能够探测到波长在700纳米～1000纳米范围内的红外光，探测角度为60，其中红外光波长在880纳米附近时，其灵敏度达到最大。从电路原理图我们可以看到，上电后红色LED2先点亮，红色LED1处于熄灭状态，检测到火焰时，数字信号端D0输出低电平，红色LED1将点亮。红外火焰探头将外界红外光的强弱变化转化为电流的变化，通过A/D转换器反映为0～255范围内数值的变化。外界红外光越强，数值越小；红外光越弱，数值越大。

---

**4. 实验组件**

| ![img](media/KS5016.png)   |![img](media/KE4020.png) |![img](media/4pin.png)  |
| ------------------------ | ------------------------ | ---------------------------- |
| 主板 x1   |   火焰传感器 x1 | 4P线(反向) x1 | 
| ![img](media/USB.jpg) | ![img](media/ABC13.png) |    |
| USB线 x1    |一字螺丝刀 x1  |    |

---

**5. 模块接线图**

![img](media/261501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Flame_sensor.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
 /*  
 * 名称   : Flame sensor
 * 功能   : 读取火焰传感器的模拟值和数字信号值
 * 作者   : http://www.keyes-robot.com/ 
*/
int val1 = 0;   //设置value1为0
int val2 = 0;   //设置value2为0
int PIN_ANALOG_IN = A5;   //火焰传感器的模拟引脚定义为A5
int PIN_DIGITAL_IN = A4;   //火焰传感器的数字引脚定义为A4

void setup() {
  Serial.begin(9600);   //波特率设置为9600
  pinMode(PIN_ANALOG_IN, INPUT);    //将传感器的模拟引脚设置为输入模式
  pinMode(PIN_DIGITAL_IN, INPUT);    //将传感器的数字引脚设置为输入模式
}

void loop() {
  val1 = analogRead(PIN_ANALOG_IN);    //读取传感器的模拟信号
  val2 = digitalRead(PIN_DIGITAL_IN);    //读取传感器的数字信号
  Serial.print("D0:  ");    //打印字符串D0:
  Serial.print(val2);    //打印且显示val2数值
  if (val2 == 0) {    //检测到火焰，输出低电平
    Serial.print("  On fire!");
    Serial.print("  A0:  ");
    Serial.println(val1);
    delay(100);
  }
  else {    //未检测到火焰，输出高电平
    Serial.println("  All going well");
    delay(100);
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电。此时火焰传感器上的红色LED2点亮。用一字螺丝刀旋转火焰传感器上的电位器，微调使传感器上的红色LED1灯介于亮与不亮之间的**不亮**状态。

![](media/261701.png)

打开串口监视器，设置波特率为 **<u>9600</u>**。串口监视器打印出火焰传感器的D0值“1”和字符串“All going well”。当传感器检测到火焰时，红色LED1点亮，在串口监视器中可以看到D0值由“1”变为“0”，字符串“All going well”变为“on fire!”，还打印出A0值，同时传感器检测到的火焰越大，A0值反而越小。

![](media/IMG_160714.png)

![](media/261702.png)

---

**8. 代码说明**

可以参照第04课和第13课的代码说明，这里就不多做介绍了。

---

### 第20课 摇杆模块

**1. 项目介绍**

你看过游戏手柄吗？游戏手柄上有按键，还有摇杆。摇杆是什么工作原理呢？在这个套件中，就有一个摇杆模块，它的主要元件是PS2手柄摇杆。控制时，我们需要将模块的X引脚和Y引脚连接至单片机的模拟口。B引脚连接至单片机数字口，V引脚接至单片机电源输出端（3.3-5V），G引脚接单片机GND。通过读取两个模拟值和一个数字口的高低电平情况，可以判断模块上摇杆的工作状态。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 12mA

最大功率: 0.06W

输出信号: 信号端X、Y模拟电压输出(模拟信号)，信号端B是数字电平输出(数字信号) 

工作温度：-10°C ~ +50°C

尺寸：47.6mm x 23.8mm x 34.5mm

接口：2.54mm间距，5pin防反接口

---

**3. 模块原理图**

![img](media/301301.png)

其实摇杆模块的原理非常简单，内部相当于两个可调电位器（左右和上下）和一个按键。按键没有按下时被R1下拉为低电平，按下时接通VCC即为高电平，与我们前面学习过的按键模块的电平值是相反的。摇动摇杆时内部的电位器就会根据摇杆的摇动调节，从而输出不同的电压，可以读取到模拟值。

---

**4. 实验组件**

| ![img](media/KS5016.png)  |![img](media/KE4050.png) |![img](media/5pin.png)       | ![img](media/USB.jpg) |     
| ------------------------ | ------------------------ | ---------------------------- |---------------------------- |
| 主板 x1      |   摇杆模块 x1 | 5P线(反向) x1 | USB线 x1  |      

---

**5. 模块接线图**

![img](media/301501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Joystick.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : Joystick
 * 功能   : 从摇杆读取数据
 * 作者   : http://www.keyes-robot.com/ 
*/
int VRx = A0;  //定义X轴引脚
int VRy = A1;  //定义Y轴引脚
int SW = 7;    //定义Z轴引脚
int xPosition = 0;  //设置变量xPosition初始值为0
int yPosition = 0;  //设置变量yPosition初始值为0
int SW_state = 0;  //设置变量SW_state初始值为0

void setup() {
Serial.begin(9600); 
pinMode(VRx, INPUT);  //X轴引脚为输入模式
pinMode(VRy, INPUT);  //Y轴引脚为输入模式
pinMode(SW, INPUT_PULLUP);   //Z轴引脚为输入上拉模式
}

// 在loop()中，使用analogRead()读取X轴和Y轴的值
//并使用digitalRead()读取Z轴的值，然后显示它们。
void loop() {
  xPosition = analogRead(VRx); 
  yPosition = analogRead(VRy);
  SW_state = digitalRead(SW);
  Serial.print("X: ");
  Serial.print(xPosition);
  Serial.print(" | Y: ");
  Serial.print(yPosition);
  Serial.print(" | Button: ");
  Serial.println(SW_state);
  delay(100);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

串口监视器窗口将打印出当前摇杆X轴(X引脚)和Y轴(Y引脚)对应的模拟值以及Z轴(B引脚)对应的数字值，移动摇杆或按下它将改变串口监视器中的模拟值和数字值。当按下摇杆时，Button值为1；未按下摇杆时，Button值为0。X值从左到右由0增长到1023。Y值从下到上由0增长到1023。

![img](media/301701.png)

![img](media/IMG_162512.png)

在X轴上移动摇杆，使数据从小到大。

![img](media/301702.png)

在Y轴上移动摇杆，使数据从小到大。

![img](media/301703.png)

按下摇杆。

![img](media/301704.png)

---

**8. 代码说明**

| 代码                              | 说明                       |
| --------------------------------- | -------------------------- |
|pinMode(SW, INPUT_PULLUP) | 将引脚设置为输入上拉模式。 |


---

### 第21课 SK6812 RGB

**1. 项目介绍**

第02课学习了交通灯模块，利用高低电平对模块的3个LED进行点亮和熄灭实现交通灯的效果。我们这个套件中，还有一个SK6812 RGB模块，它是一个集控制电路与发光电路于一体的智能外控LED光源。每个LED原件其外型与一个5050LED灯珠相同，每个元件即为一个像素点，我们这个模块上有四个灯珠即四个像素点。

---

**2. 模块参数**

工作电压: DC 3.3V~5V

工作电流: 140mA

最大功率: 0.7W

光源: SMD 5050 RGB

IC型号: 4颗/WS2811

灰度等级: 256级

发光角度: 180°

发光颜色: 可以通过控制器调为白，红，黄，蓝，绿等

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 7.4mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/321301.png)

从原理图中我们可以看出，这四个像素点灯珠串联，其实不论多少个灯珠串联，我们都可以用一个引脚控制其中任意一个灯让它显示任意一种颜色。每个LED原件其外型与一个5050LED灯珠相同，每个元件即为一个像素点。像素点内部包含了智能数字接口数据锁存信号整形放大驱动电路，还包含有高精度的内部振荡器和12V高压可编程定电流控制部分，有效保证了像素点光的颜色高度一致。

数据协议采用单线归零码的通讯方式，像素点在上电复位以后，S端接受从控制器传输过来的数据，首先送过来的24bit数据被第一个像素点提取后，送到像素点内部的数据锁存器。LED具有低电压驱动，环保节能，亮度高，散射角度大，一致性好，超低功率，超长寿命等优点。

---

**4. 实验组件**

| ![img](media/KS5016.png)   | ![img](media/KE4009.png) |![img](media/3pin.png)   | ![img](media/USB.jpg) |    
| ------------------------ | ------------------------ | ---------------------------- |---------------------------- |
|主板 x1   |  6812 RGB模块 x1 | 3P线(反向) x1 | USB线 x1 |   
   
---

**5. 模块接线图**

![img](media/321501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**SK6812.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : sk6812 RGB
 * 功能   : 打开sk6812 RGB LED
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <FastLED.h>

#define NUM_LEDS 4  //定义SK6812 RGB模块上LED灯珠数量
#define LED_PIN  3  //定义SK6812RGB模块控制引脚为D3
CRGB leds[NUM_LEDS];

void setup() {
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(50);   //设置SK6812 RGB的亮度
}

void loop() {
  effect3();
}

void effect3() {
  delay(50) ;{  
    // 为led[0]创建一个新的CHSV颜色
    leds[0] = CHSV(160, random8(), random8(100, 255));  
    // 把上一个颜色值复制过来, 从远端开始，将每个像素复制到下一个像素, 从而沿着带状“移动”图案.
    for (int i = NUM_LEDS - 1; i > 0; i--) {
      leds[i] = leds[i - 1];
    }
  }  
  FastLED.show();
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

正确的主板板型和串口端口设置完成后，则上传代码。若代码上传不成功，提示“**FastLED.h: No such file or directory**”，请添加库文件。其添加方法请参照：[添加Arduino库文件](https://www.keyesrobot.cn/projects/KE3095/en/latest/docs/Arduino_C_%E6%95%99%E7%A8%8B/Arduino_C_%E6%95%99%E7%A8%8B.html#arduino)

再次上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，我们可以看到模块上的4个RGB LED亮出对应的颜色灯的效果。

![img](media/IMG_162353.png)

---

**8. 代码说明**

| 代码                                         | 说明                                                         |
| -------------------------------------------- | ------------------------------------------------------------ |
| FastLED.setBrightness(50)| 设置SK6812 RGB的亮度|
| leds[0] = CHSV(160, random8(), random8(100, 255)) | 为led[0]创建一个新的CHSV颜色 |
|leds[i] = leds[i - 1] |复制别的LED颜色|
|for (int i = NUM_LEDS - 1; i > 0; i--) {leds[i] = leds[i - 1];}|把上一个颜色值复制过来, 从远端开始，将每个像素复制到下一个像素, 从而沿着带状“移动”图案.|

---

### 第22课 旋转编码器计数

**1. 项目介绍**

旋转编码器模块，也叫开关编码器、旋转编码器。此款编码器有20脉冲20定位点、15脉冲30定位点两种。编码器主要用于汽车电子、多媒体音响、仪器仪表、家用电器、智能家居、计算机周边、医疗器械等领域。主要用于频率调节、亮度调节、温度调节、音量调节的参数控制等。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 2mA

最大功率: 0.01W

控制信号: 数字信号

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 30.6mm

接口：2.54mm间距，5pin防反接口

---

**3. 模块原理图**

![img](media/331301.png)

增量式编码器是将位移转换成周期性的电信号，再把这个电信号转变成计数脉冲，用脉冲的个数表明位移的巨细。旋转编码器模块采用的是20脉冲旋转编码器元件，它可以通过旋转计数正方向和反方向转动过程中输出脉冲的次数，这种转动计数是没有限制的，复位到初始状态，即从0开始计数。

旋转编码器提供两种交互方式：

- **按钮**   单击旋钮以按下按钮。按下时，按钮将 SW 引脚与 GND 引脚连接，也就是SW引脚的电平为低电平。

- **旋转**   每次旋转旋钮时，会在 DT 和 CLK 引脚上产生一个 LOW 信号。
  
    - 顺时针旋转会导致 CLK 引脚首先变低，然后 DT 引脚也变低。
    
    - 逆时针旋转会导致 DT 引脚先变低，然后 CLK 引脚变低。
    
    两个引脚将在几毫秒内返回高电平。如下图所示：
    
    ![img](media/331302.png)

---

**4. 实验组件**

| ![img](media/KS5016.png)   |![img](media/KE4049.png) |![img](media/5pin.png)  | ![img](media/USB.jpg) |     
| ------------------------ | ------------------------ | ---------------------------- |---------------------------- |
|主板 x1|   旋转编码器模块 x1  |5P线(反向) x1 | USB线 x1  |     

---

**5. 模块接线图**

![img](media/331501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Encoder.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : Encoder
 * 功能   : 旋转编码器计数
 * 作者   : http://www.keyes-robot.com/ 
*/
// 旋转编码器的引脚
int CLK = 3;  //定义旋转编码器的CLK引脚为D3
int DT = A5;   //定义旋转编码器的DT引脚为A5
int SW = A4;  //定义旋转编码器的SW引脚为A4 
 
int counter = 0;  //定义变量counter
int currentStateCLK;  //定义变量currentStateCLK
int lastStateCLK;  //定义变量lastStateCLK

String currentDir ="";
unsigned long lastButtonPress = 0;

void setup() {
  Serial.begin(9600);   //设置波特率
// 将编码器引脚设置为输入
  pinMode(CLK,INPUT);
  pinMode(DT,INPUT);
  pinMode(SW,INPUT_PULLUP);
// 读取CLK的初始状态
  lastStateCLK = digitalRead(CLK);
}
 
void loop() {        
  // 读取CLK的当前状态
  currentStateCLK = digitalRead(CLK);
 
  //如果CLK的上一个状态和当前状态不同，则表示发生了脉冲
  //仅对一个状态变化做出响应，以避免重复计数
  if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){
 
   //如果DT状态不同于CLK状态，则编码器将旋转CW，因此减小
    if (digitalRead(DT) != currentStateCLK) {
      counter --;
      currentDir ="CW";
    } else {
      //编码器旋转CCW所以递增
      counter ++;
      currentDir ="CCW";
    }
 
    Serial.print("Direction: ");
    Serial.print(currentDir);
    Serial.print(" | Counter: ");
    Serial.println(counter);
  }
 
  //记住上次的CLK状态
  lastStateCLK = currentStateCLK;
 
  //读取按钮状态
  int btnState = digitalRead(SW);
 
  //如果检测到低信号，则按钮被按下
  if (btnState == 0) {
  //如果距离上一次低脉冲时间已经过去50ms，则意味着按钮被按下、释放并再次按下
    if (millis() - lastButtonPress > 50) {
      Serial.println("Button pressed!");
    }
 
    //记住上次按按钮事件
    lastButtonPress = millis();
  }
 
  //添加一个轻微的延迟来帮助缓冲阅读
  delay(5);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

顺时针旋转编码器，串口监视器打印出来的数据 **<u>减小</u>**；逆时针旋转编码器，串口监视器打印出来的数据 **<u>增大</u>**；按下编码器中间按键，串口监视器打印 “**<u>Button pressed!</u>**”。

![img](media/IMG_164915.png)

![img](media/331701.png)

---

**8. 代码说明**

声明编码器的 CLK、DT 和 SW 引脚所连接的 Arduino 引脚。

![Img](./media/img-202405.png)


定义一些变量：

- counter每当旋钮旋转一档（咔哒声）时，变量就会增加。

- 变量currentStateCLK和lastStateCLK存储 CLK 输出的状态，用于计算旋转量。
 
- 名为的字符串currentDir将用于在串行监视器上显示当前旋转方向。

- 该变量lastButtonPress用于消除开关抖动。

![Img](./media/img-2024052.png)

在设置部分，我们首先将旋转编码器连接配置为输入，然后启用 SW 引脚上的输入上拉。我们还设置了串行监视器。我们还读取 CLK 引脚的当前值并将其存储在变量 lastStateCLK 中。

![Img](./media/img-2024053.png)

在循环部分，我们再次检查 CLK 状态并将其与lastStateCLK值进行比较。如果它们不同，则表明旋钮已被转动。我们还检查是否currentStateCLK为 1，以便仅对一种状态变化做出反应并避免重复计算。

![Img](./media/img-2024054.png)

在该if语句中，旋转方向被确定。为了实现这一点，我们只需读取 DT 引脚并将其与 CLK 引脚的当前状态进行比较。

- 如果这两个值不同，则表明旋钮顺时针转动。然后将counter递减，并将currentDir设为“CW”。

- 如果这两个值相同，则表明旋钮是逆时针转动的。然后将counter递增，并将设为currentDir“CCW”。

![Img](./media/img-2024055.png)

然后将结果打印到串行监视器。

![Img](./media/img-2024056.png)

在if声明之后，我们更新lastStateCLKCLK 的当前状态。

lastStateCLK = currentStateCLK;

下一步涉及读取按钮开关并对其进行去抖。我们首先读取当前按钮状态，当它变为低电平时，我们等待 50 毫秒，让按钮去抖。

如果按钮保持低电平超过50ms，则表明确实被按下。结果，我们打印“Button pressed!” 到串行监视器。

![Img](./media/img-2024057.png)

---

### 第23课 舵机转动

**1. 项目介绍**

之前，我们学习了如何控制电机的速度和旋转方向。在本实验中，我们将学习伺服（舵机）系统，它是一种旋转驱动器类型的电机，可以控制旋转到特定的角度。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作温度：-10°C ~ +50°C

尺寸：32.25mm x 12.25mm x 30.42mm

接口：2.54mm间距，3pin接口

---

**3. 模块原理图**

舵机密封包装，由直流电机，一套提供扭矩的变速齿轮组，可调电位器和电子控制板组成。

![img](media/341101.png)

多数舵机只能旋转180度，舵机输出的转矩比简单直流电动机更高。被广泛用于控制模型汽车、模型飞机、机器人等。舵机一般有三线，通常连接到公或母3拼线。其中两线用于电源：正极（2-正极线，红线)，负极（3-负极线，棕线），以及一个信号线（1-信号线，橙线）。如图，套件中的舵机：

![img](media/zg.png)

我们使用PWM信号驱动舵机，该信号占空比一定，频率为50Hz。PWM单个周期高电平持续时间为0.5ms ~ 2.5ms，与舵机角度的0° ~ 180°线性对应。部分对应值如下：

![img](media/zgxy.png)

改变舵机信号值，舵机将旋转到指定角度。

<span style="color: rgb(255, 76, 65);">注意:</span> 由于舵机品牌不同，对于同一信号，不同品牌的舵机旋转的角度也会有所不同。

---

**4. 实验组件**

| ![img](media/KS5016.png) |![](media/KE4022.png)   | ![img](media/USB.jpg) |
| ------------------------ | ------------------- | --------------------- | 
| 主板 x1 | 舵机驱动模块 x1|USB线 x1  |
|![](media/9G.png)   |![img](media/4pin.png) |     |
| 舵机 x1 | 4P线(反向) x1|           |

---

**5. 实验①**

（1）实验接线图

![img](media/341501.png)

（2）实验代码

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**servo_1.ino**"。

```c++
/*
 * 名称   : Servo_1
 * 功能   : 舵机旋转角度0 -> 90 -> 180，重复
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <Servo.h>

Servo myservo;  //创建舵机对象来控制舵机

int servoPin = 5;  // 定义舵机引脚D5

void setup() {
  myservo.attach(servoPin);  //选择舵机引脚D5
}

void loop() {
  myservo.write(0); //旋转到0度
  delay(1000); //延迟1s
  myservo.write(90); //旋转到90度
  delay(1000); //延迟1s
  myservo.write(180); //旋转到180度
  delay(1000); //延迟1s
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

（3）实验结果

正确的主板板型和串口端口设置完成后，则上传代码。若代码上传不成功，提示“**Servo.h: No such file or directory**”，请添加库文件。其添加方法请参照：[添加Arduino库文件](https://www.keyesrobot.cn/projects/KE3095/en/latest/docs/Arduino_C_%E6%95%99%E7%A8%8B/Arduino_C_%E6%95%99%E7%A8%8B.html#arduino)

再次上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好舵机，再用USB线连接到计算机上电。上电后，舵机由0度转到90度，停顿1秒；再转到180度，停顿1秒；然后回到0度，停顿1秒，循环转动。

![img](media/IMG_161759.png)

![img](media/IMG_161711.png)

![img](media/IMG_161814.png)

---

**6. 实验②**

（1）实验接线图

![img](media/341501.png)

（2）实验代码

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**servo_2.ino**"。

```c++
/*
 * 名称   : Servo_2
 * 功能   : 控制舵机进行扫动
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <Servo.h>

Servo myservo;  //创建舵机对象来控制舵机

int posVal = 0;    // 定义一个变量，存储舵机位置
int servoPin = 5;  // 定义舵机引脚D5

void setup() {
  myservo.attach(servoPin);  //选择舵机引脚D5
}

void loop() {
  
  for (posVal = 0; posVal <= 180; posVal += 1) { // 将servoPin上的伺服附加到伺服对象上
    // 以1度为步
    myservo.write(posVal);       // 告诉伺服电机到变量“pos”的位置
    delay(15);                   // 等待15ms让伺服电机到达位置
  }
  for (posVal = 180; posVal >= 0; posVal -= 1) { // 从180°到0°
    myservo.write(posVal);       // 告诉伺服电机到变量“pos”的位置
    delay(15);                   // 等待15ms让伺服电机到达位置
  }
}
```

（3）实验结果

（<span style="color: rgb(255, 169, 0);">特别提醒：如果已经添加过库文件“**Servo.h**”，就不需要重复添加；如果没有添加，请参照本实验前面实验①的添加方法添加库文件“**Servo.h**”。</span>）代码上传成功后，拔下USB线断电。按照接线图正确接好舵机，再用USB线连接到计算机上电，上电后，舵机在0度 ~ 180度之间来回转动，每15ms转动一度。

![img](media/IMG_161759.png)

![img](media/IMG_161711.png)

![img](media/IMG_161814.png)

---

**7. 代码说明**

| 代码                                | 说明                                                         |
| ----------------------------------- | ------------------------------------------------------------ |
|#include <Servo.h>             | Arduino专门为了主板推出的servo库，用来操作舵机。        |
| Servo myservo                       | 创建一个舵机对象来控制舵机。                                 |
| myservo.attach(servoPin) | 设置控制舵机的引脚。                               |
| myservo.write(posVal)               | 向舵机写入一个数值，来直接控制舵机的轴，角度控制。舵机转动到posVal角度值。 |


---

### 第24课 超声波测距

**1. 项目介绍**

蝙蝠和某些海洋动物都能够利用高频率的声音进行回声定位或信息交流。它们能通过口腔或鼻腔把从喉部产生的超声波发射出去，利用折回的声波来定向，并判定附近物体的位置、大小以及是否在移动。超声波是一种频率高于20000赫兹的声波，它的方向性好，穿透能力强，易于获得较集中的声能，在水中传播距离远，可用于测距、测速、清洗、焊接、碎石、杀菌消毒等。在医学、军事、工业、农业上有很多的应用。超声波因其频率下限大于人的听觉上限而得名。科学家们将每秒钟振动的次数称为声音的频率，它的单位是赫兹(Hz)。

HC-SR04超声波传感器是可以发送出一种频率很高的人类无法听到的超声波信号，这些超声波的信号碰到障碍物，就会立刻反射回来。在接收到返回的信息之后，根据发射信号和接收信号的时间差，计算出传感器和障碍物的详细距离，和蝙蝠飞行的原理一样。

---

**2. 模块参数**

超声波传感器工作电压：DC 5V 

超声波传感器工作电流：15mA

超声波传感器工作频率：40Hz

超声波传感器射程范围：2cm~4m

超声波传感器测量角度：<= 15度

超声波传感器输入触发信号：10 uS 的TTL脉冲

超声波传感器输出回响信号：输出TTL电平信号与射程成正比

工作温度：-10°C ~ +50°C

超声波传感器尺寸：45.5mm x 26.7mm x 17.6mm

超声波转接板模块尺寸：32mm x 23.8mm x 18.5mm

超声波转接板模块接口：2.54mm间距，4pin防反接口

---

**3. 模块原理图**

最常用的超声测距的方法是回声探测法。当有脉冲电压触发时（单片机给Trig引脚发送高电平），超声波发射器探头里的晶片就会振动，继而产生超声波。在超声波发射时刻的同时计数器开始计时，超声波在空气中传播，途中碰到障碍物面阻挡就立即反射回来（Echo引脚发送高电平信号给单片机），超声波接收器收到反射回的超声波就立即停止计时。

超声波是一种声波，其声速V与温度有关。一般情况下超声波在空气中的传播速度为340m/s，根据计时器记录的时间t，就可以计算出超声波探头发射点距障碍物面的距离s，即：s=340t/2 。

![img](media/351301.png)

HC-SR04超声波测距模块可提供范围为2厘米至4米的非接触式距离感测功能，测距精度可达高到3mm。超声波传感器包括超声波发射器、超声波接收器与控制电路。其基本工作原理：

(1)采用IO口Trig触发测距，给至少10us的高电平信号;

(2)模块自动发送8个40khz的方波，自动检测是否有信号返回；

(3)有信号返回，通过IO口Echo输出一个高电平，高电平持续的时间就是超声波从发射到返回的时间。

![img](media/351302.png)

---

**4. 实验组件**

| ![img](media/KS5016.png)  |![img](media/KE4039.png) |  ![img](media/USB.jpg) |
| ------------------------ | ------------------------ | ---------------------------- |
|主板 x1  | 超声波转接模块 x1 |  USB线 x1|
![img](media/ultrasonic.png) | ![img](media/4pin.png) |     |
|HC-SR04 超声波传感器 x1 | 4P线(反向) x1 |    |

---

**5. 模块接线图**

![img](media/351501.png)

![img](media/351502.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Ultrasonic.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : Ultrasonic
 * 功能   : 使用超声波模块测量距离
 * 作者   : http://www.keyes-robot.com/ 
*/
const int TrigPin = 4; // 定义TrigPin
const int EchoPin = 5; // 定义EchoPin
int duration = 0; // 将持续时间的初始值定义为0
int distance = 0; // 将距离的初始值定义为0

void setup() {
  Serial.begin(9600);        // 设置波特率为9600
  pinMode(TrigPin, OUTPUT); // 设置trigPin为输出模式
  pinMode(EchoPin, INPUT);  // 设置echoPin为输入模式
}

void loop(){
  // 使trigPin输出高电平持续10μs触发HC_SR04
  digitalWrite(TrigPin , HIGH);
  delayMicroseconds(10);
  digitalWrite(TrigPin , LOW);
  // 等待HC-SR04回到高电平并测量这个等待时间
  duration = pulseIn(EchoPin , HIGH);
  // 根据时间计算距离
  distance = (duration/2) / 28.5 ;
  Serial.print("Distance: ");
  Serial.print(distance); //串口打印距离值
  Serial.println("cm");
  delay(100); //ping之间等待100毫秒(大约20个ping /秒)。
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

放置障碍物在超声波传感器探头前感应，串口监视器窗口打印出超声波传感器与障碍物之间的距离值。

![img](media/IMG_165053.png)

![img](media/351701.png)

---

**8. 代码说明**

| 代码                               | 说明                                                         |
| ---------------------------------- | ------------------------------------------------------------ |
| duration = pulseIn(EchoPin , HIGH) | pulseIn是内置函数，专门用来读取脉冲时间间隔。这里读取的高电平的时间就是超声波从发射到返回的时间。 |
| distance = (duration/2) / 28.5     | 超声波数据转换算法。                                         |


---

### 第25课 红外遥控与接收

**1. 项目介绍**

红外线遥控是目前使用最广泛的一种通信和遥控手段。因红外线遥控装置具有体积小、功耗低、功能强、成本低等特点，录音机、音响设备、空凋机以及玩具等其它小型电器装置上纷纷采用红外线遥控。红外遥控的发射电路是采用红外发光二极管发出经过调制的红外光波；红外接收电路由红外接收二极管、三极管或硅光电池组成，它们将红外发射器发射的红外光转换为相应的电信号，再送到后置放大器。

红外接收模块选择的是VS1838B红外接收传感器元件，该元件是集接收、放大、解调一体的器件，内部IC就已经完成了解调，输出的就是数字信号。它可接收标准38KHz调制的遥控器信号。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 1.5mA

最大功率: 0.25 W

工作温度：-10°C ~ +50°C

控制信号: 数字信号

尺寸：32 x 23.8 x 10.8 mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/361301.png)

红外遥控系统的主要部分为调制、发射和接收。红外遥控是以调制的方式发射数据，就是把数据和一定频率的载波进行“与”操作，这样既可以提高发射效率又可以降低电源功耗。调制载波频率一般在30khz到60khz之间，大多数使用的是38kHz，占空比1/3的方波。红外接收的信号端加上了4.7K的上拉电阻R3，工作时，首先等待检测低电平，接收到信号后，信号端立即由高电平转为低电平。

---

**4. 实验组件**

| ![img](media/KS5016.png)   | ![img](media/KE4036.png) | ![img](media/USB.jpg) |  
| ------------------------ | ------------------------ | -------------------------------- |
| 主板 x1   | 红外接收模块 x1  | USB线 x1  |
|![img](media/remotecontrol.png) | ![img](media/3pin.png) |    | 
| Keyes 遥控器 x1  | 3P线(反向) x1 |    |

---

**5. 模块接线图**

![img](media/361501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**IR_Receiver.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : IR Receiver
 * 功能   : 解码红外线遥控器，通过串口打印出来
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <IRremote.hpp>

#define IR_RECEIVE_PIN   3   // 定义红外接收模块引脚
IRrecv irrecv(IR_RECEIVE_PIN);     // 创建一个用于接收类的类对象
decode_results results;     // 创建一个解码结果类对象

void setup() {
  Serial.begin(9600);       // 初始化串口，波特率设置为9600
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);  // 启动接收器
}

void loop() {
  if (IrReceiver.decode()) {
      Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX);  // 打印原始数据
      IrReceiver.printIRResultShort(&Serial); // 在一行中打印接收到的完整数据
      IrReceiver.printIRSendUsage(&Serial);  // 打印发送这些数据所需的语句
      IrReceiver.resume(); // 启用接收下一个值
  }
  delay(100);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

正确的主板板型和串口端口设置完成后，则上传代码。若代码上传不成功，提示“**IRremote.hpp: No such file or directory**”，请添加库文件。其添加方法请参照：[添加Arduino库文件](https://www.keyesrobot.cn/projects/KE3095/en/latest/docs/Arduino_C_%E6%95%99%E7%A8%8B/Arduino_C_%E6%95%99%E7%A8%8B.html#arduino)

再次上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

找到红外遥控器，拔出绝缘片。对准红外接收模块的红外接收传感器的接收头，按下遥控器任意按键，接收到信号后，串口监视器窗口打印出当前接收到的按键值，同时，红外接收传感器上的LED会闪烁。

![img](media/IMG_164728.png)

![img](media/361704.png)


Keyes 遥控器上每一个按键都对应着一个按键值，如下图所示。

![img](media/361703.png)

---

**8. 代码说明**

| 代码                                  | 说明                               |
| ------------------------------------- | ---------------------------------- |
| irrecv.enableIRIn();                  | 初始化红外遥控。                   |
| irrecv.decode(&results)               | 等待解码。                         |
| serialPrintUint64(results.value, HEX) | 打印出解码结果。                   |
| irrecv.resume();                      | 恢复，等待接收下一个红外遥控信号。 |

---

### 第26课 检测温湿度

**1. 项目介绍**

XHT11温湿度传感器是一款低价的入门级温湿度传感器，它主要由一个电阻式感湿元件和一个NTC测温元件组成。XHT11为4针单排引脚封装，采用单线制串行接口，只需加适当的上拉电阻，信号传输距离可达20米以上，XHT11温湿度传感器具有超快响应、抗干扰能力强、性价比极高等优点。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 2.1mA

最大功率: 0.015W

温度范围: -25 ~ +60°C (± 2℃)

湿度范围: 5 ~ 95%RH（25C°左右精度为±5%RH）

输出信号: 数字双向单总线

工作温度: -25°C ~ +60°C

尺寸：32mm x 23.8mm x 9.7mm

接口：2.54mm间距，3pin防反接口

---

**3. 模块原理图**

![img](media/381301.png)

单片机与 XHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数部分用于以后扩展,现读出为零，操作流程：一次完整的数据传输为40bit，高位先出。

**数据格式：** 8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和。

**8位校验和：** 8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据"相加所得结果的末8位。

---

**4. 实验组件**

| ![img](media/KS5016.png)   |![img](media/KE4033.png)  |![img](media/3pin.png) | ![img](media/USB.jpg) |      
| ------------------------ | -------------------------- | ---------------------------- |---------------------------- |
| 主板 x1  | XHT11温湿度传感器 x1 | 3P线(反向) x1 | USB线 x1 |   
   
---

**5. 模块接线图**

![img](media/381501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**XHT11.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : xht11
 * 功能   : XHT11检测温度和湿度值
 * 作者   : http://www.keyes-robot.com/ 
*/
#include "xht11.h"

xht11 xht(3); //定义XHT11的引脚为D3

unsigned char dht[4] = {0, 0, 0, 0};// 只接收数据的前32位，不接收奇偶校验位
void setup() {
  Serial.begin(9600);//启动串口监视器，设置波特率为9600
}

void loop() {
  if (xht.receive(dht)) { //正确检查时返回true
    Serial.print("RH:");
    Serial.print(dht[0]); //湿度的积分部分DHT[1]为小数部分系列。打印(“%”);
    Serial.print("  Temp:");
    Serial.print(dht[2]); //温度的积分部分DHT[3]为小数部分
    Serial.println("°C");
//} else {    //Read error
//  Serial.println("sensor error");
  }
  delay(500);  //等待设备读取需要500ms
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

正确的主板板型和串口端口设置完成后，则上传代码。若代码上传不成功，提示“**xht11.h: No such file or directory**”，请添加库文件。其添加方法请参照：[添加Arduino库文件](https://www.keyesrobot.cn/projects/KE3095/en/latest/docs/Arduino_C_%E6%95%99%E7%A8%8B/Arduino_C_%E6%95%99%E7%A8%8B.html#arduino)

再次上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

串口监视器打印出XHT11温湿度传感器检测到当前环境的温湿度值。

![](media/IMG_161449.png)

![](media/381701.png)

---

**8. 代码说明**

| 代码                                | 说明                                         |
| ----------------------------------- | -------------------------------------------- |
| unsigned char dht[4] = {0, 0, 0, 0} | 将读取到的温湿度数据存放到dht[4]这个数组当中 |

---

### 第27课 四位数码管计数器

**1. 项目介绍**

TM1650四位数码管模块选用的是0.36 英寸红色共阴4位数码管，其驱动芯片是TM1650。TM1650是一种带键盘扫描接口的LED驱动控制专用电路的芯片。内部集成有MCU输入输出控制数字接口、数据锁存器、LED 驱动、键盘扫描等电路。TM1650性能稳定、质量可靠、抗干扰能力强，可适用于24小时长期连续工作的应用场合。TM1650采用两线串行传输协议通讯（注意：该数据传输协议不是标准的I2C协议）。该芯片只需要通过两个引脚与MCU通讯就可以完成数码管的驱动，可以节省MCU引脚资源。

实验中使用TM1650四位数码管模块时，我们只需要2根信号线即可使单片机控制4位数码管，大大节约了控制板IO口资源。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 42mA

最大功率: 0.21W

数码管显示颜色: 红色

LED极性: 共阴

通讯方式：2线高速串行接口（CLK,DAT）

工作温 ：-10°C ~ +50°C

尺寸：47.6mm x 23.8mm x 10.6mm

接口：2.54mm间距，4pin防反接口

---

**3. 模块原理图**

![](media/411301.png)

TM1650与MCU之间的通讯采用2线高速串行接口（CLK,DAT），这两个连线分别是数据线DAT和同步时钟线CLK。其中DAT为双向数据传输线，TM1650既用该线从MCU接收数据，也用该线向MCU发送数据。

实验中我们使用封装好的库函数。如果大家有兴趣也可以接着往下学习了解 3.1 TM1650通讯时序格式和 3.2 指令集说明，然后再去了解底层的库函数是如何实现的。

3.1 TM1650通讯时序格式

TM1650采用下图1 中2线串行传输协议通讯：

![](media/411302.png)

（1）开始信号（START）/结束信号(STOP)

开始信号：保持 CLK 为“1”电平，DAT 从“1”跳“0”，认为是开始信号，如上图1的 A 段；

结束信号：保持 CLK 为“1”电平，DAT 从“0”跳“1”，认为是结束信号，如上图1的 E 段；

（2）ACK 信号

如果本次通讯正常，芯片在串行通讯的第 8 个时钟下降沿后，TM1650 主动把 DAT 拉低，直到 CLK 检测到上升沿，DAT 释放为输入状态（对芯片而言）,如上图1的 D 段。

（3）写“1”和写“0”

写“1”：保持 DAT 为“1”电平，CLK 从“0”跳到“1”,再从“1”跳到“0”，则认为是写入“1” ，如上图 1的 B 段。

写“0”：保持 DAT 为“0”电平，CLK 从“0”跳到“1”,再从“1”跳到“0”，则认为是写入“0” ，如上图 1的 C 段。

（4）一个字节（8 位）数据传输格式

![](media/411303.png)

一个字节数据的传输格式如图上 2，数据发送时 MSB 在前，LSB 在后，即高位先进。微处理器的数据通过 2 线 串行接口和 TM1650 通信，当 CLK 是高电平时，DAT 上的信号必须保持不变；只有 CLK 上的时钟信号为低电平时， DAT 上的信号才能改变。数据输入的开始条件是 CLK 为高电平时，DAT 由高变低；结束条件是 CLK 为高时，DAT 由低电平变为高电平。 

（5）写显示操作

![](media/411304.png)

ADDRESS：显示地址（68H、6AH、6CH、6EH）； 

DATA：显示数据。

（6）完整操作时序

![](media/411305.png)

command1：系统命令 48H； 

command2：系统参数设置；

ADDRESS：显示地址（68H、6AH、6CH、6EH）；

DATA：显示数据。

备注：

1.设置系统参数和写入显存数据是两个独立的过程，它们之间的顺序不影响实际应用； 

2.每次输入系统命令（48H）和系统参数设置命令都会改变系统参数，请特别注意待机指令操作。

3.2 指令集说明

（1）数据命令设置 

![](media/411306.png)

<span style="color: rgb(255, 76, 65);">注意：</span>使用的指令是 16 进制 H，输入数据和读取数据都是从高位开始。

所以在代码中我们数据命令设置为 0x48，使用TM1650点亮数码管的功能，而不使用按键扫描的功能。

（2）显示命令设置

![](media/411307.png)

<span style="color: rgb(255, 76, 65);">注意：</span>在发送上述系统显示命令前需要先输入系统命令48H,如48H+11H=1级亮度开屏显示。

B[7:0]：这里实际是一个字节数据，只是不同位部分代表不同功能。

B[6:4]：设置数码管亮度。注意：000 最亮。

B[3]：设置是否显示小数点。

B[0]：设置数码管的开屏、关屏。

（3）显存地址

![](media/411308.png)

如果要显示小数点，则必须先需要将段模式设置为 8 段输出。

![](media/411309.png)

---

**4. 实验组件**

| ![img](media/KS5016.png)  |![img](media/KE4060.png)| ![img](media/4pin.png)       | ![img](media/USB.jpg) |     
| ------------------------ | ----------------------------- | ---------------------------- |---------------------------- |
|主板 x1   |TM1650四位数码管模块 x1  | 4P线(反向) x1 | USB线 x1| 
  
---

**5. 模块接线图**

![img](media/411501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Four_digital_tube.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : TM1650 Four digital tube
 * 功能   : TM1650四数码管显示0-9999
 * 作者   : http://www.keyes-robot.com/ 
*/
#include "TM1650.h"
#define CLK A5    //TM1650的引脚定义，可以更改为其他端口      
#define DIO A4
TM1650 DigitalTube(CLK,DIO);

void setup(){
  DigitalTube.setBrightness();  //设置亮度，0- 7，默认值:2
  DigitalTube.displayOnOFF();   //显示打开或关闭，0=显示关闭，1=显示打开，默认值:1
  for(char b=1;b<5;b++){
    DigitalTube.clearBit(b);    //清晰显示位
  }
  // DigitalTube.displayDot(1,true); //Bit0显示点，在displayBit()之前使用
  DigitalTube.displayBit(1,0);    //DigitalTube.display(bit,number); bit=0~3，number=0~9
}

void loop(){
  for(int num=0; num<10000; num++){
    displayFloatNum(num);
    delay(1000);
  }
}

void displayFloatNum(float num){
  if(num > 9999)
    return;
  int dat = num*10;
   //DigitalTube.displayDot(2,true); //Bit0显示点，在displayBit()之前使用
  if(dat/10000 != 0){
    DigitalTube.displayBit(1, dat%100000/10000);  
    DigitalTube.displayBit(2, dat%10000/1000);
    DigitalTube.displayBit(3, dat%1000/100);
    DigitalTube.displayBit(4, dat%100/10);
    return;
  }
  if(dat%10000/1000 != 0){
    DigitalTube.clearBit(1); 
    DigitalTube.displayBit(2, dat%10000/1000); 
    DigitalTube.displayBit(3, dat%1000/100);
    DigitalTube.displayBit(4, dat%100/10);
    return;
  }
  if(dat%1000/100 != 0){
  DigitalTube.clearBit(1); 
  DigitalTube.clearBit(2);
  DigitalTube.displayBit(3, dat%1000/100);
  DigitalTube.displayBit(4, dat%100/10);  
  return;
}
  DigitalTube.clearBit(1); 
  DigitalTube.clearBit(2);
  DigitalTube.clearBit(3);
  DigitalTube.displayBit(4, dat%100/10);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

正确的主板板型和串口端口设置完成后，则上传代码。若代码上传不成功，提示“**TM1650.h: No such file or directory**”，请添加库文件。其添加方法请参照：[添加Arduino库文件](https://www.keyesrobot.cn/projects/KE3095/en/latest/docs/Arduino_C_%E6%95%99%E7%A8%8B/Arduino_C_%E6%95%99%E7%A8%8B.html#arduino)

再次上传代码，代码上传成功后，拔下USB线断电。按照接线图正确接好模块后再用USB线连接到计算机上电。4位数码管显示数字，从0开始，每1000毫秒加1，直至加到9999后又从0开始循环。

![img](media/IMG_095436.png)

---

**8. 代码说明**

| 代码                        | 说明                                                |
| --------------------------- | --------------------------------------------------- |
| DigitalTube.setBrightness() | 设置亮度，0- 7，默认值:2 。                         |
| DigitalTube.displayOnOFF()  | 显示打开或关闭，0=显示关闭，1=显示打开，默认值:1 。 |
| DigitalTube.clearBit(b)     | 清除位显示。                                        |


---

### 第28课 点阵显示图案

**1. 项目介绍**

点阵，多个LED组成的阵列，它们的集合称为“阵”，其中单个单元称为“点”。8 X 8点阵共由64个发光二极管组成，且每个发光二极管是放置在行线和列线的交叉点上。

第02课我们学习了一个IO口控制一个led，这节课我们来学习用更少的IO口控制更多的led。

---

**2. 模块参数**

工作电压: DC 3.3V~5V 

工作电流: 190mA

最大功率: 0.95W

通讯方式：I2C通讯

I2C通信地址：0X70

点阵屏显示颜色：蓝色

工作温度：-10°C ~ +50°C

尺寸：32mm x 23.8mm x 9.3mm

接口：2.54mm间距，4pin防反接口

---

**3. 模块原理图**

![img](media/421301.png)

如原理图所示，如果想要点亮第一行第一列的LED灯，只需要将C1置高电平、R1置低电平就可以了。如果我们想让第一行led全部点亮，只需要将R1置为低电平，C1~C8全部置为高电平就可以了。原理非常简单，但是这样设置的话我们总共需要用到16个IO口，非常浪费单片机资源。为了节省IO口不浪费单片机资源，我们特别设计了这个HT16K33_8X8点阵模块，利用HT16K33芯片驱动1个8*8点阵，只需要利用单片机的I2C通信端口就能控制点阵的64个发光二极管。

我们这款Keyes HT16K33_8X8点阵模块已经固定了通信地址，地址为0x70。

---

**4. 实验组件**

| ![img](media/KS5016.png)    |![img](media/KE4066.png) | ![img](media/4pin.png) | ![img](media/USB.jpg) |     
| ------------------------ | ---------------------------- | ---------------------------- |---------------------------- |
| 主板 x1   | HT16K33_8X8点阵模块 x1 |4P线(反向) x1 | USB线 x1 | 
  
---

**5. 取模工具的使用说明**

点阵和驱动的原理都已经介绍完了，那点阵上显示的内容是怎么来的呢，有没有比较简便的方法？这里给大家介绍一款点阵取模工具，这块工具使用的是在线版，链接：[http://dotmatrixtool.com/#](http://dotmatrixtool.com/)

现在就一起看看怎么使用吧。

①打开链接如下图：

![img](media/wps5.jpg) 

②我们的点阵是8*8的，所以调整高度为8，宽度为8，如下图：

![img](media/wps6.jpg) 

③在**Byte order** 这里选择 **Row major** 这个模式

![img](media/wps7.jpg) 

④将图案生成16进制的数据

如下图，按鼠标左键选中，右键取消，画好自己想要的图案，点击**Generate**，就会生成我们所需要的十六进制的数据了。

![img](media/wps8.jpg) 

这个生成的十六进制的代码（0x00,0x42,0x41,0x09,0x09,0x41,0x42,0x00）就是我们需要显示的内容，我们先保存好，等一下需要放到程序里面。

---

**6. 模块接线图**

![img](media/421501.png)

---

**7. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**88_dot_matrix.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
 /*
 * 名称   : 8×8 Dot-matrix Display
 * 功能   : 8x8 LED点阵显示“笑脸”图案
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <Matrix.h>  //HT16K33 8*8点阵模块库

Matrix myMatrix(A4,A5);  //I2C接口:SDA引脚接A4, SCL引脚接A5
uint8_t LedArray1[8]={0x00,0x42,0x41,0x09,0x09,0x41,0x42,0x00};
uint8_t  LEDArray[8];

void setup(){
  myMatrix.begin(0x70);  //初始化矩阵
}

void loop(){
  myMatrix.clear();
  for(int i=0; i<8; i++)
  {
    LEDArray[i]=LedArray1[i];
    for(int j=7; j>=0; j--)
    {
      if((LEDArray[i]&0x01)>0)
      myMatrix.drawPixel(j, i,1);
      LEDArray[i] = LEDArray[i]>>1;
    }
  }
  myMatrix.writeDisplay();
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**8. 实验结果**

正确的主板板型和串口端口设置完成后，则上传代码。若代码上传不成功，提示“**Matrix.h: No such file or directory**”，请添加库文件。其添加方法请参照：[添加Arduino库文件](https://www.keyesrobot.cn/projects/KE3095/en/latest/docs/Arduino_C_%E6%95%99%E7%A8%8B/Arduino_C_%E6%95%99%E7%A8%8B.html#arduino)

再次上传代码，代码上传成功后，拔下USB线断电。按照接线图正确接好模块后再用USB线连接到计算机上电。HT16K33_8X8点阵模块显示“笑脸”图案。

![img](media/IMG_095833.png)

若代码上传成功后点阵屏不显示“笑脸”图案，尝试按一下RESET键。

![img](media/RESET.jpg)

---

**9. 代码说明**

点阵上的图案是由一个字节数据类型的数组构成的，我们用下面的表格表示。其中1表示亮，0表示灭，我们可以看到是一个“笑脸”图案。

![](media/421801.png)

一列一列来看，可以知道点亮“笑脸”图案的矩阵代码为{0x00,0x42,0x41,0x09,0x09,0x41,0x42,0x00}。

---

### 第29课 TFT 屏显示

**1. 项目介绍**

在这个套件中，有一款完全兼容Arduino IDE开发环境的主板，主板上集成了一块1.8寸TFT显示屏。TFT显示屏的分辨率为128 * RGB * 160，显示方向可以调整，横屏竖屏均可。TFT显示屏的驱动IC为ST7735S，采用SPI通信方式，具有显示效果好。

---

**2. 模块参数**

材料：TFT LCD

工作电压：3.3V~5V

TFT可视角度：120度

像素个数：128列 × 160行

分辨率：128 ×（RGB）× 160

通信方式：SPI通信

驱动芯片：ST7735S

总功耗：0.11W

工作温度：-10℃~+60℃

显示类型：a-Si TFT

背光：2片LED，平行

视角：12H

尺寸：1.8寸

---

**3. 模块原理图**

![img](media/431301.png)

---

**4. 实验组件**

| ![img](media/KS5016.png) | ![img](media/USB.jpg) |  
| ------------------------ | --------------------------- |
| 主板 x1| USB线 x1 |
      
---

**5. 模块接线图**

![img](media/011301.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**TFT_Screen.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : TFT_Screen
 * 功能   : TFT屏 显示字符串，多彩图案和纯色填充屏幕
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>

// 定义TFT屏的引脚
#define CS   8
#define RST  9
#define DC   10
#define MOSI  11
#define SCK   13

Adafruit_ST7735 tft = Adafruit_ST7735(CS, DC, RST);

uint16_t rgb888_to_565 (uint32_t rgb888, bool reverse = false)
{
	uint8_t r = (rgb888 & 0xFF0000) >> 16;
	uint8_t g = (rgb888 & 0x00FF00) >> 8;
	uint8_t b = (rgb888 & 0x0000FF);

    if (reverse)
    {
        return ((b >> 3) << 11) |
                ((g >> 2) << 5) |
                (r >> 3);
    }
	
	return ((r >> 3) << 11) |
            ((g >> 2) << 5) |
            (b >> 3);
}

uint16_t rgb888_to_565(uint8_t r, uint8_t g, uint8_t b, bool reverse = false)
{
    if (reverse)
    {
        return ((b >> 3) << 11) |
                ((g >> 2) << 5) |
                (r >> 3);
    }

    return ((r >> 3) << 11) |
            ((g >> 2) << 5) |
            (b >> 3);
}

void setup()
{
  tft.initR(INITR_BLACKTAB); // 屏幕初始化

  tft.fillScreen(ST77XX_BLACK); // 填充黑屏
  delay(1000);

  tft.setCursor(0, 0); // 设置起始光标位置
  tft.setTextColor(rgb888_to_565(0xFF0000, true)); // 设置颜色
  tft.setTextWrap(true); // 设置自动换行
  tft.print("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur adipiscing ante sed nibh tincidunt feugiat. Maecenas enim massa, fringilla sed malesuada et, malesuada sit amet turpis. Sed porttitor neque ut ante pretium vitae malesuada nunc bibendum. Nullam aliquet ultrices massa eu hendrerit. Ut sed nisi lorem. In vestibulum purus a tortor imperdiet posuere. ");
  delay(2000);
  tft.setTextWrap(false);
}

void loop()
{
  // 自定义颜色, 纯色填充屏幕, 轮番闪烁
  tft.fillScreen(rgb888_to_565(255, 0, 255, true)); // 紫色
  delay(500);
  tft.fillScreen(rgb888_to_565(0xFFC0CB, true)); // 粉色
  delay(500);
  tft.fillScreen(rgb888_to_565(255, 0, 0, true)); // 红色
  delay(1500);

  // 字符串显示
  tft.setCursor(0, 30);
  tft.setTextSize(1);
  tft.setTextColor(rgb888_to_565(0xE3CF57, true));
  tft.println("hello world!");
  delay(500);

  tft.setTextColor(rgb888_to_565(0xFF00FF, true));
  tft.setTextSize(2);
  tft.println("hello world!");
  delay(500);

  tft.setTextColor(rgb888_to_565(0xFF8000, true));
  tft.setTextSize(3);
  tft.println("hello world!");
  delay(1500);

  // 绘制直线
  uint16_t color = rgb888_to_565(255, 0, 0, true); // 红色
  tft.fillScreen(ST77XX_BLACK);
  for (int16_t x=0; x < tft.width(); x+=6){
    tft.drawLine(0, 0, x, tft.height()-1, color);
    delay(10);
  }
  for (int16_t y=0; y < tft.height(); y+=6){
    tft.drawLine(0, 0, tft.width()-1, y, color);
    delay(10);
  }

  color = rgb888_to_565(0, 255, 0, true); // 绿色
  tft.fillScreen(ST77XX_BLACK);
  for (int16_t x=0; x < tft.width(); x+=6){
    tft.drawLine(tft.width()-1, 0, x, tft.height()-1, color);
    delay(10);
  }
  for (int16_t y=0; y < tft.height(); y+=6){
    tft.drawLine(tft.width()-1, 0, 0, y, color);
    delay(10);
  }

  color = rgb888_to_565(0, 0, 255, true); // 蓝色
  tft.fillScreen(ST77XX_BLACK);
  for (int16_t x=0; x < tft.width(); x+=6){
    tft.drawLine(0, tft.height()-1, x, 0, color);
    delay(10);
  }
  for (int16_t y=0; y < tft.height(); y+=6){
    tft.drawLine(0, tft.height()-1, tft.width()-1, y, color);
    delay(10);
  }

  color = rgb888_to_565(255, 0, 255, true); // 紫色
  tft.fillScreen(ST77XX_BLACK);
  for (int16_t x=0; x < tft.width(); x+=6){
    tft.drawLine(tft.width()-1, tft.height()-1, x, 0, color);
    delay(10);
  }
  for (int16_t y=0; y < tft.height(); y+=6){
    tft.drawLine(tft.width()-1, tft.height()-1, 0, y, color);
    delay(10);
  }

  delay(1500);

  // 水平线和垂直线
  static uint16_t color1 = rgb888_to_565(255, 0, 0, true);
  static uint16_t color2 = rgb888_to_565(0, 0, 255);
  tft.fillScreen(ST77XX_BLACK);
  for (int16_t y=0; y < tft.height(); y+=5){
    tft.drawFastHLine(0, y, tft.width(), color1);
    delay(30);
  }
  for (int16_t x=0; x < tft.width(); x+=5){
    tft.drawFastVLine(x, 0, tft.height(), color2);
    delay(30);
  }

  delay(1500);

  // 绘制矩形
  static uint16_t color3 = rgb888_to_565(255, 0, 0, true);
  static uint16_t color4 = rgb888_to_565(0, 0, 255, true);
  tft.fillScreen(ST77XX_BLACK);
  for (int16_t x=tft.width()-1; x > 6; x-=6){
    tft.fillRect(tft.width()/2 -x/2, tft.height()/2 -x/2 , x, x, color3); // 实心
    tft.drawRect(tft.width()/2 -x/2, tft.height()/2 -x/2 , x, x, color4); // 边框
    delay(50);
  }
  delay(1500);

  // 绘制圆形
  static uint16_t color5 = rgb888_to_565(255, 0, 0, true);
  static uint16_t color6 = rgb888_to_565(0, 0, 255, true);
  static uint8_t radius = 30;
  tft.fillScreen(ST77XX_BLACK);
  for (int16_t x=0; x < tft.width()+radius; x+=radius*2)
  {
    for (int16_t y=0; y < tft.height()+radius; y+=radius*2)
      {
        tft.fillCircle(x, y, radius, color5);
        tft.drawCircle(x, y, radius, color6);
        delay(50);
      }
  }
  delay(1500);

  // 绘制三角形
  uint16_t color7 = rgb888_to_565(0, 0, 0, true);
  tft.fillScreen(ST77XX_BLACK);
  int w = tft.width() / 2;
  int x = tft.height() - 1;
  int y = 0;
  int z = tft.width();
  for(int t = 0 ; t <= 15; t++){
    tft.drawTriangle(w, y, y, x, z, x, color7);
    x-=4;
    y+=4;
    z-=4;
    color7+=100;
    delay(100);
    }

  delay(1500);

  // 绘制圆角矩形
  uint16_t color8 = rgb888_to_565(0, 0, 0, true);
  tft.fillScreen(ST77XX_BLACK);
  for(int t = 0 ; t <= 4; t+=1){
    int x = 0;
    int y = 0;
    int w = tft.width()-2;
    int h = tft.height()-2;
    for(int i = 0 ; i <= 16; i+=1)
      {
        tft.drawRoundRect(x, y, w, h, 5, color8);
        x+=2;
        y+=3;
        w-=4;
        h-=6;
        color8+=1100;
        delay(50);
      }
    color8 += 100;
  }

  delay(1500);

  // 绘制组合图案
  tft.fillScreen(ST77XX_BLACK);
  tft.fillRoundRect(25, 10, 78, 60, 8, ST77XX_WHITE);
  tft.fillTriangle(42, 20, 42, 60, 90, 40, rgb888_to_565(0xFF0000, true));
  delay(500);

  tft.fillRoundRect(25, 90, 78, 60, 8, ST77XX_WHITE);
  tft.fillRoundRect(39, 98, 20, 45, 5, rgb888_to_565(0x00FF00, true));
  tft.fillRoundRect(69, 98, 20, 45, 5, rgb888_to_565(0x00FF00, true));
  delay(500);

  tft.fillTriangle(42, 20, 42, 60, 90, 40, rgb888_to_565(0x0000FF, true));
  delay(50);

  tft.fillRoundRect(39, 98, 20, 45, 5, rgb888_to_565(0xFF0000, true));
  tft.fillRoundRect(69, 98, 20, 45, 5, rgb888_to_565(0xFF0000, true));

  tft.fillTriangle(42, 20, 42, 60, 90, 40, rgb888_to_565(0x00FF00, true));
  delay(1500);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

正确的主板板型和串口端口设置完成后，则上传代码。若代码上传不成功，提示“**Adafruit_GFX.h: No such file or directory**”，“**Adafruit_ST7735.h: No such file or directory**”和其他信息，请添加库文件。其添加方法请参照：[添加Arduino库文件](https://www.keyesrobot.cn/projects/KE3095/en/latest/docs/Arduino_C_%E6%95%99%E7%A8%8B/Arduino_C_%E6%95%99%E7%A8%8B.html#arduino)

再次上传代码，代码上传成功后，拔下USB线断电。按照接线图正确接好模块后再用USB线连接到计算机上电，TFT显示屏依次显示如下：

![Img](./media/img-2024058.png)

![Img](./media/img-2024059.png)

......

![Img](./media/img-20240511.png)

![Img](./media/img-20240512.png)

---

### 第30课 RFID刷卡

**1. 项目介绍**

RFID刷卡模块上内置的RFIDRFID-RC522射频模块采用的是Philips MFRC522原装芯片设计的读卡电路，使用方便，成本低廉，适用于设备开发、读卡器开发等高级应用的用户，也适用于需要进行射频卡终端设计/生产的用户。本模块可直接装入各种读卡器模具，通过IIC接口简单的2条线就可以直接与用户任何CPU主板或单片机相连接通信。

实验中用刷卡模块读取到的数据是4个16进制数，我们把这四个16进制数串以字符串的形式打印出来。例如，如果读取到的IC卡的数据为：0xED、0xF7、0x94、0x5A，则串口监视器显示出的信息字符串就是ED F7 94 5A；如果读取的钥匙扣的数据为：0x4C、0x09、0x6B、0x6E，则串口监视器打印出来的信息字符串就是4C 09 6B 6E。有时候看到的不是两位数，是因为前面有个0省略了，如0a它显示的就是a。不同的IC卡和钥匙扣，其数据是不一样的。

---

**2. 模块参数**

工作电压: DC 3.3V~5V

工作电流: 13~100mA /DC 5V 

空闲电流: 10~13mA /DC 5V

休眠电流: < 80uA

峰值电流: < 100mA

工作频率: 13.56MHz

最大功率: 0.5W

支持的卡类型：mifare1 S50、mifare1 S70、mifare UltraLight、mifare Pro、mifare Desfire

数据传输速率：最大10Mbit/s

工作温度：-10°C ~ +50°C

尺寸：47.6mm x 23.8mm x 9.3mm

接口：2.54mm间距，4pin防反接口

---

**3. 模块原理图**

![](media/441301.png)

RFID（Radio Frequency Identification）：无线射频识别，读卡器由频射模块及高平磁场组成。Tag应答器为待感应设备，此设备不包含电池。他只包含微型集成电路芯片及存储数据的介质以及接收和发送信号的天线。读取tag中的数据，首先要放到读卡器的读取范围内。读卡器会产生一个磁场，因为磁能生电由楞次定律，RFID Tag 就会供电，从而激活设备。

![](media/441302.png)

---

**4. 实验组件**

| ![img](media/KS5016.png)   |![img](media/KE4065.png) |![](media/WPS12.png)    |
| ------------------------ | ------------------------ | ---------------------------- |
|主板 x1  | RFID刷卡模块 x1    |IC卡/钥匙扣 x1| 
| ![img](media/4pin.png)| ![img](media/USB.jpg)  |     | 
| 4P线(反向) x1 | USB线 x1   |       |

---

**5. 模块接线图**

![img](media/441501.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**RFID.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : RFID
 * 功能   : RFID读取器UID
 * 作者   : http://www.keyes-robot.com/ 
*/
#include "MFRC522_I2C_SOFT.h"

// 0x28是SDA上的i2c地址。如果不匹配，请用i2cscanner检查您的地址.
MFRC522 mfrc522(0x28, A4, A5);   // 创建MFRC522实例.

void setup() {
  Serial.begin(115200);           // 初始化与PC的串行通信
  mfrc522.PCD_Init();             // 初始化 MFRC522
  ShowReaderDetails();            // 显示详细的PCD - MFRC522读卡器细节
  Serial.println(F("Scan PICC to see UID, type, and data blocks..."));
}

void loop() {
  // 寻找新的卡片，如果有的话选择一张
  if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) {
    delay(50);
    return;
  }
  
  // 选择一张门卡，UID和SAK分别为mfrc522.uid  
  // 保存UID
  Serial.print(F("Card UID:"));
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
    Serial.print(mfrc522.uid.uidByte[i], HEX);
  } 
  Serial.println();
}

void ShowReaderDetails() {
  // 获得MFRC522软件
  byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
  Serial.print(F("MFRC522 Software Version: 0x"));
  Serial.print(v, HEX);
  if (v == 0x91)
    Serial.print(F(" = v1.0"));
  else if (v == 0x92)
    Serial.print(F(" = v2.0"));
  else
    Serial.print(F(" (unknown)"));
  Serial.println("");
  // 返回0x00或0xFF时，可能是通信信号传输失败
  if ((v == 0x00) || (v == 0xFF)) {
    Serial.println(F("WARNING: Communication failure, is the MFRC522 properly connected?"));
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

正确的主板板型和串口端口设置完成后，则上传代码。若代码上传不成功，提示“**MFRC522_I2C.h: No such file or directory**”，请添加库文件。其添加方法请参照：[添加Arduino库文件](https://www.keyesrobot.cn/projects/KE3095/en/latest/docs/Arduino_C_%E6%95%99%E7%A8%8B/Arduino_C_%E6%95%99%E7%A8%8B.html#arduino)

再次上传代码，代码上传成功后，拔下USB线断电。按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>115200</u>**。用IC卡和钥匙扣靠近RFID模块，串口监视器打印出RFID刷卡模块读取到的数据信息。

![img](media/IMG_101921.png)

![img](media/IMG_101930.png)

![img](media/441701.png)

**注意：不同的IC卡和钥匙扣，其数据是不一样的。**<span style="background:#ff0;color:#000">请记录下你的IC卡和钥匙扣的UID码，后面的课程第44课会用到。</span>

若代码上传成功后串口监视器不打印数据信息，尝试按一下RESET键。

![img](media/RESET.jpg)

---

**8. 代码说明**

| 代码                                      | 说明                                 |
| ----------------------------------------- | ------------------------------------ |
| Wire.begin()                              | 进行IIC初始化。                      |
| mfrc522.PCD_Init()                        | MFRC522初始化。                      |
| Serial.print(mfrc522.uid.uidByte[i], HEX) | 将读取到16进制格式的值转为的字符串。 |

---

### 第31课 测大气压，海拔和温度

**1. 项目介绍**

BMP388气压传感器是一款新型高性能气压MEMS传感器，非常适用于消费电子无人机、可穿戴设备、智能家居和其他应用中的高度跟踪。BMP388气压传感器是可以检测温度和气压的传感器，是专为移动应用设计的绝对气压传感器。广泛应用于温度检测、大气压强检测、海拔高度检测、室内导航(楼层检测、电梯检测)、户外导航、休闲和运动的应用程序等。

---

**2. 模块参数**

工作电压: DC 3.3V~5V

工作电流：30uA

最大功率：0.000015W

气压测量范围: 300hPa~1100hPa (elevation +9000~-500m)

气压测量误差: ±1hPa (±1m)

气压测量精度: 0.18Pa

温度测量范围: 0℃~ 65℃

温度测量误差 ±0.5℃，maximum ±1℃

温度测量精度: 0.1℃

通讯方式: I2C通信

尺寸 ：31.6mm x 23.8mm x 9.3mm

接口：IIC接口和2.54mm间距，5pin防反接口 (INT引脚可不接)

---

**3. 模块原理图**

![Img](./media/B18.png)

BMP180气压传感器的工作原理是基于压电效应和微机械结构（MEMS）。

BMP180内部包含一个微机械结构（MEMS）传感器（压阻传感器）、一个高精度模拟-数字转换芯片（ADC）和一个带E2PROM和串行I2C接口的控制单元。在传感器内部，有一个薄膜弹性体受到外界压力的作用而产生形变，这种形变会导致传感器内部气体压力变化。当外部气压发生变化时，传感器内的气压也随之改变，压力变化引起薄膜弹性体的位移量发生微小改变，微机械结构传感器将这个微小的形变转换成输出电信号。然后，经过PLL电路、ADC等处理电路进行放大和转换，最终通过I2C接口向外输出数字信号，提供给上层系统进行数据处理和分析。

此外，BMP180还包含了一个温度传感器，可以测量传感器本身的温度，并利用温度补偿技术来消除温度对气压测量带来的误差，从而提高测量精度和稳定性。在其带有的E2PROM里存储了176位单独校准数据，用于补偿传感器的偏移等其他参数。

---

**4. 实验组件**

| ![img](media/KS5016.png)  | ![img](media/KE4040.png) |![img](media/4pin-5pin.png) | ![img](media/USB.jpg)    |
| ------------------------ | ------------------------ | ---------------------------- |---------------------------- |
|主板 x1   | BMP388气压传感器 x1 | 4P转5P线(反向) x1 |USB线 x1 |  
                
---

**5. 模块接线图**

![img](media/41_bb.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**bmp388_barometric_pressure.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : bmp388 barometric pressure
 * 功能   : bmp388气压传感器检测大气压，海拔和温度
 * 作者   : http://www.keyes-robot.com/ 
*/
#include "Waveshare_BMP388.h"

void setup() {  //把你的设置代码放在这里，只运行一次:
  bool bRet;
  PRESS_EN_SENSOR_TYPY enPressureType;
  Serial.begin(115200);
  pressSensorInit( &enPressureType );
  if(PRESS_EN_SENSOR_TYPY_BMP388 == enPressureType){
    Serial.println("Pressure sersor is BMP388");
  }
  else{
    Serial.println("Pressure sersor NULL");
  }
  Serial.println("/-------------------------------------------------------------/");
  delay(1000);
}

void loop() {  // 把主代码放在这里，反复运行:
  int32_t s32PressureVal = 0, s32TemperatureVal = 0, s32AltitudeVal = 0;
  pressSensorDataGet(&s32TemperatureVal, &s32PressureVal, &s32AltitudeVal);
  Serial.print("Pressure : "); 
  Serial.print((float)s32PressureVal / 100);
  Serial.print(" Pa"); 
  Serial.print("   Altitude : "); 
  Serial.print((float)s32AltitudeVal / 100);
  Serial.print(" m");
  Serial.print("   Temperature : "); 
  Serial.print((float)s32TemperatureVal / 100);
  Serial.print(" ℃");
  Serial.println();
  delay(50);  
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

正确的主板板型和串口端口设置完成后，则上传代码。若代码上传不成功，提示“**Waveshare_BMP388.h: No such file or directory**”，请添加库文件。其添加方法请参照：[添加Arduino库文件](https://www.keyesrobot.cn/projects/KE3095/en/latest/docs/Arduino_C_%E6%95%99%E7%A8%8B/Arduino_C_%E6%95%99%E7%A8%8B.html#arduino)

再次上传代码，代码上传成功后，拔下USB线断电。按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>115200</u>**，串口监视器显示大气压、海拔和温度。

![img](media/IMG_105029.png)

![img](media/B15.png)

若代码上传成功后串口监视器不打印数据信息，尝试按一下RESET键。

![img](media/RESET.jpg)

---

**8. 代码说明**

| 代码                                      | 说明                                 |
| ----------------------------------------- | ------------------------------------ |
|int32_t s32PressureVal = 0, s32TemperatureVal = 0, s32AltitudeVal = 0;| 设置初始值.|
|pressSensorDataGet(&s32TemperatureVal, &s32PressureVal, &s32AltitudeVal); | 检测并读取温度值，海拔高度值，大气压强值   |


---


### 第32课 检测手势方向

**1. 项目介绍**

PAJ7620U2手势识别传感器是可以识别9种不同方向的手势，包括上、下、左、右、前、后、顺时针、逆时针、摇摆。手势识别模块使用i2c接口，使用相应arduino库函数即可编程控制，手势识别模块返回的信号可用作为机器人接收的控制信号，从而实现对机器人的控制。内置的识别算法相当智能，能够把双手从生硬的按键中解放出来。手势识别传感器可用于非接触式控制场景，如非接触式鼠标，智能家居，汽车点击设备控制，机器人交互等。

---

**2. 模块参数**

工作电压：DC 3.3V~5V

工作电流：0.1mA

最大功率：0.0005W

识别距离：5cm~15cm

通讯方式：I2C通信

9种手势识别:

![Img](./media/zgxyz.png)

尺寸：31.6mm x 23.8mm x 9.3mm

接口：IIC接口和2.54mm间距，5pin防反接口 (INT引脚可不接)

---

**3. 模块原理图**

![Img](./media/B20.png)

PAJ7620U2手势传感器的工作原理是基于红外线反射和类似微软Kinect的深度视觉技术实现的。

它使用一个由红外发光二极管（IR-LED）和一对红外光传感器（IR photodiode）组成的衍射贴片（Diffraction Gratings）矩阵来发射和接收红外光信号，这样可以让传感器从各个角度接收到反射回来的红外线。当用户握手、挥手或移动其他体部，手或者身体会产生细微的变化，这些变化会导致返回传感器的反射光强度和相位发生变化。PAJ7620U2手势传感器通过计算这些光学特征的变化来识别不同的手势并跟踪其运动轨迹。

与此同时，PAJ7620U2还使用类似Kinect的深度视觉技术，来辅助判断物体和用户之间的距离。 它可以通过红外光投射在用户身上产生的影子来获得深度信息。PAJ7620U2将反射和深度信息结合起来进行处理，可以更加精确地识别大量的手势。

因为它不需要用户直接接触设备，所以可以提供更加方便、舒适和卫生的交互方式。同时，PAJ7620U2的应用范围较广，不受环境光、皮肤颜色等因素的干扰，适用于室内、室外等不同场景。

---

**4. 实验组件**

| ![img](media/KS5016.png)   |![img](media/KE4042.png) |![img](media/4pin-5pin.png)       |![img](media/USB.jpg)    |    
| ------------------------ | ------------------------ | ---------------------------- |---------------------------- |
| 主板 x1  |PAJ7620手势识别传感器 x1 | 4P转5P线(反向) x1 |USB线 x1  |   
     

---

**5. 模块接线图**

![img](media/43_bb.png)

---

**6. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**PAJ7620U2_gesture_recognition.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : PAJ7620U2 gesture recognition
 * 功能   : PAJ7620U2手势识别传感器检测手势方向
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <Wire.h>
#include "paj7620.h"
/* 
注意: 当你想识别前向/后向手势时，手势的反应时间必须小于GES_ENTRY_TIME(0.8s). 
        您也可以根据实际情况调整反应时间.
*/
#define GES_REACTION_TIME  500       // 您可以根据实际情况调整反应时间
#define GES_ENTRY_TIME    800       // 当你想识别前向/后向手势时，手势的反应时间必须小于GES_ENTRY_TIME(0.8s). 
#define GES_QUIT_TIME    1000

void setup(){
  uint8_t error = 0;
  Serial.begin(9600);
  Serial.println("\nPAJ7620U2 TEST DEMO: Recognize 9 gestures.");

  error = paj7620Init();      // 初始化Paj7620寄存器
  if (error) {
    Serial.print("INIT ERROR,CODE:");
    Serial.println(error);
  }
  else{
    Serial.println("INIT OK");
  }
  Serial.println("Please input your gestures:\n");
}

void loop(){
  uint8_t data = 0, data1 = 0, error;
  error = paj7620ReadReg(0x43, 1, &data);       // 读取 Reg_0x43/0x44 的手势结果.
  if (!error) {
    switch (data) {                  // 当检测到不同的手势时，变量`data`将被paj7620ReadReg设置为不同的值(0x43, 1, &data).
      case GES_RIGHT_FLAG:
        delay(GES_ENTRY_TIME);
        paj7620ReadReg(0x43, 1, &data);
        if(data == GES_FORWARD_FLAG) {
          Serial.println("Forward");
          delay(GES_QUIT_TIME);
        }
        else if(data == GES_BACKWARD_FLAG) {
          Serial.println("Backward");
          delay(GES_QUIT_TIME);
        }
        else{
          Serial.println("Right");
        }          
        break;
      case GES_LEFT_FLAG: 
        delay(GES_ENTRY_TIME);
        paj7620ReadReg(0x43, 1, &data);
        if(data == GES_FORWARD_FLAG) {
          Serial.println("Forward");
          delay(GES_QUIT_TIME);
        }
        else if(data == GES_BACKWARD_FLAG) {
          Serial.println("Backward");
          delay(GES_QUIT_TIME);
        }
        else{
          Serial.println("Left");
        }          
        break;
      case GES_UP_FLAG:
        delay(GES_ENTRY_TIME);
        paj7620ReadReg(0x43, 1, &data);
        if(data == GES_FORWARD_FLAG) {
          Serial.println("Forward");
          delay(GES_QUIT_TIME);
        }
        else if(data == GES_BACKWARD_FLAG) {
          Serial.println("Backward");
          delay(GES_QUIT_TIME);
        }
        else{
          Serial.println("Up");
        }          
        break;
      case GES_DOWN_FLAG:
        delay(GES_ENTRY_TIME);
        paj7620ReadReg(0x43, 1, &data);
        if(data == GES_FORWARD_FLAG) {
          Serial.println("Forward");
          delay(GES_QUIT_TIME);
        }
        else if(data == GES_BACKWARD_FLAG) {
          Serial.println("Backward");
          delay(GES_QUIT_TIME);
        }
        else{
          Serial.println("Down");
        }          
        break;
      case GES_FORWARD_FLAG:
        Serial.println("Forward");
        delay(GES_QUIT_TIME);
        break;
      case GES_BACKWARD_FLAG:     
        Serial.println("Backward");
        delay(GES_QUIT_TIME);
        break;
      case GES_CLOCKWISE_FLAG:
        Serial.println("Clockwise");
        break;
      case GES_COUNT_CLOCKWISE_FLAG:
        Serial.println("anti-clockwise");
        break;  
      default:
        paj7620ReadReg(0x44, 1, &data1);
        if (data1 == GES_WAVE_FLAG) 
        {
          Serial.println("wave");
        }
        break;
    }
  }
  delay(50);
} 
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**7. 实验结果**

正确的主板板型和串口端口设置完成后，则上传代码。若代码上传不成功，提示“**paj7620.h: No such file or directory**”，请添加库文件。其添加方法请参照：[添加Arduino库文件](https://www.keyesrobot.cn/projects/KE3095/en/latest/docs/Arduino_C_%E6%95%99%E7%A8%8B/Arduino_C_%E6%95%99%E7%A8%8B.html#arduino)

再次上传代码，代码上传成功后，拔下USB线断电。按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**，当我们在传感器前方摆动手势时，串口监视器上将显示我们对应的手势，如下图。(<span style="color: rgb(255, 76, 65);">注意：在我们测试手势时会有大约0.8秒的延迟。</span>)

![img](media/IMG_104809.png)

![img](media/IMG_104701.png)


![img](media/B17.png)

若代码上传成功后串口监视器不打印数据信息，尝试按一下RESET键。

![img](media/RESET.jpg)

---

**8. 代码说明**

| 代码                                      | 说明                                 |
| ----------------------------------------- | ------------------------------------ |
|error = paj7620Init()|初始化Paj7620寄存器|
|error = paj7620ReadReg(0x43, 1, &data)|读取Reg_0x43/0x44 的手势结果.|
|switch (data)|当检测到不同的手势时，变量`data`将被paj7620ReadReg设置为不同的值(0x43, 1, &data)|

---

## 4. 传感器/模块组合项目课程

前面课程中，我们单独测试了传感器/模块的功能，功能比较单一。在此，我们可以将多个传感器/模块搭配使用，组合出各种各样的功能。传感器/模块种类比较多，我们只是选择几款比较经典的组合实验。你们也可以根据自己的想法，自己设置代码，组合出你想要的特别的功能。

---

### 第33课 模拟小台灯

**1. 项目介绍**

从前面的实验课程中我们学习了自锁按键模块，按下自锁按键时，单片机读取到低电平；再次按自锁按键（按起自锁按键）时，读取到高电平。在这一实验课程中，我们将自锁按键模块和SK6812 RGB模块组合模拟小台灯，实现按下自锁按键时，SK6812 RGB模块点亮；再次按下自锁按键（按起自锁按键）时，SK6812 RGB模块不亮。

---

**2. 实验组件**

| ![img](media/KS5016.png)  |![img](media/KE4045.png)|  ![img](media/USB.jpg) |
| ------------------------ | ------------------------ | ------------------------ | 
| 主板 x1   | 自锁按键模块 x1    |USB线  x1  |
| ![img](media/KE4009.png) | ![img](media/3pin.png) |      |
|  SK6812 RGB模块 x1  | 3P线(反向) x2 |    |

---

**3. 模块接线图**

![img](media/451301.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Small_table_lamp.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/* 
 * 名称   : Small_table_lamp
 * 功能   : 模拟一个小台灯
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <FastLED.h>

#define NUM_LEDS  4   //定义SK6812 RGB模块上LED灯珠数量
#define LED_PIN  3   //定义SK6812 RGB模块控制引脚为D3
CRGB leds[NUM_LEDS];

#define PIN_BUTTON  2  //将自锁按键的引脚连接到D2
int val = 0;            //用于存储键值

void setup() {
  Serial.begin(9600);//启动串口监视器，设置波特率为9600
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(50);   //设置SK6812 RGB的亮度
  pinMode(PIN_BUTTON, INPUT);  //设置自锁按键的引脚为输入模式
}

// 循环函数会一直运行下去
void loop() {
  val = digitalRead(PIN_BUTTON);  //读取自锁按键的值并将其赋值给变量val
  Serial.println(val);          //打印变量val的值
  if (val == 0) {             //按下自锁按键时读取到低电平，并打印出相关的信息
    fill_solid(leds, NUM_LEDS, CRGB::White);   // SK6812 RGB模块4个LED灯珠亮白灯
    FastLED.show();
  }
  else { 
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // SK6812 RGB模块4个LED灯珠不亮
    FastLED.show(); 
  } 
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，按下自锁按键，SK6812 RGB模块点亮，再次按下自锁按键(自锁按键按起)，SK6812 RGB模块不亮。循环进行。

![](media/IMG_6126.png)

---

**6. 代码说明**

| 代码                                   | 说明                                                         |
| -------------------------------------- | ------------------------------------------------------------ |
| val = digitalRead(PIN_BUTTON) | 读取自锁按键的值并将其赋值给变量val|
| if( ){ } else{ }       | 这是判断语句，如果（ ）里的表达式为真，则执行 if { }块内的代码。如果（ ）里表达式为假 ，则执行 else { }块内的代码。 |
| fill_solid(leds, NUM_LEDS, CRGB::White) | SK6812 RGB模块上4个LED灯珠都亮白灯|
|fill_solid(leds, NUM_LEDS, CRGB::Black)| SK6812 RGB模块上4个LED灯珠都不亮 |

---

### 第34课 自动感应门

**1. 项目介绍**

在前面实验课程中，我们使用一个输入模块控制另一个输出模块。在这一实验中，我们还是用一个模块控制另一个模块。

生活中，我们可以利用一个检测传感器控制一个有源蜂鸣器响起，做声光报警设备，如人体检测（人体红外热释传感器）等等。这一实验课程中我们将避障传感器，舵机驱动板和舵机组合实验，实现避障传感器检测到障碍物时舵机转动带动门开的效果。

---

**2. 实验组件**

| ![img](media/KS5016.png) | ![img](media/KE4022.png) | ![img](media/KE4019.png) | ![img](media/9G.png)  | 
| ------------------------ | ------------------------ | ------------------------ | ---------------------------- | 
|主板 x1| 舵机驱动模块 x1      | 避障传感器 x1      | 舵机 x1 |
|![img](media/4pin.png)|![img](media/3pin.png)        | ![img](media/USB.jpg) |![img](media/ABC13.png) |
|  4P线(反向) x1 |3P线(反向) x1 | USB线 x1 | 一字螺丝刀 x1  | 

---

**3. 模块接线图**

![img](media/461301.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Automatic_induction_door.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : Automatic induction door
 * 功能   : 避障传感器控制舵机模拟自动感应门
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <Servo.h>   //导入舵机库文件

int servoPin = 6;    // 定义舵机引脚D6
Servo myservo;        //定义舵机类的实例

int avoidPin = 2;  //定义避障传感器的引脚D2

int item = 0;

void setup() {
  Serial.begin(9600);//启动串口监视器，设置波特率为9600
  pinMode(avoidPin, INPUT);  //设置避障传感器的引脚为输入模式
  myservo.attach(servoPin);  //选择舵机引脚D5
  myservo.write(0);  //关上门
  delay(1000); //给舵机转向的时间
}

void loop() {
  item = digitalRead(avoidPin); //读取避障传感器输出的电平值
  Serial.println(item);  //打印变量item的值
  if (item == 0) {//检测到障碍物
    myservo.write(110);  //打开门
    delay(500); //给舵机转向的时间
  } else { //未检测到障碍物
    delay(2000); //延时
    myservo.write(0);  //关上门
    delay(500); //给舵机转向的时间
  }
  delay(100);//延迟100ms
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电。当避障传感器检测到障碍物时，避障传感器上SLED灯亮起，舵机转动，门打开；当避障传感器检测不到障碍物时，舵机转动，门关闭。

![](media/IMG_6099.png)

---

**6. 代码说明**

可以参照第04课，第23课和第33课的代码说明，这里就不多做介绍了。

---

### 第35课 入侵检测报警器

**1. 项目介绍**

上一课实验中我们学习了使用避障传感器检测障碍物模拟智能感应门。在这一实验课程中我们将人体红外热释传感器、SK6812 RGB模块和有源蜂鸣器模块组合实验，实现人体红外热释传感器检测到附近有人经过时，有源蜂鸣器响起，SK6812 RGB模块上4个LED灯珠闪烁的效果。

---

**2. 实验组件**

| ![img](media/KS5016.png)   |![img](media/KE4018.png)     | ![img](media/KE4010.png) |
| ------------------------ | ---------------------------- | ------------------------ |
| 主板 x1  |人体红外热释传感器 x1  | 有源蜂鸣器模块 x1  |
|![img](media/KE4009.png) | ![img](media/3pin.png)  | ![img](media/USB.jpg)| 
|SK6812 RGB模块 x1|3P线(反向) x3 | USB线  x1   | 

---

**3. 模块接线图**

![](media/471301.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**PIR_alarm.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : PIR alarm
 * 功能   : PIR控制蜂鸣器和SK6812 RGB模块模拟入侵检测报警器
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <FastLED.h>

#define NUM_LEDS 4     //定义SK6812 RGB模块上LED灯珠数量
#define LED_PIN  3     //定义SK6812 RGB模块控制引脚为D3
CRGB leds[NUM_LEDS]; 

#define PIR_PIN  2     //PIR运动传感器控制引脚为D2
#define BUZZER_PIN  A2  //定义有源蜂鸣器控制引脚为A2

int item = 0;

void setup() {
  Serial.begin(9600);//启动串口监视器，设置波特率为9600
  pinMode(PIR_PIN, INPUT);  //设置PIR运动传感器的引脚为输入模式
  pinMode(BUZZER_PIN, OUTPUT); //设置有源蜂鸣器的引脚为输出模式
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(50);   //设置SK6812 RGB的亮度
}

void loop() {
  item = digitalRead(PIR_PIN);//读取红外热释传感器输出的数字信号
  Serial.println(item);          //打印变量item的值
  if (item == 1) {  //运动检测
    digitalWrite(BUZZER_PIN, HIGH); //打开蜂鸣器
    fill_solid(leds, NUM_LEDS, CRGB::Red);   // SK6812RGB模块4个LED灯珠亮红灯
    FastLED.show();
    delay(200);//延迟 200ms
    digitalWrite(BUZZER_PIN, LOW); //关掉蜂鸣器
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // SK6812 RGB模块4个LED灯珠不亮
    FastLED.show();
    delay(200);//延迟 200ms
  } else {  //没有检测到任何信号或数据
    digitalWrite(BUZZER_PIN, LOW); //关掉蜂鸣器
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // SK6812 RGB模块4个LED灯珠不亮
    FastLED.show();
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电。当人体红外热释传感器检测到附近有人经过时，人体红外热释传感器上的红灯灭，有源蜂鸣器发出警报，SK6812 RGB模块4个LED灯珠闪烁。

![](media/IMG_6071.png)

---

**6. 代码说明**

| 代码                   | 说明                                     |
| ---------------------- | ---------------------------------------- |
| item = digitalRead(PIR_PIN) | 人体红外热释传感器读取数字电平信号输出。 |
| item == 1              | 运动检测，检测到有人经过。               |
| digitalWrite(BUZZER_PIN, HIGH) | 有源蜂鸣器响起发出警报。                 |
| fill_solid(leds, NUM_LEDS, CRGB::Red)| SK6812RGB模块4个LED灯珠亮红灯 |


---

### 第36课 变色龙

**1. 项目介绍**

在第22课的实验中我们学习了使用旋转编码器计数。在这一实验课程中我们将旋转编码器模块和SK6812 RGB模块组合实验，通过旋转编码器计数的结果，控制SK6812 RGB模块上4个LED显示不同的颜色来模拟变色龙。

---

**2. 实验组件**

| ![img](media/KS5016.png)     |![img](media/KE4049.png)     | ![img](media/KE4009.png) |
| ---------------------------- | ---------------------------- | ------------------------ |
|主板 x1|  旋转编码器模块 x1      | SK6812 RGB模块 x1     |
| ![img](media/5pin.png)       | ![img](media/3pin.png)       | ![img](media/USB.jpg)    |  
| 5P线(反向) x1| 3P线(反向) x1 | USB线  x1   |   

---

**3. 模块接线图**

![img](media/491301.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Chameleon.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : Chameleon
 * 功能   : 旋转编码器控制RGB模拟变色龙
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <FastLED.h>

//旋转编码器的接口
int CLK = 3;
int DT = A5;
int SW = A4;

#define NUM_LEDS 4     //定义SK6812 RGB模块上LED灯珠数量
#define LED_PIN  2     //定义SK6812 RGB模块控制引脚为D2
CRGB leds[NUM_LEDS];

int counter = 0;  //定义变量counter
int currentStateCLK;  //定义变量currentStateCLK
int lastStateCLK;  //定义变量lastStateCLK

String currentDir ="";
unsigned long lastButtonPress = 0;

int val;

void setup() {
  Serial.begin(9600);   //设置波特率
// 将编码器引脚设置为输入
  pinMode(CLK,INPUT);
  pinMode(DT,INPUT);
  pinMode(SW,INPUT_PULLUP);
// 读取CLK的初始状态
  lastStateCLK = digitalRead(CLK);
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(50);   //设置SK6812 RGB的亮度
}

void loop() {
  // 读取CLK的当前状态
  currentStateCLK = digitalRead(CLK);
 
  //如果CLK的上一个状态和当前状态不同，则表示发生了脉冲
  //仅对一个状态变化做出响应，以避免重复计数
  if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){
 
   //如果DT状态不同于CLK状态，则编码器将旋转CW，因此减小
    if (digitalRead(DT) != currentStateCLK) {
      counter--;
      Serial.print(counter);
      Serial.print("  ");
      val = counter % 3;
      Serial.println(val);
    } else {
      //编码器旋转CCW所以递增
      counter ++;
      Serial.print(counter);
      Serial.print("  ");
      val = counter % 3;
      Serial.println(val);
    }
  }
 
  //记住上次的CLK状态
  lastStateCLK = currentStateCLK;
 
  //读取按钮状态
  int btnState = digitalRead(SW);
 
  //如果检测到低信号，则按钮被按下
  if (btnState == 0) {
  //如果距离上一次低脉冲时间已经过去50ms，则意味着按钮被按下、释放并再次按下
    if (millis() - lastButtonPress > 50) {
      Serial.println("Button pressed!");
    }
 
    //记住上次按按钮事件
    lastButtonPress = millis();
  }
 
  //添加一个轻微的延迟来帮助缓冲阅读
  delay(5);

  if (val == 0) {
    //红色灯
    fill_solid(leds, NUM_LEDS, CRGB::Red);   // SK6812RGB模块4个LED灯珠亮红色灯
    FastLED.show();
  } else if (val == 1) {
    //绿色灯
    fill_solid(leds, NUM_LEDS, CRGB::Green);   // SK6812RGB模块4个LED灯珠亮绿色灯
    FastLED.show();
  } else {
    //蓝色灯
    fill_solid(leds, NUM_LEDS, CRGB::Blue);   // SK6812RGB模块4个LED灯珠亮蓝色灯
    FastLED.show();
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电。打开串口监视器，设置波特率为 **<u>9600</u>**。

任意方向旋转编码器，串口监视器打印出对应余数；RGB模块上的LED显示余数对应的颜色：余数0显示红色、余数1显示绿色、余数2显示蓝色。按下旋转编码器，RGB模块上LED保持当前颜色不变。是不是很像变色龙呀！

![](media/IMG_5971.png)

![](media/491501.png)

---

**6. 代码说明**

| 代码                                 | 说明                                                         |
| ------------------------------------ | ------------------------------------------------------------ |
|lastStateCLK = digitalRead(CLK) | 读取CLK的初始状态。 |
|currentStateCLK = digitalRead(CLK)|读取CLK的当前状态|
|lastStateCLK = currentStateCLK|记住上次的CLK状态|
| int btnState = digitalRead(SW)|读取按钮状态|
| val = counter % 3| %是模除运算符。代码中模除的结果是旋转编码器计数的值与3相除的余数。 |


---


### 第37课 模拟智能窗户

**1. 项目介绍**

生活中能看到各种各样的智能产品，例如智能窗帘、智能窗户、智能电视、智能灯光等等。这一课我们来学习做一个智能窗帘，利用水滴水蒸气传感器模块检测雨水，然后通过设置舵机的角度来达到关窗和开窗的效果。

当然，这只是我们模拟的一个场景，主要用于加深我们的印象，达到对模块学以致用的效果。现实生活中，智能窗户并不是使用舵机来开关的。

---

**2. 实验组件**

| ![img](media/KS5016.png)  |![img](media/KE4048.png) | ![img](media/KE4022.png)| ![img](media/9G.png)|
| ------------------------ | ------------------------ | -------------------- | ---------------------------- |   
|主板 x1  |水滴传感器 x1 | 舵机驱动模块 x1       |  舵机 x1 |
|![img](media/3pin.png) | ![img](media/4pin.png)  | ![img](media/USB.jpg) |    |
| 3P线(反向) x1 | 4P线(反向) x1 |USB线 x1 |     | 

---

**3. 模块接线图**

![img](media/511301.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Smart_window.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

<span style="color: rgb(255, 76, 65);">注意：代码中的模拟值的阈值400可以根据实际情况设置。</span>

```c++
/*  
 * 名称   : smart window
 * 功能   : 水滴传感器控制舵机转动
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <Servo.h>   //导入舵机库文件

#define PIN_ADC  A2   //定义水滴传感器的引脚A2

int adcVal = 0;       //保存液滴传感器输出的ADC值的变量
int servoPin = 5;    // 定义舵机引脚D5
Servo myservo;        //定义舵机类的实例

void setup(){
  Serial.begin(9600);
  pinMode(PIN_ADC, INPUT);  //设置水滴传感器的引脚为输入模式
  myservo.attach(servoPin);  //选择舵机引脚D5
  myservo.write(180); //打开窗户
  delay(500); //延迟500毫秒
}

void loop(){
  adcVal = analogRead(PIN_ADC); //水滴传感器连接到模拟端口A2
  Serial.println(adcVal);
  if (adcVal > 400) { //模拟值大于400
    myservo.write(0);  //关上窗户
    delay(500); //给舵机转向的时间
  } else { // 没有下雨
    myservo.write(180); //打开窗户
    delay(500); //延迟500毫秒
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电。当水滴传感器检测到一定水量，舵机转动达到关窗的效果。否则舵机转动到另一个角度，达到开窗的效果。

![img](media/IMG_5961.png)

---

**6. 代码说明**

可以参照第13课和第23课的代码说明，这里就不多做介绍了。

---

### 第38课 夜光灯

**1. 项目介绍**

前面我们已经学习了光敏传感器的工作原理，在这一项目中，我们将光敏传感器和SK6812 RGB模块组合实验，实现光敏传感器检测到光照强度模拟值高于设定值时SK6812 RGB模块上的4个LED灯珠熄灭，低于设定值时SK6812 RGB模块上的4个LED灯珠点亮的效果。

---

**2. 实验组件**

| ![img](media/KS5016.png) |![img](media/KE4009.png) |![img](media/KE4026.png) |
| ------------------------ | ------------------------ | ------------------------ |
|主板 x1| SK6812 RGB模块 x1   |光敏传感器 x1  |
| ![img](media/3pin.png)  | ![img](media/USB.jpg) |   |
| 3P线(反向) x2 | USB线 x1   |     |

---

**3. 模块接线图**

![img](media/521301.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Night_lamp.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

<span style="color: rgb(255, 76, 65);">注意：代码中的模拟值的阈值200可以根据实际情况设置。</span>

```c++
/*  
 * 名称   : Night_lamp
 * 功能   : 光敏传感器控制SK6812 RGB模块上4个LED灯珠亮灭模拟夜光灯
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <FastLED.h>

int Photosensor = A2;  //定义光敏传感器的控制引脚为A2

#define NUM_LEDS 4   //定义SK6812 RGB模块上LED灯珠数量
#define LED_PIN  3   //定义SK6812 RGB模块控制引脚为D3
CRGB leds[NUM_LEDS];

void setup() {
  Serial.begin(9600); //波特率设置为9600
  pinMode(Photosensor, INPUT);  //设置光敏传感器的引脚为输入模式
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(50); //设置SK6812 RGB模块上4个LED灯珠的亮度
}

void loop() {
  int val = analogRead(Photosensor); //读取模拟值
  Serial.println(val); // 串口打印
  if(val < 200){ //低于阈值
    fill_solid(leds, NUM_LEDS, CRGB::White);   // SK6812 RGB模块4个LED灯珠亮白灯
    FastLED.show();
  }else{ //否则
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // SK6812 RGB模块4个LED灯珠不亮
    FastLED.show();
  }
  delay(100);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

串口监视器打印出光敏传感器检测到光照强度对应的模拟值，使用手机的照明灯对着光敏传感器模块上的光敏电阻照射时，当光敏传感器检测到光照强度的模拟值大于等于200时，SK6812 RGB模块上的4个LED灯珠熄灭；否则，低于200时，SK6812 RGB模块上的4个LED灯珠点亮，实现了夜光灯的效果。

![img](media/IMG_5927.png)

---

**6. 代码说明**

| 代码                           | 说明               |
| ------------------------------ | ------------------ |
| int val = analogRead(Photosensor) | 读取模拟值。       |
| Serial.print(val)              | 串口打印出模拟值。 |


---

### 第39课 压力报警器

**1. 项目介绍**

生活中，有很多人会使用压力器来测量自己的最大压力有多少，有些压力器具有压力过大警报功能，提醒人们压力过大，需要进行减少压力以免损坏压力器。这一课我们来学习制作一个压力报警器，它虽然简单，但却是非常具有意义的。原理很简单，利用薄膜压力传感器检测到一定压力（设定压力值），检测的结果控制一个有源蜂鸣器响起。

---

**2. 实验组件**

| ![img](media/KS5016.png)|![img](media/KE4010.png)|![img](media/USB.jpg)  | 
| ---------------------------- | ---------------------------- | ------------------------ |
|主板 x1 | 有源蜂鸣器模块 x1 | USB线  x1 |
|  ![img](media/KE4069.png) |![img](media/3pin.png)|      | 
| 薄膜压力传感器 x1      |3P线(反向) x2 |     |

---

**3. 模块接线图**

![img](media/531301.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Pressure_alarm.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

<span style="color: rgb(255, 76, 65);">注意：代码中的模拟值的阈值400可以根据实际情况设置。</span>

```c++
/*  
 * 名称   : Pressure_alarm
 * 功能   : 薄膜压力传感器控制蜂鸣器
 * 作者   : http://www.keyes-robot.com/ 
*/
int item = 0;  //设置item为0
int PIN_ANALOG_IN = A2;   //薄膜压力传感器的引脚定义为A2
int buzzer = 3;   //蜂鸣器的引脚定义为D3

void setup() {
  Serial.begin(9600); //设置串口波特率为9600
  pinMode(PIN_ANALOG_IN, INPUT);    //将传感器的引脚设置为输入模式
  pinMode(buzzer, OUTPUT);  // 设置蜂鸣器引脚为输出模式.
}

void loop() {
  item = analogRead(PIN_ANALOG_IN);    //读取传感器的模拟信号
  Serial.println(item);  //串口打印薄膜压力传感器输出的模拟信号
  delay(100);  //延时
  if (item < 400) {  //压力强度的模拟值小于400
    digitalWrite(buzzer, HIGH);  //打开蜂鸣器
  } else {  //模拟值大于等于400
    digitalWrite(buzzer, LOW);  //关闭蜂鸣器
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电。为了使实验数据最精准，请将薄膜压力传感器尽量平放。

串口监视器打印出薄膜压力传感器的模拟值。用手按压薄膜时，随着压力的增大，可以看到模拟值逐渐变小。

当薄膜压力传感器的模拟值小于400时，有源蜂鸣器响起，否则有源蜂鸣器不响。

![Img](./media/img-20240611084157.png)

---

**6. 代码说明**

可以参照第13课和第33课的代码说明，这里就不多做介绍了。

---

### 第40课 温湿度仪表 

**1. 项目介绍**：

在冬季时，空气中的湿度很低，就是空气很干燥，再加上寒冷，人体的皮肤就容易过于干燥而裂，所以需要使用加湿器给家里的空气增加湿度，但是怎么知道空气过于干燥了呢？那就需要检测空气湿度的设备，前面我们已经学习过了XHT11温湿度传感器的工作原理。在这一课我们将结合XHT11温湿度传感器和主板上的TFT显示屏来制作一个温湿度仪表，在TFT显示屏上显示XHT11温湿度传感器检测到环境中的温度和湿度。

---

**2. 实验组件**

| ![img](media/KS5016.png)  |![img](media/KE4033.png)  |![img](media/3pin.png)         |![img](media/USB.jpg)    |
| ------------------------ | ---------------------------- | ----------------------------- |----------------------------- |
|主板 x1| XHT11温湿度传感器 x1|3P线(反向) x1  |USB线 x1 |


---

**3. 模块接线图**

![](media/5813011.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Temperature-humidity_meter.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : Temperature-humidity meter
 * 功能   : TFT屏显示温度和湿度模拟温湿度仪表
 * 作者   : http://www.keyes-robot.com/ 
*/
#include "xht11.h"
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>

xht11 xht(3); //定义XHT11的引脚为D3
unsigned char dht[4] = {0, 0, 0, 0};// 只接收数据的前32位，不接收奇偶校验位

// 定义TFT屏的引脚
#define CS   8
#define RST  9
#define DC   10
#define MOSI  11
#define SCK   13

Adafruit_ST7735 tft = Adafruit_ST7735(CS, DC, RST);

void setup(){
  tft.initR(INITR_BLACKTAB); // 屏幕初始化
  tft.fillScreen(ST7735_BLACK); // 清屏 
  delay(1000);
}

//TFT屏显示温度值和湿度值
void loop(){
  tft.setTextSize(2);   //设置显示字符大小
  tft.setRotation(1);   //反转90°
  tft.setTextColor(ST7735_WHITE); //设置显示字符为白色
  tft.fillScreen(ST7735_BLACK);  // 清屏 
  if (xht.receive(dht)) { //正确检查时返回true
  }
  tft.setCursor(0, 20);  //设置显示的位置
  tft.print("Temper:");  //打印字符
  tft.setCursor(90, 20); 
  tft.print(dht[2]);
  tft.setCursor(120, 20);
  tft.print("C");
  tft.setCursor(0, 50); 
  tft.print("Humid:");
  tft.setCursor(80, 50);
  tft.print(dht[0]);
  tft.setCursor(110, 50);
  tft.print("%");
  delay(500);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，上传成功后可以在主板上的TFT显示屏看到XHT11温湿度传感器检测到环境中的温度值和湿度值。

![Img](./media/img-20240611084302.png)

---

**6. 代码说明**

可以参照第26课的代码说明，这里就不多做介绍了。

---

### 第41课 TFT显示屏显示TF卡图片 

**1. 项目介绍**：

本套件中的主板内置有TF卡槽，在TF卡保存图片文件，然后将TF卡插入TF卡槽，通过编写相关程序代码，这样TFT显示屏会显示TF卡中的图片相关信息。

---

**2. 实验组件**

| ![img](media/KS5016.png) | ![img](media/WS2.png)     |![img](media/WS5.png)     |![img](media/USB.jpg) |
| ------------------------ | ---------------------------- | ----------------------------- |----------------------------- |
|主板 x1| TF卡 x1 | 读卡器 x1|USB线  x1|


---

**3. 将图片转入TF卡**

(1). 将TF卡插入读卡器

![](media/WS3.png)

(2). 将读卡器(已经插有TF卡)插入电脑的USB口上。

![](media/WS1.png)

(3). 鼠标左双击“**我的电脑(或此电脑)**”，在此页面可以看到“**U盘**”标志。鼠标右单击“**U盘**”标志，出现对话框，选择单击“**属性**”查看TF卡的文件系统是不是“**FAT32**”？如果是就可以了。

![](media/WS7.png)

![](media/WS6.png)

如果TF卡的文件系统不是“**FAT32**”，则需要修改TF卡的文件系统。鼠标右单击“**U盘**”标志，出现对话框，选择单击“**格式化**”，出现格式化页面，在此页面点击“**FAT(默认)**”后面的**下拉箭头**来选择“**FAT32**”，然后点击“**开始**”进行格式化。这样，TF卡的文件系统“**FAT**”更改成“**FAT32**”。

![](media/WS8.png)

![](media/WS9.png)

![](media/WS10.png)

(4). 全部选中文件夹中的图片(**文件类型：BMP 图片文件(.bmp)**)，然后将选中的图片**发送到**(或**复制粘贴到**)读卡器中的TF卡(U盘)。<span style="color: rgb(255, 76, 65);">（**特别注意：图片名字和格式都不能修改**）</span>

![](media/ABC12.png)

![Img](./media/ABC21.png)

(5). 图片发送到TF卡(U盘)后，退出电脑上的读卡器中的TF卡(U盘)，将TF卡正确插入主板上的TF卡槽中。

![Img](./media/WS16.png)

![Img](./media/WS17.png)

![Img](./media/WS11.png)

---

**4. 模块接线图**

![](media/KE0175.png)

---

**5. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**TFT_displays_TF_card_pattern.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : TFT_displays_TF_card_pattern
 * 功能   : TFT显示屏显示TF卡的图片
 * 作者   : http://www.keyes-robot.com/ 
*/
#include "Adafruit_GFX.h"
#include "Adafruit_ST7735.h"
#include <SD.h>
#include <SPI.h>

#if defined(__SAM3X8E__)
    #undef __FlashStringHelper::F(string_literal)
    #define F(string_literal) string_literal
#endif

// TFT显示器和TF卡将共享硬件SPI接口
//硬件SPI引脚是特定于Arduino板类型和
//不能重新映射到备用引脚。对于Arduino Uno来说，
// Duemilanove等，pin 11 = MOSI, pin 12 = MISO, pin 13 = SCK。
#define SD_CS    7  // TF卡的CS接线口
#define TFT_CS  8  // TFT 的CS接线口
#define TFT_DC   10  // TFT的DC接线口
#define TFT_RST  9  //TFT的RST接线口

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

int Delay_Timer = 300;

void setup(void) {
  Serial.begin(9600);
  Pin_Set();
  // Initialize 1.8" TFT
  //tft.initR(INITR_BLACKTAB); 
  tft.initR();
  
  Serial.println("OK!");
  tft.fillScreen(ST7735_BLACK);
  tft.setRotation(45);
  tft.setTextSize(2);
  Serial.print("Initializing SD card...");
  if (!SD.begin(SD_CS))
  {
    Serial.println("failed!");
    tft.setTextColor(ST7735_RED);
    tft.setCursor(30, 40);
    tft.print("SD Error");
    return;
   }
   else
   {
      Serial.println("SD OK");
      tft.setTextColor(ST7735_RED);
      tft.setCursor(30, 40);
      
      tft.print("SD OK");
   }
  delay(1500);
  Voltage_Test();   //A6/A7测试5V电压
  delay(10000);
}

void loop() 
{ 
  tft.setRotation(0);
  Bmp();            //TF卡读取图片测试
  //while (1);
}

void Voltage_Test()
{
  tft.setTextSize(1);
  String A6_Value = String(analogRead(A6) *5.0 / 1023);
  String A7_Value = String(analogRead(A7) *5.0 / 1023);
  Serial.println(String("A6_Value:") + A6_Value + " V");
  Serial.println(String("A7_Value:") + A7_Value + " V");
  tft.setTextColor(ST7735_BLACK);
  tft.setCursor(10, 80);
  tft.print(String("A6_Value:") + A6_Value + " V");
  tft.setCursor(10, 100);
  tft.print(String("A7_Value:") + A7_Value + " V");
  delay(250);
  tft.setTextColor(ST7735_WHITE);
  tft.setCursor(10, 80);
  tft.print(String("A6_Value:") + A6_Value + " V");
  tft.setCursor(10, 100);
  tft.print(String("A7_Value:") + A7_Value + " V");
  delay(250);
}

void Bmp()
{
  bmpDraw("car.bmp", 0, 0);
  delay(Delay_Timer);
  bmpDraw("avatar.bmp", 0, 0);
  delay(Delay_Timer);
  bmpDraw("DEMA.bmp", 0, 0);
  delay(Delay_Timer);
  bmpDraw("DLAM.bmp", 0, 0);
  delay(Delay_Timer);
  bmpDraw("mangseng.bmp", 0, 0);
  delay(Delay_Timer);
  bmpDraw("TLP.bmp", 0, 0);
  delay(Delay_Timer);
  bmpDraw("girl.bmp", 0, 0);
  delay(Delay_Timer);
}

void Pin_Set()
{
  for(int i = 0; i < 7; i++)
  {
    pinMode(i,OUTPUT);
  }
  for(int i = 14; i < 20; i++)
  {
    pinMode(i,OUTPUT);
  }

  for(int i = 0; i < 7; i++)
  {
    digitalWrite(i,HIGH);
  }
  for(int i = 14; i < 20; i++)
  {
    digitalWrite(i,HIGH);
  }
}

//这个函数打开一个Windows位图(BMP)文件，并在给定的坐标处显示它。
//它通过一次读取多个像素值的数据(而不是逐像素)来加速。
//增加缓冲区大小会占用更多Arduino宝贵的RAM，但会使加载速度更快。
//20像素似乎是一个很好的平衡.

#define BUFFPIXEL 20

void bmpDraw(char *filename, uint8_t x, uint8_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // W+H，单位为像素
  uint8_t  bmpDepth;              // 位深度(当前必须为24)
  uint32_t bmpImageoffset;        // 开始图像数据在文件
  uint32_t rowSize;               // 可能有衬垫,Not always = bmpWidth
  uint8_t  sdbuffer[3*BUFFPIXEL]; // 像素缓冲区(每像素R+G+B)
  uint8_t  buffidx = sizeof(sdbuffer); // 在sdbuffer中的当前位置
  boolean  goodBmp = false;       // 在有效的头文件解析时设置为true
  boolean  flip    = true;        // BMP是自底向上存储的
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  if((x >= tft.width()) || (y >= tft.height())) return;

  Serial.println();
  Serial.print("Loading image '");
  Serial.print(filename);
  Serial.println('\'');

  // 在SD卡上打开所要求的文件
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.print("File not found");
    return;
  }

  // 解析BMP头
  if(read16(bmpFile) == 0x4D42) { // BMP签名
    Serial.print("File size: "); Serial.println(read32(bmpFile));
    (void)read32(bmpFile); //读取&忽略创建者字节
    bmpImageoffset = read32(bmpFile); // 图像数据开始
    Serial.print("Image Offset: "); Serial.println(bmpImageoffset, DEC);
    // 读取DIB Header
    Serial.print("Header size: "); Serial.println(read32(bmpFile));
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) { // # planes——必须为` 1 `
      bmpDepth = read16(bmpFile); // 每像素色彩位数
      Serial.print("Bit Depth: "); Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed

        goodBmp = true; //支持BMP格式——继续!
        Serial.print("Image size: ");
        Serial.print(bmpWidth);
        Serial.print('x');
        Serial.println(bmpHeight);

        //BMP行(如果需要)被填充到4字节的边界
        rowSize = (bmpWidth * 3 + 3) & ~3;

     //如果bmpHeight为负，则图像按照自上而下的顺序排列
     //这不是正例，但在实际环境中已经被观察到。
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }

        // 作物区装载
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft.width())  w = tft.width()  - x;
        if((y+h-1) >= tft.height()) h = tft.height() - y;

        // 将TFT地址窗口设置为裁剪的图像边界
        tft.startWrite();
        tft.setAddrWindow(x, y, w, h);

        for (row=0; row<h; row++) { // 对于每个扫描线…

          //寻找扫描线的起始位置这可能看起来很辛苦
          //对每一行都这样做很密集，但这个
          //方法涵盖了很多细节，比如裁剪
          //和扫描线填充。而且，seek只占用
          //如果文件位置需要更改，则放置位置
          //(避免了SD库中大量的集群计算)
          if(flip) //位图以自底向上的顺序存储(普通BMP)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else     //位图自顶向下存储
            pos = bmpImageoffset + row * rowSize;
          if(bmpFile.position() != pos) { // 需要寻找吗?
            tft.endWrite();
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // 强制缓冲重新加载
          }

          for (col=0; col<w; col++) { // 对于每个像素…
            // 是时候读取更多像素数据了?
            if (buffidx >= sizeof(sdbuffer)) { // 为真
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // 设置buffidx初始值为0
              tft.startWrite();
            }

            //将像素从BMP转换为TFT格式，推送显示
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft.pushColor(tft.color565(r,g,b));
          } //结束 pixel
        } //结束 scanline
        tft.endWrite();
        Serial.print("Loaded in ");
        Serial.print(millis() - startTime);
        Serial.println(" ms");
      } //结束goodBmp
    }
  }

  bmpFile.close();
  if(!goodBmp) Serial.println("BMP format not recognized.");
}

//它们从TF卡文件中读取16位和32位类型。
// BMP数据存储小端序，Arduino也是小端序。
//如果在其他地方移植，可能需要颠倒下标顺序。

uint16_t read16(File f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

uint32_t read32(File f) {
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // MSB
  return result;
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**6. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，再用USB线连接到计算机上电，上传成功后可以在主板上的TFT显示屏看到“SD OK”字样，说明主板可以正常读取TF卡上的内容，然后TFT显示屏显示“A6_Value: 5.00 V，“A7_Value: 5.00 V ”字样。之后TFT显示屏循环显示TF卡上的图片文件。

![Img](./media/WS12.png)

![Img](./media/WS13.png)

......

![Img](./media/WS14.png)

![Img](./media/WS15.png)

---

### 第42课 碰撞警报器

**1. 项目介绍**

在如今大城市中，几乎家家户户都拥有一辆代步的私家车，很多私家车都是停靠在路边，很多行人在行走时都会不小心碰到私家车。这时，被碰到的私家车都会出现灯光闪烁，蜂鸣器鸣叫，提醒人们，你已经碰到车了。那我们可不可以也来模拟被碰撞了就发出警报呢？当然可以了，这个实验我们就来使用碰撞传感器控制蜂鸣器和SK6812 RGB模块来模拟碰撞警报器。

---

**2. 实验组件**

| ![img](media/KS5016.png)   |![img](media/KE4023.png)     | ![img](media/KE4010.png) |
| ------------------------ | ---------------------------- | ------------------------ |
| 主板 x1  |碰撞传感器 x1  | 有源蜂鸣器模块 x1  |
|![img](media/KE4009.png) | ![img](media/3pin.png)  | ![img](media/USB.jpg)| 
|SK6812 RGB模块 x1|3P线(反向) x3 | USB线  x1   | 

---

**3. 模块接线图**

![img](media/57130112.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Collision_alarm.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*
 * 名称   : Collision_alarm
 * 功能   : 碰撞传感器控制SK6812 RGB灯和蜂鸣器模拟触碰报警器
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <FastLED.h>

#define NUM_LEDS 4   //定义SK6812 RGB模块上LED灯珠数量
#define LED_PIN  3   //定义SK6812 RGB模块控制引脚为D3
CRGB leds[NUM_LEDS];

#define buzzerPin 2  //定义有源蜂鸣器引脚为D2

int Collision = A2; //定义碰撞传感器的引脚
int val = 0;

void setup() {
  Serial.begin(9600);  //波特率设置为9600
  pinMode(Collision, INPUT);   //设置碰撞传感器的引脚D3为输入模式
  pinMode(buzzerPin, OUTPUT); //将buzzerPin设置为输出模式
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(50);   //设置SK6812 RGB的亮度  
}

void loop() {
  val = digitalRead(Collision);  //读取碰撞传感器的值
  Serial.println(val);      //打印碰撞传感器的值
  if (val == 0) {   //碰撞时的值为0
    digitalWrite(buzzerPin, HIGH); //蜂鸣器鸣叫
    fill_solid(leds, NUM_LEDS, CRGB::Red);   // SK6812 RGB模块4个LED灯珠亮红灯
    FastLED.show();
    delay(300);
    fill_solid(leds, NUM_LEDS, CRGB::Green);   // SK6812 RGB模块4个LED灯珠亮绿色
    FastLED.show();
    delay(300);
    fill_solid(leds, NUM_LEDS, CRGB::Yellow);   // SK6812 RGB模块4个LED灯珠亮黄灯
    FastLED.show();
    delay(300);
    fill_solid(leds, NUM_LEDS, CRGB::Blue);   // SK6812 RGB模块4个LED灯珠亮蓝色
    FastLED.show();
    delay(300);
  }
  else {    //无碰撞时的值为1
    digitalWrite(buzzerPin, LOW); //蜂鸣器不叫
    fill_solid(leds, NUM_LEDS, CRGB::Black);   // SK6812 RGB模块4个LED灯珠不亮
    FastLED.show();
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

当手按压碰撞传感器上的金属弹片时，传感器上的灯亮红灯，蜂鸣器鸣叫，SK6812 RGB模块上的4个LED灯珠闪烁不同的颜色灯。

![Img](./media/img-20240611084538.png)

---

**6. 代码说明**

可以参照第21课和第33课的代码说明，这里就不多做介绍了。

---

### 第43课 智能红外遥控灯

**1. 项目介绍**

在前面实验中，我们学会了SK6812 RGB模块上4个LED灯珠亮不同颜色灯、也学会了使用红外接收模块，并将接收到的遥控器对应的键值打印出来。在这一实验课程中，我们将红外接收模块和SK6812 RGB模块组合实验，实现用红外遥控器控制SK6812 RGB模块上4个LED灯珠的亮灭以及控制SK6812 RGB模块上4个LED灯珠显示不同颜色灯。

在这一实验课程中我们使用“ ① ”、“ ② ”、“ ③ ”、“ ④ ”、“ ⑤ ”、“ ⑥ ”、“ ⑦ ”、“ ⑧ ”、“ ⑨ ”等九个按键来控制SK6812 RGB模块上4个LED灯珠分别亮红色灯、绿色灯、蓝色灯、橙色灯、黄色灯、紫色灯、金色灯、粉红色灯、品红色灯。如果想要使用“ OK ”键来控制模块上4个LED灯珠亮和灭的两种情况该如何实现呢？这一实验课程我们将学习使用一个新的基本数据类型 —— boolean，来实现同一个按键控制LED亮灭的效果。

**boolean 数据类型**，变量存储为 8 位（1 个字节）的数值形式，**只能是 True 或是 False**。boolean 变量的值显示为 True 或 False（在使用 Print 的时候），或者 #TRUE# 或 #FALSE#（在使用 Write # 的时候）。使用关键字True 与 False 可将 boolean 变量赋值为这两个状态中的一个。

设置代码，按下“ OK ”键且满足某一条件，点亮模块上4个LED灯珠；再次按下“ OK ”键且满足另一条件，熄灭模块上4个LED灯珠。这个条件我们用 boolean 来实现是最简单方便的，因为 boolean 只有 True 或是 False 两种状态。我们只需要设置按下“ OK ”键的同时 flag 为 true，即可点亮模块上4个LED灯珠；同理，按下“ OK ”键的同时 flag 为 false，熄灭模块上4个LED灯珠。

---

**2. 实验组件**

| ![img](media/KS5016.png) |![img](media/KE4036.png)     | ![img](media/KE4009.png) |
| -------------------------------- | ---------------------------- | ------------------------ |
|主板 x1|红外接收模块 x1        | SK6812 RGB模块 x1     |
| ![img](media/remotecontrol.png) | ![img](media/3pin.png)       | ![img](media/USB.jpg)    | 
| Keyes 遥控器 x1 | 3P线(反向) x2 | USB线  x1 |

---

**3. 模块接线图**

![img](media/571301.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Intelligent_IR_Control.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : Intelligent_IR_Control
 * 功能   : 红外遥控SK6812RGB模块上4个LED灯珠亮不同颜色灯
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <Arduino.h>
#include <IRremote.hpp>
#include <FastLED.h>

#define IR_RECEIVE_PIN  3   // 定义红外接收模块引脚D3
IRrecv irrecv(IR_RECEIVE_PIN);     // 创建一个用于接收类的类对象
decode_results results;     // 创建一个解码结果类对象

#define NUM_LEDS 4   //定义SK6812 RGB模块上LED灯珠数量
#define LED_PIN  2   //定义SK6812 RGB模块控制引脚为D2
CRGB leds[NUM_LEDS];

boolean flag = true;  //LED标志钻头

void setup() {
  Serial.begin(9600);
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);  // 启动接收器
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(50);   //设置SK6812 RGB的亮度
}

void loop() {
  if (IrReceiver.decode()) {
      Serial.print(IrReceiver.decodedIRData.decodedRawData, HEX);  // 打印原始数据
      // IrReceiver.printIRResultShort(&Serial); // 在一行中打印接收到的完整数据
      // IrReceiver.printIRSendUsage(&Serial);  // 打印发送这些数据所需的语句
      handleControl(IrReceiver.decodedIRData.decodedRawData);      // 处理来自远程控制的命令
      IrReceiver.resume(); // 启用接收下一个值
  }
}

void handleControl(unsigned long value){
  if (value == 0xBF40FF00 && flag == true){ // 接收数字“OK” 
    fill_solid(leds, NUM_LEDS, CRGB::White);   // SK6812 RGB模块4个LED灯珠亮白灯
    FastLED.show();
    delay(10);
    Serial.println("  led on");
    flag = false;
  } 
  else if (value == 0xBF40FF00 && flag == false){ // 接收数字“OK”
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // SK6812 RGB模块4个LED灯珠不亮
    FastLED.show(); 
    delay(10);
    Serial.println("  led off"); 
    flag = true;
  }
  else if(value == 0xE916FF00){
    fill_solid(leds, NUM_LEDS, CRGB::Red);  // SK6812 RGB模块4个LED灯珠亮红色灯
    FastLED.show();
    delay(10);
    Serial.println("  red led");

  } 
  else if(value == 0xE619FF00){
    fill_solid(leds, NUM_LEDS, CRGB::Green);  // SK6812 RGB模块4个LED灯珠亮绿色灯
    FastLED.show();
    delay(10);
    Serial.println("  green led");
  } 
  else if(value == 0xF20DFF00){
    fill_solid(leds, NUM_LEDS, CRGB::Blue);  // SK6812 RGB模块4个LED灯珠亮蓝色灯
    FastLED.show();
    delay(10);
    Serial.println("  blue led");
  }
  else if(value == 0xF30CFF00){
    fill_solid(leds, NUM_LEDS, CRGB::Orange);  // SK6812 RGB模块4个LED灯珠亮橙色灯
    FastLED.show();
    delay(10);
    Serial.println("  orange led");
  }
  else if(value == 0xE718FF00){
    fill_solid(leds, NUM_LEDS, CRGB::Yellow);  // SK6812 RGB模块4个LED灯珠亮黄色灯
    FastLED.show();
    delay(10);
    Serial.println("  yellow led");
  }
  else if(value == 0xA15EFF00){
    fill_solid(leds, NUM_LEDS, CRGB::Purple);  // SK6812 RGB模块4个LED灯珠亮紫色灯
    FastLED.show();
    delay(10);
    Serial.println("  purple led");
  }
  else if(value == 0xF708FF00){
    fill_solid(leds, NUM_LEDS, CRGB::Gold);  // SK6812 RGB模块4个LED灯珠亮金色灯
    FastLED.show();
    delay(10);
    Serial.println("  gold led");
  }
  else if(value == 0xE31CFF00){
    fill_solid(leds, NUM_LEDS, CRGB::Pink);  // SK6812 RGB模块4个LED灯珠亮粉红色灯
    FastLED.show();
    delay(10);
    Serial.println("  pink led");
  }
  else if(value == 0xA55AFF00){
    fill_solid(leds, NUM_LEDS, CRGB::Magenta);  // SK6812 RGB模块4个LED灯珠亮品红色灯
    FastLED.show();
    delay(10);
    Serial.println("  magenta led");
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

![Img](./media/img-20240611084629.png)

找到红外遥控器，拔出绝缘片，对准红外接收模块的红外接收传感器的接收头。第一次按下红外遥控器上的 “ **OK** ” 键，SK6812 RGB模块上4个LED灯珠被点亮，实现开灯的效果。串口监视器打印出按下的按键值和SK6812 RGB模块上4个LED灯珠亮灯情况：“**<u>BF40FF00  led on</u>**” 。

再次按下红外遥控器上的 “ **OK** ” 键，SK6812 RGB模块上4个LED灯珠熄灭，实现关灯的效果。串口监视器打印出按下的按键值和SK6812 RGB模块上4个LED灯珠亮灯情况：“**<u>BF40FF00  led off</u>**” 。

按下红外遥控器上的 “ **①** ” 键，SK6812 RGB模块上4个LED灯珠亮红色灯。串口监视器打印出按下的按键值和SK6812 RGB模块上4个LED灯珠亮灯情况：“**<u>E916FF00  red led</u>**” 。

按下红外遥控器上的 “ **②** ” 键，SK6812 RGB模块上4个LED灯珠亮绿色灯。串口监视器打印出按下的按键值和SK6812 RGB模块上4个LED灯珠亮灯情况：“**<u>E619FF00  green led</u>**” 。

按下红外遥控器上的 “ **③** ” 键，SK6812 RGB模块上4个LED灯珠亮蓝色灯。串口监视器打印出按下的按键值和SK6812 RGB模块上4个LED灯珠亮灯情况：“**<u>F20DFF00  blue led</u>**” 。

按下红外遥控器上的 “ **④** ” 键，SK6812 RGB模块上4个LED灯珠亮橙色灯。串口监视器打印出按下的按键值和SK6812 RGB模块上4个LED灯珠亮灯情况：“**<u>F30CFF00  orange led</u>**” 。

按下红外遥控器上的 “ **⑤** ” 键，SK6812 RGB模块上4个LED灯珠亮黄色灯。串口监视器打印出按下的按键值和SK6812 RGB模块上4个LED灯珠亮灯情况：“**<u>E718FF00  yellow led</u>**” 。

按下红外遥控器上的 “ **⑥** ” 键，SK6812 RGB模块上4个LED灯珠亮紫色灯。串口监视器打印出按下的按键值和SK6812 RGB模块上4个LED灯珠亮灯情况：“**<u>A15EFF00  purple led</u>**” 。

按下红外遥控器上的 “ **⑦** ” 键，SK6812 RGB模块上4个LED灯珠亮金色灯。串口监视器打印出按下的按键值和SK6812 RGB模块上4个LED灯珠亮灯情况：“**<u>F708FF00  gold led</u>**” 。

按下红外遥控器上的 “ **⑧** ” 键，SK6812 RGB模块上4个LED灯珠亮粉红色灯。串口监视器打印出按下的按键值和SK6812 RGB模块上4个LED灯珠亮灯情况：“**<u>E31CFF00  pink led</u>**” 。

按下红外遥控器上的 “ **⑨** ” 键，SK6812 RGB模块上4个LED灯珠亮品红色灯。串口监视器打印出按下的按键值和SK6812 RGB模块上4个LED灯珠亮灯情况：“**<u>A55AFF00  magenta led</u>**” 。

![img](media/571501.png)

---

**6. 代码说明**

| 代码                | 说明                                                         |
| ------------------- | ------------------------------------------------------------ |
| boolean flag = true | 设置一个变量 flag 为 boolean 数据类型，数值为 true。         |
| flag = false        | boolean 数据类型的 flag 赋值为 false ，以便再次按下 “OK”键时满足熄灭SK6812 RGB模块上4个LED灯珠的条件。 |

---

### 第44课 智能门禁系统

**1. 项目介绍**

生活中，很多门禁系统都是使用射频模块进行开锁的，既方便又安全。这一课，学习利用RFID522刷卡模块和舵机设置一个智能门禁系统。

原理很简单，使用RFID522刷卡模块感应，使用IC卡或者钥匙卡来开锁，舵机的作用即门禁锁。

---

**2. 实验组件**

| ![img](media/KS5016.png) | ![img](media/KE4065.png) |![img](media/KE4022.png)|![](media/9G.png) |
| ------------------------ | ------------------------ | ---------------------------- | ------------------------ | 
|主板 x1 |RFID刷卡模块 x1    |舵机驱动模块 x1  |舵机 x1|
| ![img](media/WPS12.png)     | ![img](media/4pin.png) | ![img](media/USB.jpg) |      |
|  IC卡/钥匙扣 x1 | 4P线(反向) x2 |USB线  x1 |    |

---

**3. 模块接线图**

![](media/591301.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Intelligent_access_control.ino**"。

**<span style="background:#ff0;color:#000">特别注意：对于不同的IC卡和钥匙扣，其读取的IC卡和钥匙扣的UID码值可能都不一样。在代码运行前，需要将你自己的IC卡和钥匙扣的UID码值替换程序代码中的UID码（UID码在第30课完成实验可以得知），替换位置如下图所示。</span>**

![](media/591401.png)

```c++
/* 
 * 名称   : Intelligent_access_control
 * 功能   : RFID控制舵机模拟开门
 * 作者   : http://www.keyes-robot.com/ 
*/
#include "MFRC522_I2C_SOFT.h"
//IIC引脚默认为A4和A5
// 0x28是SDA上的i2c地址。如果不匹配，请用i2cscanner检查您的地址.
MFRC522 mfrc522(0x28, A4, A5);   // 创建MFRC522实例.

#include <Servo.h>
Servo myservo;  // 创建舵机对象来控制舵机
int servoPin = 6; // 定义舵机引脚D6

String rfid_str = "";

void setup() {
  Serial.begin(115200);
  mfrc522.PCD_Init();
  ShowReaderDetails();           // 显示PCD - MFRC522读卡机
  Serial.println(F("Scan PICC to see UID, type, and data blocks..."));
  
  myservo.attach(servoPin);  //选择舵机引脚D6
  myservo.write(0); 
  delay(500);
}

void loop() {
   if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) {
    delay(50);
    return;
  }
  
  //选择一张门卡，UID和SAK分别为mfrc522.uid。
  
  // 保存UID
  rfid_str = "";  //字符串清空
  Serial.print(F("Card UID:"));
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    rfid_str = rfid_str + String(mfrc522.uid.uidByte[i], HEX);  //转化字符串到实数
    //Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
    //Serial.print(mfrc522.uid.uidByte[i], HEX);
  } 
  Serial.println(rfid_str);
  
  if (rfid_str == "ce62ce6f" || rfid_str == "fb858372") {
    myservo.write(180);
    delay(500);
    Serial.println("  open the door!");
    }
}

void ShowReaderDetails() {
  // 获得MFRC522软件
  byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
  Serial.print(F("MFRC522 Software Version: 0x"));
  Serial.print(v, HEX);
  if (v == 0x91)
    Serial.print(F(" = v1.0"));
  else if (v == 0x92)
    Serial.print(F(" = v2.0"));
  else
    Serial.print(F(" (unknown)"));
  Serial.println("");
  // 当返回0x00或0xFF时，可能无法传输通信信号
  if ((v == 0x00) || (v == 0xFF)) {
    Serial.println(F("WARNING: Communication failure, is the MFRC522 properly connected?"));
  }
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电。上传成功后打开串口监视器，波特率设置为 **<u>115200</u>**。使用正确的IC卡或者钥匙扣刷卡时，串口监视器打印出卡的UID码并提示 “**open the door!**”，同时舵机转动到相应的角度模拟开门的效果。

![](media/MGR_12345.png)

![](media/591501.png)

---

**6. 代码说明**

在前面的课程中，使用RFID刷卡模块已经测试出来了IC卡和钥匙扣的UID码。这一课利用对应的UID码信息来控制舵机转动对应的角度，模拟开门的效果。

---

### 第45课 智能火警装置

**1. 项目介绍**

你知道智能火警装置吗？根据国家犯罪记录局（NCRB）的估计，从2010年至2014年，印度发生的火灾事故已造成超过120万死亡。即使为火灾事故采取了许多预防措施，但这些自然/人为灾难却还是时有发生。发生火灾时，为了营救人员和灭火，非常需要消防员等人力资源。随着技术的进步、机器人技术的发展，未来很有可能使用智能火警装置代替消防员灭火。这将提高灭火的效率，也能保障消防员的生命安全。

在这个项目中，我们将学习如何使用主板构建一个非常简单的智能火警装置。用火焰传感器检测火焰，当检测到火焰时，RGB闪烁蜂鸣器警报来提醒人们，并启动130电机吹灭火源。

---

**2. 实验组件**

| ![img](media/KS5016.png) | ![img](media/KE4009.png) | ![img](media/KE4038.png) | ![img](media/KE4020.png) |
| ------------------------ | ------------------------ | ------------------------ | ------------------------ |
|主板 x1|SK6812 RGB模块 x1     |130电机模块 x1     | 火焰传感器 x1      | 
| ![img](media/KE4010.png)|![](media/4pin.png) |![](media/3pin.png) | ![](media/USB.jpg) |
|有源蜂鸣器 x1|4P线(反向) x2 | 3P线(反向) x2 | USB线 x1 |
| ![](media/WS4.png)    |   ![](media/ABC13.png)    | ![](media/6.png)|          |
| 电池盒  x1  | 一字螺丝刀 x1  |AA电池 x6 |        |

---

**3. 模块接线图**

![](media/481301.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Intelligent_fire_alarm_device.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后外接电源供电，上电后，观察实验结果。**

<span style="color: rgb(255, 76, 65);">注意：代码中的模拟值的阈值500可以根据实际情况设置。</span>

```c++
/*  
 * 名称   : Intelligent_fire_alarm_device
 * 功能   : 火焰传感器控制130风扇模块，RGB模块和蜂鸣器
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <FastLED.h>

#define NUM_LEDS 4   //定义SK6812 RGB模块上LED灯珠数量
#define LED_PIN  3   //定义SK6812 RGB模块控制引脚为D3
CRGB leds[NUM_LEDS];

#define firePin  A5  //定义火焰传感器引脚为A5
#define buzzerPin 2  //定义有源蜂鸣器引脚为D2
#define INA 4  //INA对应IN+，引脚为D4
#define INB 5  //INB对应IN-，引脚为D5

int item = 0;

void setup() {
  Serial.begin(9600);
  pinMode(firePin, INPUT);  //将firePin设置为输入模式
  pinMode(buzzerPin, OUTPUT); //将buzzerPin设置为输出模式
  pinMode(INA, OUTPUT); //将INA设置为输出模式
  pinMode(INB, OUTPUT);//将INB设置为输出模式
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(50);   //设置SK6812 RGB的亮度
}

void loop() {
  item = analogRead(firePin);//将火焰传感器连接到A5上，将模拟值读取到item
  Serial.println(item); //串口显示模拟值
  if (item < 500) {//500以下  
    fill_solid(leds, NUM_LEDS, CRGB::Red);   // SK6812 RGB模块4个LED灯珠亮红灯
    FastLED.show();
    digitalWrite(buzzerPin, HIGH); //蜂鸣器鸣叫
    delay(300);
    fill_solid(leds, NUM_LEDS, CRGB::Black);   // SK6812 RGB模块4个LED灯珠不亮
    FastLED.show();
    digitalWrite(buzzerPin, LOW); //蜂鸣器不叫
    delay(300);
    digitalWrite(INA, LOW); //打开电风扇
    digitalWrite(INB, HIGH);
    
  } else {//否则，关闭风扇
    fill_solid(leds, NUM_LEDS, CRGB::Black);   // SK6812 RGB模块4个LED灯珠不亮
    FastLED.show();
    digitalWrite(buzzerPin, LOW); //蜂鸣器不叫
    digitalWrite(INA, LOW); //打开电风扇
    digitalWrite(INB, LOW);
  } 
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

由于风扇在转动时，所需的电流比其他传感器要大，会引起电路中电压电流波动，特别是风扇进行正反转时，电压电流波动过大，导致主板的电压电流过低，会导致复位。

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块，给主板外接电源供电（将装有电池的电池盒接到主板上）。上电后，打开串口监视器，设置波特率为 **<u>9600</u>**。

串口监视器上打印出火焰的模拟值，当模拟值低于500时，SK6812 RGB模块上的4个LED闪烁蜂鸣器警报来提醒人们，并且启动风扇来灭火。

![Img](./media/img-20240611084721.png)


---

**6. 代码说明**

在代码中我们设置了阈值为500（item < 500），阈值可以根据实际情况更改。当火焰传感器检测到模拟值低于这个阈值时，SK6812 RGB模块上的4个LED闪烁红灯蜂鸣器警报来提醒人们，并且启动风扇来灭火；否则，SK6812 RGB模块上的4个LED不亮蜂鸣器不叫，风扇关闭。风扇的驱动方式请查看上面的第12课。


---

### 第46课 楼道感应灯

**1. 项目介绍**

如今智能家居发展迅速，你使用过智能家居当中的智能楼道感应灯吗？在晚上或天黑时，当我们跺跺脚或者拍拍手时，智能楼道感应灯自动亮起；当没有声音或白天天亮时时，智能楼道感应灯处于熄灭状态。智能楼道感应灯上安装有光敏传感器和声音传感器，光敏传感器检测光照强度，声音传感器将外界声音的大小，转换成对应数值。智能楼道感应灯设置一个声音强度临界点，当光照强度低于某一值，声音转换后对应的数值超过该声音强度临界点时，灯光亮起一段时间。 

在这一实验课程中，我们将光敏传感器，声音传感器和SK6812 RGB模块组合实验，学习制作一个最简单的智能楼道感应灯。

---

**2. 实验组件**

| ![img](media/KS5016.png) |![img](media/KE4026.png)| ![img](media/KE4027.png) |![img](media/ABC13.png) |
| ------------------------ | ------------------------ | ------------------------ |------------------------ |
|主板 x1| 光敏传感器 x1  |声音传感器 x1     |一字螺丝刀 x1|
|![img](media/KE4009.png) | ![img](media/3pin.png) | ![img](media/USB.jpg) |    |
| SK6812 RGB模块 x1     | 3P线(反向) x3 | USB线 x1   |      |

---

**3. 模块接线图**

![img](media/5213011.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Corridor_sensing_light.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

<span style="color: rgb(255, 76, 65);">注意：代码中的模拟值的阈值可以根据实际情况设置。</span>

```c++
/*  
 * 名称   : Corridor_sensing_light
 * 功能   : 声音传感器和光敏传感器控制SK6812 RGB模块模拟楼道感应灯
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <FastLED.h>

int microPin = A7;  //定义声音传感器的控制引脚为A7
int PhotosensorPin = A2;  //定义光敏传感器的控制引脚为A2

#define NUM_LEDS 4   //定义SK6812 RGB模块上LED灯珠数量
#define LED_PIN  2   //定义SK6812 RGB模块控制引脚为D2
CRGB leds[NUM_LEDS];

void setup() {
  Serial.begin(9600); //波特率设置为9600
  pinMode(microPin, INPUT);  //设置声音传感器的引脚为输入模式
  pinMode(PhotosensorPin, INPUT);  //设置光敏传感器的引脚为输入模式  
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(50); //设置SK6812 RGB模块上4个LED灯珠的亮度
}

void loop() {
  int val1 = analogRead(PhotosensorPin); //读取光敏传感器的模拟值
  int val2 = analogRead(microPin); //读取声音传感器的模拟值
  Serial.print("Photosensor: ");  // 串口打印Photosensor:
  Serial.print(val1);  // 串口打印val1
  Serial.print("  ");  // 串口打印
  Serial.print("microphone: "); // 串口打印microphone:
  Serial.print(val2);  // 串口打印val2
  if(val1 < 200){ //光照模拟值低于阈值200
    if(val2 > 150){ //声音强度超过阈值150
      fill_solid(leds, NUM_LEDS, CRGB::White);   // SK6812 RGB模块4个LED灯珠亮白灯
      FastLED.show();
      Serial.println("  led on"); 
      delay(5000);
    }else{ //否则
      fill_solid(leds, NUM_LEDS, CRGB::Black);  // SK6812 RGB模块4个LED灯珠不亮
      FastLED.show();
      Serial.println("  led off");
      delay(50);
    }
  }else{ //否则
      fill_solid(leds, NUM_LEDS, CRGB::Black);  // SK6812 RGB模块4个LED灯珠不亮
      FastLED.show(); 
      Serial.println("  led off");
      delay(50); 
  }
  delay(100);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，打开串口监视器，设置波特率为 **<u>9600</u>**。

![img](media/830a.png)

串口监视器打印出声音传感器接收到的声音对应的模拟值和光敏传感器检测光照强度的模拟值。遮住光敏传感器光照使检测到的光照强度模拟值低于200时，对准MIC头大声说话，接收到的声音增大时模拟值也增大，当模拟值大于150时，SK6812 RGB模块上4个LED灯珠亮起5秒，然后熄灭。这样就可以模拟智能楼道感应灯。（**注意：如果声音变化对应的模拟值没有变化并且一直都是数字0，需要用一字螺丝刀顺时针旋转电位器来调节。**）

![Img](./media/img-20240611084821.png)

![img](media/521501.png)

---

**6. 代码说明**

| 代码                           | 说明               |
| ------------------------------ | ------------------ |
| int val2 = analogRead(microPin) | 读取模拟值。       |
| Serial.print(val2)              | 串口打印出模拟值。 |


---

### 第47课 控制舵机和LED

**1. 项目介绍**

前面已经学习过舵机，SK6812 RGB模块，自锁按键模块和旋转电位器等相关知识，那它们可不可以组合制成更复杂的实验呢？当然可以。则在本实验中，自锁按键控制SK6812 RGB模块上的4个LED亮起，然后旋转电位器控制舵机来回缓慢地转动。

---

**2. 实验组件**

| ![img](media/KS5016.png)     | ![img](media/KE4064.png)     |![img](media/KE4009.png) |
| ---------------------------- | ---------------------------- | ------------------------ |
|主板 x1| 旋转电位器 x1 |SK6812 RGB模块 x1 | 
| ![img](media/KE4045.png) |![](media/KE4022.png)  |![](media/9G.png) |
| 自锁按键 x1 | 舵机驱动模块 x1 | 舵机 x1 |
| ![img](media/3pin.png)  | ![img](media/4pin.png) | ![img](media/USB.jpg) |
|3P线(反向) x3 | 4P线(反向) x1 | USB线  x1 |

---

**3. 模块接线图**

![img](media/5313012.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Control_servo_led.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*
 * 名称   : Control_servo_led
 * 功能   : 控制舵机转动和LED
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <FastLED.h>
#include <Servo.h>

Servo myservo;  //创建舵机对象来控制舵机
int servoPin = 5;  // 定义舵机引脚D5

#define NUM_LEDS 4   //定义SK6812 RGB模块上LED灯珠数量
#define LED_PIN  3   //定义SK6812 RGB模块控制引脚为D3
CRGB leds[NUM_LEDS];

#define PIN_BUTTON  2  //将自锁按键的引脚连接到D2
#define PIN_ANALOG_IN  A2  //电位器传感器的引脚定义为A2

int val1 = 0;   //用于存储键值
int val2 = 0;   //设置val2为0

void setup() {
  Serial.begin(9600);   //波特率设置为9600
  pinMode(PIN_ANALOG_IN, INPUT);    //将传感器的引脚设置为输入模式
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(50);   //设置SK6812 RGB的亮度
  myservo.attach(servoPin);  //选择舵机引脚D5
  myservo.write(0); // 舵机转动0°
  delay(500); // 等待500ms
}

void loop() {
  val1 = digitalRead(PIN_BUTTON);  //读取自锁按键的值并将其赋值给变量val1
  if (val1 == 0) {       //按下自锁按键时读取到低电平
    fill_solid(leds, NUM_LEDS, CRGB::Red);   // SK6812 RGB模块4个LED灯珠亮红灯
    FastLED.show();
  }
  else {
    fill_solid(leds, NUM_LEDS, CRGB::Black);   // SK6812 RGB模块4个LED灯珠不亮
    FastLED.show();
  }  
  val2 = analogRead(PIN_ANALOG_IN);    //读取电位器的模拟信号
  myservo.write((map(val2, 0, 1023, 0, 180)));   // 电位器的模拟值映射到舵机的位置
  delay(10);   // 等待10ms让舵机到达对应的位置
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电。按下自锁按键时，SK6812 RGB模块上的4个LED亮起，然后旋转电位器，舵机则来回缓慢地转动。

![Img](./media/img-20240611115542.png)

---

**6. 代码说明**

可以参照第13课和第33课的代码说明，这里就不多做介绍了。

---

### 第48课 气象播报

**1. 项目介绍**

每天晚上的天气预报节目都会播报未来几天的气候情况，包括每个城市的气压，海拔、气温、风向和风的强度等各种气候。在本实验，我们将使用BMP388气压传感器检测气压，海拔，气温，并且在TFT显示屏显示。

---

**2. 实验组件**

| ![img](media/KS5016.png)  | ![img](media/KE4040.png) |![img](media/4pin-5pin.png) | ![img](media/USB.jpg)    |
| ------------------------ | ------------------------ | ---------------------------- |---------------------------- |
|主板 x1   | BMP388气压传感器 x1 | 4P转5P线(反向) x1 |USB线 x1 | 

---

**3. 模块接线图**

![](media/581301.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Weather_broadcast.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*  
 * 名称   : Weather broadcast
 * 功能   : TFT屏显示气压传感器检测大气压，海拔和温度模拟气象播报
 * 作者   : http://www.keyes-robot.com/ 
*/
#include "Waveshare_BMP388.h"
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>

// 定义TFT屏的引脚
#define CS   8
#define RST  9
#define DC   10
#define MOSI  11
#define SCK   13

Adafruit_ST7735 tft = Adafruit_ST7735(CS, DC, RST);

void setup() {  //把你的设置代码放在这里，只运行一次:
  bool bRet;
  PRESS_EN_SENSOR_TYPY enPressureType;
  pressSensorInit( &enPressureType );
  tft.initR(INITR_BLACKTAB); // 屏幕初始化
  tft.fillScreen(ST7735_BLACK); // 清屏 
}

void loop() {  // 把主代码放在这里，反复运行:
  tft.setTextSize(1.5);   //设置显示字符大小
  tft.setRotation(1);   //反转90°
  tft.setTextColor(ST7735_WHITE); //设置显示字符为白色
  tft.fillScreen(ST7735_BLACK);  // 清屏 
  int32_t s32PressureVal = 0, s32TemperatureVal = 0, s32AltitudeVal = 0;
  pressSensorDataGet(&s32TemperatureVal, &s32PressureVal, &s32AltitudeVal);
  tft.setCursor(10, 20);  //设置显示的位置
  tft.print("Pressure:");
  tft.setCursor(70, 20); 
  tft.print((float)s32PressureVal / 100);
  tft.setCursor(130, 20);
  tft.print("Pa");
  tft.setCursor(10, 50); 
  tft.print("Altitude:");
  tft.setCursor(70, 50); 
  tft.print((float)s32AltitudeVal / 100);
  tft.setCursor(120, 50);
  tft.print("m");
  tft.setCursor(10, 80);
  tft.print("Temperature:");
  tft.setCursor(90, 80); 
  tft.print((float)s32TemperatureVal / 100);
  tft.setCursor(130, 80);
  tft.print("C");
  delay(500);  
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电。上电后，可以在TFT显示屏上看到当前的气压、海拔和气温。

![Img](./media/img-20240611085050.png)

---

**6. 代码说明**

可以参照第31课的代码说明，这里就不多做介绍了。

---

### 第49课 激光防盗

**1. 项目介绍**

在新闻节目中经常会听到很多有关盗窃事件，很多财产都被盗贼盗走，损失很多。现在很多家庭里都没有安装防盗装置，来发出警报以提醒人们。那我们可不可以帮他们制作一个防盗装置呢？在本实验课程中，我们将使用激光模块，光敏传感器，SK6812 RGB模块，蜂鸣器组合一起来制作激光防盗装置。

---

**2. 实验组件**

| ![img](media/KS5016.png) | ![img](media/KE4043.png) |![img](media/KE4009.png)     |![img](media/KE4026.png) |
| ------------------------ | ---------------------------- | ----------------------------- |----------------------------- |
|主板 x1| 激光模块 x1 | SK6812 RGB模块 x1| 光敏传感器 x1 |
| ![img](media/KE4010.png) |![img](media/3pin.png)  | ![img](media/USB.jpg) |  |
| 有源蜂鸣器 x1 | 3P线(反向) x4|USB线 x1 |     |

---

**3. 模块接线图**

![](media/5813012.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Laser_anti-theft.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

```c++
/*
 * 名称   : Laser_anti-theft
 * 功能   : 激光防盗
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <FastLED.h>

#define NUM_LEDS 4   //定义SK6812 RGB模块上LED灯珠数量
#define LED_PIN  2   //定义SK6812 RGB模块控制引脚为D2
CRGB leds[NUM_LEDS];

#define photosensorPin  A7  //定义光敏传感器引脚为A7
#define buzzerPin  A2  //定义有源蜂鸣器引脚为A2

int laserPin = 3; //定义激光引脚为D3
int item = 0; //定义变量item

void setup() {
  Serial.begin(9600);
  pinMode(photosensorPin, INPUT);  //将photosensorPin设置为输入模式
  pinMode(buzzerPin, OUTPUT); //将buzzerPin设置为输出模式
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(50);   //设置SK6812 RGB的亮度
  pinMode(laserPin, OUTPUT);     //将激光引脚定义为输出模式
}

void loop() {
  digitalWrite(laserPin, HIGH);     //打开激光
  item = analogRead(photosensorPin);//将光敏传感器连接到A2上，将模拟值读取到item
  Serial.println(item); //串口显示模拟值
  if (item < 800) {//800以下  
    fill_solid(leds, NUM_LEDS, CRGB::Red);   // SK6812 RGB模块4个LED灯珠亮红灯
    FastLED.show();
    digitalWrite(buzzerPin, HIGH); //蜂鸣器鸣叫
    delay(200);
    fill_solid(leds, NUM_LEDS, CRGB::Yellow);   // SK6812 RGB模块4个LED灯珠亮黄灯
    FastLED.show();
    digitalWrite(buzzerPin, LOW); //蜂鸣器不叫
    delay(200); 
  } else {//否则
    fill_solid(leds, NUM_LEDS, CRGB::Black);   // SK6812 RGB模块4个LED灯珠不亮
    FastLED.show();
    digitalWrite(buzzerPin, LOW); //蜂鸣器不叫
  } 
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电。上电后，可以看到激光模块发出的激光正对着光敏传感器模块上的光敏电阻，在光敏传感器模块和激光模块之间用物体挡住激光，发现SK6812 RGB模块上的4个LED灯珠依次闪烁红灯和黄灯，蜂鸣器不断地鸣叫。

![Img](./media/img-20240611085205.png)

---

**6. 代码说明**

可以参照第4课，第13课和第21课的代码说明，这里就不多做介绍了。

---

### 第50课 智能散热装置

**1. 项目介绍**

生活中，我们的电脑或者电路板芯片等器件会由于工作时间过长或者功耗过大的问题而发热严重，所以我们常常需要一个散热装置。

在前面的课程我们学习了如何使用温度传感器和电机模块，这一课我们学习把它们结合起来做成一个智能散热装置。当检测到环境温度高于某一个值时的时候，电机开启，从而达到降低环境温度、散热效果，再把此刻的温度值显示在四位数码管中。

---

**2. 实验组件**

| ![img](media/KS5016.png) | ![img](media/KE4060.png) |![img](media/KE4038.png)     |
| ------------------------ | ---------------------------- | ----------------------------- |
|主板 x1| TM1650四位数码管模块 x1 | 130电机模块 x1|
| ![img](media/KE4025.png) | ![img](media/3pin.png)  | ![](media/6.png)|
|  NTC-MF52AT模拟温度传感器 x1 | 3P线(反向) x1|AA电池 x6 |
| ![img](media/4pin.png)    | ![img](media/USB.jpg) | ![](media/WS4.png) | 
| 4P线(反向) x2 |USB线 x1 | 电池盒 x1 |

---

**3. 模块接线图**

![](media/5813013.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**heat_abstractor.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

<span style="color: rgb(255, 76, 65);">注意：代码中的温度阈值30可以根据实际情况设置。</span>

```c++
/*  
 * 名称   : heat abstractor
 * 功能   : 模拟温度传感器控制四位数码管和电机模拟散热装置
 * 作者   : http://www.keyes-robot.com/ 
*/
#include "TM1650.h" //导入TM1650库文件

//两个端口分别为A4和A5, 可以更改为其他端口
#define DIO A4
#define CLK A5
TM1650 DigitalTube(CLK,DIO);

#define PIN_ANALOG_IN  A2 //模拟温度传感器(热敏电阻传感器)的引脚为A2

float Rt=0;      //NTC 热敏电阻
float R=10000;   //具有固定电阻值的10K电阻
float T2=273.15+25; //转换成开尔文温度
float B=3950;    //B值是热敏电阻的一个重要参数
float K=273.15;  //开氏度 (K°)
float VR=0;

void setup() {
  pinMode(PIN_ANALOG_IN, INPUT);  //设置PIN_ANALOG_IN为输入模式
  DigitalTube.setBrightness();  //设置亮度，0- 7，默认值:2
  DigitalTube.displayOnOFF();   //显示打开或关闭，0=显示关闭，1=显示打开，默认值:1
  for(char b=1;b<5;b++){
    DigitalTube.clearBit(b);    //数码管清除位显示
  }

  DigitalTube.displayBit(1,0);  //数码管显示(位,数字); 位=0~3  数字=0~9
  
  //电机的IN+连接到D2，IN-连接到D6
  pinMode(2, OUTPUT);
  pinMode(6, OUTPUT);
}

void loop() {
  int AnalogValue = analogRead(PIN_ANALOG_IN);  //读A2引脚的模拟值
  VR = (float)(AnalogValue / 1023.0 * 5.0);  //转换成电压值
  Rt = (5.0 - VR) / VR * 4700;    //计算NTC热敏电阻
  float temper = 1/(1/T2+log(Rt/R)/B)-K+0.5; //计算温度
  displayFloatNum(temper); //4位数码管显示温度值
  if (temper > 30) { //当温度超过32℃时，打开风扇
    digitalWrite(2, LOW);
    digitalWrite(6, HIGH);
  } else { //否则，请关闭风扇
    digitalWrite(2, LOW);
    digitalWrite(6, LOW);
  }
  delay(100);
}

void displayFloatNum(float temper){
  if(temper > 9999)
    return;
  int dat = temper*10;
   //DigitalTube.displayDot(2,true); //Bit0显示点  在displayBit()之前使用。
  if(dat/10000 != 0){
    DigitalTube.displayBit(0, dat%100000/10000);  
    DigitalTube.displayBit(1, dat%10000/1000);
    DigitalTube.displayBit(2, dat%1000/100);
    DigitalTube.displayBit(3, dat%100/10);
    return;
  }
  if(dat%10000/1000 != 0){
    DigitalTube.clearBit(0); 
    DigitalTube.displayBit(1, dat%10000/1000); 
    DigitalTube.displayBit(2, dat%1000/100);
    DigitalTube.displayBit(3, dat%100/10);
    return;
  }
  if(dat%1000/100 != 0){
  DigitalTube.clearBit(0); 
  DigitalTube.clearBit(1);
  DigitalTube.displayBit(2, dat%1000/100);
  DigitalTube.displayBit(3, dat%100/10);  
  return;
}
  DigitalTube.clearBit(0); 
  DigitalTube.clearBit(1);
  DigitalTube.clearBit(2);
  DigitalTube.displayBit(3, dat%100/10);
}
```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后，给主板外接电源供电（将装有电池的电池盒接到主板上）。上电后，可以在四位数码管模块上看到当前温度值，当温度超过代码中设定的30°C时，风扇转动，散热。

![](media/IMG_5831.png)

---

**6. 代码说明**

变量的设置与存储检测值，与前面我们学习的一样，也是通过设置一个温度的阈值（阈值30可以根据实际情况重新更改），超过这个阈值进行控制电机转动，四位数码管显示温度值。

---

### 第51课 超声波雷达

**1. 项目介绍**

蝙蝠飞行与获取猎物是通过回声定位的。回声定位：某些动物能通过口腔或鼻腔把从喉部产生的超声波发射出去，利用折回的声音来定向，这种空间定向的方法称为回声定位。科学家们从蝙蝠身上得到的启示发明了雷达，即雷达的天线相当于蝙蝠的嘴，而天线发出的无线电波就相当于蝙蝠的超声波，雷达接收电波的荧光屏就相当于蝙蝠的耳朵。

这一课我们就来学习制作一个简易雷达。将HC-SR04 超声波传感器、无源蜂鸣器、SK6812 RGB模块和主板上的TFT显示屏组合实验，利用距离大小控制无源蜂鸣器响起对应频率的声音、SK6812 RGB模块上4个LED灯珠亮起对应颜色灯，然后把这个距离显示在TFT显示屏上。这样就搭建好了一个简易的超声波雷达系统。

---

**2. 实验组件**

| ![img](media/KS5016.png) | ![img](media/ultrasonic.png) | ![img](media/KE4039.png)|![](media/WS4.png)|
| ---------------------------- | ---------------------------- | ----------------------------- | ----------------------------- |
|主板 x1  |HC-SR04 超声波传感器 x1 | 超声波转接模块 x1 |电池盒 x1 |
| ![img](media/KE4067.png) | ![img](media/KE4009.png)  |![](media/6.png)| ![img](media/3pin.png) |
| 无源蜂鸣器 x1  | SK6812 RGB模块 x1  | AA电池 x6 |3P线(反向) x2|
| ![img](media/4pin.png) | ![img](media/USB.jpg)  |  |  |
| 4P线(反向) x1 | USB线  x1  |   |     |

---

**3. 模块接线图**

![img](media/561301.png)

---

**4. 实验代码**

本项目中使用的代码保存在文件夹“<u>**...\项目代码\Arduino_C_代码**</u>”中，我们可以在此路径下打开代码文件''**Ultrasonic_radar.ino**"。

**注意：为了避免上传代码不成功，请上传代码前不要连接模块。代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，观察实验结果。**

<span style="color: rgb(255, 76, 65);">注意：代码中的距离阈值可以根据实际情况设置。</span>

```c++
/*  
 * 名称   : Ultrasonic radar
 * 功能   : 超声波控制TFT显示屏，蜂鸣器和RGB灯模拟超声波雷达
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>
#include <FastLED.h>  //导入FastLED库文件

// 定义TFT屏的引脚
#define CS   8
#define RST  9
#define DC   10
#define MOSI  11
#define SCK   13

Adafruit_ST7735 tft = Adafruit_ST7735(CS, DC, RST);

#define BUZZER_PIN  A2    //定义无源蜂鸣器引脚为A2

#define NUM_LEDS  4   //定义SK6812 RGB模块上LED灯珠数量
#define LED_PIN  3   //定义SK6812 RGB模块控制引脚为D3
CRGB leds[NUM_LEDS];

int TrigPin = 2; //设置Trig引脚为D2
int EchoPin = 6; //设置Echo引脚为D6
int distance;     //超声波测量距离

float checkdistance() { //得到的距离
  // 事先给一个短的低电平，以确保一个干净的高脉冲;
  digitalWrite(TrigPin, LOW);
  delayMicroseconds(2);
  // 传感器由10微秒或更长时间的高脉冲触发
  digitalWrite(TrigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(TrigPin, LOW);
  // 从传感器读取信号:一个高电平脉冲
  // 它的持续时间是从发送ping命令到从对象接收回显的时间(以微秒为单位)
  float distance = pulseIn(EchoPin, HIGH) / 58.00;  //转换成距离
  delay(300);
  return distance;
}

void setup() {
  pinMode(BUZZER_PIN, OUTPUT);  // 设置蜂鸣器为输出模式
  pinMode(TrigPin, OUTPUT);     //设置Trig引脚作为输出
  pinMode(EchoPin, INPUT);      //设置Echo引脚作为输入
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(50);   //设置SK6812 RGB的亮度
  tft.initR(INITR_BLACKTAB); // 屏幕初始化
  tft.fillScreen(ST7735_BLACK); // 清屏 
  delay(1000);
}

void loop() {
  tft.setTextSize(1);   //设置显示字符大小
  tft.setRotation(1);   //反转90°
  tft.setTextColor(ST7735_WHITE); //设置显示字符为白色
  tft.fillScreen(ST7735_BLACK);  // 清屏 
  distance = checkdistance();//超声波测距
  tft.setCursor(10, 50);  //设置显示的位置
  tft.print("distance:");  //打印字符
  tft.setCursor(70, 50); 
  tft.print(distance);
  tft.setCursor(100, 50);  //设置显示的位置
  tft.print("cm");  //打印字符
  delay(300);
  tft.fillScreen(ST7735_BLACK); // 清屏  
  if (distance <= 10) {   
    fill_solid(leds, NUM_LEDS, CRGB::Red);  // SK6812 RGB模块4个LED灯珠亮红色灯
    FastLED.show();
    tone(BUZZER_PIN, 262); //DO播放1000ms
    delay(1000);

} else if (distance > 10 && distance <= 20) {
    fill_solid(leds, NUM_LEDS, CRGB::Green);  // SK6812 RGB模块4个LED灯珠亮绿色灯
    FastLED.show();
    tone(BUZZER_PIN, 349); //Fa播放500ms
    delay(500);

} else {
    fill_solid(leds, NUM_LEDS, CRGB::Blue);  // SK6812 RGB模块4个LED灯珠亮蓝色灯
    FastLED.show();
    tone(BUZZER_PIN, 494); //Si播放125ms
    delay(125);
  }
}

```

参照第01课或第02课的示例，根据主板选择对应的主板板型（Arduino Uno）和串口端口，然后将示例代码上传到主板上。

---

**5. 实验结果**

上传代码，代码上传成功后，拔下USB线断电，按照接线图正确接好模块后，给主板外接电源供电（将装有电池的电池盒接到主板上）。

当超声波传感器检测到障碍物距离范围在10cm 以内时，SK6812 RGB模块上的4个LED灯亮红色，并将检测到障碍物的距离显示在TFT显示屏上。同时无源蜂鸣器发出声响，起到提示的作用。

![Img](./media/img-20240611085702.png)

当超声波传感器检测到障碍物距离范围在10cm ~ 20cm 以内时，SK6812 RGB模块上的4个LED灯亮绿色，并将检测到障碍物的距离显示TFT显示屏上。同时无源蜂鸣器发出声响，起到提示的作用。

![Img](./media/img-20240611085339.png)

当超声波传感器检测到障碍物距离范围在20cm 以外时，SK6812 RGB模块上的4个LED灯亮蓝色，并将检测到障碍物的距离显示在TFT显示屏上。同时无源蜂鸣器发出声响，起到提示的作用。

![Img](./media/img-20240611085951.png)

---

**6. 代码说明**

| 代码                                             | 说明                                           |
| ------------------------------------------------ | ---------------------------------------------- |
| digitalWrite(TrigPin, LOW); delayMicroseconds(2) | 事先给一个短的低电平，以确保一个干净的高脉冲。 |

---


