### 第19课  语音温湿控系统

#### 19.1 项目介绍

本教程介绍如何使用温湿度传感器、风扇模块、智能语音模块和I2C LCD1602模块，构建一个智能温湿度控制系统。

该系统的温湿度传感器能够测量环境温度和湿度，并根据需求控制风扇降温。当温度超过设定阈值时，系统会自动开启风扇，将环境温度降至设定值以下。系统的 I2C LCD1602模块 显示当前温度和湿度值。同时，我们对着智能语音模块发出类似的“当前温度是多少”和“当前湿度是多少”等命令词，智能语音模块接收到相应命令词后语音播报当前温度和当前湿度的百分数值。该系统能够实现自动调节环境温湿度的功能，对于需要控制环境温湿度的项目具有很好的应用价值。

这个应用场景需要通过单线通信来获取XHT11温湿度传感器的数据，并将温度和湿度数值显示在I2C LCD1602模块上。还能语音播报检测到的当前温度和当前湿度，同时，当温度或湿度超过设定的阈值时，风扇模块会开启，用来降低温度以保护家里的动植物。这种模块的优点是安装容易，且功能强大。它不仅可以通过PWM调节速度，还能够通过单线通信来传输数据。总的来说，这个应用场景是一个非常实用的解决方案，可以帮助主人监测和控制家居的环境。

#### 19.2 实验组件

|![](media/esp32.png)|![](media/xht11.png)|![](media/SU-03T-1.png)|![](media/3p.png)|
|-|-|-|-|
|ESP32 Plus主板 *1|XHT11传感器 *1|智能语音模块 *1|3P线 *1|
|![](media/lcd2.png)|![](media/motor.png)|![](media/4p.png)|![](media/usb.png)|
|I2C LCD1602模块 *1|130电机模块 *1|4P线 *3|USB线 *1|

#### 19.3 模块接线图

智能语音模块、XHT11温湿度传感器、I2C LCD1602模块和130电机模块的控制引脚：

|XHT11温湿度传感器（S引脚）|io17|
|-|-|
|130电机模块（IN+引脚）|io18|
|130电机模块（IN-引脚）|io19|
|智能语音模块（TXD引脚）|io16|
|智能语音模块（RXD引脚）|io27|
|I2C 1602 LCD模块（SDA引脚）|SDA|
|I2C 1602 LCD模块（SCL引脚）|SCL|

⚠️ **特别注意：智能家居已经组装好了，这里不需要把XHT11温湿度传感器、电机模块、I2C LCD1602模块和智能语音模块拆下来又重新组装和接线，这里再次提供接线图，是为了方便您编写代码！**

![](media/pjt20.png)

#### 19.4 代码流程图

![](media/project19.png)


#### 19.5 实验代码 

⚠️ **注意：代码中的条件阈值可以根据实际情况自行设置。**

打开“Thonny”软件，点击“此电脑” → “D:” → “MicroPython资料” → “MicroPython_代码”。并鼠标左键双击“Project_19_tem_hum_control_system.py”。

```Python
# 作者 : www.keyes-robot.com

from machine import Pin, PWM, I2C, UART
import time
import dht

# 引脚定义
RX_PIN = 27
TX_PIN = 16
DHT11_PIN = 17
MOTOR_PIN1 = 19
MOTOR_PIN2 = 18

# 变量定义
yuyin = 0
temperature = 0
humidity = 0

# LCD初始化
i2c = I2C(scl=Pin(22), sda=Pin(21), freq=400000)
lcd = None

try:
    from i2c_lcd import I2cLcd
    lcd = I2cLcd(i2c, 0x27, 2, 16)
except:
    print("LCD初始化失败")

# SU-03T语音模块串口初始化
uart = UART(2, baudrate=9600, rx=RX_PIN, tx=TX_PIN)

# DHT11初始化
dht_sensor = dht.DHT11(Pin(DHT11_PIN))

# 电机初始化
motor_pin1 = Pin(MOTOR_PIN1, Pin.OUT)
motor_pin2 = Pin(MOTOR_PIN2, Pin.OUT)

# 串口通信协议定义
UART_SEND_MAX = 32
UART_MSG_HEAD_LEN = 2
UART_MSG_FOOT_LEN = 2

# 消息号定义
U_MSG_bozhensgshu = 1
U_MSG_boxiaoshu = 2
U_MSG_bobao1 = 3
U_MSG_bobao2 = 4
U_MSG_bobao3 = 5
U_MSG_bobao4 = 6
U_MSG_bobao5 = 7
U_MSG_bobao6 = 8
U_MSG_bobao7 = 9
U_MSG_bobao8 = 10
U_MSG_bobao9 = 11
U_MSG_bobao10 = 12
U_MSG_bobao11 = 13
U_MSG_bobao12 = 14
U_MSG_bobao13 = 15
U_MSG_bobao14 = 16
U_MSG_bobao15 = 17
U_MSG_bobao16 = 18
U_MSG_bobao17 = 19
U_MSG_bobao18 = 20

# 消息头和尾
g_uart_send_head = bytearray([0xaa, 0x55])
g_uart_send_foot = bytearray([0x55, 0xaa])

def uart_send_impl(buff):
    """串口发送实现"""
    uart.write(buff)

def int16_to_int32(value):
    """16位整数转32位整数"""
    if value & 0x8000:  # 负数
        return value | 0xFFFF0000
    else:  # 正数
        return value

def uart_bobao1():
    """播报函数1"""
    buff = bytearray(UART_SEND_MAX)
    # 消息头
    buff[0:2] = g_uart_send_head
    # 消息号
    buff[2] = U_MSG_bobao1
    # 消息尾
    buff[3:5] = g_uart_send_foot
    uart_send_impl(buff[:5])

def uart_bobao2():
    """播报函数2"""
    buff = bytearray(UART_SEND_MAX)
    # 消息头
    buff[0:2] = g_uart_send_head
    # 消息号
    buff[2] = U_MSG_bobao2
    # 消息尾
    buff[3:5] = g_uart_send_foot
    uart_send_impl(buff[:5])

def uart_bobao4():
    """播报函数4"""
    buff = bytearray(UART_SEND_MAX)
    # 消息头
    buff[0:2] = g_uart_send_head
    # 消息号
    buff[2] = U_MSG_bobao4
    # 消息尾
    buff[3:5] = g_uart_send_foot
    uart_send_impl(buff[:5])
    
def uart_bozhensgshu(zhengshu):
    """播报整数"""
    buff = bytearray(UART_SEND_MAX)
    # 消息头
    buff[0:2] = g_uart_send_head
    # 消息号
    buff[2] = U_MSG_bozhensgshu
    # 整数数据（32位，小端序）
    value = int16_to_int32(zhengshu)
    buff[3] = value & 0xFF
    buff[4] = (value >> 8) & 0xFF
    buff[5] = (value >> 16) & 0xFF
    buff[6] = (value >> 24) & 0xFF
    # 消息尾
    buff[7:9] = g_uart_send_foot
    uart_send_impl(buff[:9])
    
def motor_control(enable):
    """电机控制"""
    if enable:
        # 电机正转
        motor_pin1.value(1)
        motor_pin2.value(0)
        print("Motor ON")
    else:
        # 电机制动
        motor_pin1.value(0)
        motor_pin2.value(0)
        print("Motor OFF")

def read_dht11():
    """读取DHT11温湿度"""
    global temperature, humidity
    try:
        dht_sensor.measure()
        temperature = dht_sensor.temperature()
        humidity = dht_sensor.humidity()
        return True
    except Exception as e:
        print("DHT11读取失败:", e)
        return False

def update_lcd():
    """更新LCD显示"""
    if lcd:
        lcd.clear()
        lcd.move_to(0, 0)
        lcd.putstr("Temp(C):{}".format(temperature))
        lcd.move_to(0, 1)
        lcd.putstr("Hum(%RH):{}".format(humidity))

def handle_voice_command():
    """处理语音命令"""
    global yuyin
    if uart.any():
        yuyin = uart.read(1)
        if yuyin:
            yuyin = yuyin[0]  # 获取字节值
            print("收到语音命令:", yuyin)
            
            if yuyin == 47:  # 检测温度并播报
                if read_dht11():
                    print("温度:", temperature, "C")
                    time.sleep(3)
                    uart_bobao1()
                    time.sleep(2)
                    uart_bozhensgshu(temperature)
                    time.sleep(2)
                    uart_bobao2()
                    time.sleep(2)
                yuyin = 0 
                return True
                
            elif yuyin == 48:  # 检测湿度并播报
                if read_dht11():
                    print("湿度:", humidity, "%")
                    time.sleep(3)
                    uart_bobao4()
                    time.sleep(3)
                    uart_bozhensgshu(humidity)
                    time.sleep(2)
                yuyin = 0
                return True
    return False

# 主循环
last_update_time = 0
update_interval = 2000  # 2秒更新一次

while True:
    current_time = time.ticks_ms()
    
    # 处理语音命令
    handle_voice_command()
    
    # 定期更新温湿度和显示
    if time.ticks_diff(current_time, last_update_time) >= update_interval:
        if read_dht11():
            print("温度: {}C, 湿度: {}%".format(temperature, humidity))
            update_lcd()
            
            # 检查阈值并控制电机
            if temperature >= 28 or humidity > 70:
                motor_control(True)
            else:
                motor_control(False)
        
        last_update_time = current_time
    
    time.sleep_ms(100)  # 短暂延迟
```

#### 19.6 实验结果 

按照接线图接好线，将 ESP32 主控板通过Micro USB数据线与计算机相连供电，外接电源供电，然后单击按钮![](media/1305.png)，示例代码开始执行。

![](media/AB1.png)


示例代码开始执行之后，LCD1602显示屏实时显示XHT11传感器检测到环境中的湿度值和温度值。当空气温度大于等于28℃或空气湿度大于70%RH时，风扇模块会开启，进行散热降湿，当空气温度小于28℃且空气湿度小于等于70%RH时，风扇模块会关闭（风扇为模拟散热，散热效果一般），达到农场自动控温控湿效果，节省能源。

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

智能语音模块唤醒后，对着麦克风说：“当前温度是多少” 或 “当前温度多少” 等命令词时，接着语音播报 “正在为您读取温度” + “当前温度为” + “XHT11温湿度传感器检测到的温度值” + “度”；

对着麦克风说：“当前湿度是多少” 或 “当前湿度多少” 等命令词时，接着语音播报 “正在为您读取湿度” + “当前湿度为百分之” + “XHT11温湿度传感器检测到的湿度值”。

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

单击![](media/1311.png)“停止/启动后端进程”退出程序。