语音控制智能风扇实践
1. 前期准备与设备发现
在完成由 Lambda 函数提供的后端服务后,我们可以在 Alexa 账户中进行设备发现。具体操作如下:
- 访问链接:https://alexa.amazon.com/。
- 导航至“Smart Home | Devices”,点击“Discover”。大约 20 秒后,智能风扇应该会作为新设备列出。
完成 AWS 端的开发后,接下来我们将实现 ESP32 应用。
2. 固件开发概述
ESP32 应用的主要功能是检测风扇上哪个按钮被按下,并切换相应的继电器来设置风扇速度,同时更新 AWS 云上设备的状态。当语音命令改变设备的期望状态时,运行在 ESP32 上的应用会捕获该信息并反映在物理风扇上。
3. 硬件电路连接
对于第二个原型,我们将风扇按钮连接到 ESP32 上。具体步骤如下:
- 打开风扇的底盖,切断按钮电缆。
- 将按钮连接到 ESP32 的 GPIO 引脚。
- 继电器将控制风扇速度,取代风扇按钮。将电缆的另一端连接到继电器负载。
注意事项
:
- 处理高电压时,请采取所有必要的预防措施。可参考网站 https://ncd.io/relay-logic/ 了解如何使用带负载的继电器。
- 可以在继电器周围使用 RC 缓冲器,以保护它们免受风扇电机的感应反冲影响。例如 Okaya 的 XE1201 缓冲器(https://okaya.com/product/?id=9716226e - c62c - e111 - a207 - 0026551ab73e)。
TXS0108E 逻辑转换器模块有两个端口:
|端口|支持电压范围|
|----|----|
|Port A(底行)|1.4 V - 3.6 V|
|Port B(顶行)|1.65 V - 5.5 V|
规则是 VA 应小于 VB,当 OE 设置为高电平时,可启用 Port B 输出。在线数据手册链接:https://www.ti.com/document - viewer/TXS0108E/datasheet。
我们将使用四个按钮来控制风扇速度:
- 第一个按钮:通过关闭所有继电器停止风扇。
- 其他三个按钮:用于不同的速度设置。由于启用了 GPIO 引脚的内部上拉电阻,当没有按钮按下时,按钮的 GPIO 引脚将读取高电平。
4. 创建 PlatformIO 项目
创建一个以 ESP - IDF 为框架的新 PlatformIO 项目,具体步骤如下:
1.
编辑 platformio.ini 文件
:
[env:az - delivery - devkit - v4]
platform = espressif32
board = az - delivery - devkit - v4
framework = espidf
monitor_speed = 115200
lib_extra_dirs =
../../common/esp - idf - lib/components
build_flags =
-DWIFI_SSID=${sysenv.WIFI_SSID}
-DWIFI_PASS=${sysenv.WIFI_PASS}
-DAWS_ENDPOINT=${sysenv.AWS_ENDPOINT}
board_build.embed_txtfiles =
./tmp/private.pem.key
./tmp/certificate.pem.crt
./tmp/AmazonRootCA1.pem
AWS 设备 SDK 位于 ../../common/esp - idf - lib/components 路径下,我们将其包含在项目中。定义 AWS_ENDPOINT 为环境变量,应用将连接到该端点与 AWS IoT Core 中创建的设备进行通信。同时指定设备加密文件的路径,这些文件将被复制到 tmp 文件夹,该文件夹会从 GitHub 仓库中排除,以避免加密文件公开暴露。
- 编辑 src/CMakeList.txt 文件 :
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
set(COMPONENT_ADD_INCLUDEDIRS ".")
idf_component_register(SRCS ${app_sources})
target_add_binary_data(${COMPONENT_TARGET} "../tmp/AmazonRootCA1.pem" TEXT)
target_add_binary_data(${COMPONENT_TARGET} "../tmp/certificate.pem.crt" TEXT)
target_add_binary_data(${COMPONENT_TARGET} "../tmp/private.pem.key" TEXT)
使用 target_add_binary_data 指令指定加密文件的路径,让 ESP - IDF 知晓这些文件。
- 复制加密文件到 tmp 文件夹 :
$ mkdir tmp && cp <thing_cert_files> tmp/ && cd tmp
$ wget https://www.amazontrust.com/repository/AmazonRootCA1.pem
$ ls
AmazonRootCA1.pem certificate.pem.crt private.pem.key public.pem.key
- 复制库文件 :从 GitHub 链接 https://github.com/PacktPublishing/Internet - of - Things - with - ESP32/tree/main/ch12/smart_fan/lib 复制库文件。完成后,项目的目录结构如下:
.:
CMakeLists.txt include lib platformio.ini sdkconfig src test tmp
./lib:
aws hw README wifi
./lib/aws:
app_aws.c app_aws.h
./lib/hw:
app_hw.c app_hw.h
./lib/wifi:
app_wifi.c app_wifi.h
./src:
CMakeLists.txt main.c
./tmp:
AmazonRootCA1.pem certificate.pem.crt private.pem.key public.pem.key
- 激活 PlatformIO 虚拟环境并设置环境变量 :
$ source ~/.platformio/penv/bin/activate
(penv)$ aws iot describe - endpoint --endpoint - type iot:Data - ATS
{
"endpointAddress": "<your_endpoint>"
}
(penv)$ export AWS_ENDPOINT='\"<your_encpoint>\"'
(penv)$ export WIFI_SSID='\"<your_ssid>\"'
(penv)$ export WIFI_PASS='\"<your_password>\"'
5. 项目库介绍
项目中有三个主要的库:
-
lib/wifi/app_wifi.{c,h}
:实现 Wi - Fi 连接。
-
lib/aws/app_aws.{c,h}
:处理 AWS 通信。
-
lib/hw/app_hw.{c,h}
:处理按钮按下事件并管理继电器。
以下是这些库中的关键代码和功能:
app_wifi.h :
#ifndef app_wifi_h_
#define app_wifi_h_
typedef void (*on_connected_f)(void);
typedef void (*on_failed_f)(void);
typedef struct {
on_connected_f on_connected;
on_failed_f on_failed;
} connect_wifi_params_t;
void appwifi_connect(connect_wifi_params_t);
#endif
appwifi_connect
函数尝试连接到本地 Wi - Fi,并根据连接成功状态运行相应的回调函数。
app_hw.h :
#ifndef app_hw_h_
#define app_hw_h_
#include <stdbool.h>
#include <stdint.h>
#include "driver/gpio.h"
// GPIO pins
#define APP_BTN0 19 // OFF
#define APP_BTN1 18 // 33%
#define APP_BTN2 5 // 66%
#define APP_BTN3 17 // 100%
#define APP_OE 27 // ENABLE
#define APP_RELAY1 32
#define APP_RELAY2 33
#define APP_RELAY3 25
typedef struct
{
gpio_num_t btn_pin;
gpio_num_t relay_pin;
uint8_t val;
} btn_map_t;
typedef void (*appbtn_fan_changed_f)(uint8_t);
void apphw_init(appbtn_fan_changed_f);
uint8_t apphw_get_state(void);
void apphw_set_state(uint8_t);
#endif
apphw_init
函数根据用途将 GPIO 引脚初始化为输入或输出。按钮引脚作为输入,继电器控制引脚作为输出。该函数还接受一个函数参数,当按钮按下且相应继电器状态改变时调用该函数,以便更新设备状态。
apphw_set_state
函数用于从外部改变继电器状态。
app_aws.h :
#ifndef app_aws_h_
#define app_aws_h_
#include <stdint.h>
#define AWS_THING_NAME "myhome_fan1"
typedef void (*fan_state_changed_f)(uint8_t);
void appaws_init(fan_state_changed_f);
void appaws_connect(void *);
void appaws_publish(uint8_t);
#endif
appaws_init
函数初始化库内部,接受一个回调参数,当用户请求改变风扇状态时调用该回调。
appaws_connect
函数在本地 Wi - Fi 连接后调用,连接到 AWS 云并监控设备状态变化。
appaws_publish
函数用于在风扇速度因按钮按下而改变时更新设备状态。
6. main.c 代码整合
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "app_wifi.h"
#include "app_hw.h"
#include "app_aws.h"
#define TAG "app"
static void handle_wifi_connect(void)
{
xTaskCreatePinnedToCore(appaws_connect, "appaws_connect",
15 * configMINIMAL_STACK_SIZE, NULL, 5, NULL, 0);
}
static void handle_wifi_failed(void)
{
ESP_LOGE(TAG, "wifi failed");
}
void app_main()
{
apphw_init(appaws_publish);
appaws_init(apphw_set_state);
connect_wifi_params_t cbs = {
.on_connected = handle_wifi_connect,
.on_failed = handle_wifi_failed};
appwifi_connect(cbs);
}
在
app_main
函数中,我们初始化硬件库和 AWS 库,并设置回调函数。最后调用
appwifi_connect
函数连接到本地 Wi - Fi。
7. AWS 通信库实现
appaws_connect 函数 :
void appaws_connect(void *param)
{
memset((void *)&aws_client, 0, sizeof(aws_client));
ShadowInitParameters_t sp = ShadowInitParametersDefault;
sp.pHost = endpoint_address;
sp.port = AWS_IOT_MQTT_PORT;
sp.pClientCRT = (const char *)certificate_pem_crt_start;
sp.pClientKey = (const char *)private_pem_key_start;
sp.pRootCA = (const char *)aws_root_ca_pem_start;
sp.disconnectHandler = disconnected_handler;
aws_iot_shadow_init(&aws_client, &sp);
ShadowConnectParameters_t scp =
ShadowConnectParametersDefault;
scp.pMyThingName = thing_name;
scp.pMqttClientId = client_id;
scp.mqttClientIdLen = (uint16_t)strlen(client_id);
while (aws_iot_shadow_connect(&aws_client, &scp) !=
SUCCESS)
{
ESP_LOGW(TAG, "trying to connect");
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
uint8_t fan_powerlevel = 0;
jsonStruct_t fan_controller;
fan_controller.cb = fan_powerlevel_change_requested;
fan_controller.pData = &fan_powerlevel;
fan_controller.pKey = "powerlevel";
fan_controller.type = SHADOW_JSON_UINT8;
fan_controller.dataLength = sizeof(uint8_t);
if (aws_iot_shadow_register_delta(&aws_client, &fan_
controller) == SUCCESS)
{
ESP_LOGI(TAG, "shadow delta registered");
}
IoT_Error_t err = SUCCESS;
while (1)
{
if (xSemaphoreTake(aws_guard, 100) == pdTRUE)
{
err = aws_iot_shadow_yield(&aws_client, 250);
xSemaphoreGive(aws_guard);
}
if (err != SUCCESS)
{
ESP_LOGE(TAG, "yield failed: %d", err);
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
该函数首先初始化设备的影子,然后尝试连接到设备。连接成功后,注册一个 delta 回调函数,用于处理设备影子的变化。最后,在一个循环中监控 AWS 消息。
appaws_publish 函数 :
void appaws_publish(uint8_t val)
{
jsonStruct_t temp_json = {
.cb = NULL,
.pKey = "powerlevel",
.pData = &val,
.type = SHADOW_JSON_UINT8,
.dataLength = sizeof(val)};
char jsondoc_buffer[200];
aws_iot_shadow_init_json_document(jsondoc_buffer,
sizeof(jsondoc_buffer));
aws_iot_shadow_add_reported(jsondoc_buffer, sizeof(jsondoc_
buffer), 1, &temp_json);
if (desired_state != val)
{
aws_iot_shadow_add_desired(jsondoc_buffer,
sizeof(jsondoc_buffer), 1, &temp_json);
}
aws_iot_finalize_json_document(jsondoc_buffer,
sizeof(jsondoc_buffer));
IoT_Error_t err = SUCCESS;
if (xSemaphoreTake(aws_guard, portMAX_DELAY) == pdTRUE)
{
err = aws_iot_shadow_update(&aws_client, thing_name,
jsondoc_buffer, NULL, NULL, 4, true);
xSemaphoreGive(aws_guard);
}
if (err != SUCCESS)
{
ESP_LOGE(TAG, "publish failed: %d", err);
}
}
该函数用于更新设备的影子状态。当风扇速度因按钮按下而改变时,会准备一个 JSON 消息并发送到 AWS IoT Core。如果期望状态与当前状态不同,还会更新期望状态,以避免不必要的 delta 消息。
8. 测试步骤
烧录开发板后,我们可以进行以下测试:
1. 按下 GPIO17 处的全速按钮,在 AWS 控制台(https://aws.amazon.com/console/)上观察设备影子的
powerlevel
更新为 100。
2. 按下 GPIO19 处的关闭按钮,观察设备影子的
powerlevel
更新为 0。
3. 在浏览器中访问 Alexa 开发者控制台(https://developer.amazon.com/alexa/console/ask),进入
myhome_smartfan
技能的测试页面。在 Alexa 模拟器中输入“set smart fan to 50”,观察中间的正常速度继电器是否开启,设备影子的
powerlevel
应为 66。
通过重复测试不同的功率级别,可以确认固件是否按预期工作。
9. 项目总结与展望
这个项目涵盖了典型物联网项目的各个层面,包括传感器(按钮)、执行器(继电器)、网络连接(Wi - Fi)和云集成(AWS IoT Core)。实现 Alexa 支持是该项目的一个亮点,涉及到 AWS IoT Core 的配置、AWS Lambda 后端代码的开发以及 Alexa 开发者控制台中智能家庭技能的创建。
未来,我们可以为智能风扇添加更多功能,例如:
- 集成温度传感器,实现自动模式,根据温度读数自动设置风扇速度。
- 实现 Alexa 文档中描述的塔式风扇设备模板(https://developer.amazon.com/en - US/docs/alexa/smarthome/get - started - with - device - templates.html#tower - fan)。
- 支持无线(OTA)固件更新。
- 实现 BLE 通用属性配置文件(GATT)服务器,用于本地通信。
物联网是一个快速发展、应用广泛的领域,只要我们跟上技术的变化,就会有无数的机会。不同的 ESP32 芯片,如 ESP32 - S2、ESP32 - C3 和 ESP32 - S3,针对不同的应用场景进行了优化,大家可以尝试使用这些芯片来提升自己的技能。
语音控制智能风扇实践
10. 项目架构总结
本项目构建了一个完整的语音控制智能风扇系统,其架构主要由硬件、软件和云服务三部分组成,以下是详细的架构总结:
|部分|描述|
|----|----|
|硬件|包括 ESP32 开发板、风扇按钮、继电器、TXS0108E 逻辑转换器模块等。风扇按钮作为传感器,继电器作为执行器,逻辑转换器模块用于电压转换。|
|软件|基于 ESP - IDF 框架,使用 PlatformIO 进行项目管理。包含 Wi - Fi 连接库、AWS 通信库和硬件控制库,通过 main.c 进行整合。|
|云服务|使用 AWS IoT Core 进行设备管理和通信,通过 Lambda 函数提供后端服务,结合 Alexa 开发者控制台实现语音控制。|
下面是该项目的整体架构 mermaid 流程图:
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A(用户语音指令) --> B(Alexa 开发者控制台)
B --> C(AWS Lambda 函数)
C --> D(AWS IoT Core)
D --> E(ESP32 设备)
E --> F(风扇按钮)
F --> G(继电器)
G --> H(风扇)
I(风扇按钮操作) --> E
E --> D
D --> C
C --> B
B --> A(用户反馈)
11. 关键技术点分析
-
AWS IoT Core 影子服务
:通过
aws_iot_shadow_init和aws_iot_shadow_connect函数初始化和连接设备影子,使用aws_iot_shadow_register_delta注册 delta 回调,实现对设备状态变化的实时监控和响应。 -
JSON 消息处理
:在
appaws_publish函数中,使用aws_iot_shadow_init_json_document、aws_iot_shadow_add_reported和aws_iot_shadow_add_desired函数处理 JSON 消息,确保设备状态的准确更新。 -
GPIO 引脚控制
:在
app_hw.h中定义了 GPIO 引脚的宏和 API 函数,通过apphw_init函数初始化引脚,使用apphw_set_state函数控制继电器状态。
12. 代码优化建议
-
错误处理
:在现有代码中,虽然对一些关键操作进行了错误检查,但可以进一步完善错误处理机制,例如在
appaws_connect和appaws_publish函数中,当连接失败或发布失败时,可以进行更详细的错误日志记录和重试策略。 - 代码复用 :部分代码可以进行复用和封装,例如 JSON 消息处理部分,可以将其封装成独立的函数,提高代码的可维护性和复用性。
-
性能优化
:在
appaws_connect函数的循环中,vTaskDelay的时间可以根据实际情况进行调整,以减少不必要的等待时间,提高系统的响应速度。
13. 常见问题及解决方案
| 问题 | 解决方案 |
|---|---|
| 设备无法连接到 AWS IoT Core | 检查网络连接、AWS_ENDPOINT 环境变量、加密文件路径和权限等。 |
| 语音指令无法控制风扇 | 检查 Alexa 技能配置、AWS Lambda 函数逻辑和 ESP32 设备的消息接收和处理逻辑。 |
| 继电器状态异常 |
检查 GPIO 引脚连接、继电器驱动电路和
apphw_set_state
函数的调用逻辑。
|
14. 结语
通过这个项目,我们深入了解了如何利用 ESP32 构建一个完整的物联网系统,包括硬件连接、软件编程和云服务集成。实现语音控制智能风扇不仅提升了用户体验,还展示了物联网技术在智能家居领域的应用潜力。
在实践过程中,我们遇到了各种挑战,如设备连接问题、消息处理错误等,但通过不断调试和优化,最终实现了系统的稳定运行。同时,我们也看到了项目的可扩展性,可以通过添加更多功能来进一步完善智能风扇的性能。
希望本文能为大家在物联网开发方面提供一些参考和启示,鼓励大家不断探索和实践,将更多的创意和想法转化为实际的项目。在未来的物联网发展中,我们可以期待更多创新的应用和解决方案。
超级会员免费看

被折叠的 条评论
为什么被折叠?



