### 第22课 手机APP控制智能家居

#### 22.1 项目介绍

大部分人都有手机，现在大部分物联网产品的控制端都是用手机，使用起来就很便捷，打开手机APP，点击一下就能启动各种设备。

物联网智能家居，将智能家居通过WiFi连接家庭WiFi，用于操作的手机也要连接同一个WiFi，当然也可以是手机打开热点，智能家居连接手机的热点。连接成功后，LCD1602显示IP地址，然后在手机APP上输入对应的IP进行通讯。实现APP控制智能家居的各个功能。

#### 22.2 APP下载安装

⚠️ **<span style="color: rgb(255, 76, 65);">特别提醒：</span>如果前面已经下载安装了APP，则这一步骤可以直接跳过。**

**步骤1：** 在手机/平板浏览器的搜索框中输入官网链接： www.keyes-robot.com

![](media/wyx1.png)

**步骤2：** 找到 “**资料中心**”，并且点击它。

![](media/wyx2.png)

**步骤3：** 在页面找到 “**APP下载**”选项，并且点击它。

![](media/wyx3.png)

**步骤4：** 在 “**APP下载**” 页面，找到 “**keyes IoT home**”。

![](media/wyx4.png)

**步骤5：** 根据自己的手机/平板系统选择对应的APP下载安装。选择如下：

![](media/wyx7.png)

**安卓系统**

a\. 点击 "**点击下载**" 按钮，下载对应的 "**keyes IoT home.apk**" 文件。

![](media/wyx5.png)

b\. 按照安装提示进行安装。

![](media/waxy11.jpg)

![](media/wxyx11.jpg)

c\. 下载安装后，单击![](media/IoT-home.jpeg) 打开，出现如下图界面。

![](media/IOT-home1.png)

选择WIFI。

![](media/IOT-home2.png)

![](media/IOT-home3.jpg)


**苹果系统**

a\. 点击 "**跳转APP Store**" 按钮，跳转到 APP Store ![](media/APP-Store.png)


![](media/wyx6.png)


b\. 在 APP Store 上搜索 **keyes IoT home** ，选择 **keyes IoT home** ，然后点击 ![](media/xiazai.png) 获取，下载安装APP即可。

![](media/WASQ11.png)

c\. 下载安装后, 单击![](media/IoT-home.jpeg) 打开，出现如下图界面。

![](media/IOT-home1.png)

选择WIFI

![](media/IOT-home2.png)

![](media/IOT-home3.jpg)

#### 22.3 APP使用说明

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

|按钮:![](media/WIFI-mode.png)|功能：选择WIFI模式| |
|-|-|-|
|按钮:![](media/connect.png)|功能：连接| |
|按钮:![](media/led1.png)|控制字符：点一下发送 “a”； 再点一下发送“A”。|功能：点一下，开启LED灯；再点一下，关闭LED灯。|
|按钮:![](media/window.png)|控制字符：点一下发送 “b”； 再点一下发送“B”。|功能：点一下，开启窗户； 再点一下，关闭窗户。|
|按钮:![](media/music.png)|控制字符：点一下发送 “c”。|功能：点一下，播放一首歌曲。|
|按钮:![](media/whistle.png)|控制字符：点一下发送 “d”。|功能：点一下，蜂鸣器“哔”响一声。|
|按钮:![](media/door.png)|控制字符：点一下发送 “e”； 再点一下发送“E”。|功能：点一下，开启门； 再点一下，关闭门。|
|按钮:![](media/fan.png)|控制字符：点一下发送 “f”； 再点一下发送“F”。|功能：点一下，开启风扇； 再点一下，关闭风扇。|
|按钮:![](media/raindrop.png)| |功能：显示区显示雨滴值。|
|按钮:![](media/humful-gas.png)| |功能：显示区显示危险气体状态safe或dangerous。|
|按钮:![](media/anybody.png)| |功能：显示区显示人体检测状态someone或no one。|
|按钮:![](media/temper.png)| |功能：显示区显示温度数值 |
|按钮:![](media/humidy.png)| |功能：显示区显示湿度数值 |
|按钮:![](media/sfx1.png)|控制字符：点一下发送 “o”； 再点一下发送“O”。|功能：点一下，RGB灯开启模式一氛围灯；再点一下，关闭RGB灯。|
|按钮:![](media/sfx2.png)|控制字符：点一下发送 “p”； 再点一下发送“P”。|功能：点一下，RGB灯开启模式二跑马灯；再点一下，关闭RGB灯。|
|按钮:![](media/rgb.png)|控制字符：点一下发送 “g”； 再点一下发送“G”。以此类推。|功能：点一下，RGB灯开启红色；再点一下，关闭RGB灯。以此类推。|

#### 22.4 实验组件

|![](media/esp32.png)|![](media/xht11.png)|![](media/yellow-led2.png)|![](media/SK6812RGB.png)|
|-|-|-|-|
|ESP32 Plus主板 *1|XHT11传感器 *1|黄色LED模块 *1|SK6812RGB灯模块 *1|
|![](media/buzzer.png)|![](media/motor.png)|![](media/servo.png)|![](media/stem.png)|
|无源蜂鸣器模块 *1|130电机模块 *1|180度舵机 *2|水滴传感器模块 *1|
|![](media/pir.png)|![](media/gas.png)|![](media/fan2.png)|![](media/lcd2.png)|
|人体红外热释传感器 *1|MQ2传感器 *1|风扇叶 *1|I2C LCD1602模块 * 1|
|![](media/usb.png)|![](media/4p.png)|![](media/3p.png)| |
|USB线 *1|4P线 *3|3P线 *6| |

#### 22.5 模块接线图 

⚠️ **特别注意：智能家居已经组装好了，这里不需要把所有的传感器/模块拆下来又重新组装和接线，这里再次提供接线图，是为了方便您编写代码！**

|传感器模块名称|传感器模块引脚|ESP32 Plus主板对应的接线|
|-|-|-|
|人体红外热释传感器模块|G/V/S|G/V/io14|
|无源蜂鸣器模块|G/V/S|G/V/io25|
|黄色LED模块|G/V/S|G/V/io12|
|电机模块|GND/VCC/IN+/IN-|G/V/io19/io18|
|控制门的舵机1|棕色线/红色线/橙色线|G/V/io13|
|控制窗的舵机2|棕色线/红色线/橙色线|G/V/io5|
|MQ-2气体传感器模块|GND/VCC/D|G/V/io23|
|XHT11模块|G/V/S|G/V/io17|
|SK6812RGB灯模块|G/V/S|G/V/io26|
|LCD1602显示屏模块|GND/VCC/SDA/SCL|GND/V/SDA/SCL|
|水滴传感器模块|G/V/S|G/V/io34|

#### 22.6 APP控制LED和风扇的实验代码1

使用APP控制智能家居的LED灯和风扇的开关。

⚠️ <span style="color: rgb(255, 76, 65);">**特别提醒：**</span> 打开代码文件后，需要修改ESP32开发板需要连接的WiFi名称与密码，您需要分别将 `ChinaNet-2.4G-0DF0` 和 `ChinaNet@233` 替换为您自己的 Wi-Fi 名称和 WiFi 密码。WiFi名称和WiFi密码修改后才能上传代码，否则你的ESP32开发板将无法连接网络。

```c
const char* ssid = "ChinaNet-2.4G-0DF0";  //输入你自己的WiFi名称
const char* pwd = "ChinaNet@233"; //输入你自己的WiFi密码
```

⚠️ **<span style="color: rgb(255, 76, 65);">注意：</span> 请确保代码中的WiFi名称和WiFi密码与连接到您的计算机、手机/平板电脑、ESP32开发板和路由器的网络相同，它们必须在同一局域网（WiFi）内。**

⚠️ **<span style="color: rgb(255, 76, 65);">注意：</span>WiFi必须是2.4Ghz频段的WiFi，否则ESP32无法连接WiFi，不支持连接5GHz频段的WiFi**。

```c
/*  
 * 项目: app_test
 * 描述: 测试APP,APP控制LED与风扇
 * 编译IDE：ARDUINO IDE
 * 作者: http//www.keyes-robot.com
*/
#include <Arduino.h>
#ifdef ESP32
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#endif

#include <LiquidCrystal_I2C.h>

#define fanPin1 19 //IN+ 引脚
#define fanPin2 18 //IN- 引脚
#define led_y 12  //定义黄色引脚为12

const char* ssid = "ChinaNet-2.4G-0DF0";  // 输入你自己的WiFi名称
const char* pwd = "ChinaNet@233";  // 输入你自己的WiFi密码

#include <Wire.h>
//初始化LCD地址、列和行
LiquidCrystal_I2C lcd(0x27, 16, 2);

WiFiServer server(80);  //初始化WiFi服务

//将变量定义为检测到的值
String request;

unsigned long prevTask = 0;

void setup() {
  Serial.begin(9600);
  //连接wifi
  WiFi.begin(ssid, pwd);
  //判断是否连接
  Serial.println("Connecting to WiFi...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  delay(1000);
  //串行监视器将显示无线网络的名称和IP地址
  Serial.println("Connected to WiFi");
  Serial.print("WiFi NAME:");
  Serial.println(ssid);
  Serial.print("IP:");
  Serial.println(WiFi.localIP());

  //初始化 LCD
  lcd.init();
  //打开LCD背光
  lcd.backlight();
  //lcd.noBacklight();
  lcd.clear();
  //设置光标的位置
  lcd.setCursor(0, 0);
  //LCD打印
  lcd.print("IP:");
  //设置光标的位置
  lcd.setCursor(0, 1);
  //LCD打印
  lcd.print(WiFi.localIP());

  //设置引脚模式
  pinMode(led_y, OUTPUT);
  pinMode(fanPin1, OUTPUT);
  pinMode(fanPin2, OUTPUT);
  //启动服务
  server.begin();
}

void loop() {
  //检查客户端与网络服务器是否已连接
  //当客户端与服务器建立连接时，“server.available（）”函数返回一个用于客户端通信的WiFiClient对象。
  WiFiClient client = server.available();
  if (client) {
    Serial.println("New client connected");
    while (client.connected()) {
      //确定服务器是否发送数据
      if (client.available()) {
        request = client.readStringUntil('s');
        Serial.print("Received message: ");
        Serial.println(request);
      }

      //LED
      if (request == "a") {
        digitalWrite(led_y, HIGH);
      } else if (request == "A") {
        digitalWrite(led_y, LOW);
      }

      //fan
      if (request == "f") {
        digitalWrite(fanPin1, LOW); //pwm = 0
        analogWrite(fanPin2, 100); //LEDC通道5绑定到指定的左电机输出PWM值为100.
      } else if (request == "F") {
        digitalWrite(fanPin1, LOW); //pwm = 0
        analogWrite(fanPin2, 0); //LEDC通道5绑定到指定的左电机输出PWM值为0.
      }

      request = "";
    }
    Serial.println("Client disconnected");
  }
}
```

#### 22.7 操作步骤及实验结果1

按照接线图接好线，外接电源，选择好正确的开发板板型（ESP32 Dev Module）和 适当的串口端口（COMxx），然后单击按钮 ![](media/cou0.png) 上传示例代码至ESP32主控板。示例代码上传成功后，上电后，点击 ![](media/Serial-Port-Monitor.png) 打开串口监视器，设置波特率为9600。

⚠️ <span style="color: rgb(255, 76, 65);">**注意：手机/平板需要和智能家居连接同一个WiFi，或者手机/平板打开热点，智能家居连接手机/平板的热点，且示例代码中的WiFi名称与WiFi密码和手机/平板、智能家居是同一个WiFi名称与密码。** </span>

⚠️ **<span style="color: rgb(255, 76, 65);">注意：</span>WiFi必须是2.4Ghz频率的，否则ESP32无法连接WiFi，不支持连接5GHz频段的WiFi。**。

⚠️ **<span style="color: rgb(255, 76, 65);">特别注意：</span> 手机或平板一定要与ESP32开发板连接的是同一个WiFi，否则将无法进入控制页面。还有就是ESP32开发板在使用WiFi功能时功耗很大，需要外接DC电源(电源电压必须充足，最好使用新电池)才能满足它的工作电力需求，如果达不到它的工作电力需求ESP32板将会一直复位导致代码无法正常运行。**

如果成功连接上WiFi，串口监视器会打印出分配到的IP地址（假如串口监视器没打印出分配到的IP地址，可以按下ESP32主控板上的复位键重启，重新连接WiFi）。不同的网络（WiFi），IP地址是不一样。

![](media/WSA01.png)

同时，LCD1602显示屏也同步显示对应的IP地址。

![](media/qxe12.jpg)

⚠️ **<span style="color: rgb(255, 76, 65);">特别注意：</span>** 在本例中，您可以在APP页面中输入你自己的**ESP32 IP地址**(**这里是以 192.168.3.57 为例，而你需要将本例APP中的IP地址：192.168.3.57 修改成你自己的 ESP32 IP地址**)。

**1. 打开APP，选择WIFI**

![](media/IOT-home2.png)

![](media/IOT-home3.jpg)

**2. 使用APP控制LED和风扇**

A. APP输入IP地址（LCD1602显示出分配到的IP地址，或点击IDE的串口监视器![](media/Serial-Port-Monitor.png)，串口监视器窗口显示的IP地址）

B. 点击 **CONNECT** 连接IP地址。

C. 连接成功标志是跳出 Connected 字样，需要注意看。

D. IP地址连接上之后，然后就可以点击LED，可以看到智能家居的LED被打开；点击Fan按钮，风扇被打开。如下图操作。

![](media/APP1.jpg) 

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

#### 22.8 代码流程图

![](media/project22.png) 

#### 22.9 IoT智能家居的实验代码2 

⚠️ <span style="color: rgb(255, 76, 65);">**特别提醒：**</span> 打开代码文件后，需要修改ESP32开发板需要连接的WiFi名称与密码，您需要分别将 `ChinaNet-2.4G-0DF0` 和 `ChinaNet@233` 替换为您自己的 Wi-Fi 名称和 WiFi 密码。WiFi名称和WiFi密码修改后才能上传代码，否则你的ESP32开发板将无法连接网络。

```c
const char* ssid = "ChinaNet-2.4G-0DF0";  //输入你自己的WiFi名称
const char* pwd = "ChinaNet@233"; //输入你自己的WiFi密码
```

⚠️ **<span style="color: rgb(255, 76, 65);">注意：</span> 请确保代码中的WiFi名称和WiFi密码与连接到您的计算机、手机/平板电脑、ESP32开发板和路由器的网络相同，它们必须在同一局域网（WiFi）内。**

⚠️ **<span style="color: rgb(255, 76, 65);">注意：</span>WiFi必须是2.4Ghz频率的，否则ESP32无法连接WiFi，不支持连接5GHz频段的WiFi。**。

```c
/*  
 * 项目: IoT_smart_home
 * 描述: 手机APP控制智能家居
 * 编译IDE：ARDUINO IDE
 * 作者: http//www.keyes-robot.com
*/
#include <Arduino.h>
#ifdef ESP32
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#endif

#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#define LED_PIN   26
#define LED_COUNT 4     // 附加的新像素数
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

//将变量定义为检测到的值
String request;
const char* ssid = "ChinaNet-2.4G-0DF0";  // 输入你自己的WiFi名称
const char* pwd = "ChinaNet@233";  // 输入你自己的WiFi密码
WiFiServer server(80);  //初始化WiFi服务

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);

#include <dht11.h>
#define DHT11PIN 17
dht11 DHT11; // 初始化dht11

#include <BuzzerESP32.h>
#define buzzer_pin 25
BuzzerESP32 buzzer(buzzer_pin);   // GPIO25

#define waterPin 34
#define fanPin1 19
#define fanPin2 18
#define led_y 12           // 黄色LED引脚定义
#define gasPin 23
#define pyroelectric 14

// 舵机通道
int channel_PWM1 = 13;
int channel_PWM2 = 10;
int freq_PWM = 50; 
int resolution_PWM = 10;
const int PWM_Pin1 = 5;
const int PWM_Pin2 = 13;

String dataBuffer = "4095,0,0,32,65";

int Rainwater, gas, pir, t, h;  //定义变量
unsigned long prevTask = 0;

void setup() {
  Serial.begin(9600);
  //连接wifi
  WiFi.begin(ssid, pwd);
  //判断是否连接
  Serial.println("Connecting to WiFi...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  delay(1000);
  //串行监视器将显示无线网络的名称和IP地址
  Serial.println("Connected to WiFi");
  Serial.print("WiFi NAME:");
  Serial.println(ssid);
  Serial.print("IP:");
  Serial.println(WiFi.localIP());

  //初始化LCD
  lcd.init();
  //打开LCD背光
  lcd.backlight();
  //lcd.noBacklight();
  lcd.clear();
  //设置光标的位置
  lcd.setCursor(0, 0);
  //LCD打印
  lcd.print("IP:");
  //设置光标的位置
  lcd.setCursor(0, 1);
  //LCD打印
  lcd.print(WiFi.localIP());

  pinMode(led_y, OUTPUT);
  pinMode(fanPin1, OUTPUT);
  pinMode(fanPin2, OUTPUT);
  pinMode(waterPin, INPUT);

  buzzer.setTimbre(30);                                  // 设置timbre
  buzzer.playTone(0,0);                                  // 关闭蜂鸣器

  pinMode(gasPin, INPUT);
  pinMode(pyroelectric, INPUT);

  ledcAttach(PWM_Pin1, freq_PWM, resolution_PWM);
  ledcAttachChannel(PWM_Pin1, freq_PWM, resolution_PWM, channel_PWM1);
  ledcAttach(PWM_Pin2, freq_PWM, resolution_PWM);
  ledcAttachChannel(PWM_Pin2, freq_PWM, resolution_PWM, channel_PWM2);
  ledcWrite(PWM_Pin1, 25);
  delay(1000);
  ledcWrite(PWM_Pin2, 25);
  delay(1000);
  //启动服务
  server.begin();
}

void loop() {
  //检查客户端与网络服务器是否已连接
  //当客户端与服务器建立连接时，“server.available（）”函数返回一个用于客户端通信的WiFiClient对象。
  WiFiClient client = server.available();
  if (client) {
    Serial.println("New client connected");
    while (client.connected()) {
      //确定服务器是否发送数据
      if (client.available()) {
        request = client.readStringUntil('s');
        Serial.print("Received message: ");
        Serial.println(request);
      }
      //获取所有传感器数据
      getSensorsData();
      //将所有数据放入“数据缓冲区”
      dataBuffer = "";
      dataBuffer += String(Rainwater);
      dataBuffer += ",";
      dataBuffer += String(gas);
      dataBuffer += ",";
      dataBuffer += String(pir);
      dataBuffer += ",";
      dataBuffer += String(t);
      dataBuffer += ",";
      dataBuffer += String(h);
      //将数据发送到服务器，然后将其传输到应用程序。
      if (millis() - prevTask >= 1000) {  // 每秒钟执行一次
        prevTask = millis();
        client.print(dataBuffer);
      }

      delay(500);

      //LED
      if (request == "a") {
        digitalWrite(led_y, HIGH);
      } else if (request == "A") {
        digitalWrite(led_y, LOW);
      }

      //窗户舵机
      if (request == "b") {
        ledcWrite(PWM_Pin1, 80); //20ms的高电平约为2.5ms，即2.5/20*1024，伺服按规定角度旋转。
        delay(1000);
      } else if (request == "B") {
        ledcWrite(PWM_Pin1, 25);  //20ms的高电平约为0.5ms，即0.5/20*1024，伺服按规定角度旋转。
        delay(1000);
      }

      //蜂鸣器播放音乐
      if (request == "c") {
        birthday();
        buzzer.playTone(0,0);
      } else if (request == "C") {
        buzzer.playTone(0,0);
      }

      //蜂鸣器响
      if (request == "d") {
        buzzer.playTone(392,250);
      } else if (request == "D") {
        buzzer.playTone(0,0);
      }

      //门舵机
      if (request == "e") {
        ledcWrite(PWM_Pin2, 120);
        delay(1000);
      } else if (request == "E") {
        ledcWrite(PWM_Pin2, 25);
        delay(1000);
      }
 
      //风扇
      if (request == "f") {
        digitalWrite(fanPin2, LOW); //pwm = 0
        analogWrite(fanPin1, 100); //LEDC通道5绑定到指定的左电机输出PWM值为100.
      } else if (request == "F") {
        digitalWrite(fanPin2, LOW); //pwm = 0
        analogWrite(fanPin1, 0); //LEDC通道5绑定到指定的左电机输出PWM值为0.
      }

      //SK6812RGB打开和关闭它的红灯
      if (request == "g") {
        colorWipe(strip.Color(255,   0,   0), 50);
      } else if (request == "G") {
        colorWipe(strip.Color(0,   0,   0), 50);
      }
 
      //SK6812RGB打开和关闭它的橙灯
      if (request == "h") {
        colorWipe(strip.Color(200,   100,   0), 50);
      } else if (request == "H") {
        colorWipe(strip.Color(0,   0,   0), 50);
      }
 
      //SK6812RGB打开和关闭它的黄灯
      if (request == "i") {
        colorWipe(strip.Color(200,   200,   0), 50);
      } else if (request == "I") {
        colorWipe(strip.Color(0,   0,   0), 50);
      }

      //SK6812RGB打开和关闭它的绿灯
      if (request == "j") {
        colorWipe(strip.Color(0,   255,   0), 50);
      } else if (request == "J") {
        colorWipe(strip.Color(0,   0,   0), 50);
      }
 
      //SK6812RGB打开和关闭它的蓝绿灯
      if (request == "k") {
        colorWipe(strip.Color(0,   100,   255), 50);
      } else if (request == "K") {
        colorWipe(strip.Color(0,   0,   0), 50);
      }

      //SK6812RGB打开和关闭它的蓝灯
      if (request == "l") {
        colorWipe(strip.Color(0,   0,   255), 50);
      } else if (request == "L") {
        colorWipe(strip.Color(0,   0,   0), 50);
      }
 
      //SK6812RGB打开和关闭它的紫灯
      if (request == "m") {
        colorWipe(strip.Color(100,   0,   255), 50);
      } else if (request == "M") {
        colorWipe(strip.Color(0,   0,   0), 50);
      }

      //SK6812RGB打开和关闭它的白灯
      if (request == "n") {
        colorWipe(strip.Color(255,   255,   255), 50);
      } else if (request == "N") {
        colorWipe(strip.Color(0,   0,   0), 50);
      }

      //SK6812RGB-sfx1
      if (request == "o") {
        rainbow(10);
      } else if (request == "O") {
        colorWipe(strip.Color(0,   0,   0), 50);
      }

      //SK6812RGB-sfx2
      if (request == "p") {
        theaterChaseRainbow(50);
      } else if (request == "P") {
        colorWipe(strip.Color(0,   0,   0), 50);
      }
      request = "";
    }
    Serial.println("Client disconnected");
  }
}

void getSensorsData() { 
   //获得数据
   int chk = DHT11.read(DHT11PIN); 
   t = DHT11.temperature;
   h = DHT11.humidity;
      
   //水滴传感器
   Rainwater = analogRead(waterPin); //读取水滴传感器模拟值并将其分配给变量雨水

   //气体传感器
   gas = digitalRead(gasPin); //读取气体传感器模拟值并将其分配给可变气体

   //红外热释传感器
   pir = digitalRead(pyroelectric); //读取红外热释传感器模拟值并将其分配给可变气体
}

//将数据转换成百分比
String dataHandle(int data) {
  // 将模拟值转换为百分比
  int percentage = (data / 4095.0) * 100;
  // 如果转换后的百分比大于100，则输出10。
  percentage = percentage > 100 ? 100 : percentage;
  // 六个字符用于存储十六进制字符串，其中一个作为结束符
  char hexString[3];
  // 将十六进制值转换为6位十六进制字符串，并在前面加上前导零：0表示00,1表示01…
  sprintf(hexString, "%02X", percentage);

  return hexString;
}

void birthday()
{
  buzzer.playTone(294,250);  //参数：频率，延时，等
  buzzer.playTone(440,250);
  buzzer.playTone(392,250);
  buzzer.playTone(532,250);
  buzzer.playTone(494,250);
  buzzer.playTone(392,250);
  buzzer.playTone(440,250);
  buzzer.playTone(392,250);
  buzzer.playTone(587,250);
  buzzer.playTone(532,250);
  buzzer.playTone(392,250);
  buzzer.playTone(784,250);
  buzzer.playTone(659,250);
  buzzer.playTone(532,250);
  buzzer.playTone(494,250);
  buzzer.playTone(440,250);
  buzzer.playTone(698,250);
  buzzer.playTone(659,250);
  buzzer.playTone(532,250);
  buzzer.playTone(587,250);
  buzzer.playTone(532,500);
  buzzer.playTone(0,0);  //关
}

void colorWipe(uint32_t color, int wait) {
  for(int i=0; i<strip.numPixels(); i++) { // 对于条带中的每个像素...
    strip.setPixelColor(i, color);         // 设置像素的颜色（RAM）
    strip.show();                          // 更新条带以匹配
    delay(wait);                           // 延时
  }
}

// 彩虹在整个波段上循环。传输帧之间的延迟时间（以毫秒为单位）。
void rainbow(int wait) {
  for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
    for(int i=0; i<strip.numPixels(); i++) { // 对于条带中的每个像素...
      int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
    }
    strip.show(); // 更新条带内容
    delay(wait);  // 延时
  }
}

// Rainbow-enhanced Marquise-style. 帧间传输延迟时间（毫秒）
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) {
        int      hue   = firstPixelHue + c * 65536L / strip.numPixels();
        uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // color -> RGB
        strip.setPixelColor(c, color); // 设置像素c的值为color
      }
      strip.show();                // 更新条带内容
      delay(wait);                 // 延时
      firstPixelHue += 65536 / 90; // 一个周期的色轮超过90帧
    }
  }
}
```
#### 22.10 操作步骤及实验结果2

按照接线图接好线，外接电源，选择好正确的开发板板型（ESP32 Dev Module）和 适当的串口端口（COMxx），然后单击按钮![](media/cou0.png)上传示例代码至ESP32主控板。示例代码上传成功后，上电后，点击 ![](media/Serial-Port-Monitor.png) 打开串口监视器，设置波特率为9600。

⚠️ <span style="color: rgb(255, 76, 65);">**注意：手机/平板需要和智能家居连接同一个WiFi，或者手机/平板打开热点，智能家居连接手机/平板的热点，且示例代码中的WiFi名称与WiFi密码和手机/平板、智能家居是同一个WiFi名称与密码。** </span>

⚠️ **<span style="color: rgb(255, 76, 65);">注意：</span>WiFi必须是2.4Ghz频率的，否则ESP32无法连接WiFi，不支持连接5GHz频段的WiFi。**。

⚠️ **<span style="color: rgb(255, 76, 65);">特别注意：</span> 手机或平板一定要与ESP32开发板连接的是同一个WiFi，否则将无法进入控制页面。还有就是ESP32开发板在使用WiFi功能时功耗很大，需要外接DC电源(电源电压必须充足，最好使用新电池)才能满足它的工作电力需求，如果达不到它的工作电力需求ESP32板将会一直复位导致代码无法正常运行。**

⚠️ **<span style="color: rgb(255, 76, 65);">特别注意：</span> 一定要外接电源，外接DC电源 (电源电压必须充足，最好使用新电池) 才能满足ESP32主控板，2个舵机和风扇模块的工作电力需求，如果达不到它们的工作电力需求，手机/平板的APP 上的WiFi会断开。**

如果成功连接上WiFi，串口监视器会打印出分配到的IP地址（假如串口监视器没打印出分配到的IP地址，可以按下ESP32主控板上的复位键重启，重新连接WiFi）。不同的网络（WiFi），IP地址不一样。

![](media/WSA01.png)

同时，LCD1602显示屏也同步显示对应的IP地址。

![](media/qxe12.jpg)

⚠️ **<span style="color: rgb(255, 76, 65);">特别注意：</span>** 在本例中，您可以在APP页面中输入你自己的**ESP32 IP地址**(**这里是以 192.168.3.57 为例，而你需要将本例APP中的IP地址：192.168.3.57 修改成你自己的 ESP32 IP地址**)。

**1. 打开APP，选择WIFI**

![](media/IOT-home2.png)

![](media/IOT-home3.jpg)

**2. 使用APP控制智能家居**

A. APP输入IP地址（LCD1602显示出分配到的IP地址，或点击IDE的串口监视器![Img](./media/Serial-Port-Monitor.png)，串口监视器窗口显示的IP地址）

B. 点击 **CONNECT** 连接IP地址。

C. 连接成功标志是跳出 Connected 字样，需要注意看。

D. IP地址连接上之后，单击APP界面上对应的按钮控制对应的传感器模块工作。

⚠️ **<span style="color: rgb(255, 76, 65);">特别注意：</span> 一定要外接电源，外接DC电源(电源电压必须充足，最好使用新电池)才能满足ESP32主控板，2个舵机和风扇模块的工作电力需求，如果达不到它们的工作电力需求，ESP32主控板将会一直复位导致代码无法正常运行。**

![](media/app0.jpg)

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