
# 项目教程

---

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

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

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

---

### 第01课 Hello World

#### 1. 项目介绍

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

---

#### 2. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |
| :----------------------: | :-------------------: |
|UNO R4 WiFi/Minima主板(二选一)|       USB线 x1        |

---

#### 3. 模块接线图

<span style="color: rgb(255, 76, 65);">**注意：**</span>

如果是选择UNO R4 WiFi主板，则接线图如下：

![011301](media/3b5b5156f9418ab13121f50c5aa76303.png)

如果是选择UNO R4 Minima主板，则接线图如下：

![011301](media/d139e2d81e1d90249766be20938148e5.png)

---

#### 4. 实验代码

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

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

```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 !”
    }
  }
}
```

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

先点击“**<u>工具</u>**”→“**<u>开发板</u>**”，可以查看到Arduino UNO R4 Boards 板型。如果你是选择UNO R4 WiFi主板，则选择对应的主控板型号Arduino UNO R4 WiFi。

![011401](media/d3d081b2b26be6bbae6ea7f2d300b5b3.png)

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

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

![011401](media/3049fa82918de62d4ada1c9da21ffd56.png)

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

![011402](media/70db06d5be97ec448d46a41a0a2baca8.png)

代码上传成功。

![011403](media/5eb744ba570cb95ff01a1f7841c565ce.png)


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

先点击“**<u>工具</u>**”→“**<u>开发板</u>**”，可以查看到Arduino UNO R4 Boards开发板。如果你是选择UNO R4 Minima主板，则选择对应的主控板型号Arduino UNO R4 Minima。

![011401a](media/ad902775ba8bbfc15ce6fd859c756b55.png)

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

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

![011401a](media/c2324feaa241f45613c76423779f8016.png)

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

![011402](media/2c9b8e3084c35400e091d761b9f091f8.png)

代码上传成功。

![011403](media/77943f9e1b6e34abcb565cc66e8ede0b.png)

---

#### 5. 实验结果

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

![011501](media/d581edf9364cbd85eeb9a97f750234ff.png)

---

#### 6. 代码说明

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

--- 

### 第02课 LED 闪烁

#### 1. 项目介绍

LED，即发光二极管的简称。由含镓（Ga）、砷（As）、磷（P）、氮（N）等的[化合物](https://baike.baidu.com/item/化合物/1142931)制成。当电子与[空穴](https://baike.baidu.com/item/空穴/3517781)复合时能辐射出可见光，因而可以用来制成发光二极管。在电路及仪器中作为指示灯，或者组成文字或数字显示。砷化镓二极管发红光，磷化镓二极管发绿光，碳化硅二极管发黄光，氮化镓二极管发蓝光。因化学性质又分有机发光二极管OLED和无机发光二极管LED。

为了实验的方便，我们将紫色LED发光二极管做成了一个紫色LED模块。它的控制方法非常简单，只要让LED两端有一定的电压就可以点亮LED。在这个项目中，我们用一个最基本的测试代码来控制LED，亮一秒钟，灭一秒钟，来实现闪烁的效果。你可以改变代码中LED灯亮灭的时间，实现不同的闪烁效果。我们通过编程控制信号端S的高低电平，从而控制LED的亮灭。LED模块信号端S为高电平时LED亮起，S为低电平时LED熄灭。

---

#### 2. 模块参数

工作电压：DC 3.3~5V

控制信号：数字信号

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

尺寸：32mm x 23.5mm x 12mm

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

---

#### 3. 模块原理图

![img](media/528cd9e02183e397e521b219211b809e.jpg)

这是一个常用的LED模块，它采用F5-白发紫LED（外观白色，显示紫光）元件。同时，模块上自带一个间距为 2.54mm 的 3pin 防反插红色端子。控制时，模块上GND VCC供电后，信号端S为高电平时，模块上LED亮起；否则，信号端S为低电平时，模块上LED不亮。

模块兼容各种单片机控制板，如arduino系列单片机。

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) | ![img](media/1a121809b85e9f58d815cee433df7e14.png) |
| ------------------------ | ------------------------ | ---------------------------- | 
| UNO R4 WiFi/Minima主板(二选一)        |  Keyes 传感器扩展板 x1      |Keyes 紫色LED模块 x1      |
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |   | 
| 3P线(反向) x1 | USB线 x1             |    |

---

#### 5. 模块接线图

![img](media/c85f33e928c73bdbed7f2fdab5fac611.png)

---

#### 6. 实验代码

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

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

```c++
/*
 * 名称   : LED_Blinking
 * 功能   : LED 闪烁 1s
 * 作者   : http://www.keyes-robot.com/
*/
int ledPin = 3; // 定义LED的引脚为D3.

void setup(){
  pinMode(ledPin, OUTPUT);// 设置led引脚为输出模式.
}

void loop(){
  digitalWrite(ledPin, HIGH); // 点亮LED.
  delay(1000); // 等待1秒.
  digitalWrite(ledPin, LOW); // 熄灭LED.
  delay(1000); // 等待1秒
}
```

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

先点击“**<u>工具</u>**”→“**<u>开发板</u>**”，可以查看到Arduino UNO R4 Boards 板型。如果你是选择UNO R4 WiFi主板，则选择对应的主控板型号Arduino UNO R4 WiFi。

再点击“<u>**工具**</u>”→“**<u>端口</u>**”，选择对应的串口端口。
**<span style="background:#ff0;color:#000">
注意：将主板通过USB线连接到计算机后才能看到对应的串口端口。 </span>**

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

<br>
<br>

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

先点击“**<u>工具</u>**”→“**<u>开发板</u>**”，可以查看到Arduino UNO R4 Boards 板型。如果你是选择UNO R4 Minima主板，则选择对应的主控板型号Arduino UNO R4 Minima。

再点击“<u>**工具**</u>”→“**<u>端口</u>**”，选择对应的串口端口。
**<span style="background:#ff0;color:#000">
注意：将主板通过USB线连接到计算机后才能看到对应的串口端口。 </span>**

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

---

#### 7. 实验结果

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，你会看到模块上的紫色LED一亮一灭，循环闪烁。

![](media/ef26206346f34d3fb0f7381657de40c2.png)

![](media/3459b03b40ca73f3532e60bb3610099c.png)

---

#### 8. 代码说明

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


---

### 第03课 呼吸灯

#### 1. 项目介绍

在第02课我们学习了如何让LED闪烁。但是LED的玩法远不仅如此。在日常生活中你有没有遇到过灯光慢慢变亮或者慢慢变暗呢？这叫呼吸灯。所谓呼吸灯，就是控制LED逐渐变亮，然后逐渐变暗，循环交替。上一课我们学会了直接用高电平点亮LED，低电平熄灭LED。如果要让LED不那么亮但又不完全熄灭，介于中间状态，只需控制流过LED的电流就可以实现。电流减小LED变暗，电流增大LED变亮。所以只需要调节LED两端的电压减小或增大（电流也会随之减小或增大）就能控制LED的亮暗程度了。

数字端口电压输出只有LOW与HIGH两个开关，对应的就是0V与3.3V（或5V）的电压输出。可以把LOW定义为0，HIGH定义为1，1秒内让单片机输出500个0或者1的信号。如果这500个信号全部为1，那就是完整的3.3V；如果全部为0，那就是0V。如果010101010101这样输出，刚好一半，端口输出的平均电压就为1.65V了。这和放映电影是一个道理。我们所看的电影并不是完全连续的，它其实是每秒输出25张图片，人的肉眼分辨不出来，看上去就是连续的了，PWM也是同样的道理。如果想要不同的电压，就控制0与1的输出比例就可以了。当然这和真实的连续输出还是有差别的，单位时间内输出的0,1信号越多，控制的就越精确。

这一课学习使用PWM来控制0与1的输出比例实现控制电压。

---

#### 2. 模块参数

工作电压：DC 3.3~5V

控制信号：数字信号

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

尺寸：32mm x 23.5mm x 12mm

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

---

#### 3. 模块原理图

![img](media/528cd9e02183e397e521b219211b809e.jpg)

前面第02课我们就学习了如何控制一个LED，由原理图可以得知，控制时，模块上GND VCC供电后，信号端S为高电平时，模块上LED亮起。

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/1a121809b85e9f58d815cee433df7e14.png) | 
| ------------------------ | ------------------------ | ---------------------------- | 
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1        |Keyes 紫色LED模块 x1    |
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |   |
| 3P线(反向) x1 | USB线 x1             |  |

---

#### 5. 模块接线图

![](media/c85f33e928c73bdbed7f2fdab5fac611.png)

---

#### 6. 实验代码


```c++
/*
 * 名称   : Breathing_LED
 * 功能   : 使用PWM让led灯像呼吸一样忽明忽暗。
 * 作者   : http://www.keyes-robot.com/
*/
int ledPin = 3;  // 定义LED的引脚为D3.

void setup() {
  pinMode(ledPin,OUTPUT); // 设置LED引脚为输出模式.
}

void loop(){
  for (int value = 0 ; value < 255; value=value+1) {  //使灯光渐亮
    analogWrite(ledPin, value);
    delay(10);
  }
  for (int value = 255; value >0; value=value-1) {  //使灯光渐暗
    analogWrite(ledPin, value);
    delay(10);
  } 
} 
```

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，能看到模块上的紫色LED从暗逐渐变亮，再从亮逐渐变暗，就像呼吸一样。

![](media/ef26206346f34d3fb0f7381657de40c2.png)

---

#### 8. 代码说明

当我们需要重复执行某句话时，我们可以使用for语句。

for语句格式如下：

![Img](./media/2a7e75b97d38850d45fede05b282dbd6.png)

for循环顺序如下：

第一轮：1 → 2 → 3 → 4

第二轮：2 → 3 → 4

…

直到2不成立，for循环结束。

知道了这么个顺序之后，回到代码中：

for (int val = 0; val < 255; val++){

        ...}

for (int val = 255; val > 0; val--){

       ...}

这两个for语句实现了val的值不断由0增加到255，随之在从255减到0，再增加到255……，无限循环下去。

再看下for里面，涉及一个新函数analogWrite()。

我们知道数字口只有0和1两个状态，那如何发送一个模拟值到一个数字引脚呢？就要用到该函数。观察一下控制主板，查看数字引脚，你会发现其中6个引脚旁标有“~”，这些引脚不同于其他引脚，它们可以输出PWM信号。

**函数格式如下：**

analogWrite(ledpin,value)

analogWrite()函数用于给PWM口写入一个0 ~ 255的模拟值。所以，value是在0 ~ 255之间的值。特别注意的是，analogWrite()函数只能写入具有PWM功能的数字引脚，也就是D3，D5，D6，D9，D10，D11引脚。

---

### 第04课 自锁按键

#### 1. 项目介绍

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

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

---

#### 2. 模块参数

工作电压：DC 3.3V~5V 

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

控制信号：数字信号

尺寸：32mm x 23.8mm x 15.6mm

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

---

#### 3. 模块原理图

![img](media/7b403c5b32d212c2ea94aebcd01e738d.png)

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

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) | ![img](media/628fac0d8bdcd43d93afc13ae228177c.png) |
| ------------------------ | ------------------------ | ---------------------------- |
| UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1    |Keyes 自锁按键模块 x1  |
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |  |
| 3P线(反向) x1 | USB线 x1             |   |

---

#### 5. 模块接线图

![img](media/d1b1e10352b8e23757a28e7b060e6aca.png)

---

#### 6. 实验代码


```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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

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

![img](media/23ce0280f943ca52cee6e600a900ddd6.png)

![img](media/5b433c852c6ff9591bdf709e67626c79.png)

![img](media/96ab26639fd7de11147c7da5667fe385.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（并且可以随机更改）。UNO R4 主板上的输入引脚A0、A1、A2、A3、A4、A5可以用作数字引脚，分别对应数字引脚D14、D15、D16、D17、D18、D19。|
| Serial.begin(9600)     | 初始化串口通信，并设置波特率为9600。                         |

---

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

#### 1. 项目介绍

在这个套件中，有一个Keyes 电容触摸模块，它主要由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/56fd49a5552a32ac193119de8cb0a9ad.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/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/891f854b35e7d4d08ba203898a3fa362.png) |
| ------------------------ | ------------------------ | ---------------------------- |
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1    | Keyes 电容触摸传感器 x1 |
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |  |
| 3P线(反向) x1 | USB线 x1   |          |

---

#### 5. 模块接线图

![](media/84843ceb14152f1275e8ea227851c606.png)

---

#### 6. 实验代码

```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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

![](media/1301ca0723c9af1f66dd974fb813b6bc.png)

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

![](media/082fb76b6eb0e6ea283618e6eb0f1e3e.png)

![](media/d078f0832257eb3d807fc12ff188c096.png)

![](media/be792fa1dcbe2cfbd6cfd30f2f0f31db.png)

---

#### 8. 代码说明

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

---

### 第06课 避障传感器检测障碍物

#### 1. 项目介绍

在这个套件中，有一个Keyes 避障传感器，它主要由一对红外线发射与接收管元件组成。实验中，我们通过读取传感器上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/7de49ab018b45f26e4cfba839ea00b3f.jpg)

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

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/2b35bd88b0382b61946efaa259ccf033.png) |
| ------------------------ | ------------------------ | ---------------------------- | 
|UNO R4 WiFi/Minima主板(二选一)|  Keyes 传感器扩展板 x1    | Keyes 避障传感器 x1        |
| ![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) | ![img](media/f5504973a1c7bd3a74e8a9370a3c2358.png)|
| 3P线(反向) x1 | USB线 x1             | 一字螺丝刀 x1             |

---

#### 5. 模块接线图

![img](media/0f076724ea662770fe6b8072456df0fd.png)

---

#### 6. 实验代码

```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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

![img](media/8bf19ee47776ac781c8be8905a3adff2.jpg)

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

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

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

![img](media/d58742bf668a2e3106286b74733d0062.png)

![img](media/8c8954eebea55df8fea563d534b2ec74.png)

![img](media/b9acc4a755af97b5d67cfc7bef84264d.png)

---

#### 8. 代码说明

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

---

### 第07课 倾斜传感器

#### 1. 项目介绍

在这个套件中，有一个Keyes 倾斜传感器，主要由一个倾斜开关组成，其内部带有一颗滚珠，用来监测倾斜情况。倾斜开关可以依据模块是否倾斜而输出不同的电平信号。当开关高于水平位置倾斜时开关导通，低于水平位置时开关断开。倾斜模块可用于倾斜检测、报警器制作或者其他检测。

---

#### 2. 模块参数

工作电压: DC 3.3V~5V 

工作电流: 4.2mA

最大功率: 0.03WW

输出信号: 数字信号

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

尺寸：32mm x 23.8mm x 8mm

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

---

#### 3. 模块原理图

![img](media/848eaf35c4325ff414da270084b690c2.png)

Keyes 倾斜传感器的原理非常简单，主要是利用滚珠在开关内随不同倾斜角度的变化使滚珠开关P1的引脚1和2导通或者不导通，当滚珠开关P1的引脚1和2导通时，由于1脚接GND，所以信号端S被拉低为低电平，此时红色LED和R2组成的电路形成回路，电流经过红色LED，点亮红色LED；当滚珠开关P1的引脚1和2不导通时，滚珠开关P1的引脚2被4.7K的上拉电阻R1拉高使得信号端S为高电平，电流不经过红色LED，红色LED熄灭。


---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/194064d3dbea673dbdbc6d6f7309cf31.png) |  
| ------------------------ | ------------------------ | ---------------------------- | 
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1      |Keyes 倾斜传感器 x1  |
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |     |
| 3P线(反向) x1 | USB线 x1 |            |

---

#### 5. 模块接线图

![img](media/ea18fca26fbd7516e9e12a7d17ead18c.png)

---

#### 6. 实验代码

```c++
/*
 * 名称   : Tilt switch
 * 功能   : 读取倾斜传感器的值
 * 作者   : http://www.keyes-robot.com/ 
*/
int val; //定义一个变量val用来存储倾斜传感器输出的电平值

void setup() {
  Serial.begin(9600);
  pinMode(3, INPUT);  //将倾斜传感器的引脚连接到D3，设置为输入模式
}

void loop() {
  val = digitalRead(3); //读取模块电平信号
  Serial.println(val);  //打印倾斜传感器输出的电平值
  delay(100);   //延迟100毫秒
}
```

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

将倾斜模块往某一边倾斜，若模块上的红色LED**不亮**，串口监视器打印数字电平信号“**1**”；若模块上的红色LED点**亮**，串口监视器打印数字电平信号“**0**”。

![img](media/a12d8b927e94cfaf2a33160d2d2f9f70.png)

![img](media/2add05bfb6a9888b18f690fcc3a8b87b.png)

![img](media/dcba5b1cb1d3ea642b655843ec47c933.png)

---

#### 8. 代码说明

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


---

### 第08课 光折断计数

#### 1. 项目介绍

在这个套件中，有一个Keyes 光折断模块，它主要由 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/5c12ff10904bcde040287a35921a9585.jpg)

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

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

---

#### 4. 实验组件

| ![](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/47e138b8db24862715e221f17eba9bf9.png) |  
| --------------------- | ------------------------ | ---------------------------- |
|UNO R4 WiFi/Minima主板(二选一)     | Keyes 传感器扩展板 x1      |Keyes 光折断模块 x1 |     
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |   |
| 3P线(反向) x1 | USB线 x1  |           |

---

#### 5. 模块接线图

![](media/49fcea8337ca9c9806e9ac74bd05b594.png)

---

#### 6. 实验代码

```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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

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

![](media/cf711411bcd69bf4aff26c93bbea3f39.png)

![](media/21eb2a903622936b0d28101b2b39822d.png)

![](media/0c211d32764365b5608f174b4315dde4.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值               |

---

### 第09课 附近有人吗

#### 1. 项目介绍

在这个套件中，有一个Keyes 人体红外热释传感器，它主要由一个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/d78c512ebf2ec0ae31513a4faa159fa8.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/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png)    |![img](media/6963fc8b02f22425a78d196cbc3c8bd0.png)|    
| ------------------------ | --------------------------- | ---------------------------- |
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1 |Keyes 人体红外热释传感器 x1 |    
| ![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |     |
| 3P线(反向) x1 | USB线 x1   |          |

---

#### 5. 模块接线图

![img](media/4f18c2738e0bec25c8b1fc8886478024.png)

---

#### 6. 实验代码

```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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

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

![img](media/b9087c112e5f599619e9b8dba01352f6.png)

![img](media/d7626ec49b55bbb2e84e2c3ce107fcd0.png)

![img](media/dbf6b7d5d5d6364aa51f3e6b202dae3f.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/ef842bd728bd9aa807e0e753fe17ee63.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/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/7e77d1498bafcbd3e9f2d40103af3734.png) |    
| ------------------------ | ------------------------ | ---------------------------- | 
| UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1  |Keyes 有源蜂鸣器模块 x1  |      
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |     |
|3P线(反向) x1 | USB线 x1  |           |

---

#### 5. 模块接线图

![img](media/73d3698b7e5ccca7ee8a305493bbbb7d.png)

---

#### 6. 实验代码

```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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

![img](media/570a0afab80456cc76be518022c5938b.png)

---

#### 8. 代码说明

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


---

### 第11课 演奏音乐

#### 1. 项目介绍

在这个套件中，有一个Keyes 8002b功放 喇叭模块，这个模块主要由一个可调电位器、一个喇叭和一个音频放大芯片组成。上一课我们学习了有源蜂鸣器模块的使用方法，这一课我们来学习套件中的8002b功放 喇叭模块的使用方法。这个模块主要功能是：可以对输出的小音频信号进行放大，大概放大倍数为8.5倍，并且可以通过自带的小功率喇叭播放出来，也可以用来播放音乐，作为一些音乐播放设备的外接扩音设备。

---

#### 2. 模块参数

工作电压: DC 3.3V~5V 

工作电流: ≤350mA

最大功率: 1.75W

放大芯片: SC8002B

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

尺寸：47.6mm x 23.8mm x 10mm

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

---

#### 3. 模块原理图

![img](media/8a829e3b601a243c1fb65fca8d6a7b57.jpg)

其实这个喇叭就类似于于一个无源蜂鸣器，上一课我们介绍过，有源蜂鸣器自带振荡源，只要我们给它足够的电压就能响起来，而8002b功放-喇叭元件内部不带震荡电路，需要在元件正极（也就是1脚）输入不同频率的方波，负极（也就是2脚）接地，从而控制蜂鸣器响起不同频率的声音。

8002B是一款AB类单声道带关断模式，桥式音频功率放大器。在输入1KHz，5V工作电压时，最大驱动功率为：3W，(4Ω负载THD<10%)；2W，(4Ω负载，THD<1%)；音频范围内总谐波失真噪音小于 1%（20Hz~20KHz）；8002B应用电路简单，只需要极少数外围器件，就能提供高品质的输出功率。8002B输出不需要外接耦合电容或上举电容、缓冲网络、反馈电阻。

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png)    |![img](media/84216693acf9a54e2e8f4090b9154228.png)    |
| ------------------------ | --------------------------- | ---------------------------- | 
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1 |Keyes 8002b功放 喇叭模块 x1 |
| ![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |![img](media/f5504973a1c7bd3a74e8a9370a3c2358.png)|
|3P线(反向) x1 | USB线 x1             |一字螺丝刀 x1 |

---

#### 5. 模块接线图

![img](media/c2bea02d3b7f001e55f5a7e514222042.png)

---

#### 6. 实验代码

```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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电，功放喇叭模块播放一首音乐。如果觉得喇叭声音太大或太小，可以使用一字螺丝刀调节模块上的电位器以调整音量大小。

![img](media/b8f0ecc63c089bb796ce65f61bcc2074.png)

![img](media/e4e66942b8c54b09746882823d4ba7fa.png)

---

#### 8. 代码说明

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

---

### 第12课 风扇转动

#### 1. 项目介绍

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

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

---

#### 2. 模块参数

工作电压: DC 3.3V~5V 

最大输入电流：1A

电机驱动电压：5V

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

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

控制信号: 数字信号

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

尺寸：32mm x 23.8mm x 24.5mm

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

---

#### 3. 模块原理图

![img](media/381e7461586cc80615540dfd77d479e2.jpg)

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


---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/0fb16f82134e03f951cb8b8c383577ef.png) |
| ------------------------ | ------------------------ | ---------------------------- |
|UNO R4 WiFi/Minima主板(二选一)|Keyes 传感器扩展板 x1     | Keyes 130电机模块 x1     |
| ![img](media/1ff24756e347dcc14db599711e89d922.png)       |![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg)    | ![](media/bae3eacd95d794ed928a1d837f2073c6.png)    |
| 4P线(反向) x1 | USB线 x1   | 电源适配器  x1         |

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

---

#### 5. 模块接线图

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

![img](media/25cfb1a1d58fd4c15f7d594eedb46609.png)

---

#### 6. 实验代码


```c++
/*
 * 名称   : 130DC Fan motor
 * 功能   : 电机顺、逆旋转
 * 作者   : http://www.keyes-robot.com/ 
*/
//定义电机的两个引脚接口，分别为A0和A1
int INA = A0;   //INA对应IN+
int INB = A1;  //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(1000);
  //顺时针方向转
  digitalWrite(INA, LOW);
  digitalWrite(INB, HIGH);
  delay(2000);
  //停止
  digitalWrite(INA, LOW);
  digitalWrite(INB, LOW);
  delay(1000);
}
```

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

代码上传成功后，拔下USB线断电，按照接线图正确接好模块，将电源电源适配器连接到家庭电路给UNO R4主板供电（<span style="color: rgb(255, 76, 65);">注意：</span> 防止风扇有时侯工作不正常，另外还需要用USB线连接到计算机给UNO R4 WiFi主板上电，这样保证风扇能正常工作；否则，风扇可能会工作不正常）。上电后风扇逆时针转动2秒；停止1秒；顺时针转动2秒；停止1秒；循环交替。

![img](media/90713e189a022dd8a1c98004bafe566d.png) 

---

#### 8. 代码说明

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

---

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

#### 1. 项目介绍

在这个套件中，有一个Keyes 滑动电位器，它一个模拟传感器。前面我们学习过的传感器，都是数字传感器。例如我们前面学习的人体红外热释传感器，当传感器检测到附近有人在运动时，我们读取到高电平（5V），当未检测到附近有人在运动时，我们读取到低电平（0V），而在0~5V中间的电压值，我们数字IO口无法读取到，当然人体红外热释传感器也只能输出高低电平。而模拟传感器就可以通过我们UNO R4主板上的6个模拟口读取中间的电压值。

---

#### 2. 模块参数

工作电压: DC 3.3V~5V 

工作电流: 0.55mA

工作功率: 0.00275W

输出信号: 模拟信号

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

尺寸：32mm x 23.8mm x 28.4mm

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

---

#### 3. 模块原理图

![img](media/55ec915342b40230f5a8c8f29cfca17b.png)

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

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

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

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png)  | ![img](media/f05eaa57ea2095d03699704fa7c33864.png)  |    
| ------------------------ | ------------------------- | ---------------------------- | 
| UNO R4 WiFi/Minima主板(二选一)|  Keyes 传感器扩展板 x1 |Keyes 滑动电位器模块 x1 |    
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |    |
|3P线(反向) x1 | USB线 x1  |           |

---

#### 5. 模块接线图

![img](media/d1b690dc26bdc4025ed2b11e599ab3da.png)

---

#### 6. 实验代码


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

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

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

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

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

![img](media/a464531c625359ea3347e78947fef86c.png)

![img](media/807fb1afd1c578435696325b3eea8ecc.png)

---

#### 8. 代码说明

| 代码                                      | 说明                                                         |
| ----------------------------------------- | ------------------------------------------------------------ |
| pinMode(PIN_ANALOG_IN, INPUT); | 由“ int PIN_ANALOG_IN = A0; ”知道，定义滑动电位器的模拟管脚为A0，“INPUT”设置为输入模式。 |
| analogRead(PIN_ANALOG_IN)                 | 从指定的模拟引脚读取模拟值。为了兼容性，默认analogRead()分辨率为 10 位。详细了解请参考链接：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. 项目介绍

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

---

#### 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/c6e187ccac493cd61a644be8f88c66eb.jpg)

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


---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/ecf322b694f16f05026377a9464d7117.png) |    
| ------------------------ | ------------------------ | ---------------------------- | 
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1      |Keyes 水滴传感器 x1 |     
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |   |
| 3P线(反向) x1 | USB线 x1   |          |

---

#### 5. 模块接线图

![img](media/7fd6bfddf8efaa2b8d1b36acf64660b3.png)

---

#### 6. 实验代码

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

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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

![img](media/f6ca98631c73b0116511f2d20b19ed40.png)

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

![img](media/24e86f3819995bacbeb05504c36bd900.png)

![img](media/67ae64779e165122d807e8d346436e8c.png)

---

#### 8. 代码说明

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

---

### 第15课 检测声量

#### 1. 项目介绍

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

---

#### 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/80e0a523c80f30197d6d68476b8e9bbf.png)

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

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/d2f1d258aa291e3efb405237f2fb8fc7.png) | 
| ------------------------ | ------------------------ | ---------------------------- |
|UNO R4 WiFi/Minima主板(二选一)|  Keyes 传感器扩展板 x1      |Keyes 声音传感器 x1     |
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |  ![img](media/f5504973a1c7bd3a74e8a9370a3c2358.png) | 
| 3P线(反向) x1 | USB线 x1             |一字螺丝刀 x1             |

---

#### 5. 模块接线图

![img](media/4c42111e41443480acf8db122f64c6b1.png)

---

#### 6. 实验代码

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

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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

![img](media/6259a014b165f42bee624660923c3459.png)

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

![img](media/771c95b8868c423b8a7df1245de869cd.png)

![img](media/e63d254100df0a1e41bb6faecb5139e4.png)

---

#### 8. 代码说明

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

---

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

#### 1. 项目介绍

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

---

#### 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/e33c5f56d3a49da83f5bcc55182bed08.png)

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

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) |  ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/b60bc89e299325a03217aa1e0e4fe4f4.png) |    
| ------------------------ | ------------------------ | ---------------------------- |
| UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1  |Keyes 光敏电阻传感器 x1  |     
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |    |
|3P线(反向) x1 | USB线 x1  |           |

---

#### 5. 模块接线图

![img](media/711227b2ea49d7b04c80d44e2161d144.png)

---

#### 6. 实验代码

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

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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

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

![img](media/8167e23bd33361bf533bddcae5c22dde.png)

![img](media/f9a943b42d979776b15972cea0996eb4.png)

---

#### 8. 代码说明

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

---

### 第17课 模拟温度传感器

#### 1. 项目介绍

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

---

#### 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/bc645075aeb9352893e06da13fecd2d4.png)

Keyes 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/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png)    |![img](media/02075ba5d86485fa607a4f642b1675a0.png)|    
| ------------------------ | --------------------------- | ---------------------------- |
| UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1 |Keyes NTC-MF52AT模拟温度传感器 x1 |   
| ![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |     |
| 3P线(反向) x1 | USB线 x1   |          |

---

#### 5. 模块接线图

![img](media/7de8f1abe67878d07edcaf8010598323.png)

---

#### 6. 实验代码

```c++
/*  
 * 名称   : Temperature sensor
 * 功能   : 用热敏电阻传感器制作温度计
 * 作者   : http://www.keyes-robot.com/ 
*/
#define PIN_ANALOG_IN   A0

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);    //读A0引脚的模拟值
  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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

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

![img](media/97967d611c39bc0aad5d01792d79d7f5.png)

![img](media/7c4d1c3312edeb241362a1a54535c12f.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. 项目介绍

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

通过采集模块上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/9dc8bcd4d6ed5986f24ab58f23f4f621.png)

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

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/47ae764c377f2a93c081f473586bbcca.png) |      
| ------------------------ | ------------------------ | ---------------------------- | 
|UNO R4 WiFi/Minima主板(二选一) | Keyes 传感器扩展板 x1  |Keyes 薄膜压力传感器 x1  | 
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |    |    
|3P线(反向) x1 | USB线 x1  |           |

---

#### 5. 模块接线图

![img](media/b3beb4aa3ae5cbd6e87fd4f7a7535ccb.png)

---

#### 6. 实验代码

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

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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

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

![img](media/64080dc22422a92d4d6ffc5450b4cfef.png)

![img](media/6bd2ef6cda8fa170985060e7c979c41e.png)

---

#### 8. 代码说明

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

---

### 第19课 摇杆模块

#### 1. 项目介绍

你看过游戏手柄吗？游戏手柄上有按键，还有摇杆。摇杆是什么工作原理呢？在我们这个套件中，就有一个Keyes 摇杆模块，它的主要元件是PS2手柄摇杆。控制时，我们需要将模块的X端口和Y端口连接至单片机的模拟口。B端口连接至单片机数字口，V端口接至单片机电源输出端（3.3-5V），GND接单片机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/3885f70899d9a0ed4e2e57294521cead.png)

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

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/7a3bffdf74455717f81e49291a94535a.png) |     
| ------------------------ | ------------------------ | ---------------------------- |
| UNO R4 WiFi/Minima主板(二选一) |Keyes 传感器扩展板 x1        | Keyes 摇杆模块 x1 |       
|![img](media/54d1e28b3b1bec156db70b15730b9e0e.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |    |
| 5P线(反向) x1 | USB线 x1  |           |

---

#### 5. 模块接线图

![img](media/06b8b5c89216ae65d42f930f9197b9d0.png)

---

#### 6. 实验代码

```c++
/*  
 * 名称   : Joystick
 * 功能   : 从摇杆读取数据
 * 作者   : http://www.keyes-robot.com/ 
*/
int VRx = A3;  //定义X轴引脚
int VRy = A2;  //定义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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

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

![img](media/29803b1a37c2df6acdd8f364fd198c46.png)

![img](media/b21fa2aa40292e90504140686bf4f36e.png)

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

![img](media/7293e260cffd597d9643745d5d0ac960.png)

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

![img](media/29c0007757e95143be8edf57fcc48c5c.png)

按下摇杆。

![img](media/98a21b3f9b91c483b3757293bbd52720.png)

---

#### 8. 代码说明

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

---

### 第20课 舵机转动

#### 1. 项目介绍

![img](media/70ce4be3d48fd2e3706ca08840e18314.png)

舵机是一种位置伺服的驱动器，主要是由外壳、电路板、无核心马达、齿轮与位置检测器所构成。舵机有很多规格，但所有的舵机都有外接三根线。由于舵机品牌不同，颜色也会有所差异，我们实验用到的这款舵机分别用棕、红、橙三种颜色进行区分，棕色为接地线，红色为电源正极，橙色为信号线。

![img](media/a51126b39b85f6d915afe2e08e444d54.png)

舵机分为360度舵机、180度舵机和90度舵机，我们实验用到的这款舵机为90度舵机，但是它转动的角度范围最大接近180度，所以我们也可把它当做180度舵机使用，控制原理都是一样的。

![img](media/09cdeea8f8a62d575e1dd8236712d948.png)

---

#### 2. 模块参数

工作电压: DC 3.3V~5V 

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

尺寸：32.25mm x 12.25mm x 30.42mm

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

---

#### 3. 模块原理图

![img](media/5ec4a1c922a7203dd8e3341120d932be.png)

舵机的控制信号是周期为20ms （50Hz）的PWM（脉冲宽度调制）信号。

舵机的转动的角度是通过调节PWM信号的占空比来实现的，一般在 0.5ms ~ 2.5ms 的范围内去控制，总间隔为 2ms，相对应舵盘的位置为0度 ~ 180度，呈线性变化。当脉冲宽度为 5ms 时，舵机旋转至中间角度，大于 5ms 时舵机旋转角度增大，小于 5ms 时舵机旋转角度减小。

也就是说，舵机的控制需要单片机产生一个周期为20ms的脉冲信号，以0.5ms到2.5ms的高电平来控制舵机转动的角度。具体脉冲参数下图所示：

![img](media/be4d3d2b73d9c6258d3c8f595841b15c.png)

注意，由于舵机品牌不同，对于同一信号，不同品牌的舵机旋转的角度也会有所不同。

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![](media/034d286250927d6eed077db610e35861.png)   |![](media/460ec103035638e948d2b1a272e62178.png)   |    
| ------------------------ | ------------------- | --------------------- |
| UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1 |Keyes 舵机驱动模块 x1| 
|![](media/25f82bd1b415a8203a32392488131376.png)   |![img](media/1ff24756e347dcc14db599711e89d922.png) | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |
| 舵机 x1 | 4P线(反向) x1|USB线 x1             |

---

#### 5. 实验

##### 5.1 实验①：

###### （1）实验接线图

![img](media/69734a079eb27498fd80c2a576ec28ee.png)

###### （2）实验代码

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

Servo myservo;  //创建伺服对象来控制伺服电机

int servoPin = A1;  // 定义舵机引脚A1

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

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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

###### （3）实验结果

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

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

![img](media/1faa20c502d4ca9bce3afef173a83f9d.png)

![img](media/945e330e18de633008bd32f9191e8d70.png)

![img](media/25e16fdae54e24f0bf9d536b0f43b31c.png)

---

##### 5.2 实验②：

###### （1）实验接线图

![img](media/69734a079eb27498fd80c2a576ec28ee.png)

###### （2）实验代码

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

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

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

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

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/1faa20c502d4ca9bce3afef173a83f9d.png)

![img](media/945e330e18de633008bd32f9191e8d70.png)

![img](media/25e16fdae54e24f0bf9d536b0f43b31c.png)

---

#### 6. 代码说明

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


---

### 第21课 测距

#### 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/31b675aa9eaedc87e9eaccf110bf3956.png)

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

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

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

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

![img](media/536617830d23d47cc74e5b9c37d9402a.png)

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/d527053929462c5025cbb77b63af8c8e.png) | 
| ------------------------ | ------------------------ | ---------------------------- |
|UNO R4 WiFi/Minima主板(二选一) | Keyes 传感器扩展板 x1  |Keyes 超声波转接模块 x1 |
![img](media/9cf8c08b3226a4b79005700830303d79.png) | ![img](media/1ff24756e347dcc14db599711e89d922.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) | 
|HC-SR04 超声波传感器 x1 | 4P线(反向) x1 | USB线 x1             |

---

#### 5. 模块接线图

![img](media/1a4383e3a89df2eb82a5616802b61a8d.png)

![img](media/834a18054d57a88860807d578b1dac0e.png)

---

#### 6. 实验代码

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

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

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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

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

![img](media/36896c2c9684d8f69cf02dd035d50dc8.png)

![img](media/fd39e25d925cb8fb5c95a9005edd586a.png)

---

#### 8. 代码说明

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


---

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

#### 1. 项目介绍

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

Keyes 红外接收模块选择的是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/ddee2b72e660b9ec932d2ed9fa24d1cb.png)

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

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) |![img](media/034d286250927d6eed077db610e35861.png) | ![img](media/68a8618879423c0a54d0ea10f7f65258.png) |
| ------------------------ | ------------------------ | -------------------------------- |
| UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1    |Keyes 红外接收模块 x1  | 
|![img](media/aa263684292be588142e139e5d944991.png) | ![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |
| Keyes 遥控器 x1                  | 3P线(反向) x1 | USB线 x1             |

---

#### 5. 模块接线图

![img](media/e40c73a39c8c8d3e212ebf2eb0eb2bb8.png)

---

#### 6. 实验代码

```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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

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

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

![img](media/b3e66c94bc2a3990a8526c5e99f91a25.png)

![img](media/cca8cdf859cd087006521bad1a679c15.png)


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

![img](media/bc85d79cfb281cd25e7ca9f1ff8b89ff.png)

---

#### 8. 代码说明

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

---

### 第23课 检测温湿度

#### 1. 项目介绍

在这个套件中，有一个Keyes XHT11温湿度传感器。XHT11作为一款低价的入门级温湿度传感器，它主要由一个电阻式感湿元件和一个NTC测温元件组成。XHT11为4针单排引脚封装，采用单线制串行接口，只需加适当的上拉电阻，信号传输距离可达20米以上，Keyes 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/771ad05f1f5908cee98a8bdcd7cdaa11.png)

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

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

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

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png)   |![img](media/65e0cd99d69eb4c74ce5d1be531bf263.png)   |      
| ------------------------ | -------------------------- | ---------------------------- |
| UNO R4 WiFi/Minima主板(二选一)|  Keyes 传感器扩展板 x1 |Keyes XHT11温湿度传感器 x1 |    
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |     |
| 3P线(反向) x1 | USB线 x1 |            |

---

#### 5. 模块接线图

![img](media/0c1ef92c9c4b60b740e4cfddc9713833.png)

---

#### 6. 实验代码

```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(1000);  //等待设备读取需要1000ms
}
```

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

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

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

![](media/ed43459f5d2f553e68d9df0e3b223966.png)

![](media/6b4a1183b52761ead52c63a53bf6c75b.png)

---

#### 8. 代码说明

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


---

### 第24课 实时时间

#### 1. 项目介绍

这个模块主要用到的芯片是美国DALLAS公司推出的I2C总线接口实时时钟芯片DS1307，它可独立于CPU工作，不受CPU主晶振及其电容的影响；计时准确，月累积误差一般小于10秒。此芯片还具有主电源掉电情况下的时钟保护电路，DS1307的时钟靠后备电池维持工作，拒绝CPU对其读出和写入访问。同时还具有备用电源自动切换控制电路，因而可在主电源掉电和其它一些恶劣环境场合中保证系统时钟的定时准确性。DS1307具有产生秒、分、时、日、月、年等功能，且具有闰年自动调整功能。同时，DS1307芯片内部还集成有一定容量、具有掉电保护特性的静态RAM，可用于保存一些关键数据。实验中，我们测试出传感器检测的实时时间，并且在串口监视器上显示测试数据。

---

#### 2. 模块参数

工作电压：DC 3.3V~5V

工作电流：1.5mA

最大功率：0.0075W

中断类型: 全天时间

存储器容量: 56bytes

存储器类型: RAM

接口类型: I2C

时钟频率: 32.768kHz

类型: RTC

时钟：时间精确到2100年（具有闰年补偿）

通讯方式：I2C通讯

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

尺寸：47.6mm x 23.8mm x 9.3mm

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

---

#### 3. 模块原理图

![img](media/f6c9ed4dff9d8ae5253fdf971f1aef58.png)

DS1307串行实时时钟(RTC)是一个低功耗、全二进制编码的十进制(BCD)时钟/日历加上56字节的NV SRAM。地址和数据通过一个I2C，双向总线串行传输。时钟/日历提供秒、分钟、小时、天、日期、月和年的信息。月底日期会根据少于31天的月份进行自动调整，包括对闰年的修正。时钟工作在24小时或12小时的格式，与AM/PM指示器。DS1307有一个内置的电源感应电路，可以检测电源故障并自动切换到备用电源。当部件从备用电源开始进行操作时，将继续进行计时操作。

主要引脚定义如下： 

| DS1307引脚 | 定义                 |
| ---------- | -------------------- |
| X1、X2     | 32.768kHz 晶振接入端 |
| VBAT       | +3V 电池电压输入     |
| VCC        | 电源电压             |
| SQW        | 方波驱动器           |
| SCL        | 串行时钟             |
| SDA        | 串行数据             |

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png)      |![img](media/004c087d2099f7de5bc03795657d587b.png)|      
| ------------------------ | ----------------------------- | ---------------------------- |
|UNO R4 WiFi/Minima主板(二选一) | Keyes 传感器扩展板 x1 |Keyes DS1307时钟传感器模块 x1 |    
| ![img](media/1ff24756e347dcc14db599711e89d922.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |     |
|4P线(反向) x1 | USB线 x1 |            |

---

#### 5. 模块接线图

![img](media/b8e05c7eb644e84c1f7fe8dc3d51410f.png)

---

#### 6. 实验代码

```c++
/*  
 * 名称   : DS1307 Real Time Clock
 * 功能   : 读取DS1307时钟模块的年/月/日/时/分/秒/周
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <Wire.h>
#include "RtcDS1307.h"  //DS1307时钟模块库
RtcDS1307<TwoWire> Rtc(Wire); //I2C接口:SDA引脚接A4, SCL引脚接A5

void setup(){
  Serial.begin(57600);//波特率设置为57600
  Wire.begin();        // 初始化I2C
  Rtc.Begin();
  Rtc.SetIsRunning(true);
  Rtc.SetDateTime(RtcDateTime(__DATE__, __TIME__));  
}

void loop(){
  // 打印年/月/日/小时/分/ 秒/周
  Serial.print(Rtc.GetDateTime().Year());
  Serial.print("/");
  Serial.print(Rtc.GetDateTime().Month());
  Serial.print("/");
  Serial.print(Rtc.GetDateTime().Day());
  Serial.print("    ");
  Serial.print(Rtc.GetDateTime().Hour());
  Serial.print(":");
  Serial.print(Rtc.GetDateTime().Minute());
  Serial.print(":");
  Serial.print(Rtc.GetDateTime().Second());
  Serial.print("    ");
  Serial.println(Rtc.GetDateTime().DayOfWeek());
  delay(1000);//延迟1秒
}
```

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

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

串口监视器打印出年、月、日、时、分、秒、周，并每秒刷新一次，显示如下图。

![img](media/723200017a6649fb7f8467f45f375a28.png)

![img](media/411c2d9eddcc50fdc055fe9feda5cd82.png)

---

#### 8. 代码说明

| 代码                          | 说明                       |
| ----------------------------- | -------------------------- |
| Rtc.Begin()                   | 启动DS1307实时时钟。       |
| Rtc.GetDateTime()             | 获取当前系统的时间和日期。 |
| Rtc.SetDateTime()             | 设置时间。                 |
| Rtc.GetDateTime().Year()      | 返回年份。                 |
| Rtc.GetDateTime().Month()     | 返回月份。                 |
| Rtc.GetDateTime().Day()       | 返回日期。                 |
| Rtc.GetDateTime().Hour()      | 返回小时。                 |
| Rtc.GetDateTime().Minute()    | 返回分钟。                 |
| Rtc.GetDateTime().Second()    | 返回秒数。                 |
| Rtc.GetDateTime().DayOfWeek() | 返回星期。                 |

---

### 第25课 计数器

#### 1. 项目介绍

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

实验中使用Keyes 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/eb07787502bbaf0c14131d016f2883d6.png)

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

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

3.1 TM1650通讯时序格式

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

![](media/63aed00d97b389cfb15c1debb49d2db2.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/e45148b601dce0fd7da8fbc2102dec94.png)

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

（5）写显示操作

![](media/4cb3281cedff66fc96ab3da75613e1cb.png)

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

（6）完整操作时序

![](media/e20965db007aede711bf45bbef19852c.png)

command1：系统命令 48H； 
command2：系统参数设置；
ADDRESS：显示地址（68H、6AH、6CH、6EH）；
DATA：显示数据。

备注：
1.设置系统参数和写入显存数据是两个独立的过程，它们之间的顺序不影响实际应用； 
2.每次输入系统命令（48H）和系统参数设置命令都会改变系统参数，请特别注意待机指令操作。

3.2 指令集说明

（1）数据命令设置 

![](media/b0f8e205f12653928030259e0130f330.png)

注意：使用的指令是 16 进制 H，输入数据和读取数据都是从高位开始。

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

（2）显示命令设置

![](media/3e186d039da931602afd3c387500635a.png)

注意：在发送上述系统显示命令前需要先输入系统命令48H,如48H+11H=1级亮度开屏显示。

B[7:0]：这里实际是一个字节数据，只是不同位部分代表不同功能。
B[6:4]：设置数码管亮度。注意：000 最亮。
B[3]：设置是否显示小数点。
B[0]：设置数码管的开屏、关屏。

（3）显存地址

![](media/722fb054625407eaa3a3112e820c2520.png)

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

![](media/b5a4d24dc937f71e1effef03475f2487.png)

---

#### 4. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png)      |![img](media/5c6233a12f9e14a0ebabfd07f4c5a33f.png)|      
| ------------------------ | ----------------------------- | ---------------------------- |
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1 |Keyes TM1650四位数码管模块 x1| 
| ![img](media/1ff24756e347dcc14db599711e89d922.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |     |
| 4P线(反向) x1 | USB线 x1|             |

---

#### 5. 模块接线图

![img](media/e9f4d427f09b702041be8cceb5564058.png)

---

#### 6. 实验代码

```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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 7. 实验结果

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

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

![img](media/a6737cc409662fd38dc8f772deaca9f2.png)

---

#### 8. 代码说明

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


---

### 第26课 显示图案

#### 1. 项目介绍

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

第二课我们学习了一个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/f1417ace9ff97a4031e4e9c740bd4a3a.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/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png)     |![img](media/58efefbc64ef910d3df39f069eddc788.png)  |     
| ------------------------ | ---------------------------- | ---------------------------- |
| UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1 |Keyes HT16K33_8X8点阵模块 x1 | 
| ![img](media/1ff24756e347dcc14db599711e89d922.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |     |
|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/4b67c96db67dde86c96eadb2fae36198.png)

---

#### 7. 实验代码

```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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 8. 实验结果

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

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

![img](media/659c178eab572adbb590df42c3d2a820.png)

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

![img](media/72d202922940df45ca4a638a890c8319.jpg)

---

#### 9. 代码说明

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

![](media/e4d05895556f5755d56fa9853d6a5aa1.png)

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

---

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

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

---

### 第27课 模拟小台灯

#### 1. 项目介绍

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

---

#### 2. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png)|![img](media/628fac0d8bdcd43d93afc13ae228177c.png)|
| ------------------------ | ------------------------ | ------------------------ |
| UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1    |Keyes 自锁按键模块 x1    |
| ![img](media/1a121809b85e9f58d815cee433df7e14.png) | ![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |
| Keyes 紫色LED模块 x1     | 3P线(反向) x2 | USB线  x1    |   

---

#### 3. 实验接线图

![img](media/f91429d3da03cc985a8b91e8bb0f1d0c.png)

---

#### 4. 实验代码

```c++
/* 
 * 名称   : Button_control_led
 * 功能   : 模拟一个小台灯
 * 作者   : http://www.keyes-robot.com/ 
*/
#define PIN_BUTTON  5  //将自锁按键的引脚连接到D5
int val = 0;            //用于存储键值

int ledpin = 3;    // 定义LED引脚D3

void setup() {
  Serial.begin(9600);//启动串口监视器，设置波特率为9600
  pinMode(PIN_BUTTON, INPUT);  //设置自锁按键的引脚为输入模式
  pinMode(ledpin, OUTPUT);   //设置LED引脚为输出模式
}

// 循环函数会一直运行下去
void loop() {
  val = digitalRead(PIN_BUTTON);  //读取自锁按键的值并将其赋值给变量val
  Serial.println(val);          //打印变量val的值
  if (val == 0) {             //按下自锁按键时读取到低电平
    digitalWrite(ledpin, HIGH);  //LED点亮
  }
  else {   //按起自锁按键时读取到高电平
    digitalWrite(ledpin, LOW);  //LED不亮
  } 
}
```

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 5. 实验结果

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

![](media/8b9a0ef2ed5ccd7c1f18991f96d07d94.png)

---

#### 6. 代码说明

| 代码                                   | 说明                                                         |
| -------------------------------------- | ------------------------------------------------------------ |
| val = digitalRead(PIN_BUTTON) | 读取自锁按键的值并将其赋值给变量val|
| if( ){ } else{ }       | 这是判断语句，如果（ ）里的表达式为真，则执行 if { }块内的代码。如果（ ）里表达式为假 ，则执行 else { }块内的代码。 |
|digitalWrite(ledpin, HIGH);  | 点亮LED|

---

### 第28课 障碍物检测报警器

#### 1. 项目介绍

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

生活中，我们可以利用一个检测传感器控制一个有源蜂鸣器响起，做声光报警设备，如检测倾斜（倾斜模块）等等。这一实验课程中我们将避障传感器和有源蜂鸣器模块组合实验，实现避障传感器检测到障碍物时有源蜂鸣器响起的效果。

---

#### 2. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) | ![img](media/2b35bd88b0382b61946efaa259ccf033.png) | ![img](media/7e77d1498bafcbd3e9f2d40103af3734.png) | 
| ------------------------ | ------------------------ | ------------------------ | ---------------------------- | 
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1      |Keyes 避障传感器 x1      | Keyes 有源蜂鸣器模块 x1  |
|![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |![img](media/f5504973a1c7bd3a74e8a9370a3c2358.png) | |
| 3P线(反向) x2 | USB线 x1             | 一字螺丝刀 x1         |    |

---

#### 3. 实验接线图

![img](media/15e7e41ddb18e587b3384522c08d09de.png)

---

#### 4. 实验代码

```c++
/*  
 * 名称   : Avoiding alarm
 * 功能   : 避障传感器控制蜂鸣器
 * 作者   : http://www.keyes-robot.com/ 
*/
int item = 0;

void setup() {
  Serial.begin(9600);//启动串口监视器，设置波特率为9600
  pinMode(3, INPUT);  //避障传感器连接D3，设置为输入模式
  pinMode(5, OUTPUT); //将蜂鸣器连接到D5上并设置为输出模式
}

void loop() {
  item = digitalRead(3); //读取避障传感器输出的电平值
  Serial.println(item);  //打印变量item的值
  if (item == 0) {//检测到障碍物
    digitalWrite(5, HIGH);//蜂鸣器响起
  } else { //未检测到障碍物
    digitalWrite(5, LOW); //蜂鸣器关闭
  }
  delay(100);//延迟100ms
}
```

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 5. 实验结果

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电。当避障传感器检测到障碍物时，避障传感器上SLED灯亮起，同时有源蜂鸣器发出声响；当避障传感器检测不到障碍物时，有源蜂鸣器停止发出声响。

![](media/7c753ee1ce4e705b7d50788347abfabe.png)

---

#### 6. 代码说明

| 代码                   | 说明                     |
| ---------------------- | ------------------------ |
| item == 0              | 避障传感器检测到障碍物。 |
| digitalWrite(5, HIGH) | 有源蜂鸣器发出声响。     |
| digitalWrite(5, LOW)  | 有源蜂鸣器停止发出声响。 |


---

### 第29课 防盗报警器

#### 1. 项目介绍

上一课实验中我们学习了使用避障传感器检测障碍物进行报警提醒。在这一实验课程中我们将人体红外热释传感器、紫色LED模块和有源蜂鸣器模块组合实验，实现人体红外热释传感器检测到附近有人经过时，有源蜂鸣器响起，紫色LED模块上的LED闪烁的效果。

---

#### 2. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png)     |![img](media/6963fc8b02f22425a78d196cbc3c8bd0.png)     | ![img](media/7e77d1498bafcbd3e9f2d40103af3734.png) |
| ------------------------ | ---------------------------- | ------------------------ |------------------------ |
| UNO R4 WiFi/Minima主板(二选一) | Keyes 传感器扩展板 x1  |Keyes 人体红外热释传感器 x1  | Keyes 有源蜂鸣器模块 x1  |
|![img](media/1a121809b85e9f58d815cee433df7e14.png) | ![img](media/278286c09e3e34f820b4754221ac23aa.png)  |![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg)|   |
|Keyes 紫色LED模块 x1|3P线(反向) x3 |USB线  x1   |   |

---

3. 模块接线图

![](media/0e85bc3a43ba5b74baa47fe646fa8c64.png)

---

#### 4. 实验代码

```c++
/*  
 * 名称   : PIR alarm
 * 功能   : PIR控制蜂鸣器和led模块
 * 作者   : http://www.keyes-robot.com/ 
*/
#define PIR_PIN  5     //PIR运动传感器控制引脚为D5
#define BUZZER_PIN  8  //定义有源蜂鸣器控制引脚为D8
int item = 0;

int ledpin = 3;    // 定义LED引脚D3

void setup() {
  Serial.begin(9600);//启动串口监视器，设置波特率为9600
  pinMode(PIR_PIN, INPUT);  //设置PIR运动传感器的引脚为输入模式
  pinMode(BUZZER_PIN, OUTPUT); //设置有源蜂鸣器的引脚为输出模式
  pinMode(ledpin, OUTPUT);   //设置LED引脚为输出模式
}

void loop() {
  item = digitalRead(PIR_PIN);//读取红外热释传感器输出的数字信号
  Serial.println(item);          //打印变量item的值
  if (item == 1) {  //运动检测
    digitalWrite(BUZZER_PIN, HIGH); //打开蜂鸣器
    digitalWrite(ledpin, HIGH);  // LED点亮
    delay(200); 
    digitalWrite(BUZZER_PIN, LOW); //关闭蜂鸣器
    digitalWrite(ledpin, LOW);  // LED不亮
    delay(200);
  } else {  //没有检测到任何信号或数据
    digitalWrite(BUZZER_PIN, LOW); //关闭蜂鸣器
    digitalWrite(ledpin, LOW);  // LED不亮
  }
}
```

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 5. 实验结果

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

![](media/0842d72c59a3724db080c35c9e353ec4.png)

---

#### 6. 代码说明

| 代码                   | 说明                                     |
| ---------------------- | ---------------------------------------- |
| item = digitalRead(PIR_PIN) | 人体红外热释传感器读取数字电平信号输出。 |
| item == 1              | 运动检测，检测到有人经过。               |
| digitalWrite(BUZZER_PIN, HIGH) | 有源蜂鸣器响起发出警报。                 |


---

### 第30课 智能温湿度控制系统

#### 1. 项目介绍

本课程介绍如何使用UNO R4主板、温湿度传感器、电机模块，构建一个智能温湿度控制系统。

该系统能够测量环境温度和湿度，并根据需求控制风扇来降温降湿。当温度或湿度超过设定的阈值时，系统会自动开启风扇，将环境中的温度或湿度降至设定值以下来保护农场里的动植物。该系统能够实现自动调节环境温湿度的功能，对于需要控制环境温湿度的项目具有很好的应用价值。

总的来说，这个应用场景是一个非常实用的解决方案，可以帮助农场主人监测和控制农场的环境，提高农业生产效率。

---

#### 2. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) | ![img](media/0fb16f82134e03f951cb8b8c383577ef.png) |
| ------------------------ | ------------------------ | ------------------------ |
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1     |Keyes 130电机模块 x1     |  
|  ![img](media/65e0cd99d69eb4c74ce5d1be531bf263.png) | ![](media/1ff24756e347dcc14db599711e89d922.png) |  ![](media/278286c09e3e34f820b4754221ac23aa.png)  |
|Keyes 温湿度传感器 x1      | 4P线(反向) x1 |3P线(反向) x1| 
|![](media/fa9ebfb506b2f154108baba98dffa9ee.jpg)       | ![](media/bae3eacd95d794ed928a1d837f2073c6.png)    |      |
|USB线 x1                | 电源适配器  x1         |       |

---

3. 模块接线图

![](media/0db8e4546f0fc0f51ef02414774af04f.png)

---

#### 4. 实验代码

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

```c++
/*  
 * 名称   : Temperature_humidity_control_system
 * 功能   : 温湿度传感器控制130风扇模块
 * 作者   : 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
  pinMode(A4, OUTPUT); //INA对应IN+，将A4设置为输出模式
  pinMode(A5, OUTPUT);//INB对应IN-，将A5置为输出模式
}

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");
    if (dht[2] > 32 || dht[0] > 80) {//温度高于32℃或湿度高于80%RH。  
      digitalWrite(A4, LOW);//打开电风扇
      digitalWrite(A5, HIGH);  
    } else {//否则，关闭风扇
      digitalWrite(A4, LOW);
      digitalWrite(A5, LOW);  
    }
  } else {    //Read error
    Serial.println("sensor error");
  }
  delay(200);  //等待设备读取需要200ms 
}
```

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 5. 实验结果

由于风扇在转动时，所需的电流比其他传感器要大，会引起电路中电压电流波动，特别是风扇进行正反转时，电压电流波动过大，导致UNO R4主板的电压电流过低，会导致复位。

代码上传成功后，拔下USB线断电，按照接线图正确接好模块，将电源电源适配器连接到家庭电路给UNO R4主板供电（<span style="color: rgb(255, 76, 65);">注意：</span> 防止风扇有时侯工作不正常，另外还需要用USB线连接到计算机给UNO R4主板上电，这样保证风扇能正常工作；否则，风扇可能会工作不正常）。上电后，打开串口监视器，设置波特率为 **<u>9600</u>**。

串口监视器上打印出温湿度传感器的温度值和湿度值，温度达到32℃或湿度达到80%RH时，电机模块会开启，进行散热或降湿（风扇为模拟散热降湿，散热降湿效果一般）；否则，电机模块会关闭，达到农场自动控温控湿效果，节省能源。

![](media/bb685112968192b0168dee79039c57be.png)

---

#### 6. 代码说明

在代码中我们设置了阈值为32和80（dht[2] > 32 || dht[0] > 80），阈值可以根据实际情况更改。当温度达到32℃或湿度达到80%RH时，风扇将自动开启；否则关闭。风扇的驱动方式请查看上面的第12课。

---

### 第31课 夜光灯

#### 1. 项目介绍

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

---

#### 2. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/b60bc89e299325a03217aa1e0e4fe4f4.png) |
| ------------------------ | ------------------------ | ------------------------ |
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1      |Keyes 光敏传感器 x1  |
|![img](media/1a121809b85e9f58d815cee433df7e14.png) | ![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |  
| Keyes 紫色LED模块 x1   | 3P线(反向) x2 | USB线 x1   |  

---

3. 模块接线图

![img](media/c4cdfbb729e9a155491c758b0062c654.png)

---

#### 4. 实验代码

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

```c++
/*  
 * 名称   : Night_Lamp
 * 功能   : 光敏传感器控制LED
 * 作者   : http://www.keyes-robot.com/ 
*/
int item = 0;  //设置item为0
int PIN_ANALOG_IN = A0;   //光敏传感器的引脚定义为A0
int PIN_LED = 3;   //LED的引脚定义为D3

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

void loop() {
  item = analogRead(PIN_ANALOG_IN);    //读取传感器的模拟信号
  Serial.println(item);  //串口打印光敏传感器输出的模拟信号
  if (item > 200) {  //光照强度的模拟值大于200
    digitalWrite(PIN_LED, LOW);  //关闭LED
  } else {  //模拟值小于等于200
    digitalWrite(PIN_LED, HIGH);  //打开LED
  }
}
```

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 5. 实验结果

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

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

![img](media/7eaa2cf39900750984a1a086cd1a3e9f.png)

![img](media/3c57eff681f3a27051c797f6bbfb95c5.png)

---

#### 6. 代码说明

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

---

### 第32课 声控灯

#### 1. 项目介绍

如今智能家居发展迅速，你使用过智能家居当中的智能声控灯吗？当我们跺跺脚或者拍拍手时，智能声控灯自动亮起；当没有声音时，智能声控灯处于熄灭状态。智能声控灯上安装有声音探测传感器，这些传感器将外界声音的大小，转换成对应数值。智能声控灯设置一个临界点，当声音转换后对应的数值超过该临界点时，灯光亮起一段时间。 

在这一实验课程中，我们将声音传感器和紫色LED模块组合实验，学习制作一个最简单的智能声控灯。

---

#### 2. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/d2f1d258aa291e3efb405237f2fb8fc7.png) |![img](media/f5504973a1c7bd3a74e8a9370a3c2358.png) |
| ------------------------ | ------------------------ | ------------------------ |------------------------ |
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1      |Keyes 声音传感器 x1     |一字螺丝刀 x1|
 ![img](media/1a121809b85e9f58d815cee433df7e14.png) | ![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |  |
| Keyes 紫色LED模块 x1     | 3P线(反向) x2 | USB线 x1   |  |

---

3. 模块接线图

![img](media/22c9f85464b6f55e8c91b8011c43b95b.png)

---

#### 4. 实验代码

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

```c++
/*  
 * 名称   : sound-controlled lights
 * 功能   : 声音传感器控制LED亮灭
 * 作者   : http://www.keyes-robot.com/ 
*/
int microPin = A0;  //定义声音传感器的控制引脚为A0
#define LED_PIN  3   //定义led模块控制引脚为D3

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

void loop() {
  int val = analogRead(microPin); //读取模拟值
  Serial.print(val); // 串口打印
  if(val > 150){ //超过阈值
    digitalWrite(LED_PIN, HIGH);  //打开LED
    delay(5000);
  }else{ //否则
    digitalWrite(LED_PIN, LOW);  //关闭LED
  }
  delay(100);
}
```

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 5. 实验结果

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

![img](media/6259a014b165f42bee624660923c3459.png)

串口监视器打印出声音传感器接收到的声音对应的模拟值。对准MIC头大声说话，接收到的声音增大时模拟值也增大，当模拟值大于150时，LED亮起5秒，然后熄灭。（**注意：如果声音变化对应的模拟值没有变化并且一直都是数字0，需要用一字螺丝刀顺时针旋转电位器来调节。**）

![img](media/f791c780c3f12d2b7304c3ada9f7bebb.png)

---

#### 6. 代码说明

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


---

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

#### 1. 项目介绍

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

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

---

#### 2. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/ecf322b694f16f05026377a9464d7117.png) | ![img](media/460ec103035638e948d2b1a272e62178.png)|
| ------------------------ | ------------------------ | -------------------- | ---------------------------- |  
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1      |Keyes 水滴传感器 x1 | Keyes 舵机驱动模块 x1       | 
| ![img](media/25f82bd1b415a8203a32392488131376.png)|![img](media/278286c09e3e34f820b4754221ac23aa.png) | ![img](media/1ff24756e347dcc14db599711e89d922.png)            | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |
| 舵机 x1  | 3P线(反向) x1 | 4P线(反向) x1 |USB线 x1      | 

---

3. 模块接线图

![img](media/0b4094daef412158bf101b66aa83d36d.png)

---

#### 4. 实验代码

<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 = A1;      // 定义舵机引脚A1
Servo myservo;          //定义舵机类的实例

void setup(){
  Serial.begin(9600);
  pinMode(PIN_ADC, INPUT);   //设置水滴传感器的引脚为输入模式
  myservo.attach(servoPin);  //选择舵机引脚A1
  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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 5. 实验结果

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

![img](media/4e336fcdc29a735779aee78700bb05eb.png)

---

#### 6. 代码说明

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

---

### 第34课 体重报警器

#### 1. 项目介绍

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

---

#### 2. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png)     | ![img](media/034d286250927d6eed077db610e35861.png)     |![img](media/7e77d1498bafcbd3e9f2d40103af3734.png)     |
| ---------------------------- | ---------------------------- | ------------------------ |
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1      |Keyes 有源蜂鸣器模块 x1      |
|  ![img](media/47ae764c377f2a93c081f473586bbcca.png) |![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg)    | 
|  Keyes 薄膜压力传感器 x1      |3P线(反向) x2 | USB线  x1                | 

---

3. 模块接线图

![img](media/865356801b10a5882d2a310b35b11607.png)

---

#### 4. 实验代码

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

```c++
/*  
 * 名称   : Weight_alarm
 * 功能   : 薄膜压力传感器控制蜂鸣器
 * 作者   : http://www.keyes-robot.com/ 
*/
int item = 0;  //设置item为0
int PIN_ANALOG_IN = A0;   //薄膜压力传感器的引脚定义为A0
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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 5. 实验结果

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

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

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

![img](media/81febe251f176df6895684364489f84b.png)

---

#### 6. 代码说明

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

---

### 第35课 红外遥控灯

#### 1. 项目介绍

在前面实验中，我们学会了点亮或熄灭LED、学会了利用PWM调节灯光的亮度、学会了使用红外接收模块，并将接收到的遥控器对应的键值打印出来。在这一实验课程中，我们将红外接收模块和紫色LED模块组合实验，实现用红外遥控器控制紫色LED的亮灭以及控制紫色LED显示不同亮度。

当红外接收模块接收到红外遥控器的按键值时，通过设置此按键值的输出PWM值实现设置不同LED亮度的效果，控制LED的亮灭也一样。

在这一实验课程中我们使用 “①“、”②“、”③”三个按键来控制紫色LED实现弱亮、正常亮、强亮三种不同亮度。如果想要使用 “OK” 键这一个按键来控制LED亮和灭的两种情况该如何实现呢？这一实验课程我们将学习使用一个新的基本数据类型 —— boolean，来实现同一个按键控制LED亮灭的效果。

**boolean 数据类型**，变量存储为 8位（1 个字节）的数值形式，**只能是 True 或是 False**。boolean 变量的值显示为 True 或 False（在使用 Print 的时候），或者 #TRUE# 或 #FALSE#（在使用 Write # 的时候）。使用关键字True 与 False 可将 boolean 变量赋值为这两个状态中的一个。

设置代码，按下“OK”键且满足某一条件，点亮LED；按下“OK”键且满足另一条件，熄灭LED。这个条件我们用 boolean 来实现是最简单方便的，因为 boolean 只有 True 或是 False 两种状态。我们只需要设置按下“OK”键的同时 flag 为 true，即可点亮LED；同理按下“OK”键的同时 flag 为 false，熄灭LED。

---

---

#### 2. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png)         | ![img](media/034d286250927d6eed077db610e35861.png)     |![img](media/68a8618879423c0a54d0ea10f7f65258.png)     | ![img](media/1a121809b85e9f58d815cee433df7e14.png) |
| -------------------------------- | ---------------------------- | ------------------------ |------------------------ |
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1        |Keyes 红外接收模块 x1        | Keyes 紫色LED模块 x1     |
| ![img](media/aa263684292be588142e139e5d944991.png) | ![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg)    |  |
| Keyes 遥控器 x1                  | 3P线(反向) x2 | USB线  x1                |  |

---

3. 模块接线图

![img](media/d53656be370290eb137ba321b73aa705.png)

---

#### 4. 实验代码

```c++
/*  
 * 名称   : IR Control LED
 * 功能   : 红外遥控LED开关
 * 作者   : http://www.keyes-robot.com/ 
*/
#include <Arduino.h>
#include <IRremote.hpp>

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

#define LED_PIN  3   //定义LED模块控制引脚为D3

boolean flag = true;  //LED标志钻头

void setup() {
  Serial.begin(9600);
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);  // 启动接收器
  pinMode(LED_PIN, OUTPUT);
}

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” 
    digitalWrite(LED_PIN, HIGH); // 点亮LED.
    delay(10);
    Serial.println("  led on");
    flag = false;
  } 
  else if (value == 0xBF40FF00 && flag == false){ // 接收数字“OK”
    digitalWrite(LED_PIN, LOW); // 熄灭LED.
    delay(10);
    Serial.println("  led off"); 
    flag = true;
  }
  else if(value == 0xE916FF00){
    analogWrite(LED_PIN, 85);
    delay(10);
    Serial.println("  weak light");

  } 
  else if(value == 0xE619FF00){
    analogWrite(LED_PIN, 170);
    delay(10);
    Serial.println("  normal light");
  } 
  else if(value == 0xF20DFF00){
    analogWrite(LED_PIN, 255);
    delay(10);
    Serial.println("  strong light");
  }
}
```

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 5. 实验结果

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

![](media/a8ef8ed83e2fe3cbdb6456f480d49071.png)

找到红外遥控器，拔出绝缘片，对准红外接收模块的红外接收传感器的接收头。第一次按下红外遥控器上的 “**<u>OK</u>**” 键，紫色LED被点亮，实现开灯的效果。串口监视器打印出按下的按键值和LED灯的亮灭情况：“**<u>BF40FF00  led on</u>**” 。

再次按下红外遥控器上的 “**<u>OK</u>**” 键，紫色LED熄灭，实现关灯的效果。串口监视器打印出按下的按键值和LED灯的亮灭情况：“**<u>BF40FF00  led off</u>**” 。

按下红外遥控器上的 “**①**” 键，紫色LED被设置为弱亮。串口监视器打印出按下的按键值和LED灯的亮灭情况：“**<u>E916FF00  weak light</u>**” 。

按下红外遥控器上的 “**②**” 键，紫色LED被设置为正常亮。串口监视器打印出按下的按键值和LED灯的亮灭情况：“**<u>E619FF00  normal light</u>**” 。

按下红外遥控器上的 “**③**” 键，紫色LED被设置为强亮。串口监视器打印出按下的按键值和LED灯的亮灭情况：“**<u>F20DFF00  strong light</u>**” 。


![img](media/dbe6acd98d9ed3dd7ae24404d78b3544.png)

---

#### 6. 代码说明

| 代码                | 说明                                                         |
| ------------------- | ------------------------------------------------------------ |
| boolean flag = true | 设置一个变量 flag 为 boolean 数据类型，数值为 true。         |
| flag = false        | boolean 数据类型的 flag 赋值为 false ，以便再次按下再次按下 “OK”键时满足熄灭LED的条件。 |


---

### 第36课 模拟温度散热装置

#### 1. 项目介绍

生活中，我们的电脑或者电路板芯片等器件会由于工作时间过长或者功耗过大的问题而发热严重，所以我们常常需要一个散热装置。

在前面的课程我们学习了如何使用温度传感器和电机模块，这一课我们学习把它们结合起来做成一个智能散热装置。当检测到环境温度高于某一个值时的时候，电机开启，从而达到降低环境温度、散热效果。再把此刻的温度值显示在四位数码管中。

---

#### 2. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png) | ![img](media/034d286250927d6eed077db610e35861.png)     |![img](media/0fb16f82134e03f951cb8b8c383577ef.png)     |
| ------------------------ | ---------------------------- | ----------------------------- |
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1         |  Keyes 130电机模块 x1|
| ![img](media/5c6233a12f9e14a0ebabfd07f4c5a33f.png)   | ![img](media/02075ba5d86485fa607a4f642b1675a0.png) | ![img](media/278286c09e3e34f820b4754221ac23aa.png)         |
|Keyes TM1650四位数码管模块 x1 | Keyes NTC-MF52AT模拟温度传感器 x1 | 3P线(反向) x1|
| ![img](media/1ff24756e347dcc14db599711e89d922.png)    | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg)    | ![](media/bae3eacd95d794ed928a1d837f2073c6.png)        | 
| 4P线(反向) x2 |USB线 x1                | 电源适配器  x1             |

---

3. 模块接线图

![](media/cdb65ef31c985acd8c5d60720639ebcf.png)

---

#### 4. 实验代码

<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);
  DigitalTube.setBrightness();  //设置亮度，0- 7，默认值:2
  DigitalTube.displayOnOFF();   //显示打开或关闭，0=显示关闭，1=显示打开，默认值:1
  for(char b=1;b<5;b++){
    DigitalTube.clearBit(b);    //DigitalTube.clearBit(0~3);清晰位显示
  }

  DigitalTube.displayBit(1,0);  //DigitalTube.Display(bit,number); bit=0---3  number=0---9
  //电机连接到A0和A1
  pinMode(A0, OUTPUT);
  pinMode(A1, 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) { //当温度超过30℃时，打开风扇
    digitalWrite(A0, LOW);
    digitalWrite(A1, HIGH);
  } else { //否则，请关闭风扇
    digitalWrite(A0, LOW);
    digitalWrite(A1, 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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 5. 实验结果

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后，将电源电源适配器连接到家庭电路给UNO R4主板供电（<span style="color: rgb(255, 76, 65);">注意：</span> 防止风扇有时侯工作不正常，另外还需要用USB线连接到计算机给UNO R4 WiFi主板上电，这样保证风扇能正常工作；否则，风扇可能会工作不正常）。上电后，可以在四位数码管模块上看到当前温度值，当温度超过代码中设定的30°C时，风扇转动，散热。

![](media/1bae25b5ccf713377d23ae17504610c1.png)

---

#### 6. 代码说明

变量的设置与存储检测值，与前面我们学习的一样，也是通过设置一个温度的阈值（阈值30可以根据实际情况重新更改），超过这个阈值进行控制电机转动，四位数码管显示温度值。


---


### 第37课 超声波雷达

#### 1. 项目介绍

蝙蝠飞行与获取猎物是通过回声定位的。回声定位：某些动物能通过口腔或鼻腔把从喉部产生的超声波发射出去，利用折回的声音来定向，这种空间定向的方法称为回声定位。科学家们从蝙蝠身上得到的启示发明了雷达，即雷达的天线相当于蝙蝠的嘴,而天线发出的无线电波就相当于蝙蝠的超声波,雷达接收电波的荧光屏就相当于蝙蝠的耳朵。

这一课我们就来学习制作一个简易雷达。将HC-SR04 超声波传感器、8002b功放 喇叭模块、紫色LED模块和TM1650四位数码管模块组合实验，利用距离大小控制功放喇叭模块模块响起对应频率的声音、紫色LED闪烁快慢或点亮，然后把这个距离显示在四位数码管上。这样就搭建好了一个简易的超声波雷达系统。

---

#### 2. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png)     | ![img](media/034d286250927d6eed077db610e35861.png) |![img](media/9cf8c08b3226a4b79005700830303d79.png) | ![img](media/d527053929462c5025cbb77b63af8c8e.png)|
| ---------------------------- | ---------------------------- | ----------------------------- |----------------------------- |
|UNO R4 WiFi/Minima主板(二选一)| Keyes 传感器扩展板 x1      |HC-SR04 超声波传感器 x1      | Keyes 超声波转接模块 x1       |
| ![img](media/84216693acf9a54e2e8f4090b9154228.png)     |![img](media/1a121809b85e9f58d815cee433df7e14.png)     | ![img](media/5c6233a12f9e14a0ebabfd07f4c5a33f.png)      | ![img](media/278286c09e3e34f820b4754221ac23aa.png)       | 
| Keyes 8002b功放 喇叭模块 x1  |Keyes 紫色LED模块 x1 | Keyes TM1650四位数码管模块 x1 |3P线(反向) x2|
|![img](media/1ff24756e347dcc14db599711e89d922.png)       | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) | ![img](media/f5504973a1c7bd3a74e8a9370a3c2358.png)  |          |
| 4P线(反向) x2 | USB线  x1  | 一字螺丝刀*1     |         |

---

3. 模块接线图

![img](media/1fb70c8b2c4a0b797ad776f9f71b7ab6.png)

---

#### 4. 实验代码

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

```c++
/*  
 * 名称   : Ultrasonic radar
 * 功能   : 超声波控制四位数管，蜂鸣器和LED灯模拟超声波雷达
 * 作者   : http://www.keyes-robot.com/ 
*/
#include "TM1650.h" //导入TM1650库文件
//四位数码管的接口为A4和A5
#define DIO A4
#define CLK A5
TM1650 DigitalTube(CLK,DIO);

#define BUZZER_PIN  2    //定义喇叭引脚为D2

#define LED_PIN  8   //定义led模块控制引脚为D8

int TrigPin = A0; //设置Trig引脚为A0
int EchoPin = A1; //设置Echo引脚为A1
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);  // 设置蜂鸣器为输出模式
  
  DigitalTube.setBrightness();  //设置亮度，0- 7，默认值:2
  DigitalTube.displayOnOFF();   //显示打开或关闭，0=显示关闭，1=显示打开，默认值:1
  for(char b=1;b<5;b++){
    DigitalTube.clearBit(b);    //DigitalTube.clearBit(0 to 3); 清空位显示
  }
  
  DigitalTube.displayBit(1,0);  //DigitalTube.Display(bit,number); bit= 0 - 3，number= 0 - 9
  pinMode(TrigPin, OUTPUT);     //设置Trig引脚作为输出
  pinMode(EchoPin, INPUT);      //设置Echo引脚作为输入
 
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  distance = checkdistance();//超声波测距
  displayFloatNum(distance); //数码管显示距离
  if (distance <= 10) {   
    digitalWrite(LED_PIN, HIGH); // 点亮LED.
    delay(50);
    digitalWrite(LED_PIN, LOW); // 熄灭LED.
    delay(50);
    tone(BUZZER_PIN, 262); //DO播放125ms
    delay(125);

} else if (distance > 10 && distance <= 20) {
    digitalWrite(LED_PIN, HIGH); // 点亮LED.
    delay(500);
    digitalWrite(LED_PIN, LOW); // 熄灭LED.
    delay(500);
    tone(BUZZER_PIN, 349); //Fa播放500ms
    delay(500);

} else {
    digitalWrite(LED_PIN, HIGH); // 点亮LED.
    tone(BUZZER_PIN, 494); //Si播放1000ms
    delay(1000);
  }
}

void displayFloatNum(float distance){
  if(distance > 9999)
    return;
  int dat = distance*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课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 5. 实验结果

代码上传成功后，拔下USB线断电，按照接线图正确接好模块后再用USB线连接到计算机上电。

当超声波传感器检测到障碍物距离范围在10cm 以内时，紫色LED模块上的LED快速闪烁，并将检测到障碍物的距离显示在四位数码管上。同时8002b功放 喇叭模块发出声响，起到提示的作用。

![img](media/ddbbf6091d62328f66c11cfdc2157f6c.png)

当超声波传感器检测到障碍物距离范围在10cm ~ 20cm 以内时，紫色LED模块上的LED慢速闪烁，并将检测到障碍物的距离显示在四位数码管上。

![img](media/204ee54e802ce8ea8e41c1d65a368b21.png)

当超声波传感器检测到障碍物距离范围在20cm 以外时，紫色LED模块上的LED点亮，并将检测到障碍物的距离显示在四位数码管上。

![img](media/13ba14f498a18a9d9a89d92abd61ca47.png)

---

#### 6. 代码说明

| 代码                                             | 说明                                           |
| ------------------------------------------------ | ---------------------------------------------- |
| digitalWrite(TrigPin, LOW); delayMicroseconds(2) | 事先给一个短的低电平，以确保一个干净的高脉冲。 |

---

### 第38课 综合实验

#### 1. 项目介绍

我们已经学习了所有的模块和传感器的使用方法，也学习了将它们搭配在一起组合实验。在这一实验课程中我们将搭配更多的模块和传感器组合在一起。参考前面实验编程的方法，利用按键模块，实现每按一次按键，功能就变换一次的效果。

实验多种多样，大家可以发挥想象力，搭配模块和传感器做出更多具有意义的实验。

---

#### 2. 实验组件

| ![img](media/64277f167fbad96050ad2405112fdd0a.png)     |  ![img](media/2b35bd88b0382b61946efaa259ccf033.png)  | ![img](media/891f854b35e7d4d08ba203898a3fa362.png)     | ![img](media/f05eaa57ea2095d03699704fa7c33864.png)     |
| ---------------------------- | ------------------------ | ---------------------------- | ---------------------------- |
| UNO R4 WiFi/Minima主板(二选一)| Keyes 避障传感器 x1  | Keyes 电容触摸传感器 x1        | Keyes 滑动电位器模块 x1      |
| ![img](media/034d286250927d6eed077db610e35861.png)     | ![img](media/7a3bffdf74455717f81e49291a94535a.png) | ![img](media/9cf8c08b3226a4b79005700830303d79.png) | ![img](media/d527053929462c5025cbb77b63af8c8e.png)     |
| Keyes 传感器扩展板 x1          | Keyes 摇杆模块 x1        | HC-SR04 超声波传感器 x1      | Keyes 超声波转接模块 x1      |
| ![img](media/1a121809b85e9f58d815cee433df7e14.png)     | ![img](media/65e0cd99d69eb4c74ce5d1be531bf263.png)    | ![img](media/278286c09e3e34f820b4754221ac23aa.png)       | ![img](media/1ff24756e347dcc14db599711e89d922.png)       |
| Keyes 紫色LED模块 x1         | Keyes XHT11温湿度传感器 x1     | 3P线(反向) x5 | 4P线(反向) x1|
| ![img](media/54d1e28b3b1bec156db70b15730b9e0e.png)  | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg)   | ![img](media/f5504973a1c7bd3a74e8a9370a3c2358.png)  |     |
| 5P线(反向)x1 |    USB 线 *1     | 一字螺丝刀*1 |            |

---

3. 模块接线图

![img](media/073b38e56849985e0d19094c96593a0c.png)

---

#### 4. 实验代码

```c++
/*
 * 名称   : Comprehensive experiment
 * 功能   : 多个传感器/模块协同工作
 * 作者   : http://www.keyes-robot.com/ 
*/
#include "xht11.h"

#define LED_PIN  3   //定义led模块控制引脚为D3

//摇杆模块接口
int xyzPins[] = {A3, A2, 7};   //x, y, z

//滑动电位器模块引脚连接到端口A4
int resPin = A4;

//避障传感器引脚连接到IO端口D5
int Avoid = 5;

//超声波传感器的接口
int Trig = A0;
int Echo = A1;

//电容触摸传感器的端口为D8
int Touch = 8;

int PushCounter = 0;//存储键被按下的次数
int yushu = 0;

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

bool ir_flag = 1;
float out_X, out_Y, out_Z;

void counter() {
  delay(10);
  ir_flag = 0;
  if (!digitalRead(Touch)) {
    PushCounter++;
  }
}

void setup() {
  Serial.begin(9600);//波特率设置为9600
  pinMode(xyzPins[0], INPUT); //x axis. 
  pinMode(xyzPins[1], INPUT); //y axis. 
  pinMode(xyzPins[2], INPUT_PULLUP);   //Z轴是一个按钮
  pinMode(Touch, INPUT);//电容触摸传感器
  attachInterrupt(digitalPinToInterrupt(Touch), counter, FALLING);  //外部中断0，下降沿触发
  pinMode(Avoid, INPUT);//避障传感器
  pinMode(resPin, INPUT);//滑动电位器
  pinMode(Trig, OUTPUT);//超声波传感器
  pinMode(Echo, INPUT);
  pinMode(LED_PIN, OUTPUT);
  delay(1000);
}

void loop() {
  yushu = PushCounter % 6;
  if (yushu == 0) {  //余数为0
    yushu_0();  //LED闪烁
} else if (yushu == 1) {  //余数是1
    yushu_1();  //避障传感器检测障碍物
} else if (yushu == 2) {  //余数是2
    yushu_2();  //显示摇杆值
} else if (yushu == 3) {  //余数是3
   yushu_3();  //显示滑动电位器的模拟值
} else if (yushu == 4) {  //余数是4
   yushu_4();  //显示XHT11温湿度传感器的温湿度值
} else if (yushu == 5) {  //余数是5
   yushu_5();  //显示超声波检测到的距离
  } 
}

//紫色LED模块上的LED闪烁
void yushu_0() {
  digitalWrite(LED_PIN, HIGH);  //打开LED
  delay(500);
  digitalWrite(LED_PIN, LOW);  //熄灭LED
  delay(500);
}

//读避障传感器的数字信号
void yushu_1() {
  int val = digitalRead(Avoid);//读取避障传感器的数字电平输出
  Serial.print(val);//串口打印值
  if (val == 0) {//障碍物检测
    Serial.println("   There are obstacles");
  }
  else {//未发现障碍物
    Serial.println("   All going well");
  }
  delay(100);
}

//读摇杆模块的值
void yushu_2() {
  int xVal = analogRead(xyzPins[0]);
  int yVal = analogRead(xyzPins[1]);
  int zVal = digitalRead(xyzPins[2]);
  Serial.println("X,Y,Z: " + String(xVal) + ", " +  String(yVal) + ", " + String(zVal));
  delay(500);
}

//读滑动电位器的模拟值
void yushu_3() {
  int adcVal = analogRead(resPin); 
  Serial.println(adcVal);
  delay(100);
}

//读XHT11温湿度传感器的温湿度
void yushu_4() {
  if (xht.receive(dht)) {  //检查正确时返回true
    Serial.print("RH:");
    Serial.print(dht[0]);  //湿度的积分部分，DHT[1]是分数部分
    Serial.print("%  ");
    Serial.print("Temp:");
    Serial.print(dht[2]);  //温度的积分部分，DHT[3]是分数部分
    Serial.println("C");
  } else {   //读取错误
    Serial.println("sensor error");
  }
  delay(200);
}

//超声波传感器测距
void yushu_5() {
  float distance = checkdistance();
  Serial.print("distance:");
  Serial.print(distance);
  Serial.println("cm");
  delay(100);
}

float checkdistance() {
  digitalWrite(Trig, LOW);
  delayMicroseconds(2);
  digitalWrite(Trig, HIGH);
  delayMicroseconds(10);
  digitalWrite(Trig, LOW);
  float distance = pulseIn(Echo, HIGH) / 58.00;
  delay(10);
  return distance;
}
```

参照第01课或第02课的示例，根据不同的 UNO R4 主板选择不同的主板板型和串口端口。

---

#### 5. 实验结果

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

（1）初始时手没有按触摸传感器上的感应区，按下次数为 0 ，余数为 0 ，紫色LED模块上的LED循环闪烁。

![](media/5fecc5419b4e74a0f78d8325f3b62106.png)

（2）按一下触摸传感器上的感应区（时间稍长以便能检测到按键按下），SK6812 RGB模块上的4个LED灯珠停止闪烁。此时按下次数为 1 ，余数为 1 ，实验实现避障传感器检测障碍物并读取高低电平的功能。

![](media/aca3dc4f7d9657ff2eb6835b579e6369.png)

当传感器没有检测到障碍物时，val为 **<u>1</u>**，串口监视器打印出 “**<u>1  All going well</u>**” ，传感器上的SLED灯 **<u>不亮</u>**；
当传感器检测到障碍物时，val为 **<u>0</u>**，串口监视器打印出 “**<u>0  There are obstacles</u>**” ，传感器上的SLED灯 **<u>亮</u>**。

![](media/ca7c68f20393e19b04622f7db60f36c5.png)

![](media/83a5fffda89144b5d8c13c9d2d7717dc.png)

![](media/4f0fe633aa932cfeca9dee892b4b4260.png)


**注意：如果在打开串口监视器之前<u>按一下触摸传感器上的感应区</u>，次数变为1。再打开串口监视器时程序会复位，次数会变为0，需要再次按一下触摸传感器上的感应区重新设置次数为1。**

（3）再按一下触摸传感器上的感应区，按下次数为 2 ，余数为 2 。实验实现读取当前摇杆模块X轴和Y轴对应的模拟值以及Z轴（B接口）对应的数字值的功能。串口监视器打印出当前摇杆模块X轴、Y轴和Z轴对应的值。

![](media/aca3dc4f7d9657ff2eb6835b579e6369.png)

![](media/c61a6daab0e8c2f6ce11f5be7104cf1b.png)

![](media/5d084ff9d3a739e6dcaad3522b704979.png)

（4）再按一下触摸传感器上的感应区，按下次数为 3 ，余数为 3 。实验实现读取滑动电位器模块的模拟值，当移动滑动电位器模块上的滑杆时，串口监视器打印出当前输出的模拟值。

![](media/aca3dc4f7d9657ff2eb6835b579e6369.png)

![](media/07a80a16e3e73b5618cb2a47c63d9a75.png)

![](media/3259a57a1acd7a8ef9b6ccefb290d426.png)

（5）再按一下触摸传感器上的感应区，按下次数为 4 ，余数为 4 。实验实现的功能是XHT11温湿度传感器检测环境中的温度和湿度，在串口监视器显示检测到当前环境中的温度和湿度值。

![](media/aca3dc4f7d9657ff2eb6835b579e6369.png)

![](media/9d290b212d8008d25b6a2cedf5f29a14.png)

![](media/0a7f438445b4eadae1b600d0781d5d08.png)

（6）再按一下触摸传感器上的感应区，按下按键次数为 5 ，余数为 5 。实验实现的功能是利用超声波传感器模块检测距离并在串口打印出来，串口监视器显示图如下。

![](media/aca3dc4f7d9657ff2eb6835b579e6369.png)

![](media/1db27b897ff9c94577eac1a81504e9aa.png)

![](media/e317c47cd04e0ffb3fe9acce5d94efc8.png)

（7）再按一下触摸传感器上的感应区，按下次数为 6 ，余数为 0 。实现初始时紫色LED模块上的LED循环闪烁的效果。不断地按下触摸传感器上的感应区，余数循环变化，实验功能也循环变化。

---

#### 6. 代码说明:

可以参照前面的项目实验的代码说明，这里就不多做介绍了。

---

<span style="color: rgb(255, 76, 65); font-size: 28px;">**特别注意：以下两个关于WiFi的项目课程只是用于UNO R4 WiFi主板**</span>

---

### 第39课 读取WiFi的IP

#### 1. 项目介绍

UNO R4 WiFi主板上内置ESP32模块，ESP32用于赋予开发板Wi-Fi®功能。Wi-Fi®模块的比特率高达150 Mbps，ESP32模块内置了迹线天线，这意味着你不需要外部天线就可以使用UNO R4 WiFi主板的WiFi连接功能。要使用UNO R4 WiFi的Wi-Fi®功能，请使用内置于UNO R4 Core的WiFiS3库。

在本项目中，我们通过UNO R4 WiFi主板连接WiFi，并读取对应WiFi的IP地址。

---

#### 2. 实验组件

| ![img](media/5bbca91a9f65ad7233abe983f2929b79.png) | ![img](media/fa9ebfb506b2f154108baba98dffa9ee.jpg) |
| ------------------------ | --------------------- |
| UNO R4 WiFi主板 x1  | USB线  x1             |

---

3. 模块接线图

![](media/3b5b5156f9418ab13121f50c5aa76303.png)

---

#### 4. 实验代码

路由器的SSID是无线网的无线名称。SSID是 ServiceSetldentifier 的缩写，意思是：服务集标识。SSID技术可以将一个无线局域网分为几个需要不同身份验证的子网络，每一个子网络都需要独立的身份验证，只有通过身份验证的用户才可以进入相应的子网络，防止未被授权的用户进入本网络。所以在代码运行之前，需要配置 WiFi 名称和密码，将其修改为你自己使用的 WiFi 名称和密码，如下图所示。

![](media/2e0517a25518f171b85b5fbbec0f5402.png)

```c++
/*
 * 名称   : WiFi IP
 * 功能   : 连接到路由器，读取WiFi对应的IP地址
 * 作者   : http://www.keyes-robot.com/ 
*/
#include "WiFiS3.h"
#include "arduino_secrets.h"

//请在 Secret 选项卡/arduino_secrets.h 中输入您的敏感数据
char ssid[] = "ChinaNet_2.4G";   // 您的网络 SSID(名称)
char pass[] = "ChinaNet@233";    // 您的网络密码(用于 WPA，或用作 WEP 密钥)
int keyIndex = 0;                // 您的网络密钥索引号(仅 WEP 需要)
int status = WL_IDLE_STATUS;
WiFiServer server(80);

void setup() {
  Serial.begin(9600);  // 初始化串口9600

  // 检查 WiFi 模块:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // 不继续
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  // 尝试连接WiFi网络:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to Network named: ");
    Serial.println(ssid);                   // 打印网络名称(SSID)

    // 连接到WPA/WPA2网络，如果使用open或WEP网络，请更改此行:
    status = WiFi.begin(ssid, pass);
    // 等待10秒连接:
    delay(10000);
  }
  server.begin();                           // 在端口80上启动web服务器
  printWifiStatus();                        // 你现在已经连接上了，所以打印出状态
}

void loop() {
  WiFiClient client = server.available();   // 实时监听即将到来的客户端

  if (client) {                             // 如果你监听到客户端的话,
    Serial.println("new client");           // 在串口上打印一条消息
    String currentLine = "";                // 创建一个字符串来保存从客户端传入的数据
    while (client.connected()) {            // 在客户端连接时循环
      if (client.available()) {             // 如果有字节需要从客户端读取,
        char c = client.read();             // 读取一个字节，然后
        Serial.write(c);                    // 打印到串行监视器上
        if (c == '\n') {                    // 如果字节是换行符

          // 如果当前行为空，则一行中有两个换行符.
          // 这是客户端HTTP请求的结束，因此发送一个响应:
          if (currentLine.length() == 0) {
            // HTTP首部总是以响应代码开始 (e.g. HTTP/1.1 200 OK)
            // 以及一个内容类型，以便客户端知道将要发生什么，然后是一个空行:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println();
            
            // HTTP响应以另一个空行结束:
            client.println();
            // 跳出while循环:
            break;
          } else {    // 如果有换行符，清除currentLine:
            currentLine = "";
          }
        } else if (c != '\r') {  // 如果你有除回车字符以外的其他字符,
          currentLine += c;      // 并且将其添加到currentLine的末尾
        }
      }     
    }
    // 断开连接:
    client.stop();
    Serial.println("client disconnected");
  }
}

void printWifiStatus() {
  // 打印你所连接的网络的SSID:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // 打印主控板接收到的IP地址:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // 打印接收到的信号强度:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}
```

参照第01课或第02课的示例，根据 UNO R4 WiFi主板选择对应的Arduino UNO R4 WiFi板型和串口端口。

---

#### 5. 实验结果

代码上传成功后，打开串口监视器，设置波特率为 **<u>9600</u>**。

开始连接，串口监视器打印出“**<u>Attempting to connect to Network named: ChinaNet_2.4G</u>**” 提示正在连接到路由器的SSID，当UNO R4 WiFi主板上的ESP32模块成功连接到路由器的 SSID 时，串行监视器将打印出 WiFi 分配给ESP32模块的 **<u>IP地址</u>**。

![img](media/83327f1bf81de5222251172b04298244.png)

**注意：如果打开串口监视器且设置好波特率，串口监视器窗口还是没有显示任何信息，可以尝试按下UNO R4 WiFi主板上的RESET按键。**

![RESET](media/72d202922940df45ca4a638a890c8319.jpg)

---

#### 6. 代码说明

| 代码                                     | 说明                                                         |
| ---------------------------------------- | ------------------------------------------------------------ |
| WiFi.begin(ssid, pass)| 连接初始化连接。ssid为 WiFi 名称，pass为连接 WiFi 所用的密码。 |
| WiFi.status()                            | 调用完成后，wifi并不会立即就连接上。此接口函数的作用就是检查wifi是否已经连接上。当返回值为WL_CONNECTED时表示已经连接上。该函数还有其它返回值，表示wifi连接失败等情况。 |
| WiFi.localIP()   | 显示本机WiFi的ip。  |
|WiFi.RSSI()|显示本机WiFi的强度。|
|server.available()|实时监听即将到来的客户端|

---

### 第40课 WiFi网页控制

#### 1. 项目介绍

在前面的实验中，我们已经了解了UNO R4 WiFi主板上ESP32模块的WiFi功能，也读取了对应WiFi的IP地址。在本课程实验中，我们将利用ESP32模块的WiFi功能通过网页来控制多个传感器/模块工作，实现WiFi网页控制传感器模块的效果。

---

#### 2. 实验组件

| ![img](media/5bbca91a9f65ad7233abe983f2929b79.png)  | ![](media/034d286250927d6eed077db610e35861.png) |![](media/0fb16f82134e03f951cb8b8c383577ef.png) | ![](media/7e77d1498bafcbd3e9f2d40103af3734.png)        |
| ------------------------- | --------------------- | ---------------------------- | ------------------------------ |
| UNO R4 WiFi主板 x1| Keyes 传感器扩展板 x1  |Keyes 130电机模块 x1  | Keyes 有源蜂鸣器模块 x1    |
| ![](media/84216693acf9a54e2e8f4090b9154228.png)          |![](media/1a121809b85e9f58d815cee433df7e14.png)          |![](media/460ec103035638e948d2b1a272e62178.png) | ![](media/25f82bd1b415a8203a32392488131376.png)     | 
|  Keyes 8002b功放喇叭模块 x1     |Keyes 紫色LED模块 x1     |Keyes 舵机驱动模块 x1   | 舵机 x1   |
|  ![](media/278286c09e3e34f820b4754221ac23aa.png)          | ![](media/1ff24756e347dcc14db599711e89d922.png)            |![](media/fa9ebfb506b2f154108baba98dffa9ee.jpg)        | ![](media/bae3eacd95d794ed928a1d837f2073c6.png)| 
| 3P线(反向) x3 |  4P线(反向) x2   |USB线  x1 | 电源适配器  x1    |
|![](media/fb36a3a8a8d1199e484dc78474ca83a4.png)              | ![](media/f5504973a1c7bd3a74e8a9370a3c2358.png)   |   |     |
|智能手机/平板电脑 **(自备)** x1 | 一字螺丝刀*1   |  |     |

---

3. 模块接线图

![](media/fde41d301c0add5cf2da36fc76fa64cd.png)

---

#### 4. 实验代码

在代码运行之前，需要配置 WiFi 名称和密码，将其修改为你自己使用的WiFi 名称和密码，如下图所示。

![](media/4b1024f3637dad3e7069708023e13066.png)

```c++
/*
 * 名称   : WiFi Web Page Control  
 * 功能   : WIFI 网页控制多个传感器模块
 * 作者   : http://www.keyes-robot.com/ 
*/
#include "WiFiS3.h"
#include "arduino_secrets.h" 
#include <Servo.h>

//请在 Secret 选项卡/arduino_secrets.h 中输入您的敏感数据
char ssid[] = "ChinaNet_2.4G";   // 您的网络 SSID(名称)
char pass[] = "ChinaNet@233";    // 您的网络密码(用于 WPA，或用作 WEP 密钥)
int keyIndex = 0;                // 您的网络密钥索引号(仅 WEP 需要)
int val = 0;

int ledpin = 12;    // 定义LED引脚D12
int Active_Buzzer = 3;   //定义有源蜂鸣器引脚为D3
int Passive_Buzzer = 8;   //定义8002b功放喇叭模块引脚为D5
int INA = A4;  //定义电机模块的IN+(INA)引脚为A4
int INB = A5;  //定义电机模块的IN-(INB)引脚为A5
int servoPin = A1;  // 定义舵机引脚A1
Servo myservo;  //创建舵机对象来控制舵机

int status = WL_IDLE_STATUS;
WiFiServer server(80);

void setup() {
  Serial.begin(9600);      // 初始化串口9600
  pinMode(ledpin, OUTPUT);   //设置LED引脚为输出模式
  pinMode(INA, OUTPUT);      // 设置电机模块的IN+(INA)引脚为输出模式
  pinMode(INB, OUTPUT);      // 设置电机模块的IN-(INB)引脚为输出模式
  pinMode(Active_Buzzer, OUTPUT);      // 设置有源蜂鸣器引脚为输出模式
  pinMode(Passive_Buzzer, OUTPUT);      // 设置8002b功放喇叭模块引脚为输出模式
  myservo.attach(servoPin);  //选择舵机引脚A1
  myservo.write(0); //旋转到0度
  delay(1000); //延迟1s

  // 检查 WiFi 模块:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // 不继续
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  // 尝试连接WiFi网络:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to Network named: ");
    Serial.println(ssid);                   // 打印网络名称(SSID)

    // 连接到WPA/WPA2网络，如果使用open或WEP网络，请更改此行:
    status = WiFi.begin(ssid, pass);
    // 等待10秒连接:
    delay(10000);
  }
  server.begin();                           // 在端口80上启动web服务器
  printWifiStatus();                        // 你现在已经连接上了，所以打印出状态
}


void loop() {
  WiFiClient client = server.available();   // 实时监听即将到来的客户端

  if (client) {                             // 如果你监听到客户端的话,
    Serial.println("new client");           // 在串口上打印一条消息
    String currentLine = "";                // 创建一个字符串来保存从客户端传入的数据
    while (client.connected()) {            // 在客户端连接时循环
      if (client.available()) {             // 如果有字节需要从客户端读取,
        char c = client.read();             // 读取一个字节，然后
        Serial.write(c);                    // 打印到串行监视器上
        if (c == '\n') {                    // 如果字节是换行符

          // 如果当前行为空，则一行中有两个换行符.
          // 这是客户端HTTP请求的结束，因此发送一个响应:
          if (currentLine.length() == 0) {
            // HTTP首部总是以响应代码开始 (e.g. HTTP/1.1 200 OK)
            // 以及一个内容类型，以便客户端知道将要发生什么，然后是一个空行:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println();

            // HTTP响应的内容跟在首部之后:
            client.print("<p style=\"font-size:7vw;\">Click <a href=\"/A\">here</a> turn on LED<br></p>");
            client.print("<p style=\"font-size:7vw;\">Click <a href=\"/B\">here</a> turn off LED<br></p>");
            client.print("<p style=\"font-size:7vw;\">Click <a href=\"/C\">here</a> turn on fan<br></p>");
            client.print("<p style=\"font-size:7vw;\">Click <a href=\"/D\">here</a> turn off fan<br></p>");
            client.print("<p style=\"font-size:7vw;\">Click <a href=\"/E\">here</a> play music<br></p>");
            client.print("<p style=\"font-size:7vw;\">Click <a href=\"/F\">here</a> turn on buzzer<br></p>");
            client.print("<p style=\"font-size:7vw;\">Click <a href=\"/G\">here</a> turn off buzzer<br></p>");
            client.print("<p style=\"font-size:7vw;\">Click <a href=\"/H\">here</a> <br>servo turn to 180</p>");
            client.print("<p style=\"font-size:7vw;\">Click <a href=\"/I\">here</a> <br>servo turn to 0</p>");
           
            // HTTP响应以另一个空行结束:
            client.println();
            // 跳出while循环:
            break;
          } else {    // 如果有换行符，清除currentLine:
            currentLine = "";
          }
        } else if (c != '\r') {  // 如果你有除回车字符以外的其他字符,
          currentLine += c;      // 并且将其添加到currentLine的末尾
        }

        // 检查客户端请求是否存在 "GET /A"or"GET /B"or"GET /C"or"GET /D"or"GET /E"or"GET /F"or"GET /G"or"GET /H"or"GET /I":
        if (currentLine.endsWith("GET /A")) {    // GET /A 打开LED
          digitalWrite(ledpin, HIGH);       
        }
        if (currentLine.endsWith("GET /B")) {    // GET /B 关闭LED
          digitalWrite(ledpin, LOW);
        }
        if (currentLine.endsWith("GET /C")) {  // GET /C 电机模块转动
          digitalWrite(INA, LOW);                
          analogWrite(INB, 100);
        }
        if (currentLine.endsWith("GET /D")) {  // GET /D 电机模块不转
          digitalWrite(INA, LOW);               
          analogWrite(INB, 0);
        }
        if (currentLine.endsWith("GET /E")) {  // GET /E 8002b功放喇叭模块演奏音乐
          music();                               
        }
        if (currentLine.endsWith("GET /F")) {  // GET /F 有源蜂鸣器鸣叫
          digitalWrite(Active_Buzzer, HIGH);            
        }
        if (currentLine.endsWith("GET /G")) {  // GET /G 有源蜂鸣器不响
          digitalWrite(Active_Buzzer, LOW);             
        }
        if (currentLine.endsWith("GET /H")) {  // GET /H 舵机转到180°
          myservo.write(180); //旋转到180度
          delay(1000); //延迟1s
        }
        if (currentLine.endsWith("GET /I")) {  // GET /I 舵机转到0°
          myservo.write(0); //旋转到0度
          delay(1000); //延迟1s
        }
      } 
    }
    // 断开连接:
    client.stop();
    Serial.println("client disconnected");
  }
}

void printWifiStatus() {
  // 打印你所连接的网络的SSID:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // 打印主控板接收到的IP地址:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // 打印接收到的信号强度:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
  
  // 在浏览器中打开http://[ip]:
  Serial.print("To see this page in action, open a browser to http://");
  Serial.println(ip);
}

void music() {
  #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; // 改变这个可使歌曲变慢或变快
  // 乐曲的音符后面跟着持续时间.
  // 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;

  // 重复旋律的音符
  // 记住，数组是音符数的两倍(音符+持续时间)
  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 *= 1.5; // 给打点音符增加一半的持续时间
    }
    // 只在90%的时间里演奏这个音符，留下10%作为暂停
    tone(Passive_Buzzer, melody[thisNote], noteDuration*0.9);
  // 等待特定的时间后再演奏下一个音符.
    delay(noteDuration);
     noTone(Passive_Buzzer);  // 下一个音节前停止波形产生前的下一个说明.
  }
}
```

参照第01课或第02课的示例，根据 UNO R4 WiFi主板选择对应的Arduino UNO R4 WiFi板型和串口端口。

---

#### 5. 实验结果

代码上传成功后，按照接线图正确接好传感器模块，将电源电源适配器连接到家庭电路给UNO R4主板供电（<span style="color: rgb(255, 76, 65);">注意：</span> 防止风扇有时侯工作不正常，另外还需要用USB线连接到计算机给UNO R4 WiFi主板上电，这样保证风扇能正常工作；否则，风扇可能会工作不正常）。打开串口监视器，设置波特率为 **<u>9600</u>**，串口监视器窗口显示IP信息如下图所示。

![](media/83327f1bf81de5222251172b04298244.png)

**注意：如果打开串口监视器且设置好波特率，串口监视器窗口还是没有显示任何信息，可以尝试按下UNO R4 WiFi主板或传感器扩展板上的RESET按键。**

![](media/72d202922940df45ca4a638a890c8319.jpg)

接着打开手机浏览器，在下图所示的浏览器搜索框里输入上面步骤检测到的WIFI的IP地址（192.168.0.83），接着点击“**访问**”。（<span style="color: rgb(255, 76, 65);">**特别注意：代码中的WiFi名称和密码与手机(或电脑)连入的网络(或WiFi)的名称和密码要是同一个。并且电脑和手机要在同一个的网络(或WiFi)下。**</span>）

![Img](./media/3dc3296dc017bd80a8327a1ba9c9ac9d.jpg)

![Img](./media/29950b450db3136f6499f8054abc4dbc.jpg)

几秒种后进入WiFi网页，说明UNO R4 WiFi主板上的ESP32模块成功连接上了WiFi。

![](media/256d161179a63dd3c69f49d2b9cd5c8e.jpg)

![](media/b854521018e228ee01390ef8303c4766.png)

| 按钮                  | 点击     | 效果                                                         |
| --------------------- | -------- | ------------------------------------------------------------ |
| ![](media/3b365f8351cb4075f10999a4ed811fe4.png) | 点击here | 打开紫色LED模块，LED点亮。 |
| ![](media/4b876780173635d35e764c0cce0fd990.png) | 点击here | 关闭紫色LED模块，LED熄灭。 |
| ![](media/76b9896324dc9dba0f42fdcb6c7e00e9.png) | 点击here | 打开电机模块，风扇转动。 |
| ![](media/c7d3baf2f83103223a3b8621f2880834.png) | 点击here | 关闭电机模块关闭，风扇不转。     |
| ![](media/c00ef2483efa9fef0687e9bc37497934.png) | 点击here |打开8002b功放喇叭模块，播放一首动听的音乐。 |
| ![](media/1fbc849cdf5bee27d95089a21e08f94e.png) | 点击here | 打开有源蜂鸣器，发出鸣叫声。 |
| ![](media/096dab439760064ee4fabb2be2a0d58b.png) | 点击here | 关闭有源蜂鸣器，不发声。 |
| ![](media/f80b4493f7c0e9d47b2f4859d00ab7ec.png) | 点击here | 舵机旋转到180°处位置。 |
| ![](media/488e9285365b391f6cf4bc6e5bb9566a.png) | 点击here | 舵机旋转到0°处位置。|

---

#### 6. 代码说明:

可以参照前面的项目实验的代码说明，这里就不多做介绍了。

---




