# 桌面迷你蓝牙智能车

## 1、桌面迷你智能车简介

![第1页-1](./media/dfgdfgdlgjnksdjfgnd.PNG)

我们要让机器人听我们的话，就得给机器人下达指令，下指令时说人类的语言没有用，只能编写机器人能听懂的程序语言。编程不仅对那些未来要当程序员的孩子有用，而且对其他孩子也有很大的作用。编程就是把大问题分割成小问题，然后解决问题的过程，对孩子的逻辑分析能力，创造能力，动手能力，解决问题的能力有极大的提升。今天给大家推荐一款桌面迷你编程智能车，这款智能车可以让孩子们轻松学习编程，并且获得有关电子，机械，控制逻辑和计算机科学的实践知识。他是基于ARDUINO的开源机器人，他的安装和接线十分简单，组件都通过螺钉和铜柱连接，只需要几个简单的步骤就可以组装完成。他提供了18个编程的课程项目，由简单到复杂，一步一步，学习怎么去编写机器人能”听”懂的语言。

## 2、桌面迷你智能车特点

1.功能多多：避障功能，跟随功能，红外遥控，蓝牙控制，循迹功能，演奏音乐等。
2.组装简单：无需焊接电路，只需几个简单的步骤即可组装该机器人。
3.结构坚固：构成车体的部分是优质亚克力材质，电机用是优质的N20金属电机。
4.扩展性强：配置了电机驱动扩展板，可以扩展其他的传感器和模块。
5.多种控制：红外遥控器控制，手机遥控控制(苹果和安卓手机都可)。
6.学习基础编程：使用Arduino IDE的C语言编程，可以接触底层代码。

## 3、桌面迷你智能车参数

- 工作电压：5v

- 输入电压：7-12V
- 最大输出电流：1A
- 最大耗散功率：25W(T=75℃)
- 电机转速：5V 63 rpm/min
- 电机驱动形式：双路H桥驱动
- 超声波感应角度：<15度
- 超声波探测距离：2cm-400cm
- 红外遥控距离：10米(实测)
- 蓝牙遥控距离：50米(实测)
- 蓝牙APP控制：支持Android和IOS系统
- 可接入外部DC 7~12V的电压。

## 4、桌面迷你智能车清单

当收到这个智能车套件的时候，首先看到是一个包装精美的外盒，每个配件被安全且有序的装在外盒里面的小盒子里，先来清点一下：

| No   | Product Name                                  | Quantity | Picture                                                      |
| ---- | --------------------------------------------- | -------- | ------------------------------------------------------------ |
| 1    | keyes UNO R3开发板                            | 1        | ![image-20251224161935817](./media/image-20251224161935817.png) |
| 2    | Keyes L298P电机驱动扩展板                     | 1        | ![image-20251224161924872](./media/image-20251224161924872.png) |
| 3    | 红外接收传感器                                | 1        | ![image-20251224161903608](./media/image-20251224161903608.png) |
| 4    | 三路循迹传感器                                | 1        | ![image-20251224161851992](./media/image-20251224161851992.png) |
| 5    | 电机连接板A                                   | 1        | ![image-20251224161815412](./media/image-20251224161815412.png) |
| 6    | 电机连接板B                                   | 1        | ![image-20251224161827913](./media/image-20251224161827913.png) |
| 7    | 避障传感器                                    | 2        | ![image-20251224162105273](./media/image-20251224162105273.png) |
| 8    | HC-SR04超声波传感器                           | 1        | ![image-20251224162040683](./media/image-20251224162040683.png) |
| 9    | 4.0蓝牙模块                                   | 1        | ![image-20251224162125147](./media/image-20251224162125147.png) |
| 10   | 小功放声音模块                                | 1        | ![image-20251224162144043](./media/image-20251224162144043.png) |
| 11   | XH2.54转PH2.05P连接线                         | 1        | ![image-20251224162200699](./media/image-20251224162200699.png) |
| 12   | HX-2.544P双头连接线                           | 1        | ![image-20251224162217167](./media/image-20251224162217167.png) |
| 13   | HX-2.543P连接线                               | 3        | ![image-20251224162234006](./media/image-20251224162234006.png) |
| 14   | XH2.54-3Pin连接线                             | 1        | ![img](./media/clip_image19.png)![image-20251224162245579](./media/image-20251224162245579.png) |
| 15   | 双头JST-PH2.0MM-2P连接线                      | 2        | ![image-20251224162306203](./media/image-20251224162306203.png) |
| 16   | 18650电池盒                                   | 1        | ![image-20251224100931858](./media/image-20251224100931858-1766563958349-5.png) |
| 17   | 4节5号带线电池盒                              | 1        | ![image-20251224100942991](./media/image-20251224100942991-1766563958349-6.png) |
| 18   | M1.6*10MM 圆头 十字                           | 6        | ![image-20251224101046372](./media/image-20251224101046372-1766563958349-7.png) |
| 19   | M1.6 304 不锈钢                               | 6        | ![image-20251224101101870](./media/image-20251224101101870-1766563958349-8.png) |
| 20   | M2*10MM 圆头 十字                             | 6        | ![image-20251224101113926](./media/image-20251224101113926-1766563958349-11.png) |
| 21   | M2 镀镍                                       | 6        | ![image-20251224101231636](./media/image-20251224101231636-1766563958349-9.png) |
| 22   | M3*12MM 圆头 十字                             | 10       | ![image-20251224101253752](./media/image-20251224101253752-1766563958349-10.png) |
| 23   | M3*6MM 圆头                                   | 24       | ![image-20251224101306916](./media/image-20251224101306916-1766563958349-12.png) |
| 24   | M3*10MM平头                                   | 4        | ![image-20251224101404399](./media/image-20251224101404399-1766563958349-13.png) |
| 25   | M3镀镍                                        | 25       | ![image-20251224101432433](./media/image-20251224101432433-1766563958349-14.png) |
| 26   | M2304平垫                                     | 6        | ![image-20251224101442781](./media/image-20251224101442781-1766563958349-15.png) |
| 27   | 双通M3*40MM                                   | 4        | ![image-20251224101450991](./media/image-20251224101450991-1766563958349-16.png) |
| 28   | 单通M3*5+6MM                                  | 2        | ![image-20251224101502347](./media/image-20251224101502347-1766563958349-17.png) |
| 29   | 双通M3*10MM                                   | 4        | ![image-20251224101510556](./media/image-20251224101510556-1766563958349-18.png) |
| 30   | 单通M3*8+6MM                                  | 2        | ![image-20251224101519727](./media/image-20251224101519727-1766563958349-19.png) |
| 31   | 橡胶轮子                                      | 2        | ![image-20251224101530374](./media/image-20251224101530374-1766563958349-20.png) |
| 32   | N20电机 支架                                  | 2        | ![image-20251224101538366](./media/image-20251224101538366-1766563958349-21.png) |
| 33   | 智能车亚克力板蓝色透明2片黄色透  明1片厚度3MM | 1        | ![image-20251224101856126](./media/image-20251224101856126-1766563958349-22.png) |
| 34   | 螺丝刀                                        | 1        | ![image-20251224101915360](./media/image-20251224101915360-1766563958349-23.png) |
| 35   | USB线                                         | 1        | ![image-20251224101933683](./media/image-20251224101933683-1766563958350-24.png) |
| 36   | W420钢珠万向轮                                | 1        | ![image-20251224101946545](./media/image-20251224101946545-1766563958350-25.png) |
| 37   | 遥控器                                        | 1        | ![image-20251224102001370](./media/image-20251224102001370-1766563958350-26.png) |
| 38   | 草帽LED                                       | 1        | ![image-20251224102014102](./media/image-20251224102014102-1766563958350-27.png) |
| 39   | 3Pin双母头杜邦线                              | 2        | ![image-20251224102027322](./media/image-20251224102027322-1766563958350-28.png) |
| 40   | 黑色 扎带                                     | 6        | ![image-20251224102443461](./media/image-20251224102443461-1766563958350-29.png) |
| 41   | 螺丝刀                                        | 1        | ![image-20251224102454501](./media/image-20251224102454501-1766563958350-30.png) |

## 5、桌面迷你智能车安装

|                               |                            安装 1                            |
| ----------------------------- | :----------------------------------------------------------: |
| 安装所需零件                  | ![image-20251222092745244](./media/image-20251222092745244-1766563958350-32.png) |
| 分布安装1（亚克力板字面朝下） | ![image-20251222092921516](./media/image-20251222092921516-1766563958350-31.png) |
| 完成                          | ![image-20251222093058670](./media/image-20251222093058670-1766563958350-33.png) |
| 分布安装2                     | ![image-20251222093400230](./media/image-20251222093400230-1766563958350-34.png) |
| 完成                          | ![image-20251222093421686](./media/image-20251222093421686-1766563958350-35.png) |
|                               |                            安装 2                            |
| 安装所需零件                  | ![image-20251222093506475](./media/image-20251222093506475-1766563958350-36.png) |
| 分布安装1                     | ![image-20251222093638502](./media/image-20251222093638502-1766563958350-37.png) |
| 完成                          | ![image-20251222093725131](./media/image-20251222093725131-1766563958350-38.png) |
| 分布安装2                     | ![image-20251222094556932](./media/image-20251222094556932-1766563958350-39.png) |
| 完成                          | ![image-20251222094616702](./media/image-20251222094616702-1766563958350-40.png) |
| 分步安装3                     | ![image-20251222094640033](./media/image-20251222094640033-1766563958350-41.png) |
| 完成                          | ![image-20251222094653192](./media/image-20251222094653192-1766563958350-42.png) |
|                               |                            安装 3                            |
| 安装所需零件                  | ![image-20251222094741565](./media/image-20251222094741565-1766563958350-43.png) |
| 分步安装1                     | ![image-20251222094801763](./media/image-20251222094801763-1766563958350-44.png) |
| 完成                          | ![image-20251222094820844](./media/image-20251222094820844-1766563958350-45.png) |
| 分步安装2                     | ![image-20251222094851010](./media/image-20251222094851010-1766563958350-46.png) |
| 完成                          | ![image-20251222094909441](./media/image-20251222094909441-1766563958350-47.png) |
|                               |                            安装 4                            |
| 安装所需零件                  | ![image-20251222094941959](./media/image-20251222094941959-1766563958350-48.png) |
| 安装                          | ![image-20251222094958098](./media/image-20251222094958098-1766563958350-49.png) |
| 完成                          | ![image-20251222095013498](./media/image-20251222095013498-1766563958350-50.png) |
|                               |                            安装 5                            |
| 安装所需零件                  | ![image-20251222095043152](./media/image-20251222095043152-1766563958350-51.png) |
| 分布安装1（亚克力板字面朝下） | ![image-20251222095121494](./media/image-20251222095121494-1766563958350-52.png) |
| 完成                          | ![image-20251222095137706](./media/image-20251222095137706-1766563958350-53.png) |
| 分步安装2                     | ![image-20251222095159754](./media/image-20251222095159754-1766563958350-54.png) |
| 完成                          | ![image-20251222095248258](./media/image-20251222095248258-1766563958350-55.png) |
| 分步安装3                     | ![image-20251222095308013](./media/image-20251222095308013-1766563958350-56.png) |
| 完成                          | ![image-20251222095318867](./media/image-20251222095318867-1766563958350-57.png) |
|                               |                            安装 6                            |
| 安装所需零件                  | ![image-20251222095346367](./media/image-20251222095346367-1766563958350-60.png) |
| 安装                          | ![image-20251222095400447](./media/image-20251222095400447-1766563958350-58.png) |
| 完成                          | ![image-20251222095415642](./media/image-20251222095415642-1766563958350-59.png) |
|                               |                            安装 7                            |
| 安装所需零件                  | ![image-20251222095444921](./media/image-20251222095444921-1766563958350-61.png) |
| 安装                          | ![image-20251222095459046](./media/image-20251222095459046-1766563958350-62.png) |
| 完成                          | ![image-20251222095534854](./media/image-20251222095534854-1766563958350-63.png) |
|                               |                            安装 8                            |
| 安装所需零件                  | ![image-20251222095559727](./media/image-20251222095559727-1766563958350-64.png) |
| 需先给功放模块插线            | ![image-20251222095809931](./media/image-20251222095809931-1766563958350-65.png) |
| 安装                          | ![image-20251222095822244](./media/image-20251222095822244-1766563958350-66.png) |
| 完成                          | ![image-20251222095836175](./media/image-20251222095836175-1766563958350-67.png) |
| 安装所需零件                  | ![image-20251222095858888](./media/image-20251222095858888-1766563958350-68.png) |
| 安装                          | ![image-20251222095910314](./media/image-20251222095910314-1766563958350-69.png) |
| 完成                          | ![image-20251222095921919](./media/image-20251222095921919-1766563958350-70.png) |
| 电机A接线图                   | ![image-20251222095947926](./media/image-20251222095947926-1766563958350-71.png) |
| 电机B接线图                   | ![image-20251222100004877](./media/image-20251222100004877-1766563958350-72.png) |
| 功放模块接线图                | ![image-20251222100144439](./media/image-20251222100144439-1766563958350-73.png) |
| 红外接收传感器接线图          | ![image-20251222100216322](./media/image-20251222100216322-1766563958350-74.png) |
| 循迹传感器接线图              | ![image-20251222100254546](./media/image-20251222100254546-1766563958350-75.png) |
| 避障传感器 1接线图            | ![image-20251222100321177](./media/image-20251222100321177-1766563958350-76.png) |
| 避障传感器 2接线图            | ![image-20251222100341856](./media/image-20251222100341856-1766563958350-77.png) |
| 超声波接线图                  | ![image-20251222100403378](./media/image-20251222100403378-1766563958350-78.png) |
| 电源接线图                    | ![image-20251222100430051](./media/image-20251222100430051-1766563958350-79.png) |
| 完成渲染效果                  | ![image-20251222100458106](./media/image-20251222100458106-1766563958350-80.png) |

## 6、开始学习 Arduino

### (1) 安装 Arduino IDE

我们先到 arduino 官方的网站https://www.arduino.cc/en/software下载最新版本的 arduino 开发软件。

Arduino 软件有很多版本，有 wodows, mac linux 系统的（如下图），而且还有过去老的版本，你只需要下载一个适合系统的版本。

这里我们以WINDOWS系统的为例给大家介绍一下下载和安装的步骤。（注意：Arduino版本更新迭代较快，软件安装界面和版本可能教程不同，但按照步骤都基本一致，下载后直接安装就行，与其他电脑软件安装都一样的）

![image-20251222100805197](./media/image-20251222100805197-1766544390068-1-1766563958350-81.png)

### (2) Arduino UNO R3 开发板

在开始所有的项目之前，我们首先要了解下面这片KEYES UNO R3开发板，因为这个智能车的核心就是这个开发板。

![](./media/3c9703d92135d7225c9978cd708c906236210a6d7704d2a5ddc9bc0e028bcb11-1766563958350-82.jpg)

Arduino UNO R3 开发板是我们最新推出的一款易用型开源控制器，硬件上与 ArduinoUNO相比并没有大的变动。硬件上，我们用ATmega16U2代替了8U2，这个更新为是USB接口芯片服务的，理论上它让UNO能模拟USB HID，比如MIDI/Joystick/Keyboard。

![](./media/811c9267d0490b28d5c8184013b8b00c92565d1c309b236be265c15a8452e85d-1766563958350-83.jpg)

它具有 14 个数字输入/输出引脚（其中 6 个可用作 PWM 输出），6 个模拟输入，一个 16 MHz 石英晶体，一个 USB 连接，一个电源插孔，2 个 ICSP 接头和一个复位按钮。

![](./media/0e8daabd7bfdbc767509aa83c808ea485397e86507427f25726bde5ba75b0b42-1766563958350-84.jpg)

它包含支持微控制器所需的一切；只需使用 USB 电缆将其连接到计算机，或使用 AC-DC适配器或电池为其供电即可开始使用。

| 参数名称                    | 详细规格                                                 |
| --------------------------- | -------------------------------------------------------- |
| Microcontroller             | ATmega328P-PU                                            |
| Operating Voltage           | 5V                                                       |
| Input Voltage (recommended) | DC7-12V                                                  |
| 数字引脚                    | 14 (D0-D13)<br>(其中包含6个PWM输出口)                    |
| PWM引脚                     | 6个(D3, D5, D6, D9, D10, D11)                            |
| 模拟输入引脚                | 6个(A0-A5)                                               |
| 每个I/O引脚的直流电流       | 20 mA                                                    |
| 3.3V引脚的直流电流          | 50 mA                                                    |
| Flash Memory                | 32 KB (ATmega328P-PU) of which 0.5 KB used by bootloader |
| SRAM                        | 2 KB (ATmega328P-PU)                                     |
| EEPROM                      | 1 KB (ATmega328P-PU)                                     |
| 时钟频率                    | 16 MHz                                                   |
| LED按键                     | D13                                                      |

### (3) Arduino IDE 设置和工具栏介绍

装好了开发板的驱动，我们下面要了解 Arduino 开发软件的使用了，首先我们点击电脑桌面上的 Arduino 图标

![](./media/714593f4048b9fa279209198b107726995a7d8c67055d7c70a02f917eb7572ec-1766563958350-85.jpg)，打开 Arduino IDE。

![](./media/93e9701b26c4318dfcc45969460ab78d2207ef825f1b7cb42743bb6b05fc958e-1766563958350-86.jpg)

为了避免在将程序上载到板上时出现任何错误，必须选择正确的Arduino板名称，该名称与连接到计算机的电路板相匹配。转到Tools→Board，然后选择你的板。

![](./media/f52ba63ec69f8b0d8bbfbc9695e53ab319aac9cd85e4dfe6ae9347fdc4e6865a-1766563958350-87.jpg)

然后再选择正确的 COM 口（将开发板连接到电脑可看到对应 COM 口）。

![](./media/3b43c6a2cb0a00256276b42ea084888b689db5759b1b6c81722cad9d8e3af96c-1766563958350-88.jpg)

这里注意：不同主板COM后面的数字是不同的，我们这里是COM3，但实际操作请以接入板子后出现的端口为准。如果不能确认开发板端口是哪个，拔掉开发板看下没接开发板上图出现了哪些端口，接上开发板新增加了哪些端口，那增加的端口自然就是开发板的端口了。

**端口问题排查（即以上操作没有出现开发板端口，如果出现了直接跳过这部分内容即可）：**

1.将开发板接到电脑其他USB接口测试（可能连接不稳定导致的）；

2.如果身边有其他数据线最好手机的充电线，可以更换不同数据线重新测试）；

3.驱动没有安装（一般开发板连接到电脑上后就自动安装驱动但可能各种原因导致驱动没有安装）

​    1.将下载驱动程序（[点此下载](https://xiazai.keyesrobot.cn/software/cp2102/drivers_uno.7z)）。

​    2.打开设备管理器（同时按下WIN+X,在弹出的页面选择设备管理器即可）

​    3.将USB先的一端插入Arduino，另一端插入计算机上的USB插座。第一次将UNO板连接到计算机时，右键单击“计算机”图标->“属性”->     单击“设备管理器”，在“其他设备”下，您应看到“未知设备”旁边带有一个黄色警告三角形。这是您的Arduino。

![](./media/12e9988fd54097d75e6a2b9cebb930b16008bad83b6ae26c039fac22bfdcb274-1766563958350-89.jpg)

然后右键单击设备，然后选择顶部菜单选项（更新驱动程序软件...），如下图所示。

![](./media/8dccff3de1fdc9dc14caf79024bd91543bd8083ac7be6ecaed7d2ccfb433bd5e-1766563958350-90.jpg)

然后将提示您“自动搜索更新的驱动程序软件”或“浏览我的计算机以获取驱动程序软件”。如下图所示。在此页面中，选择“浏览我的计算机以获取驱动程序软件”。

![](./media/c3c3ee14a609110bb8659013ea63c3c3b9bdc005f92c658e2210a229426ecdfd-1766563958350-91.jpg)

之后，选择浏览器选项并导航到Arduino安装的“drivers”文件夹（这里的驱动就是前面我们下载的压缩包，记得将下载的压缩包解压）。

![](./media/db3779708b3467b750d7b70f42c4d44f9e9a1538edf5119900df5b3c9607205b-1766563958350-92.jpg)

单击“下一步”，您可能会收到安全警告，如果这样，则允许安装该软件。如下图所示。

![](./media/20ac75a4b404dc1da9a77faf95b97ebe2ed4e2279775fc09ee09144c7b9cf8f7-1766563958350-93.jpg)

安装软件后，您将收到确认消息。安装完成后，单击“关闭”。

![](./media/e199b5ccc0b95ed4e01ff4e5c3eeb8bf5b3b3ad8ea9affc5f76f066e1b3b94ec-1766563958350-94.jpg)

现在驱动程序已经安装好。您将看到如下图所示的设备。

![](./media/8e92bbe0ad49f3a761b5514e9729e311398b0339444f9ac722b7545e75924813-1766563958350-95.jpg)

我们的程序上传到板之前，我们必须演示 Arduino IDE 工具栏中出现的每个符号的功能。

![](./media/1bef857e93ff251ed77f5eb71fd2296131df391eb3d4928649f830bc9ea59064-1766563958350-96.jpg)

- A 用于检查是否存在任何编译错误。 

- B 用于将程序上传到 Arduino 板。 
- C 用于创建新草图的快捷方式。 
- D用于直接打开示例草图之一。 
- E 用于保存草图。 
- F 用于从板接收串行数据并将串行数据发送到板的串行监视器。

### (4) 启动你的第一个程序

上面我们学习了怎么下载软件和安装开发板的驱动，那下面我们就开始正式开始第一个程序，打开文件选择例子，选择第一个文件 BASIC 里面的 BLINK 程序

![](./media/d4fb3b5e61e3e773ca8d42a2b5d75b174401fc9077f36332718ecb492fde57ef-1766563958350-97.jpg)

![](./media/b961139f48ac49594a47e26dbffdb18bb543204b936968ea4133de610acaaaa5-1766563958350-98.jpg)

按照前面方法设置板和 COM 口，IDE 右下角显示对应板和 COM 口。

![](./media/3fa7ab0c2f8218903875a3d5d78edec4ba39416633f53fb49e73cdf98969b9ed-1766563958350-100.jpg)

点击 图标开始编译程序，检查错误，检查无误。

![](./media/cc93950b3e22fbc1a7e18d9edd0e15c7185652a0b822682d75f3c5599b6fc9d1-1766563958350-99.jpg)

点击点击 图标开始上传程序，上传成功。

![](./media/8b289b4f05cb13919d33992de2562ba55a2697384d453252537047c36cd9badd-1766563958350-101.jpg)

程序上传成功，板载的LED灯亮一秒钟，灭一秒钟，恭喜你的第一个程序完成了！

## 7、桌面迷你智能车课程

### 代码库文件下载

[点击下载代码库文件](./代码库文件.7z)

### 第1课 LED灯

***(1)项目介绍：***

![image-20251224105104208](./media/image-20251224105104208-1766563958350-102.png)

前面我们安装了keyes UNOR3开发板的驱动。接下来的项目我们就要由简单到复杂，一步一步探索Arduino的世界了。首先我们要来完成经典的“Arduino点亮LED”,也就是Blink项目。Blink对于学习Arduino的爱好者而言，是最基础的项目是新手必须经历的一个练习。

LED,发光二极管的简称。由含镓(Ga)、砷(As)、磷(P)、氮(N)等的化合物制成。当电子与空穴复合时能辐射出可见光，因而可以用来制成发光二极管。在电路及仪器中作为指示灯，或者组成文字或数字显示。为了实验的方便，我们将LED发光二极管做成了一个模块，在第一个项目中，我们用一个最基本的测试代码来控制LED,亮一秒钟，灭一秒钟，来实现闪烁的效果。你可以改变代码中LED灯亮灭的时间，实现不同的闪烁效果。LED模块信号端S为高电平时LED亮起，S为低电平时LED熄灭。

![image-20251224105141867](./media/image-20251224105141867-1766563958350-103.png)

***(2)模块参数：***

- 控制接口：数字口
- 工作电压：DC 3.3-5V
- 排针间距：2.54mm
- LED显示颜色：红色

***(3) 项目组件：***

![image-20251224141202200](./media/image-20251224141202200-1766563958350-105.png)

***(4) 接线图：***

![image-20251224111906359](./media/image-20251224111906359-1766563958350-104.png)

由上图我们可以看到，扩展板是堆叠在开发板上的，LED模块的-接到了扩展板的G,LED模块的+接到了扩展板的5V,LED模块的S已经接到了扩展板上的D9接口，接好线之后我们开始编写代码。

***（5）项目代码：***

```C
void setup()
{
  pinMode(9, OUTPUT);// initialize digital pin 9 as an output.
}
void loop() // the loop function runs over and over again forever
{
  digitalWrite(9, HIGH); // turn the LED on (HIGH is the voltage level)
  delay(1000); // wait for a second
  digitalWrite(9, LOW); // turn the LED off by making the voltage LOW
  delay(1000); // wait for a second
}
//*******************************************************************
```

***(6)项目结果：***
点击上传程序，你应该看到D9脚接着的LED打开和关闭，而且间隔的时间是一秒钟。

***(7)代码说明：***
pinMode(9,OUTPUT)-在使用Arduino的引脚之前，你需要告诉开发板它是INPUT还是OUTPUT。我们使用一个内置的“函数”pinMode(来做到这一点。
digitalWrite(9,HIGH)-当使用引脚作为OUTPUT时，可以将其命令为HIGH(输出5伏)或LOW(输出0伏)。

***(8)项目拓展：***
前面我们控制了LED模块亮1秒钟，灭一秒钟，现在我们来拓展一下思路，通过改变delay的时间来改变LED灯闪烁的频率。
代码如下：

```c
void setup() {  // initialize digital pin 9 as an output.
  pinMode(9, OUTPUT);
}
// the loop function runs over and over again forever
void loop()
{
  digitalWrite(9, HIGH); // turn the LED on (HIGH is the voltage level)
  delay(100); // wait for 0.1 second
  digitalWrite(9, LOW); // turn the LED off by making the voltage LOW
  delay(100); // wait for 0.1 second
}
//****************************************************************
```

### 第2课LED亮度的调节

***(1)项目介绍：***
前面课程中，我们详细的介绍了通过代码控制LED亮灭，实现闪烁的效果。这节课我们使用PWM来控制LED亮度不断地变化，模拟我们呼吸的效果。PWM是使用数字手段来控制模拟输出的一种手段。使用数字控制产生占空比不同的方波(一个不停在高电平与低电平之间切换的信号)来控制模拟输出。一般来说端口的输入电压只有两个0V与5V。如果想要改变灯的亮度怎么办呢个?有同学说串联电阻，对，这个方法是正确的。但是，如果想要得到不同的亮度，且在不同亮度之间来回变动怎么办呢?不可能不停地切换电阻吧。这种情况下就需要使用PWM了，那它是怎么控制的呢?

![image-20251224112702280](./media/image-20251224112702280-1766563958350-106.png)

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

***(2)项目组件：***

![image-20251224141232359](./media/image-20251224141232359-1766563958350-109.png)

***（3）接线图：***

Arduino的PWM引脚在3，5，6，9，10，11，上一小节的接线刚刚好在9脚，所以我们这个接线不用变

![image-20251224113026178](./media/image-20251224113026178-1766563958350-107.png)

***（4）项目代码：***
我们来看Arduino 代码:

```c
int ledPin = 9; // Define the LED pin at D9
int value;
void setup () {
  pinMode (ledPin, OUTPUT); // initialize ledpin as an output.
}
void loop () {
  for (value = 0; value < 255; value = value + 1) {
    analogWrite (ledPin, value); // LED lights gradually light up
    delay (5); // delay 5ms
  }
  for (value = 255; value > 0; value = value - 1) {
    analogWrite (ledPin, value); // LED gradually goes out
    delay (5); // delay 5ms
  }
}

```

***（5）项目结果：***

代码下载完成后，我们可以看到LED会有个逐渐由亮到灭的一个缓慢过程，而不是直接的亮灭，如同呼吸一般，均匀变化。

***（6）代码说明：***
当我们需要重复执行某句话时，我们可以使用for语句。
for语句格式如下：
![image-20251224113232762](./media/image-20251224113232762-1766563958350-108.png)
for循环顺序如下：
第一轮：1→2→3→4

第二轮：2→3→4

…
直到2不成立，for循环结束。
知道了这么个顺序之后，回到代码中：
for (int value =0;value <255;value=value+1){….}
for (int value =255;value>0;value=value-1){….}
这两个for语句实现了value的值不断由0增加到255,随之在从255减到0,在增加到255……无限循环下去。
再看下for里面，涉及一个新函数analogWrite()。
我们知道数字口只有0和1两个状态，那如何发送一个模拟值到一个数字引脚呢?就要用到该函数。观察一下Arduino板，查看数字引脚，你会发现其中6个引脚旁标有“~”,这些引脚不同于其他引脚，它们可以输出PWM信号。
函数格式如下：
analogWrite(pin,value)
analogWrite()函数用于给 PWM口写入一个0~255的模拟值。所以,value 是在0~255之间的值。特别注意的是，analogWrite（）函数只能写入具有PWM功能的数字引脚，也就是3，5，6，9，10，11引脚。
PWM是一项通过数字方法来获得模拟量的技术。数字控制来形成一个方波，方波信号只有开关两种状态(也就是我们数字引脚的高低)。通过控制开与关所持续时间的比值就能模拟到一个0到5V之间变化的电压。开(学术上称为高电平)所占用的时间就叫做脉冲宽度，所以PWM也叫做脉冲宽度调制。
通过下面五个方波来更形象的了解一下PWM。

![image-20251224113448379](./media/image-20251224113448379-1766563958350-110.png)

上图绿色竖线代表方波的一个周期。每个analogWrite（value）中写入的value都能对应一个百分比，这个百分比也称为占空比（Duty Cycle），指的是高电平在周期内占的时间比值，也就是：占空比=高电平时间/周期时间。图中，从上往下，第一个方波，占空比为0%，对应的value为0。LED亮度最低，也就是灭的状态。高电平持续时间越长，也就越亮。所以，最后一个占空比为100%的对应value是255，LED最亮。50%就是最亮的一半了，25%则相对更暗。
PWM比较多的用于调节LED灯的亮度。或者是电机的转动速度，电机带动的车轮速度也就能很容易控制了，在玩一些Arduino机器人时，更能体现PWM的好处。

***（7）项目拓展：***

我们不改变灯的脚位，只是改变程序里面delay的值，看看它如何改变渐变效果。

```c
int ledPin = 9; // Define the LED pin at D9
void setup () {
  pinMode (ledPin, OUTPUT); // initialize ledpin as an output.
}
void loop () {
  for (int value = 0; value < 255; value = value + 1) {
    analogWrite (ledPin, value); // LED lights gradually light up
    delay (30); // delay 30ms
  }
  for (int value = 255; value > 0; value = value - 1) {
    analogWrite (ledPin, value); // LED gradually goes out
    delay (30); // delay 30ms
  }
}

```

上传代码到开发板，看LED渐变的效果是不是慢了一些。

### 第3课蜂鸣器模块实验

***（1）项目介绍：***
用Arduino 可以完成的互动作品有很多，最常见也最常用的就是声光展示了，前面一直都是在用LED做实验，本实验就设计电路发出声音，能够发出声音最常见的元器件就是蜂鸣器和喇叭，两者相比较蜂鸣器更简单和易用，可蜂鸣器又分有源蜂鸣器和无源蜂鸣器，我们在本实验中采用的是无源蜂鸣器。
在套件中包含一个功放模块，它主要由一个无源蜂鸣器和一个放大电路组成。使用时我们可以在模块信号端输入不同频率的方波，控制蜂鸣器响起不同的声音。同时，我们可以通过旋转功放模块上的电位器，调节声音放大倍数，也就是调节声音的大小。在这一课程中，我们通过代码控制蜂鸣器发声，从最简单的发出某一频率声音，到让蜂鸣器发出do re mifaso la si do声音，最后，让蜂鸣器播放特定的歌曲。

***（2）模块参数：***

- 工作电压：DC5V
- 工作电流：≥500mA
- 最大功率：2W
- 工作温度：0-40℃
- 尺寸:49mmx31mmx15.6mm
- 喇叭功率：0.15W
- 喇叭声音：80dB

***(3) 项目组件：***

![image-20251224141307142](./media/image-20251224141307142-1766563958350-111.png)

***(4) 接线图：***

接线注意：无源蜂鸣器模块的G、V、S引脚分别连接到扩展板上的G、V、D3，连接好电源。

![image-20251224114142439](./media/image-20251224114142439-1766563958350-112.png)

***（5）项目代码：***

```c
int beeppin = 3; //定义喇叭引脚为D3
void setup() {
  pinMode(beeppin, OUTPUT); //设置buzzer为输出模式
}
void loop() {
  for (int i = 0; i < 100; i++) { //1ms方波驱动输出一个频率的声音
    digitalWrite(beeppin, HIGH);
    delay(1);
    digitalWrite(beeppin, LOW);
    delay(1);
  }

  for (int i = 0; i < 100; i++) { //2ms方波驱动输出另一个频率的声音
    digitalWrite(beeppin, HIGH);
    delay(2);
    digitalWrite(beeppin, LOW);
    delay(2);
  }
}

```

***（6）项目结果***：
在UNOR3板上上传代码成功，按照接线图上电后，拨码开关拨打到“ON”端。上传代码完成后，蜂鸣器循环响起两种不同频率的声音；上传扩展代码，蜂鸣器循环响起《欢乐颂》歌曲。旋转功放模块上电位器，可以调节声音的大小。

***（7）项目拓展：***
从上面的实验看，如果我们能够控制好频率和节拍，那就有可能演奏出动听的音乐。因此，我们首先需要搞清楚各音调的频率，具体见下表：

低音：

| 音调 音符 | 1#   | 2#   | 3#   | 4#   | 5#   | 6#   | 7#   |
| --------- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| A         | 221  | 248  | 278  | 294  | 330  | 371  | 416  |
| B         | 248  | 278  | 294  | 330  | 371  | 416  | 467  |
| C         | 131  | 147  | 165  | 175  | 196  | 221  | 248  |
| D         | 147  | 165  | 175  | 196  | 221  | 248  | 278  |
| E         | 165  | 175  | 196  | 221  | 248  | 278  | 312  |
| F         | 175  | 196  | 221  | 234  | 262  | 294  | 330  |
| G         | 196  | 221  | 234  | 262  | 294  | 330  | 371  |

中音：

| 音调 音符 | 1    | 2    | 3    | 4    | 5    | 6    | 7    |
| --------- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| A         | 441  | 495  | 556  | 589  | 661  | 742  | 833  |
| B         | 495  | 556  | 624  | 661  | 742  | 833  | 935  |
| C         | 262  | 294  | 330  | 350  | 393  | 441  | 495  |
| D         | 294  | 330  | 350  | 393  | 441  | 495  | 556  |
| E         | 330  | 350  | 393  | 441  | 495  | 556  | 624  |
| F         | 350  | 393  | 441  | 495  | 556  | 624  | 661  |
| G         | 393  | 441  | 495  | 556  | 624  | 661  | 742  |

高音：

| 音调   音符 | 7#   | 2#   | 3#   | 4#   | 5#   | 6#   | 7#   |
| ----------- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| A           | 882  | 990  | 1112 | 1178 | 1322 | 1484 | 1665 |
| B           | 990  | 1112 | 1178 | 1322 | 1484 | 1665 | 1869 |
| C           | 525  | 589  | 661  | 700  | 786  | 882  | 990  |
| D           | 589  | 661  | 700  | 786  | 882  | 990  | 1112 |
| E           | 661  | 700  | 786  | 882  | 990  | 1112 | 1248 |
| F           | 700  | 786  | 882  | 935  | 1049 | 1178 | 1322 |
| G           | 786  | 882  | 990  | 1049 | 1178 | 1322 | 1484 |

我们知道了音调的频率后，下一步就是控制音符的演奏时间。每个音符都会播放一定的时间，这样才能构成一首优美的曲子，而不是生硬的一个调的把所有的音符一股脑的都播放出来。音符节奏分为一拍、半拍、1/4拍、1/8拍，我们规定一拍音符的时间为1；半拍为0.5；1/4拍为0.25；1/8拍为0.125…，所以我们可以为每个音符赋予这样的拍子播放出来，音乐就成了。
这里我们具体以《欢乐颂》为例：

![image-20251224115332877](./media/image-20251224115332877-1766563958350-113.png)

从简谱看，该音乐是D调的，这里的各音符对应的频率对应的是上表中D调的部分。另外，该音乐为四分之四拍，每个对应为1拍。几个特殊音符说明如下：

第一，普通音符。如第一个音符3，对应频率350，占1拍。
第二，带下划线音符，表示0.5拍。
第三，有的音符后带一个点，表示多加0.5拍，即1+0.5
第四，有的音符后带一个一，表示多加1拍，即1+1
第五，有的两个连续的音符上面带弧线，表示连音，可以稍微改下连音后面那个音的频率，比如减少或增加一些数值（需自己调试），这样表现会更流畅，其实不做处理，影响也不大。下面，看具体代码：

```c
#define D0 -1
#define D1 262
#define D2 293
#define D3 329
#define D4 349
#define D5 392
#define D6 440
#define D7 494
#define M1 523
#define M2 586
#define M3 658
#define M4 697
#define M5 783
#define M6 879
#define M7 987
#define H1 1045
#define H2 1171
#define H3 1316
#define H4 1393
#define H5 1563
#define H6 1755
#define H7 1971
//列出全部D调的频率
#define WHOLE 1
#define HALF 0.5
#define QUARTER 0.25
#define EIGHTH 0.25
#define SIXTEENTH 0.625
//列出所有节拍
int tune[] =       //根据简谱列出各频率
{
  M3, M3, M4, M5,
  M5, M4, M3, M2,
  M1, M1, M2, M3,
  M3, M2, M2,
  M3, M3, M4, M5,
  M5, M4, M3, M2,
  M1, M1, M2, M3,
  M2, M1, M1,
  M2, M2, M3, M1,
  M2, M3, M4, M3, M1,
  M2, M3, M4, M3, M2,
  M1, M2, D5, D0,
  M3, M3, M4, M5,
  M5, M4, M3, M4, M2,
  M1, M1, M2, M3,
  M2, M1, M1
};
float durt[] =      //根据简谱列出各节拍
{
  1, 1, 1, 1,
  1, 1, 1, 1,
  1, 1, 1, 1,
  1 + 0.5, 0.5, 1 + 1,
  1, 1, 1, 1,
  1, 1, 1, 1,
  1, 1, 1, 1,
  1 + 0.5, 0.5, 1 + 1,
  1, 1, 1, 1,
  1, 0.5, 0.5, 1, 1,
  1, 0.5, 0.5, 1, 1,
  1, 1, 1, 1,
  1, 1, 1, 1,
  1, 1, 1, 0.5, 0.5,
  1, 1, 1, 1,
  1 + 0.5, 0.5, 1 + 1,
};
int length;
int tonepin = 3; //得用3号接口
void setup()
{
  pinMode(tonepin, OUTPUT);
  length = sizeof(tune) / sizeof(tune[0]); //计算长度
}
void loop()
{
  for (int x = 0; x < length; x++)
  {
    tone(tonepin, tune[x]);
    delay(500 * durt[x]); //这里用来根据节拍调节延时，500这个指数可以自己调整，在该音乐中，用500比较合适。
    noTone(tonepin);
  }
  delay(2000);
}
```

上传代码到开发板，怎么样，你的欢乐颂响起了没有？

### 第4课红外避障传感器实验

***（1）项目介绍：***
红外避障传感器模块对环境光线适应能力强，其具有一对红外线发射与接收管，发射管发射出一定频率的红外线，当检测方向遇到障碍物（反射面）时，红外线反射回来被接收管接收，经过比较器电路处理之后，绿色指示灯会亮起，同时信号输出接口输出数字信号（一个低电平信号），可通过电位器旋钮调节检测距离，有效距离范围2~30cm，工作电压为3.3V-5V。该传感器的探测距离可以通过电位器调节、具有干扰小、便于装配、使用方便等特点，可以广泛应用于机器人避障、避障小车、流水线计数及黑白线循迹等众多场合。我们把测试结果在串口监视器上显示，并且利用结果控制1个外接LED模块上的LED亮灭。

***（2）模块参数：***

- 工作电压：3.3-5V（DC）
- 接口：3PIN接口
- 输出信号：数字信号
- 感应距离：2-40cm，检测距离可以通过电位器进行调节
- 检测角度：35°

***（3）项目组件：***

![image-20251224141340411](./media/image-20251224141340411-1766563958350-114.png)

***（4）接线图：***

![image-20251224120106988](./media/image-20251224120106988-1766563958350-115.png)

接线注意：用导线把左边的避障传感器连接到电机驱动扩展板上的接口（G、V、A1），右边的避障传感器连接到接口（G、V、A2），模拟口在数字口不够的情况下，模拟口也可以当数字口使用，模拟口A0相当于数字口14，A1相当于数字口15，以此类推。

***（5）项目代码：***

```c
int l_sensorPin = A1; //定义左边避障传感器接A1
int l_val;
void setup() {
  Serial.begin(9600);  //设置波特率为9600
  pinMode(l_sensorPin, INPUT);//将l_sensorPin设置为输入模式
}

void loop() {
  l_val = digitalRead(l_sensorPin);//读取避障传感器的值
  Serial.print("l_val="); //在串口打印出来
  Serial.println(l_val);
}
```

***（6）项目结果：***

实验中我们利用USB线供电，上传好测试代码，打开串口监视器，设置波特率为9600，当我们接近避障传感器时信号端输出低电平，如下图：

![image-20251224120320181](./media/image-20251224120320181-1766563958350-116.png)

***（7）代码说明：***
Serial.begin(9600);
代表设置波特率为9600；代码串口监视器中显示数据；
Serial.println(I_val);
代码串口监视器中显示数据，数据输出成功自动换行。

***（8）项目拓展：***

![image-20251224120420979](./media/image-20251224120420979-1766563958350-117.png)

```c
int l_sensorPin = A1;
int led = 9;
int l_val;
void setup()
{
  pinMode(l_sensorPin, INPUT);
  pinMode(led , OUTPUT);
}
void loop()
{
  l_val = digitalRead(l_sensorPin);
  if (l_val == 0)
  {
    digitalWrite(led, HIGH);
  }
  else
  {
    digitalWrite(led, LOW);
  }
}
```

当传感器检测到障碍物（输出0）时，外接LED模块LED亮起，否则LED熄灭。

### 第5课循迹传感器

***（1）项目介绍：***

![image-20251224120601477](./media/image-20251224120601477-1766563958350-118.png)

循迹传感器实际上是红外传感器。此处使用的组件是TCRT5000红外管。其工作原理是利用红外光对颜色的不同反射率，然后将反射信号的强度转换为电流信号。

在检测过程中，黑色在高电平时处于活动状态，而白色在低电平时处于活动状态。检测高度为0-3厘米。
KEYES三路循迹模块在一块板上集成了三个TCRT5000红外管，接线和控制更加方便。通过旋转传感器上的可调电位器，可以调节传感器的检测灵敏度。

***（2）模块参数：***

- 工作电压：3.3-5V（DC）
- 接口:5PIN
- 输出信号：数字信号
- 检测高度：0-3厘米

特别说明：在测试之前，请旋转传感器上的电位器以调整检测灵敏度。当将LED调整在ON和OFF之间的阈值时，灵敏度是最好的。

![image-20251224120829660](./media/image-20251224120829660-1766563958350-119.png)

***（3）项目组件：***

![image-20251224141424060](./media/image-20251224141424060-1766563958350-121.png)

***（4）接线图：***

![image-20251224121952891](./media/image-20251224121952891-1766563958350-120.png)

循迹传感器接扩展板的D11、D7、D8引脚。

***（5）项目代码：***

```c
int L_pin = 11;  //pins of  left line tracking sensor
int M_pin = 7;  //pins of  middle line tracking sensor
int R_pin = 8;  //pins of  right  line tracking sensor
int val_L, val_R, val_M; // 定义三个传感器的变量值
void setup()
{
  Serial.begin(9600); // initialize serial communication at 9600 bits per second
  pinMode(L_pin, INPUT); // make the L_pin as an input
  pinMode(M_pin, INPUT); // make the M_pin as an input
  pinMode(R_pin, INPUT); // make the R_pin as an input
}
void loop()
{
  val_L = digitalRead(L_pin);//read the L_pin:
  val_R = digitalRead(R_pin);//read the R_pin:
  val_M = digitalRead(M_pin);//read the M_pin:
  Serial.print("left:");
  Serial.print(val_L);
  Serial.print(" middle:");
  Serial.print(val_M);
  Serial.print(" right:");
  Serial.println(val_R);
  delay(500);// delay in between reads for stability
}
```

***（6）项目结果：***

上传代码带开发板，打开串口监视，可以看到左中右三个循迹传感器的状态，在没有接收到信号的时候，三个传感器都是高电平状态，显示的数值是1。如果我们用白纸去遮挡传感器，传感器的状态都变成了0。

![image-20251224122341871](./media/image-20251224122341871-1766563958350-123.png)

![image-20251224122357429](./media/image-20251224122357429-1766563958350-122.png)

***（7）代码说明：***

Serial.begin（9600）-初始化串口，串口通信波特率为9600；

pinMode-定义单片机PIN 脚模式是输入还是输出，input 是输入，output是输出；

digitalRead-读取引脚电平状态,一般有两种状态,HIGH 或者 LOW。

***（8）项目拓展：***
上面我们了解了循迹传感器的工作原理，接下来我们在第9脚接上一个LED灯，然后通过读取循迹传感器的状态，来控制LED的亮和灭。如下图接线：

![image-20251224122540980](./media/image-20251224122540980-1766563958350-124.png)

我们开始来编写代码：

```c
int L_pin = 11;  //pins of  left line tracking sensor
int M_pin = 7;  //pins of  middle line tracking sensor
int R_pin = 8;  //pins of  right  line tracking sensor
int val_L, val_R, val_M; // 定义三个传感器的变量值
void setup()
{
  Serial.begin(9600); // initialize serial communication at 9600 bits per second
  pinMode(L_pin, INPUT); // make the L_pin as an input
  pinMode(M_pin, INPUT); // make the M_pin as an input
  pinMode(R_pin, INPUT); // make the R_pin as an input
  pinMode(9, OUTPUT);
}
void loop() {
  val_L = digitalRead(L_pin);//read the L_pin:
  val_R = digitalRead(R_pin);//read the R_pin:
  val_M = digitalRead(M_pin);//read the M_pin:
  Serial.print("left:");
  Serial.print(val_L);
  Serial.print(" middle:");
  Serial.print(val_M);
  Serial.print(" right:");
  Serial.println(val_R);

  if (val_L == LOW || val_M == LOW || val_R == LOW) //if line tracking sensor 检测到信号
  {
    digitalWrite(9, HIGH);//LED 灯灭
  }
  else//如果left line tracking sensor 没有检测到信号
  {
    digitalWrite(9, LOW);//LED 灯亮
  }
}
```

### 第6课超声波模块

***（1）项目介绍：***

![image-20251224160705196](./media/image-20251224160705196-1766563958351-200.png)

HC-SR04超声波传感器像蝙蝠一件使用纳来确定到物体的距离。它提供出色的非接触范围检测，具有高精度和稳定的读数。它带有超声波发射和接收模块。HC-SR04或超声波传感器被广泛用于创建障碍物检测和距离测量应用以及其他各种应用的电子项目中。在这里，我们介绍使用arduino和超声传感器测量距离的简单方法。

***（2）超声波参数：***

- 电源:+5VDC
- 静态电流：<2mA
- 工作电流：15mA
- 有效角度：<15°
- 测距范围：2cm-400cm
- 分辨率：0.3厘米
- 测量角度：30度
- 触发输入脉冲宽度：10uS

***（3）项目组件：***

![image-20251224141528163](./media/image-20251224141528163-1766563958350-125.png)

***（4）超声波模块知识：***

原理：看超声波的图可知，像是有两个眼睛，其一边是发射超声的，一边是接收超声波的，然后检测从发射遇到障碍物返回被接收到所需的时间t，再根据声音在空气中的传播速度大概是343m/s，距离=速度*时间，由于超声波发射返回是两段路程了，所以需要除以2，故超声波测到的距离=（速度*时间）/2超声波模块的使用方法及时序图：
1、使用GPIO引脚给SR04的Trig 引脚至少10μs的高电平信号，触发SR04模块测距功能；
2、触发后，模块会自动发送8个40KHz的超声波脉冲，并自动检测是否有信号返回。这步会由模块内部自动完成。
3、如有信号返回，Echo引脚会输出高电平，高电平持续的时间就是超声波从发射到返回的时间。

![image-20251224133402415](./media/image-20251224133402415-1766563958350-127.png)

![image-20251224133436781](./media/image-20251224133436781-1766563958350-128.png)

***（5）接线图：***
接线注意：超声波传感器模块的VCC引脚连接至keyestudio V5传感器扩展板的5v（V），Trig 引脚至数字12(S),Echo 引脚至数字13(S),Gnd 引脚至Gnd(G)。

![image-20251224133603678](./media/image-20251224133603678-1766563958350-129.png)

***（6）项目代码：***

```c
int trigPin = 12;    // Trigger
int echoPin = 13;    // Echo
long duration, cm, inches;
void setup() {
  //Serial Port begin
  Serial.begin (9600);
  //Define inputs and outputs
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}
void loop() {
  // The sensor is triggered by a HIGH pulse of 10 or more microseconds.
  // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  // Read the signal from the sensor: a HIGH pulse whose
  // duration is the time (in microseconds) from the sending
  // of the ping to the reception of its echo off of an object.
  duration = pulseIn(echoPin, HIGH);
  // Convert the time into a distance
  cm = (duration / 2) / 29.1;   // Divide by 29.1 or multiply by 0.0343
  inches = (duration / 2) / 74; // Divide by 74 or multiply by 0.0135
  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();
  delay(200);
}
```

***（7）项目结果：***
上传好测试代码到开发板，打开串口监视器，设置波特率为9600，我们可以看到超声波模块显示的距离，单位是厘米和英寸。用手阻挡超声波模块，我们看到显示距离的数值变小了。

![image-20251224133732132](./media/image-20251224133732132-1766563958350-135.png)

***（8）代码说明：***

int trigPin-这个是定义发射超声波的脚位，通常是输出，

int echoPin-这个是定义接收超声波的脚位，通常是输入。

cm=(duration/2)/29.1-

inches =(duration/2)/74-

我们可以使用以下公式计算距离：

距离=（行驶时间/2）x声速

声音速度为:343m/s=0.0343 cm/uS=1/29.1 cm/uS

或英寸:13503.9in/s=0.0135in /uS=1/74in /uS

我们需要将传播时间除以2，因为我们必须考虑到波浪已发送，撞击物体然后返回到传感器。

***（9）项目拓展：***
我们刚刚测出了超声波显示的距离，那我们动动脑筋，能不能用测出的距离来做一些控制呢，
如果控制一个LED灯的亮和灭。我们来试一下，在D9脚接上一个LED灯模块。

![image-20251224135030535](./media/image-20251224135030535-1766563958350-131.png)

```c
int trigPin = 12;    // Trigger
int echoPin = 13;    // Echo
long duration, cm, inches;
void setup() {
  Serial.begin (9600);  //Serial Port begin
  pinMode(trigPin, OUTPUT);  //Define inputs and outputs
  pinMode(echoPin, INPUT);
  pinMode(9, OUTPUT);
}
void loop()
{
  // The sensor is triggered by a HIGH pulse of 10 or more microseconds.
  // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  // Read the signal from the sensor: a HIGH pulse whose
  // duration is the time (in microseconds) from the sending
  // of the ping to the reception of its echo off of an object.
  duration = pulseIn(echoPin, HIGH);
  // Convert the time into a distance
  cm = (duration / 2) / 29.1;   // Divide by 29.1 or multiply by 0.0343
  inches = (duration / 2) / 74; // Divide by 74 or multiply by 0.0135
  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();
  delay(50);
  if (cm >= 2 && cm <= 10) {
    digitalWrite(9, HIGH);
  }
  else {
    digitalWrite(9, LOW);
  }
}
```

### 第7课 红外接收原理及应用

***(1)项目介绍：***
红外遥控在日常生活中随处可见，它被用来控制各种家电，如电视、音响、录影机和卫星信号接收器。红外遥控是由红外发射和红外接收系统组成的，也就是一个红外遥控器和红外接收模块和一个能解码的单片机组成的。

红外发射的遥控器发射的38K红外载波信号是由遥控器里的编码芯片对其进行编码。它是以一段引导码，用户码，数据码，数据反码组成，利用脉冲的时间间隔来区别是0还是1信号（高电平低电平之比约为1：1时被认为是信号0），而编码就是由这些0、1信号组成。同一个遥控器的用户码是不变的，用数据码不同来分辨遥控器按的键不同。当按下遥控器按键时，遥控器发送出红外载波信号，红外接收器接收到信号时程序对载波信号进行解码，通过数据码的不同来判断按下的是哪个键。单片机由接收到的01信号进行解码，由此判断遥控器按下的是什么键。

![image-20251224135400426](./media/image-20251224135400426-1766563958350-130.png)

红外接收我们用的是一个红外接收模块，主要由红外接收头组成，它是集接收、放大、解调一体的器件，它内部IC就已经完成了解调，能够完成从红外线接收到输出与TTL电平信号兼容的所有工作，输出的就是数字信号。他适用于红外线遥控和红外线数据传输。接收器做成的红外接收模块只有三个引脚，信号线，VCC，GND。与arduino和其他单片机连接通信非常方便。

***（2）模块参数：***

- 工作电压：3.3-5V（DC）
- 接口：3PIN接口
- 输出信号：数字信号
- 接收角度：90度
- 频率:38khz
- 接收距离：10米

![image-20251224135539650](./media/image-20251224135539650-1766563958350-132.png)

***（3）项目组件：***

![image-20251224141750063](./media/image-20251224141750063-1766563958350-133.png)

***（4）接线图：***
接线注意：由于红外接收传感器输入的数字信号，将红外接收传感器模块的“-”、“+”和S引脚分别用导线连接到keyestudio传感器扩展板G(GND),V(VCC),A3,模拟口在数字口不够的情况下，模拟口也可以当数字口使用，模拟口A0相当于数字口14，A1相当于数字口15，以此类推。

![image-20251224140213569](./media/image-20251224140213569-1766563958350-134.png)

***（5）项目代码：***
在编写代码之前，要先导入红外的库文件，具体步骤请参考，（如何导入arduino库文件）这个文档。

```c
#include <IRremote.h>     // IRremote库声明  
int RECV_PIN = A3;        //定义红外接收器的引脚为A3
IRrecv irrecv(RECV_PIN);
decode_results results;   //解码结果放在 decode results结构的 result中
void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // 启动接收器
}
void loop() {
  if (irrecv.decode(&results))//解码成功，收到一组红外讯号
  {
    Serial.println(results.value, HEX);//以16进制换行输出接收代码
    irrecv.resume(); // 接收下一个值
  }
  delay(100);
}
```

***（6）项目结果：***
上传好测试代码，打开串口监视器，设置波特率为9600，拿出遥控器，对准红外接收传感器发送信号，即可看相应按键的键值，如果按键时间过长，容易出现乱码。

![image-20251224140441326](./media/image-20251224140441326-1766563958350-137.png)

我们通过测试得出的数值，做了一个遥控器按键值表，方便以后使用。

![image-20251224140519372](./media/image-20251224140519372-1766563958350-136.png)

***（7）代码说明：***
irrecv.enablelRIn（）-启动红外解码后，这时候IRrecv对象会在后台接收红外线信号。
decode（）-接着就可以利用decode（）函数持续检查，看看有没有解码成功。
irrecv.decode(&results)解码成功,这个函数会返回true,并把结果放在results里面,在解码一个红外线信号之后，要运行resume（）函数，这样才会持续接收下一组信号。

***（8）项目拓展：***
我们刚刚解码了红外遥控器的按键值，那我们能不能用测出的按键值来做一些控制呢，如果控制一个LED灯的亮和灭。我们来试一下，在9脚接上一个LED灯模块。红外接收器的脚位不变，当有遥控器的按键按下时，接在数字引脚9上的发光LED就会点亮，再按一下按键，led 熄灭，接线图如下：![image-20251224140624182](./media/image-20251224140624182-1766563958350-138.png)

```c
#include <IRremote.h>
int RECV_PIN = A3;//定义红外接收器的引脚为A3
int LED_PIN = 9; //定义发光LED引脚数字9
int a = 0;
IRrecv irrecv(RECV_PIN);
decode_results results;
void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // 初始化红外接收器
  pinMode(LED_PIN, OUTPUT); //设置发光LED引脚数字4
}
void loop() {
  if (irrecv.decode(&results)) {
    Serial.println(results.value, HEX);
    if (results.value == 0xFF02FD & a == 0) //由上面的键值码，我们用的遥控器上的OK键，如果按下OK键
    {
      digitalWrite(LED_PIN, HIGH); //LED点亮
      a = 1;
    }
    else if (results.value == 0xFF02FD & a == 1) //再按一下
    {
      digitalWrite(LED_PIN, LOW); //LED熄灭
      a = 0;
    }
    irrecv.resume(); // 接收下一个值
  }
}
```

### 第8课蓝牙遥控的原理及应用

***（1）项目介绍：***

![image-20251224140745934](./media/image-20251224140745934-1766563958350-140.png)

蓝牙是近几十年来最流行的一种简单的无线通信模块，易于使用，已在大多数电池供电的设备中使用。蓝牙标准进行了许多升级，以不断满足客户和技术的需求。几年来，发生了许多变化，包括数据传输速率，可穿戴设备和loT设备以及安全系统的功耗。

在这里，我们将学习HM-10BLE4.0。HM-10是一种随时可用的蓝牙4.0模块。该模块用于建立无线数据通信。该模块是使用德州仪器（TI）的CC2540或CC2541蓝牙低功耗（BLE）片上系统（SoC）设计的。

***（2）蓝牙参数：***

- 蓝牙协议：蓝牙V4.0BLE串口收发无字节限制
- 工作距离：在开放环境中，实现50-100m超远距离通讯
- 工作频率：2.4GHz ISM频段
- 调制方式：GFSK（高斯频移键控）
- 传输功率:-23dbm,-6dbm,0dbm,6dbm,可通过AT命令修改。
- 灵敏度:0.1%BER时≤-84dBm
- 传输速率：异步：6K字节；同步：6k字节
- 安全功能：身份验证和加密
- 支持服务:中央和外围UUID FFEO,FFE1
- 功耗：自动休眠模式，待机电流400uA~800uA，传输期间为8.5mA。
- 电源:5VDC
- 工作温度：-5至+65摄氏度

***（3）项目组件：***

![image-20251224141115367](./media/image-20251224141115367-1766563958350-139.png)

***（4）接线图：***

![image-20251224141916703](./media/image-20251224141916703-1766563958350-141.png)

蓝牙是直接插在电机驱动扩展板上的，注意一下方向，而且在上传代码之前不要插上蓝牙。

***（5）项目代码：***

```c
char ble_val; //字符变量，用于存放蓝牙接收到的值
void setup() {
  Serial.begin(9600);
}
void loop() {
  if (Serial.available() > 0) //判断串口缓存区是否有数据
  { 
    ble_val = Serial.read();  //读取串口缓存区的数据
    Serial.println(ble_val);  //打印出来
  }
}
```

（上传代码之前不要连接蓝牙模块，因为代码的上传也是用的串口通信，跟蓝牙的串口通信会有冲突，导致代码上传不成功）
上传代码到开发板，然后再插上蓝牙模块，等待手机发出的指令。

***（6）下载蓝牙测试APP：***

刚刚的代码是读取串口的接收到的信号，那么我们还需要一个能够发出信号的东西，我们项目中就用手机来发送字符，蓝牙接收到手机软件发出的字符信号，在开发板的串口打印出来。我们还要在手机上下载一个APP。

苹果手机下载APP方法：

首先到苹果的应用商店APP STORE，搜索BLE Scammer 4.0，找到这个应用，然后下载到手机

![image-20251224142353328](./media/image-20251224142353328-1766563958350-142.png)

安卓手机下载APP方法：

1、[点击下载软件](https://cbz.cc/api/shares/pLl/files/a3b53a2b-754d-4fb0-98bb-e2decad2f7f0)

2、下载后使用数据线复制到手机里面安装，注意不要使用QQ发送到手机，会改变APP格式导致安装失败。

3.安装完成后，打开应用程序并启用“位置和蓝牙”权限。

软件使用方法：

苹果的手机和安卓系统的使用方法几乎一样，我们这里以苹果手机作为例子讲解一下APP的使用。
打开手机，扫描蓝牙设备，蓝牙BLE4.0的名称为HMSoft。蓝牙4.0没有配对密码。因此，点击connect 连接到蓝牙，则可以开始使用它。

![image-20251224145248089](./media/image-20251224145248089-1766563958350-143.png)

在完成与HMSoft的连接后，单击它，您将获得许多选项，如设备信息，通用访问权限，通用属性，自定义服务。在所有这些选项中，单击“定制服务”。

![image-20251224145354897](./media/image-20251224145354897-1766563958350-144.png)

选择了定制服务后，出现这样的一个界面

![image-20251224145421474](./media/image-20251224145421474-1766563958350-145.png)

点击蓝色的(Read,Notify,WriteWithoutResponse),进入到下面这个界面

![image-20251224145447848](./media/image-20251224145447848-1766563958351-146.png)

点击Write Value,出现输入HEX 或者Text的界面

![image-20251224145523865](./media/image-20251224145523865-1766563958351-147.png)

打开Arduino 开发软件上的串口监测，我们在Text的界面输入一个0或者其他的字符

![image-20251224145544875](./media/image-20251224145544875-1766563958351-148.png)

然后点击Write，看看串口监测是不是收到0的信号。

![image-20251224145605911](./media/image-20251224145605911-1766563958351-149.png)

***（7）代码说明：***
Serial.available（）的意思是：返回串口缓冲区中当前剩余的字符个数。一般用这个函数来判断串口的缓冲区有无数据，当Serial.available（）>0时，说明串口接收到了数据，可以读取；
Serial.read（）指从串口的缓冲区取出并读取一个Byte的数据，比如有设备通过串口向Arduino 发送数据了，我们就可以用Serial.read（）来读取发送的数据。

***（8）项目拓展：***
上面的项目，我们讲解了蓝牙接收到手机发送的信号并且在开发板的串口显示出来，那接下来我们就要想一下了，我们可以利用接收到的信号去做一些事情吗，答案是肯定的，我们这里就利用手机发送的命令去打开或者关闭一个LED灯。看接线图，在D9脚接了一个LED。

![image-20251224145806563](./media/image-20251224145806563-1766563958351-150.png)

```c
int ledpin = 9;
void setup()
{
  Serial.begin(9600);
  pinMode(ledpin, OUTPUT);
}
void loop()
{
  int i;
  if (Serial.available())
  {
    i = Serial.read();
    Serial.println("DATA RECEIVED:");
    if (i == 1)
    {
      digitalWrite(ledpin, HIGH);
      Serial.println("led on");
    }
    if (i == 0)
    {
      digitalWrite(ledpin, LOW);
      Serial.println("led off");
    }
  }
}
```

![image-20251224145952981](./media/image-20251224145952981-1766563958351-154.png)![image-20251224150000326](./media/image-20251224150000326-1766563958351-151.png)

点击手机APP上的Write并发送1或和0以控制LED。当您发送1时，LED将打开，而当您发送0时，LED将关闭。

### 第9课电机的驱动和调速

***（1）项目介绍：***
驱动电机的方法有很多，我们这个智能车用到的是最常用的L298P这个方案，L298P是ST意法半导体公司出品的优秀大功率电机专用驱动芯片，可直接驱动直流电机、二相、四相步进电机，驱动电流达2A，电机输出端采用8只高速肖特基二极管作为保护。我们根据L298P的电路设计了一款扩展板，叠层的设计可直接插接到开发板上使用，降低了用户使用和驱动电机的技术难度。我们来看一下这个板子的电路图和示意图：

![image-20251224150120503](./media/image-20251224150120503-1766563958351-152.png)

![image-20251224150131322](./media/image-20251224150131322-1766563958351-153.png)

为了调节小车上的4个电机，使得电机电机的驱动方向与后续的课程代码描述一致。驱动板上自带8个跳线帽，也可用于控制电机转向，例如当MA电机接口前方2个跳线帽由横向连接改为纵向连接时，MA电机的转动方向就和原来的转动方向相反。

![image-20251224150207172](./media/image-20251224150207172-1766563958351-155.png)

***（2）规格参数：***

- 逻辑部分输入电压：DC5V
- 驱动部分输入电压：DC7-12V
- 逻辑部分工作电流：<36mA
- 驱动部分工作电流：<2A
- 最大耗散功率：25W（T=75℃）
- 控制信号输入电平：高电平2.3V<Vin<5V，低电平-0.3V<Vin<1.5V
- 工作温度：-25+130℃

***（3）驱动小车运行原理：***

根据上面电机驱动板的电路图和示意图，我们知道了A电机的方向引脚在D2，调速引脚在D6，B电机的方向引脚在D4，调速引脚在D5，按照以下表格的运动逻辑，我们就可以知道如何通过控制数字口，PWM口控制2个电机转动，从而实现智能小车的行走。其中PWM值范围为0-255，设置数字越大，电机转动越快。（左边B电机，右边A电机）

|        | D2   | D6(PWM) | 电机(A) | D4   | D5(PWM) | 电机(B) |
| ------ | ---- | ------- | ------- | ---- | ------- | ------- |
| 前进   | HIGH | 200     | 正转    | HIGH | 200     | 正转    |
| 后退   | LOW  | 200     | 反转    | LOW  | 200     | 反转    |
| 右旋转 | LOW  | 200     | 反转    | HIGH | 200     | 正转    |
| 左旋转 | HIGH | 200     | 正转    | LOW  | 200     | 反转    |
| 停止   | /    | 0       | 停止    | /    | 0       | 停止    |

***（4）项目组件：***

![image-20251224150535321](./media/image-20251224150535321-1766563958351-156.png)

***（5）接线图：***

![image-20251224150619578](./media/image-20251224150619578-1766563958351-157.png)

***（6）项目代码：***

```c
int MA = 2; //定义电机A方向控制引脚为D2
int PWMA = 6; //定义电机A速度控制引脚为D6
int MB = 4; //定义电机B方向控制引脚为D4
int PWMB = 5; //定义电机B速度控制引脚为D5
void setup() {
  pinMode(MA, OUTPUT); //配置电机引脚为输出模式
  pinMode(PWMA, OUTPUT);
  pinMode(MB, OUTPUT);
  pinMode(PWMB, OUTPUT);
}
void loop() {
  //前进1秒
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
  delay(1000);
  //后退1秒
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
  delay(1000);
  //左转1秒
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
  delay(1000);
  //右转1秒
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
  delay(1000);
  //停止1秒
  analogWrite(PWMA, 0);
  analogWrite(PWMB, 0);
  delay(1000);
}
```

***（7）项目结果：***

上传代码成功，上电后，智能车前进1秒，后退1秒，左转1秒，右转1秒，停止1秒，循环。
***（8）代码说明：***
digitalWrite（MB，LOW）；电机的正反转是靠高低电平的转换来实现的，控制电机正反转的脚位用一般的数字脚位就可以了。
analogWrite（PWMB，200）；电机的速度调节是靠PWM来实现的，控制电机调速的脚位必须是Arduino 的PWM 脚位。
***（9）项目拓展：***

我们来通过调整PWM控制电机的速度，为后面我们控制车速做一个铺垫，接线不变。

```c
int MA = 2; //定义电机A方向控制引脚为D2
int PWMA = 6; //定义电机A速度控制引脚为D6
int MB = 4; //定义电机B方向控制引脚为D4
int PWMB = 5; //定义电机B速度控制引脚为D5
void setup() {
  pinMode(MA, OUTPUT); //配置电机引脚为输出模式
  pinMode(PWMA, OUTPUT);
  pinMode(MB, OUTPUT);
  pinMode(PWMB, OUTPUT);
}
void loop() {
  //前进1秒
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 100); //电机A速度为100
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 100); //电机B速度为100
  delay(1000);
  //后退1秒
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 100); //电机A速度为100
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 100); //电机B速度为100
  delay(1000);
  //左转1秒
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 100); //电机B速度为100
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 100); //电机A速度为100
  delay(1000);
  //右转1秒
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 100); //电机A速度为100
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 100); //电机B速度为100
  delay(1000);
  //停止1秒
  analogWrite(PWMA, 0);
  analogWrite(PWMB, 0);
  delay(1000);
}
```

### 第10课传感器项目综合扩展

***（1）综合扩展1：自制简易小钢琴***

接线图：蓝牙模块+蜂鸣器模块，接线与上面一样

![image-20251224151210349](./media/image-20251224151210349-1766563958351-158.png)

前面我们学习了怎么使用功放与蓝牙模块，这节课我们用功放模块结合APP来自制一个简易钢琴。

```c
int beeppin = 3;  // 蜂鸣器的pin
char blue_val;
//低音
#define D0 -1
#define D1 262
#define D2 293
#define D3 329
#define D4 349
#define D5 392
#define D6 440
#define D7 494
//中音
#define M1 523
#define M2 586
#define M3 658
#define M4 697
#define M5 783
#define M6 879
#define M7 987
//高音
#define H1 1045
#define H2 1171
#define H3 1316
#define H4 1393
#define H5 1563
#define H6 1755
#define H7 1971
//列出全部D调的频率
void setup() {
  Serial.begin(9600);//设置波特率为9600
  pinMode(beeppin, OUTPUT); //扬声器的pin设置为输出模式
}
void loop(void)
{
  if (Serial.available() > 0) {
    blue_val = Serial.read();
    Serial.println(blue_val);//串口打印蓝牙的值
  }
  switch (blue_val) {
    case  '1':  tone(beeppin, D1); break; //接收到'1',播放音符DO
    case  '2':  tone(beeppin, D2); break; //接收到'2',播放音符Re
    case  '3':  tone(beeppin, D3); break; //接收到'3',播放音符Mi
    case  '4':  tone(beeppin, D4); break; //接收到'4',播放音符Fa
    case  '5':  tone(beeppin, D5); break; //接收到'5',播放音符So
    case  '6':  tone(beeppin, D6); break; //接收到'6',播放音符La
    case  '7':  tone(beeppin, D7); break; //接收到'7',播放音符Si
    case  '8':  tone(beeppin, M1); break; //接收到'8',播放音符Do
    case  'S':  noTone(beeppin); break; //接收到'S',停止播放
  }
}
```

上传代码完成后再连接APP，当我们按下APP界面的DO，RE，MI.等键时，蜂鸣器就会响起对应的旋律，是不是很好玩？

***（2）综合扩展2：基于Arduino的倒车雷达系统***
接线图：超声波模块+蜂鸣器模块+led模块，接线跟上面一样。

![image-20251224151403913](./media/image-20251224151403913-1766563958351-159.png)

这节课我们将把前面学习过的超声波传感器、蜂鸣器模块、LED灯模块结合起来做一个超声波雷达系统：

```c
int beeppin = 3;  // 蜂鸣器的pin
int ledPin = 9; //led灯接D9
int trigPin = 12; //TRIG引脚接D12
int echoPin = 13; //ECHO引脚接D13
int distance;

void buzzer1()  //蜂鸣器报警
{
  tone(beeppin, 900);
  delay(100);  //响声间隔100ms 听上去更急促
  noTone(beeppin);
  delay(100);
}
void buzzer2()  //蜂鸣器报警
{
  tone(beeppin, 800);
  delay(400);  //响声间隔500ms
  noTone(beeppin);
  delay(400);
}

int get_distance() { //超声波测距函数
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH); //给TRIG引脚至少10us的时间触发
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  distance = pulseIn(echoPin, HIGH) / 58; //检测脉冲宽度，并计算出距离
  Serial.print("distance:");  //串口打印出距离，单位为cm
  Serial.print(distance);
  Serial.println("cm");
}
void setup() {
  Serial.begin(9600); //设置波特率为9600
  pinMode(beeppin, OUTPUT);     //设置蜂鸣器引脚输出模式
  pinMode(ledPin, OUTPUT);   //LED引脚为输出模式
  pinMode(trigPin, OUTPUT); //定义TRIG为输出模式
  pinMode(echoPin, INPUT); //定义ECHO为输入模式
}
void loop() {
  get_distance();
  if (distance < 10) {
    analogWrite(ledPin, 255); //距离近灯更亮
    buzzer1();  //报警
  }
  else if (distance < 20) {
    analogWrite(ledPin, 100); //没那么亮
    buzzer2();
  }
  else {
    analogWrite(ledPin, 0); //灯灭
  }
}
```

我们用手靠近超声波传感器的时候，蜂鸣器会发出警报，led灯也会亮起。继续接近超声波时，蜂鸣器报警更急促了，led灯也更亮了。我们发现这里只是用到了超声波传感器来检测前面的障碍，那么左右两边的障碍物呢？这个就留给小伙伴们自己去完成了哈（提示：红外避障传感器）。

### 桌面迷你智能车项目

所有的东西都已经准备完毕，我们正式开始桌面小车的编程项目。前面我们从简单的传感器和模块开始，循序渐进完成模块传感器的测试项目，现在再来完成几个不同类型的机器人，最后我们把所有学到的知识结合到一起，完成一个综合的项目：多功能桌面小车。注意：本项目中的各传感器/模块上标有（G）表示负极，是连接到控制板或传感器扩展板上的G或-或GND；标有（V）表示正极，是连接到控制板或扩展板上的V或VCC或+或5V。

### 第11课画地为牢

***（1）项目介绍：***
前面我们详细的介绍了智能车上各个传感器、模块、扩展板的使用方法。在这里我们可以结合前面课程中知识制作一个画地为牢智能车。实验中，我们通过循迹传感器检测智能车底部是否存在黑线，然后根据检测结果控制两个电机的转动，从而把智能车关在黑线圈中即画地为牢。
***（2）流程图：***
画地为牢智能车具体逻辑如下表格。

![image-20251224151818241](./media/image-20251224151818241-1766563958351-161.png)

![image-20251224151845351](./media/image-20251224151845351-1766563958351-160.png)

按照前面思路设计好智能车后，我们就需要按照设计思路开始制作智能车。我们需要设计对应的接线，测试代码，然后接线上传代码，运行，确保智能车能够实现理想中的功能。

***（3）接线图：循迹模块+电机***

![image-20251224151948354](./media/image-20251224151948354-1766563958351-162.png)

接线注意：用导线把循迹模块连接到电机驱动扩展板上P1接口的G、V、D11、D7、D8；A、B两电机分别对应的连接到电机驱动扩展板上的接口A和接口B，电源接到BAT接口。

***（4）测试代码：***

```c
int L_pin = 11; //定义左边传感器引脚为D11
int M_pin = 7; //定义中间传感器引脚为D7
int R_pin = 8; //定义右边传感器引脚为D8
int MA = 2; //定义电机A方向控制引脚为D2
int PWMA = 6; //定义电机A速度控制引脚为D6
int MB = 4; //定义电机A方向控制引脚为D4
int PWMB = 5; //定义电机A速度控制引脚为D5
int L_val, M_val, R_val;

void advance() { //小车前进
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}

void back() { //小车后退
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
}

void turnL() { //小车左转
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
}

void turnR() { //小车右转
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}

void stopp() { //小车停止
  analogWrite(PWMA, 0); //电机A速度为0
  analogWrite(PWMB, 0); //电机B速度为0
}

void setup() {
  Serial.begin(9600); //设置波特率为9600
  pinMode(L_pin, INPUT); //循迹传感器引脚都配置为输入模式
  pinMode(M_pin, INPUT);
  pinMode(R_pin, INPUT);
  pinMode(MA, OUTPUT); //配置电机引脚为输出模式
  pinMode(PWMA, OUTPUT);
  pinMode(MB, OUTPUT);
  pinMode(PWMB, OUTPUT);
}
void loop() {
  L_val = digitalRead(L_pin); //读取左边传感器的值
  M_val = digitalRead(M_pin); //读中间传感器的值
  R_val = digitalRead(R_pin); //读取右边传感器的值
  if ( L_val == 0 && M_val == 0 && R_val == 0 ) { //当都没有检测到黑线时前进
    advance();
  }
  else { //否则任一巡线传感器检测到黑线就后退再左转
    back();
    delay(500);
    turnL();
    delay(300);
  }
}
```

***（5）测试结果：***
当小车行驶过程中检测到黑线立即撤退，然后左转继续行驶。



### 第12课循线智能车

***（1）项目介绍：***

前面我们详细的介绍了画地为牢智能车的实现方法。在这里我们可以结合前面课程中知识制作一个循迹智能车。实验中，我们还是通过循迹传感器检测智能车底部是否存在黑线，然后根据检测结果控制两个电机的转动，从而控制智能车沿着黑线行走。

***（2）流程图：***
循迹智能车具体逻辑如下表格。

![image-20251224152233767](./media/image-20251224152233767-1766563958351-163.png)

![image-20251224152447329](./media/image-20251224152447329-1766563958351-164.png)

按照前面思路设计好智能车后，我们就需要按照设计思路开始制作智能车。我们需要设计对应的接线，测试代码，然后接线上传代码，运行，确保智能车能够实现理想中的功能。

***（3）接线图：***

巡线模块+电机

![image-20251224152608414](./media/image-20251224152608414-1766563958351-165.png)

接线注意：用导线把循迹模块连接到电机驱动扩展板上P1接口的G、V、D11、D7、D8；A、B两电机分别对应的连接到电机驱动扩展板上的接口A和接口B，电源接到BAT接口。

***（4）测试代码：***

```c
int L_pin = 11; //定义左边传感器引脚为D11
int M_pin = 7; //定义中间传感器引脚为D7
int R_pin = 8; //定义右边传感器引脚为D8
int MA = 2; //定义电机A方向控制引脚为D2
int PWMA = 6; //定义电机A速度控制引脚为D6
int MB = 4; //定义电机A方向控制引脚为D4
int PWMB = 5; //定义电机A速度控制引脚为D5
int L_val, M_val, R_val;

void advance() { //小车前进
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}

void back() { //小车后退
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
}

void turnL() { //小车左转
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
}

void turnR() { //小车右转
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}

void stopp() { //小车停止
  analogWrite(PWMA, 0); //电机A速度为0
  analogWrite(PWMB, 0); //电机B速度为0
}
void setup() {
  Serial.begin(9600); //设置波特率为9600
  pinMode(L_pin, INPUT); //循迹传感器引脚都配置为输入模式
  pinMode(M_pin, INPUT);
  pinMode(R_pin, INPUT);
  pinMode(MA, OUTPUT); //配置电机引脚为输出模式
  pinMode(PWMA, OUTPUT);
  pinMode(MB, OUTPUT);
  pinMode(PWMB, OUTPUT);
}
void loop() {
  L_val = digitalRead(L_pin); //读取左边传感器的值
  M_val = digitalRead(M_pin); //读中间传感器的值
  R_val = digitalRead(R_pin); //读取右边传感器的值
  if (M_val == 1) { //中间检测到黑线
    if (L_val == 1 && R_val == 0) { //如果左边检测到黑线，右边没有，左转
      turnL();
    }
    else if (L_val == 0 && R_val == 1) { //否则如果右边检测到黑线，左边没有，右转
      turnR();
    }
    else { //否则前进
      advance();
    }
  }
  else { //中间没检测到黑线
    if (L_val == 1 && R_val == 0) { //如果左边检测到黑线，右边没有，左转
      turnL();
    }
    else if (L_val == 0 && R_val == 1) { //否则如果右边检测到黑线，左边没有，右转
      turnR();
    }
    else { //否则停止
      stopp();
    }
  }
}
```

***（5）测试结果：***
将驱动扩展板堆叠在UNOR3板上，上传好代码，按照接线图接线，将拨码开关拨至ON端后，智能车能够沿着黑线行走。

### 第13课超声波跟随智能车

***（1）项目介绍：***

实验中，我们通过避障传感器检测智能车左右两方是否存在障碍物，检测智能车和前方障碍物的距离，然后根据这三个数据控制两个电机的转动，从而控制智能车的运动状态。

***（2）流程图：***
跟随智能车具体逻辑如下表格。

![image-20251224152913854](./media/image-20251224152913854-1766563958351-166.png)

![image-20251224152932532](./media/image-20251224152932532-1766563958351-167.png)

按照前面思路设计好智能车后，我们就需要按照设计思路开始制作智能车。我们需要设计对应的接线，测试代码，然后接线上传代码，运行，确保智能车能够实现理想中的功能。

***（3）接线图：***

超声波模块+电机+红外避障传感器接线注意：A、B两电机分别对应的连接电机驱动扩展板上的接口A和接口B；超声波传感器模块的V引脚至V，T（Trig）引脚至数字12（S），E（Echo）引脚至数字13（S），G引脚至G；用导线把左边的避障传感器连接到电机驱动扩展板上的接口（G、V、A1），右边的避障传感器连接到接口（G、V、A2），电源接到BAT接口。

![image-20251224153023464](./media/image-20251224153023464-1766563958351-168.png)

***（4）测试代码：***

```c
int trigPin = 12; //定义TRIG引脚接D12
int echoPin = 13; //定义ECHO引脚接D13
int distance;
int MA = 2; //定义电机A方向控制引脚为D2
int PWMA = 6; //定义电机A速度控制引脚为D6
int MB = 4; //定义电机A方向控制引脚为D4
int PWMB = 5; //定义电机A速度控制引脚为D5
int l_sensorPin = A1; //定义左边避障传感器接A1
int r_sensorPin = A2; //定义右边避障传感器接A2
int l_val, r_val;
int get_distance() { //超声波测距函数
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH); //给TRIG引脚至少10us的时间触发
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  distance = pulseIn(echoPin, HIGH) / 58; //检测脉冲宽度，并计算出距离
  delay(50);  //延时50ms
  Serial.print("distance:");  //串口打印出距离
  Serial.print(distance);
  Serial.println("cm");
}

void setup() {
  Serial.begin(9600);  //设置波特率为9600
  pinMode(trigPin, OUTPUT); //定义TRIG为输出模式
  pinMode(echoPin, INPUT); //定义ECHO为输入模式
  pinMode(MA, OUTPUT); //配置电机引脚为输出模式
  pinMode(PWMA, OUTPUT);
  pinMode(MB, OUTPUT);
  pinMode(PWMB, OUTPUT);
  pinMode(l_sensorPin, INPUT);//将l_sensorPin设置为输入
  pinMode(r_sensorPin, INPUT);//将r_sensorPin设置为输入
}

void loop() {
  get_distance();  //调用测距函数
  l_val = digitalRead(l_sensorPin);//读取避障传感器的值
  r_val = digitalRead(r_sensorPin);
  if (distance <= 7 || l_val == 0 && r_val == 0) {
    back();
  }
  else if (distance > 7 && (l_val == 0 && r_val == 1)) {
    turnR();
  }
  else if (distance > 7 && (l_val == 1 && r_val == 0)) {
    turnL();
  }
  else if ((distance > 7 && distance <= 15) && (l_val == 1 && r_val == 1)) {
    stopp();
  }
  else if ((distance > 15 && distance <= 35) && (l_val == 1 && r_val == 1)) {
    advance();
  }
  else if (distance > 35 && (l_val == 1 && r_val == 1)) {
    stopp();
  }
}

void advance() { //小车前进
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}

void back() { //小车后退
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
}

void turnL() { //小车左转
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
}

void turnR() { //小车右转
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}

void stopp() { //小车停止
  analogWrite(PWMA, 0); //电机A速度为0
  analogWrite(PWMB, 0); //电机B速度为0
}
```

好了，桌面迷你蓝牙智能车跟随功能效果的代码全部编写好了，上传程序，看看精彩的效果！（在上传程序代码前，需要把蓝牙模块取下，否则代码会上传失败。需要上传代码成功后，再连接蓝牙模块。）
***（5）测试结果：***
将驱动扩展板堆叠在UNO R3板上，上传好代码，按照接线图接线，将拨码开关拨至ON端后，智能车能够随着前方障碍物的移动而移动。

### 第14课走迷宫智能车

***（1）项目介绍：***
在上课程中，我们制作了一个跟随智能车。实际上，利用同样的电子元件，同样的接线方法，我们只需要更改一个测试代码就可以将跟随智能车变为避障智能车。

***（2）流程图：***
避障智能车具体逻辑如下表格。

![image-20251224153333814](./media/image-20251224153333814-1766561615475-1-1766563958351-169.png)

![image-20251224153350360](./media/image-20251224153350360-1766563958351-170.png)

使用的电子元件，接线方法和课程四一样，更换测试代码，运行，确保智能车能够实现理想中的功能。

***（3）接线图：***

超声波模块+电机+红外避障传感器+蜂鸣器模块

![image-20251224153449589](./media/image-20251224153449589-1766563958351-171.png)

接线注意：A、B两电机分别对应的连接电机驱动扩展板上的接口A和接口B；超声波传感器模块的V引脚至V，T（Trig）引脚至数字12（S），E（Echo）引脚至数字13（S），G引脚至G；用导线把左边的避障传感器连接到电机驱动扩展板上的接口（G、V、A1），右边的避障传感器连接到接口（G、V、A2），电源接到BAT接口。功放模块接到（G，V，D3）

***（4）测试代码***

```c
int l_sensorPin = A1; //定义左边避障传感器接A1
int r_sensorPin = A2; //定义右边避障传感器接A2
int l_val, r_val;

int trigPin = 12;  //定义TRIG引脚接D12
int echoPin = 13;   //定义ECHO引脚接D13
int distance;

int beeppin = 3;  // 蜂鸣器的pin

int MA = 2; //定义电机A方向控制引脚为D2
int PWMA = 6; //定义电机A速度控制引脚为D6
int MB = 4; //定义电机A方向控制引脚为D4
int PWMB = 5; //定义电机A速度控制引脚为D5

void setup ()
{
  Serial.begin(9600);     //测量结果将通过此串口输出至 PC 上的串口监视器
  pinMode(l_sensorPin, INPUT);//将l_sensorPin设置为输入
  pinMode(r_sensorPin, INPUT);//将r_sensorPin设置为输入
  pinMode(echoPin, INPUT);      //设置EchoPin 为输入模式
  pinMode(trigPin, OUTPUT);     //设置超声波数字IO脚模式，OUTPUT为输出
  pinMode(beeppin, OUTPUT);     //设置蜂鸣器引脚输出模式
  pinMode(MA, OUTPUT); //配置电机引脚为输出模式
  pinMode(PWMA, OUTPUT);
  pinMode(MB, OUTPUT);
  pinMode(PWMB, OUTPUT);
}

void loop()
{
  get_distance();  //调用测距函数
  l_val = digitalRead(l_sensorPin);//读取避障传感器的值
  r_val = digitalRead(r_sensorPin);
  if (distance > 10) { //前方距离大于10cm时
    if (l_val == 1 && r_val == 1) { //如果左右两边都没有障碍物，前进
      advance();
    }
    else if (l_val == 0 && r_val == 1) { //否则如果左边有障碍物，右边没有，右转
      turnR();
      buzzer();  //报警
    }
    else if (l_val == 1 && r_val == 0) { //否则如果右边有障碍物，左边没有，左转
      turnL();
      buzzer();  //报警
    }
    else { //否则左右两边都有障碍物，后退再左转
      buzzer();  //报警
      back();
      delay(200);
      turnL();
      delay(200);
    }
  }

  else { //前方距离小于等于10cm时
    buzzer();  //报警
    if (l_val == 1 && r_val == 1) { //如果左右两边都没有障碍物，左转
      turnL();
    }
    else if (l_val == 0 && r_val == 1) { //如果左边有障碍物，右边没有，右转
      turnR();
    }
    else if (l_val == 1 && r_val == 0) { //如果右边有障碍物，左边没有，左转
      turnL();
    }
    else { //否则左右两边都有障碍物，后退再左转
      back();
      delay(200);
      turnL();
      delay(200);
    }
  }
}

void get_distance() {
  digitalWrite(trigPin, LOW);     // 通过Trig/Pin 发送脉冲，触发 HC-SR04 测距，使发出发出超声波信号接口低电平2μs
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);    // 使发出发出超声波信号接口高电平10μs，这里是至少10μs
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);     // 保持发出超声波信号接口低电平
  distance = pulseIn(echoPin, HIGH) / 58; // 读出脉冲时间,将脉冲时间转化为距离（单位：厘米）
  Serial.println(distance);        //输出距离值
}

void buzzer()  //蜂鸣器报警
{
  tone(beeppin, 900);
  delay(100);  //响声间隔100ms 听上去更急促
  noTone(beeppin);
}

void advance() { //小车前进
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}

void back() { //小车后退
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
}

void turnL() { //小车左转
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
}

void turnR() { //小车右转
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}

void stopp() { //小车停止
  analogWrite(PWMA, 0); //电机A速度为0
  analogWrite(PWMB, 0); //电机B速度为0
}
```

***（5）测试结果：***
将驱动扩展板堆叠在UNOR3板上，上传好代码，按照课程三接线图接线，将拨码开关拨至ON端后，智能车能够自动避开障碍物行走。

### 第15课红外遥控智能车

***（1）项目介绍：***
前面的学习中我们详细的介绍了智能车上各个传感器、模块、扩展板的使用方法。在这里我们可以再结合前面课程中知识制作一个红外控制智能车。在传感器项目第四课中，我们已经测试出红外遥控器各个按键对应的键值。实验中，我们可以通过代码设置（键值），让对应的按键控制智能车对应的运动状态。

***（2）流程图：***
循迹智能车具体逻辑如下表格：

![image-20251224153805165](./media/image-20251224153805165-1766563958351-172.png)

![image-20251224153820461](./media/image-20251224153820461-1766563958351-173.png)

按照前面思路设计好智能车后，我们就需要按照设计思路开始制作智能车。我们需要设计对应的接线，测试代码，然后接线上传代码，运行，确保智能车能够实现理想中的功能。

***（3）接线图：电机+红外接收模块***
接线注意：由于红外接收传感器输入的数字信号，将红外接收传感器模块用导线连接到电机驱动扩展板上的G、V、A3，A、B两电机分别对应的连接到堆叠在UNOR3板上的电机驱动扩展板上的接口A和接口B，电源接到BAT接口。

![image-20251224153917724](./media/image-20251224153917724-1766563958351-174.png)

***（4）测试代码：***

```c
#include <IRremote.h>
int RECV_PIN = A3; //定义IO口A3
IRrecv irrecv(RECV_PIN);
decode_results results;//声明一个IRremote库函数独有的变量类型
int IR_val;
int MA = 2; //定义电机A方向控制引脚为D2
int PWMA = 6; //定义电机A速度控制引脚为D6
int MB = 4; //定义电机A方向控制引脚为D4
int PWMB = 5; //定义电机A速度控制引脚为D5


void advance() { //小车前进
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}

void back() { //小车后退
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
}

void turnL() { //小车左转
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
}

void turnR() { //小车右转
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}

void stopp() { //小车停止
  analogWrite(PWMA, 0); //电机A速度为0
  analogWrite(PWMB, 0); //电机B速度为0
}
void setup() {
  Serial.begin(9600);  //设置波特率为9600
  pinMode(MA, OUTPUT); //配置电机引脚为输出模式
  pinMode(PWMA, OUTPUT);
  pinMode(MB, OUTPUT);
  pinMode(PWMB, OUTPUT);
  irrecv.enableIRIn();// 使能红外接收
}
void loop() {
  if (irrecv.decode(&results)) { //是否接收到红外遥控信号
    IR_val = results.value;
    Serial.println(IR_val, HEX); //串口打印数据
    switch (IR_val) {
      case 0xFF629D:  advance();  break;
      case 0xFFA857:  back();  break;
      case 0xFF22DD:  turnL();  break;
      case 0xFFC23D:  turnR();  break;
      case 0xFF02FD:  stopp();  break;
    }
    irrecv.resume();// 接收下个数据
  }
}
```

好了，上传程序，红外遥控器对准红外接收器，按下红外遥控器对应按键，看看效果吧！（注意：在上传测试代码前，需要把蓝牙模块取下，否则测试代码会上传失败。需要上传代码成功后，再连接蓝牙模块。）

***（5）测试结果：***
将驱动扩展板堆叠在UNOR3板上，上传好代码，按照接线图接线，将拨码开关拨至ON端后，我们就能用红外遥控控制智能车运动了。

### 第16课蓝牙遥控智能车

***（1）项目介绍：***
前面课程中，我们利用红外控制智能车运动，在这课程中我们可以做一个蓝牙控制智能车。既然是控制智能车，那就有一个控制端和被控制端。课程中我们把手机当做控制端（主机），HM-10蓝牙模块（从机）连接的智能车当做被控制端。使用时，我们需要在手机上安装一个APP，然后连接HM-10蓝牙模块，然后我们利用蓝牙APP上各个按钮，控制智能车实现各种运动状态。

***（2）流程图：***

先取下蓝牙模块，程序代码上传后，再连接蓝牙模块和打开串口监视器，设置波特率为9600。对准蓝牙模块按下手机APP按钮，我们可以看到APP按钮对应的控制字符，如下图。

![image-20251224154149741](./media/image-20251224154149741-1766563958351-175.png)

经过测试，我们得出了手机APP上各个按钮对应的控制字符和各个按钮对应的功能，这里我们整理了一个表格如下：

![image-20251224154218651](./media/image-20251224154218651-1766563958351-176.png)

![image-20251224154236898](./media/image-20251224154236898-1766563958351-177.png)

![image-20251224154303645](./media/image-20251224154303645-1766563958351-178.png)

![image-20251224154338552](./media/image-20251224154338552-1766563958351-179.png)

***（3）手机APP使用方法：***
1、[安卓系统手机APP点击下载](https://xiazai.keyesrobot.cn/APP/Desktop%20Car.apk)

2、使用数据线复制到手机安装，安装成功，显示图标如下。

![image-20251224154701527](./media/image-20251224154701527-1766563958351-180.png)

3.点击上图图标，进入 APP，显示如下图。

![image-20251224154726472](./media/image-20251224154726472-1766563958351-181.png)

4.REV4板上传代码成功后，连接蓝牙，上电后，蓝牙模块上LED闪烁。点击APP![image-20251224154750129](./media/image-20251224154750129-1766563958351-182.png)

图标，搜索到蓝牙，显示如下图。

![image-20251224154828819](./media/image-20251224154828819-1766563958351-183.png)

5.点击连接，蓝牙连接成功，显示如下图，蓝牙模块上LED变为常亮。

![image-20251224154851237](./media/image-20251224154851237-1766563958351-184.png)

苹果系统手机APP

1.打开App Store。![image-20251224154943951](./media/image-20251224154943951-1766563958351-185.png)

2.点击搜索,搜索 keyestudio,下载搜索到的keyes BT car。

![image-20251224154959070](./media/image-20251224154959070-1766563958351-186.png)

3.打开keyes BT car。

![image-20251224155010876](./media/image-20251224155010876-1766563958351-187.png)

4.开启手机蓝牙，点击左上角的connect按钮，进行蓝牙搜索和连接。

![image-20251224155049737](./media/image-20251224155049737-1766563958351-188.png)

5.点击桌面小车的![image-20251224155420972](./media/image-20251224155420972-1766563958351-189.png)图片按钮，进入控制桌面小车的界面

![image-20251224155435021](./media/image-20251224155435021-1766563958351-190.png)

![image-20251224155445543](./media/image-20251224155445543-1766563958351-191.png)

6.点击右下角的![image-20251224155507653](./media/image-20251224155507653-1766563958351-192.png)图片按钮，进入控制音乐界面。

![image-20251224155517665](./media/image-20251224155517665-1766563958351-193.png)

***（4）接线图：蓝牙+电机***

![image-20251224155601227](./media/image-20251224155601227-1766563958351-194.png)

接线注意：蓝牙模块的RXD、TXD、GND、VCC分别对应的接到电机驱动扩展板上的TX,RX,-(GND),+(VCC),而蓝牙模块的STATE和BRK两引脚不需要接,电源接到BAT接口。
A、B两电机分别对应的连接到电机驱动扩展板上的接口A和接口B；蓝牙模块的RXD、TXD,GND,VCC分别对应的接到电机驱动扩展板上的TX,RX,-(GND),+(VCC),而蓝牙模块的STATE和BRK两引脚不需要接，电源接到BAT接口。

***（5）测试代码：***

```c
int MA = 2; //定义电机A方向控制引脚为D2
int PWMA = 6; //定义电机A速度控制引脚为D6
int MB = 4; //定义电机A方向控制引脚为D4
int PWMB = 5; //定义电机A速度控制引脚为D5
char blue_val;

void setup() {
  Serial.begin(9600);  //设置波特率为9600
  pinMode(MA, OUTPUT); //配置电机引脚为输出模式
  pinMode(PWMA, OUTPUT);
  pinMode(MB, OUTPUT);
  pinMode(PWMB, OUTPUT);
}
void loop() {
  if (Serial.available() > 0) { //接收到蓝牙信号
    blue_val = Serial.read(); //接收到的信号赋给blue_val
    Serial.println(blue_val);  //串口监视器显示蓝牙信号
    switch (blue_val) {
      case  'F':  advance();  break;  //接收到‘F’前进
      case  'B':  back();  break;  //接收到‘B’后退
      case  'L':  turnL();  break;  //接收到‘L’左旋转
      case  'R':  turnR();  break;  //接收到‘R’右旋转
      case  'Q':  turnL1();  break;  //接收到‘L’左转弯
      case  'E':  turnR1();  break;  //接收到‘R’右转弯
      case  'S':  stopp();  break;  //接收到‘S’停止
    }
  }
}
void advance() { //小车前进
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}

void back() { //小车后退
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
}

void turnL() { //小车左旋转
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, 200); //电机B速度为200
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
}
void turnL1() { //小车左转弯
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 100); //电机B速度为100
}
void turnR() { //小车右旋转
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}
void turnR1() { //小车右转弯
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 100); //电机A速度为100
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}
void stopp() { //小车停止
  analogWrite(PWMA, 0); //电机A速度为0
  analogWrite(PWMB, 0); //电机B速度为0
}
```

好了，按住蓝牙APP的前进、后退、左转弯、右转弯、停止、左旋转、右旋转的按钮控制桌面迷你蓝牙智能车分别前进、后退、左转弯、右转弯、停止、左旋转、右旋转的程序代码全编写完了。上传程序，看看效果。（在上传测试代码前，需要把蓝牙模块取下，否则代码会上传失败。需要上传代码成功后，再连接蓝牙模块。）

***（6）测试结果：***
将驱动扩展板堆叠在UNOR3板上，上传好代码，按照接线图接线，将拨码开关拨至ON端后，手机APP连接蓝牙成功后，我们就能用手机APP控制智能车运动了。

![image-20251224155939656](./media/image-20251224155939656-1766563958351-195.png)

### 第17课蓝牙调速智能车

***（1）项目介绍：***
前面课程中，我们利用蓝牙控制智能车，在这课程中我们做一个蓝牙可以控制速度的智能车。既然要控制智能车速度，我们可以将速度定义一个变量speeds来表示。项目中我们只要改变这是变量speeds就可以改变智能车的速度啦。下面让我们通过代码来实现。

***（2）流程图：***
按照前面思路设计好智能车后，我们就需要按照设计思路开始制作智能车。我们需要设
计对应的接线，测试代码，然后接线上传代码，运行，确保智能车能够实现理想中的功能。

***（3）接线图：***

蓝牙+电机

![image-20251224160105242](./media/image-20251224160105242-1766563958351-196.png)

接线跟上一课一样

***（4）测试代码：***

```c
int MA = 2; //定义电机A方向控制引脚为D2
int PWMA = 6; //定义电机A速度控制引脚为D6
int MB = 4; //定义电机A方向控制引脚为D4
int PWMB = 5; //定义电机A速度控制引脚为D5
int speeds = 200; //初始化速度为200
char blue_val;

void setup() {
  Serial.begin(9600); //设置波特率为9600
  pinMode(MA, OUTPUT); //配置电机引脚为输出模式
  pinMode(PWMA, OUTPUT);
  pinMode(MB, OUTPUT);
  pinMode(PWMB, OUTPUT);
}

void loop() {
  if (Serial.available() > 0) { //接收到蓝牙信号
    blue_val = Serial.read(); //接收到的信号赋给blue_val
    Serial.println(blue_val);  //串口监视器显示蓝牙信号
    switch (blue_val) {
      case  'F':  advance();  break;  //接收到‘F’前进
      case  'B':  back();  break;  //接收到‘B’后退
      case  'L':  turnL();  break;  //接收到‘L’左转
      case  'R':  turnR();  break;  //接收到‘R’右转
      case  'Q':  turnL1();  break;  //接收到‘L’左转弯
      case  'E':  turnR1();  break;  //接收到‘R’右转弯
      case  'S':  stopp();  break;  //接收到‘S’停止
      case  'W':  speeds_a();  break;  //接收到‘W’加速
      case  'Z':  speeds_d();  break; //接收到‘Z’减速
    }
  }
}
void advance() { //小车前进
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, speeds); //电机A速度为speeds
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, speeds); //电机B速度为speeds
}

void back() { //小车后退
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, speeds); //电机A速度为speeds
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, speeds); //电机B速度为speeds
}

void turnL() { //小车左旋转
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, speeds); //电机B速度为speeds
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, speeds); //电机A速度为speeds
}
void turnL1() { //小车左转弯
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 100); //电机B速度为100
}
void turnR() { //小车右旋转
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, speeds); //电机A速度为speeds
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, speeds); //电机B速度为speeds
}
void turnR1() { //小车右转弯
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 100); //电机A速度为100
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}
void stopp() { //小车停止
  analogWrite(PWMA, 0); //电机A速度为0
  analogWrite(PWMB, 0); //电机B速度为0
}

void speeds_a() { //增速函数
  int a_flag = 1; //用于while循环
  while (a_flag) {
    Serial.println(speeds);  //显示速度
    if (speeds < 255) { //最大增到255
      speeds++;
      delay(10);  //调节增速的速度
    }
    blue_val = Serial.read();
    if (blue_val == 'S')a_flag = 0; //接收到‘S’停止加速
  }
}
void speeds_d() { //减速函数
  int d_flag = 1; //用于while循环
  while (d_flag) {
    Serial.println(speeds);  //显示速度
    if (speeds > 0) { //最小减到0
      speeds--;
      delay(10);    //调节减速的速度
    }
    blue_val = Serial.read();
    if (blue_val == 'S')d_flag = 0; //接收到‘S’停止减速
  }
}
```

***（5）测试结果：***

![image-20251224160237662](./media/image-20251224160237662-1766563958351-197.png)

### 第18课多功能桌面小车

***（1）项目介绍：***
在前面课程中，我们只是让智能车实现单个功能，那我们能不能把所有功能合在一起呢？能，在这一课程中，我们利用一个代码测试智能车，智能车包含前面课程中讲到的所有功能，我们利用手机蓝牙APP上按钮自动切换各种功能，简单方便。

***（2）流程图：***
按照前面思路设计好智能车后，我们就需要按照设计思路开始制作智能车。我们需要设计对应的接线，测试代码，然后接线上传代码，运行，确保智能车能够实现理想中的功能。

***（3）接线图：***

接线注意：

循迹模块连接到电机驱动扩展板上P1接口的G、V、D11、D7、D8；

超声波传感器模块的VCC引脚连接至连接到电机驱动扩展板上，V引脚至V，T（Trig）引脚至数字12（S），E（Echo）引脚至数字13（S），G引脚至G；

左边的避障传感器连接到电机驱动扩展板上的接口（G、V、A1），

右边的避障传感器连接到接口（G、V、A2）；
红外接收传感器模块用导线连接到电机驱动扩展板上的G、V、A3；A、B两电机分别对应的连接到电机驱动扩展板上的接口A和接口B；
功放模块G、V、S引脚分别对应的连接到电机驱动扩展板上的G（GND）、5V、数字3 (S);

蓝牙模块的RXD、TXD、GND、VCC分别对应的接到电机驱动扩展板上的TX、RX、-（GND）、+（VCC），而蓝牙模块的STATE和BRK两引脚不需要接，电源接到BAT接口

![image-20251224160454151](./media/image-20251224160454151-1766563958351-198.png)

***（4）测试代码：***

```c
#define D0 -1
#define D1 262
#define D2 293
#define D3 329
#define D4 349
#define D5 392
#define D6 440
#define D7 494
#define M1 523
#define M2 586
#define M3 658
#define M4 697
#define M5 783
#define M6 879
#define M7 987
#define H1 1045
#define H2 1171
#define H3 1316
#define H4 1393
#define H5 1563
#define H6 1755
#define H7 1971
//列出全部D调的频率
#define WHOLE 1
#define HALF 0.5
#define QUARTER 0.25
#define EIGHTH 0.25
#define SIXTEENTH 0.625
//列出所有节拍
int tune[] =       //根据简谱列出各频率
{
  D5, D5, D6, D5, M1, D7,
  D5, D5, D6, D5, M2, M1,
  D5, D5, M5, M3, M1, D7, D6,
  M4, M4, M3, M1, M2, M1
};
float durt[] =      //根据简谱列出各节拍
{
  0.5, 0.5, 1, 1, 1, 1 + 1,
  0.5, 0.5, 1, 1, 1, 1 + 1,
  0.5, 0.5, 1, 1, 1, 1, 1,
  0.5, 0.5, 1, 1, 1, 1 + 1
};
#include <IRremoteTank.h>  //导入红外的库
int RECV_PIN = A3; //定义IO口A3
IRrecv irrecv(RECV_PIN);
decode_results results;//声明一个IRremote库函数独有的变量类型
int IR_val;
char blue_val;
int beeppin = 3;  // 蜂鸣器的pin
int length;

int L_pin = 11; //定义左边传感器引脚为D11
int M_pin = 7; //定义中间传感器引脚为D7
int R_pin = 8; //定义右边传感器引脚为D8
int L_val, M_val, R_val;

int MA = 2; //定义电机A方向控制引脚为D2
int PWMA = 6; //定义电机A速度控制引脚为D6
int MB = 4; //定义电机A方向控制引脚为D4
int PWMB = 5; //定义电机A速度控制引脚为D5
int speeds = 200; //初始化速度为200

int l_sensorPin = A1; //左边避障传感器接A1
int r_sensorPin = A2; //右边避障传感器接A2
int l_val, r_val;

int trigPin = 12; //TRIG引脚接D12
int echoPin = 13; //ECHO引脚接D13
int distance;

void setup() {
  Serial.begin(9600); //设置波特率为9600
  pinMode(beeppin, OUTPUT);     //设置蜂鸣器引脚输出模式
  length = sizeof(tune) / sizeof(tune[0]); //计算长度
  pinMode(L_pin, INPUT); //循迹传感器引脚都配置为输入模式
  pinMode(M_pin, INPUT);
  pinMode(R_pin, INPUT);
  pinMode(l_sensorPin, INPUT);//将l_sensorPin设置为输入
  pinMode(r_sensorPin, INPUT);//将r_sensorPin设置为输入
  pinMode(trigPin, OUTPUT); //定义TRIG为输出模式
  pinMode(echoPin, INPUT); //定义ECHO为输入模式
  pinMode(MA, OUTPUT); //配置电机引脚为输出模式
  pinMode(PWMA, OUTPUT);
  pinMode(MB, OUTPUT);
  pinMode(PWMB, OUTPUT);
  irrecv.enableIRIn();// 使能红外接收
}

void loop() {
  if (Serial.available() > 0) { //接收到蓝牙信号
    blue_val = Serial.read(); //接收到的信号赋给blue_val
    Serial.println(blue_val);  //串口监视器显示蓝牙信号
    switch (blue_val) {
      case  'F':  advance();  break;  //接收到‘F’前进
      case  'B':  back();  break;  //接收到‘B’后退
      case  'L':  turnL();  break;  //接收到‘L’左旋
      case  'Q':  turnL1();  break;  //接收到‘Q’左转
      case  'R':  turnR();  break;  //接收到‘R’右旋
      case  'E':  turnR1();  break;  //接收到‘E’右转
      case  'S':  stopp(); noTone(beeppin); break;  //接收到‘S’电机停止转动，功放停止
      case  'W':  speeds_a();  break;  //接收到‘W’加速
      case  'Z':  speeds_d();  break; //接收到‘Z’减速
      case  'U':  follow();  break; //接收到‘U’，进入跟随模式
      case  'Y':  avoid();  break;  //接收到‘Y’，进入避障模式
      case  'G':  prison(); break;  //接收到‘G’，画地为牢模式
      case  'X':  track();  break;  //接收到‘X’，巡黑线模式
      case  '1':  tone(beeppin, D1); break; //接收到'1',播放音符DO
      case  '2':  tone(beeppin, D2); break; //接收到'2',播放音符Re
      case  '3':  tone(beeppin, D3); break; //接收到'3',播放音符Mi
      case  '4':  tone(beeppin, D4); break; //接收到'4',播放音符Fa
      case  '5':  tone(beeppin, D5); break; //接收到'5',播放音符So
      case  '6':  tone(beeppin, D6); break; //接收到'6',播放音符La
      case  '7':  tone(beeppin, D7); break; //接收到'7',播放音符Si
      case  '8':  tone(beeppin, M1); break; //接收到'8',播放音符Do
      case  '9':  play(); break; //接收到'9',播放音乐
    }
  }
  if (irrecv.decode(&results)) { //是否接收到红外遥控信号
    IR_val = results.value;
    Serial.println(IR_val, HEX); //串口打印数据
    switch (IR_val) {
      case 0xFF629D:  advance();  break;  //前进
      case 0xFFA857:  back();  break;  //后退
      case 0xFF22DD:  turnL();  break;  //左转
      case 0xFFC23D:  turnR();  break;  //右转
      case 0xFF02FD:  stopp();  break;  //停止
    }
    irrecv.resume();// 接收下个数据
  }
}

void advance() { //小车前进
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, speeds); //电机A速度为speeds
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, speeds); //电机B速度为speeds
}

void back() { //小车后退
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, speeds); //电机A速度为speeds
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, speeds); //电机B速度为speeds
}

void turnL() { //小车左旋转
  digitalWrite(MB, LOW); //电机B反转
  analogWrite(PWMB, speeds); //电机B速度为speeds
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, speeds); //电机A速度为speeds
}
void turnL1() { //小车左转弯
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 200); //电机A速度为200
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 100); //电机B速度为100
}
void turnR() { //小车右旋转
  digitalWrite(MA, LOW); //电机A反转
  analogWrite(PWMA, speeds); //电机A速度为speeds
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, speeds); //电机B速度为speeds
}
void turnR1() { //小车右转弯
  digitalWrite(MA, HIGH); //电机A正转
  analogWrite(PWMA, 100); //电机A速度为100
  digitalWrite(MB, HIGH); //电机B正转
  analogWrite(PWMB, 200); //电机B速度为200
}
void stopp() { //小车停止
  analogWrite(PWMA, 0); //电机A速度为0
  analogWrite(PWMB, 0); //电机B速度为0
}

void speeds_a() { //增速函数
  int a_flag = 1; //用于while循环
  while (a_flag) {
    Serial.println(speeds);  //显示速度
    if (speeds < 255) { //最大增到255
      speeds++;
      delay(10);  //调节增速的速度
    }
    blue_val = Serial.read();
    if (blue_val == 'S')a_flag = 0; //接收到‘S’停止加速
  }
}

void speeds_d() { //减速函数
  int d_flag = 1; //用于while循环
  while (d_flag) {
    Serial.println(speeds);  //显示速度
    if (speeds > 0) { //最小减到0
      speeds--;
      delay(10);    //调节减速的速度
    }
    blue_val = Serial.read();
    if (blue_val == 'S')d_flag = 0; //接收到‘S’停止减速
  }
}

int get_distance() { //超声波测距函数
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH); //给TRIG引脚至少10us的时间触发
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  distance = pulseIn(echoPin, HIGH) / 58; //检测脉冲宽度，并计算出距离
  Serial.print("distance:");  //串口打印出距离
  Serial.print(distance);
  Serial.println("cm");
}

void buzzer()  //蜂鸣器报警
{
  tone(beeppin, 900);
  delay(100);  //响声间隔100ms 听上去更急促
  noTone(beeppin);
  delay(100);
}

void follow() {
  int follow_flag = 1;
  while (follow_flag) {
    get_distance();  //调用测距函数
    l_val = digitalRead(l_sensorPin);//读取避障传感器的值
    r_val = digitalRead(r_sensorPin);
    if (distance <= 5 || l_val == 0 && r_val == 0) {
      back();
    }
    else if (distance > 5 && (l_val == 0 && r_val == 1)) {
      turnR();
    }
    else if (distance > 5 && (l_val == 1 && r_val == 0)) {
      turnL();
    }
    else if ((distance > 5 && distance <= 15) && (l_val == 1 && r_val == 1)) {
      stopp();
    }
    else if ((distance > 15 && distance <= 35) && (l_val == 1 && r_val == 1)) {
      advance();
    }
    else if (distance > 35 && (l_val == 1 && r_val == 1)) {
      stopp();
    }
    blue_val = Serial.read();
    if (blue_val == 'S') { //接收到‘S’退出循环，小车停止
      follow_flag = 0;
      stopp();
    }
  }
}

void avoid() {
  int avoid_flag = 1;
  while (avoid_flag) {
    get_distance();  //调用测距函数
    l_val = digitalRead(l_sensorPin);//读取避障传感器的值
    r_val = digitalRead(r_sensorPin);
    if (distance > 10) { //前方距离大于10cm时
      if (l_val == 1 && r_val == 1) { //如果左右两边都没有障碍物，前进
        advance();
      }
      else if (l_val == 0 && r_val == 1) { //否则如果左边有障碍物，右边没有，右转
        buzzer();  //报警
        turnR();
      }
      else if (l_val == 1 && r_val == 0) { //否则如果右边有障碍物，左边没有，左转
        buzzer();  //报警
        turnL();
      }
      else { //否则左右两边都有障碍物，后退再左转
        buzzer();  //报警
        back();
        delay(200);
        turnL();
        delay(200);
      }
    }

    else { //前方距离小于等于10cm时
      buzzer();  //报警
      if (l_val == 1 && r_val == 1) { //如果左右两边都没有障碍物，左转
        turnL();
      }
      else if (l_val == 0 && r_val == 1) { //如果左边有障碍物，右边没有，右转
        turnR();
      }
      else if (l_val == 1 && r_val == 0) { //如果右边有障碍物，左边没有，左转
        turnL();
      }
      else { //否则左右两边都有障碍物，后退再左转
        back();
        delay(200);
        turnL();
        delay(200);
      }
    }
    blue_val = Serial.read();
    if (blue_val == 'S') { //接收到‘S’退出循环，小车停止
      avoid_flag = 0;
      stopp();
    }
  }
}

void prison() {
  int prison_flag = 1;
  while (prison_flag) {
    L_val = digitalRead(L_pin); //读取左边传感器的值
    M_val = digitalRead(M_pin); //读中间传感器的值
    R_val = digitalRead(R_pin); //读取右边传感器的值
    if ( L_val == 0 && M_val == 0 && R_val == 0 ) { //当没有检测到黑线时前进
      advance();
    }
    else { //否则任一巡线传感器检测到黑线就后退再左转
      back();
      delay(500);
      turnL();
      delay(300);
    }
    blue_val = Serial.read();
    if (blue_val == 'S') { //接收到‘S’退出循环，小车停止
      prison_flag = 0;
      stopp();
    }
  }
}

void track() {
  int track_flag = 1;
  while (track_flag) {
    L_val = digitalRead(L_pin); //读取左边传感器的值
    M_val = digitalRead(M_pin); //读中间传感器的值
    R_val = digitalRead(R_pin); //读取右边传感器的值
    if (M_val == 1) { //中间检测到黑线
      if (L_val == 1 && R_val == 0) { //如果左边检测到黑线，右边没有，左转
        turnL();
      }
      else if (L_val == 0 && R_val == 1) { //否则如果右边检测到黑线，左边没有，右转
        turnR();
      }
      else { //否则前进
        advance();
      }
    }
    else { //中间没检测到黑线
      if (L_val == 1 && R_val == 0) { //如果左边检测到黑线，右边没有，左转
        turnL();
      }
      else if (L_val == 0 && R_val == 1) { //否则如果右边检测到黑线，左边没有，右转
        turnR();
      }
      else { //否则停止
        stopp();
      }
    }
    blue_val = Serial.read();
    if (blue_val == 'S') { //接收到‘S’退出循环，小车停止
      track_flag = 0;
      stopp();
    }
  }
}

void play() {
  for (int x = 0; x < length; x++)
  {
    tone(beeppin, tune[x]);
    delay(500 * durt[x]); //这里用来根据节拍调节延时，500这个指数可以自己调整，在该音乐中，我发现用500比较合适。
    noTone(beeppin);
    blue_val = Serial.read(); //再次接收
    if (blue_val == '9')break; //再次按下播放音乐键，停止播放
  }
}
```

好了，蓝牙多功能控制智能车的程序都已经编写好了，上传程序，实际操作下看看效果。（在上传程序代码前，需要把蓝牙模块取下，否则代码会上传失败。需要上传代码成功后，再连接蓝牙模块。）

***（5）测试结果：***
将驱动扩展板堆叠在UNOR3板上，上传好代码，按照接线图接线，将拨码开关拨至ON端后，手机APP连接蓝牙成功后，我们就能用手机APP控制智能车运动了。我们可以通过按下对应按钮实现对应功能，通过停止钮来停止功能。

注意：利用安卓系统手机APP点击![image-20251224160705196](./media/image-20251224160705196-1766563958351-199.png)，测试语音控制时，不能实现语音控制功能。

## 8、常见问题解答

（1）小车无反应答：1.请检查电池电量是否充足。
                                     2.请检查接线是否正确。
（2）电脑识别不了USB端口  

​                               答：1.请确保已参考第6章的第3小节安装了CP2102驱动程序。
​                                      2.请检查USB线是否良好。

（3）无法上传代码
           答：1.请尝试是否可以单独使用UNOR3主板进行烧录（拔掉外围传感器，排除外部干扰）。
                  2.请确保在上传代码期间蓝牙模块没有插在扩展板上，因为蓝牙模块使用的是V4.0主板的RX\TX引脚，而上传代码时也需要使       用的UNOR3主板的RX\TX引脚，如果在上传代码期间扩展板上接着蓝牙模块会影响上传代码。                  
（4）蓝牙遥控有干扰
答：小车右侧的避障传感器在感应到障碍物时会形成红外线反射，从而影响到红外接收传感器的数值。
