### 第15课 语音动感世界

#### 15.1 项目介绍

在前面的教程中，已经了解过SK6812模块、智能语音模块和无源蜂鸣器的工作原理和应用。

在本项目中，使用智能语音模块来控制SK6812RGB灯模块和无源蜂鸣器，实现自动化语音控制多彩灯光及蜂鸣器发出鸣叫声与播放美妙音乐。


#### 15.2 实验组件

|![](media/esp32.png)|![](media/SU-03T-1.png)|![](media/SK6812RGB.png)|![](media/4p.png)|
|-|-|-|-|
|ESP32 Plus主板 *1|智能语音模块 *1|SK6812RGB灯 *1|4P线 *1 |
|![](media/buzzer.png)|![](media/3p.png)|![](media/usb.png)| |
|无源蜂鸣器模块 *1|3P线 *2|USB线 *1| |

#### 15.3 模块接线图

智能语音模块、无源蜂鸣器和SK6812RGB灯模块的控制引脚：

|SK6812RGB灯（S引脚）|io26|
|-|-|
|无源蜂鸣器（S引脚）|io25|
|智能语音模块（TXD引脚）|io16|
|智能语音模块（RXD引脚）|io27|

⚠️ **特别注意：智能家居已经组装好了，这里不需要把智能语音模块、无源蜂鸣器和SK6812RGB灯模块拆下来又重新组装和接线，这里再次提供接线图，是为了方便您编写代码！**

![](media/pjt17.png)

#### 15.4 代码流程图

![](media/project15.png)

#### 15.5 实验代码

```c++
/*
 * 项目: voice_control_sk6812_buzzer
 * 描述: 智能语音模块控制SK6812和蜂鸣器来模拟语音智能多彩世界
 * 编译IDE：ARDUINO IDE
 * 作者: http//www.keyes-robot.com
*/

//导入相关库文件
#include <SoftwareSerial.h>
#include <BuzzerESP32.h> 
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
 #include <avr/power.h> // 需要 16 MHz Adafruit Trinket
#endif

// 定义引脚常量
const int RX_PIN = 27;   // 引脚 GPIO27 为 RX
const int TX_PIN = 16;   // 引脚 GPIO16 为 TX
const int LED_PIN = 26;  // NeoPixel数据引脚
const int LED_COUNT = 4; // 新像素数

BuzzerESP32 buzzer(25); // 初始化GPIO25的蜂鸣器

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

SoftwareSerial mySerial(RX_PIN, TX_PIN); // 定义软件串口引脚（RX, TX）

void setup() {
  Serial.begin(9600); // 硬件串口（与电脑通信）
  mySerial.begin(9600); // 软件串口（与外设通信）

  #if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
    clock_prescale_set(clock_div_1);
  #endif

  strip.begin();           // 初始化新像素条
  strip.show();            // 关闭所有像素
  strip.setBrightness(50); // 设置亮度（最大255）
}

void loop() { 
  if (mySerial.available()) { // 接收语音控制模块的外设数据
    int yuyin = mySerial.read(); // 将接收到的外设数据进行赋值   
    switch(yuyin) { //进行判断
      case 11: birthday(); break;  // 接收到的数据为11,播放音乐
      case 12: buzzer.playTone(0, 0); break;  // 接收到的数据为12,关闭蜂鸣器
      case 13: colorWipe(strip.Color(255, 0, 0), 50); break;  // 接收到的数据为13,打开红灯   
      case 14: colorWipe(strip.Color(0, 0, 0), 50); break;   // 接收到的数据为14,关闭红灯
      case 15: colorWipe(strip.Color(0, 255, 0), 50); break; // 接收到的数据为15,打开绿灯
      case 16: colorWipe(strip.Color(0, 0, 0), 50); break;   // 接收到的数据为16,关闭绿灯
      case 17: colorWipe(strip.Color(0, 0, 255), 50); break; // 接收到的数据为17,打开蓝灯
      case 18: colorWipe(strip.Color(0, 0, 0), 50); break;   // 接收到的数据为18,关闭蓝灯
      case 36: theaterChaseRainbow(50); break; // 接收到的数据为36,打开彩灯,彩虹增强型追逐型
      case 37: colorWipe(strip.Color(0, 0, 0), 50); break;   // 接收到的数据为37,关闭彩灯
      case 38: buzzer.playTone(784, 250);  break;   // 接收到的数据为37,打开蜂鸣器
      case 39: buzzer.playTone(0, 0); break;   // 接收到的数据为37,关闭蜂鸣器
    }
    mySerial.println(yuyin);
    Serial.println(yuyin);
  }
}

// 用一种颜色填充灯带
void colorWipe(uint32_t color, int wait) {
  for(int i=0; i<strip.numPixels(); i++) { 
    strip.setPixelColor(i, color); // 设置像素颜色
    strip.show();                  // 更新灯带
    delay(wait);                   // 暂停
  }
}

// 彩虹增强剧院帐篷。在帧之间传递延迟时间（毫秒）。
void theaterChaseRainbow(int wait) {
  int firstPixelHue = 0;     // 第一个像素以红色开始（色调0）
  for(int a=0; a<30; a++) {  // 重复30次...
    for(int b=0; b<3; b++) { //  ‘b’从0到2...
      strip.clear();         //  将RAM中的所有像素设置为0（关闭）
      // “c”从“b”开始计数，以3为增量到条带的末尾…
      for(int c=b; c<strip.numPixels(); c += 3) {
        // 像素‘c’的色调被偏移一定的量，
        // 使色轮沿着条带的长度（strip. numpixels（）步骤）完整旋转一次（范围65536）：
        int hue = firstPixelHue + c * 65536L / strip.numPixels();
        uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
        strip.setPixelColor(c, color); // 设置像素c的值为color
      }
      strip.show();                // 用新内容更新条带
      delay(wait);                 // 暂停一会儿
      firstPixelHue += 65536 / 90; // 一个周期的色轮超过90帧
    }
  }
}

void birthday() {
  // 演奏生日旋律 - 参数为（频率，持续时间）
  buzzer.playTone(294, 250);  // D4
  buzzer.playTone(440, 250);  // A4
  buzzer.playTone(392, 250);  // G4
  buzzer.playTone(532, 250);  // C5  
  buzzer.playTone(494, 250);  // B4
  buzzer.playTone(392, 250);  // G4
  buzzer.playTone(440, 250);  // A4
  buzzer.playTone(392, 250);  // G4
  buzzer.playTone(587, 250);  // D5
  buzzer.playTone(532, 250);  // C5  
  buzzer.playTone(392, 250);  // G4
  buzzer.playTone(784, 250);  // G5
  buzzer.playTone(659, 250);  // E5
  buzzer.playTone(532, 250);  // C5  
  buzzer.playTone(494, 250);  // B4
  buzzer.playTone(440, 250);  // A4
  buzzer.playTone(698, 250);  // F5
  buzzer.playTone(659, 250);  // E5
  buzzer.playTone(532, 250);  // C5  
  buzzer.playTone(587, 250);  // D5
  buzzer.playTone(532, 500);  // C5   - 长时
  buzzer.playTone(0, 0);      // 关闭蜂鸣器
}
```

#### 15.6 实验结果

按照接线图接好线，外接电源，选择好正确的开发板板型（ESP32 Dev Module）和 适当的串口端口（COMxx），然后单击按钮![](media/cou0.png)上传代码。上传代码成功后，通过智能语音模块来控制无源蜂鸣器和SK6812RGB灯。

对着智能语音模块上的麦克风，使用唤醒词 “你好，小智” 或 “小智小智” 来唤醒智能语音模块，同时喇叭播放回复语 “有什么可以帮到您”；

智能语音模块唤醒后，对着麦克风说：“播放音乐” 等命令词时，喇叭播放对应的回复语 “已为您播放音乐”，同时蜂鸣器播放音乐；

对着麦克风说：“关闭音乐” 等命令词时，喇叭播放对应的回复语 “已为您停止音乐”，同时蜂鸣器停止播放音乐；

对着麦克风说：“打开红灯” 等命令词时，喇叭播放对应的回复语 “已为您打开红灯”，同时SK6812灯亮红色灯；

对着麦克风说：“关闭红灯” 等命令词时，喇叭播放对应的回复语 “已为您关闭红灯”，同时SK6812灯熄灭；

对着麦克风说：“打开绿灯” 等命令词时，喇叭播放对应的回复语 “已为您打开绿灯”，同时SK6812灯亮绿色灯；

对着麦克风说：“关闭绿灯” 等命令词时，喇叭播放对应的回复语 “已为您关闭绿灯”，同时SK6812灯熄灭；

对着麦克风说：“打开蓝灯” 等命令词时，喇叭播放对应的回复语 “已为您打开蓝灯”，同时SK6812灯亮蓝色灯；

对着麦克风说：“关闭蓝灯” 等命令词时，喇叭播放对应的回复语 “已为您关闭蓝灯”，同时SK6812灯熄灭；

对着麦克风说：“打开彩灯” 等命令词时，喇叭播放对应的回复语 “已为您打开彩灯”，同时SK6812灯亮彩色灯；

对着麦克风说：“关闭彩灯” 等命令词时，喇叭播放对应的回复语 “已为您关闭彩灯”，同时SK6812灯熄灭；

对着麦克风说：“打开蜂鸣器” 或 “蜂鸣器开始鸣叫”等命令词时，喇叭播放对应的回复语 “已打开蜂鸣器”，同时蜂鸣器开始鸣叫；

对着麦克风说：“关闭蜂鸣器” 或 “蜂鸣器停止鸣叫” 等命令词时，喇叭播放对应的回复语 “已关闭蜂鸣器”，同时蜂鸣器停止鸣叫。

![Img](../../media/image-15.gif)