首先感谢所有帮助过我的佬们!
声明:本人是零基础小白(大四了,为了做毕设才开始学的,之前只学了两个月C++,没搞过电控,纯小镇做题家),全是根据网上视频、咨询各种大佬、自己摸索尝试学来的,从3.4开始零基础学,3.31正式完工,通了七八个夜,近20天干到凌晨三四点,终于摸索出了一个结果,本篇以作纪念,激励自己未来更进一步!欢迎大家把自己遇到的问题和解决办法发到评论区,大家一起讨论!
(大部分遇到的代码格式报错,我都是找DeepSeek帮我改,一些由于设置不正确导致的报错,我遇到的都在下面了,如果我有讲不对的地方,也请大家帮我指正在评论区,实在不行的就重新安装ESP-IDF,重新导入小智源码,很多时候即使代码报错也可以烧录的,主要是看烧录过程报的错)
————————————————————————前言————————————————————————
Q:为什么要用ESP32(搭载小智AI)借助HomeAssistant平台,通过MQTT协议,控制另一个ESP8266上的设备?而不直接用蓝牙呢?
A:因为小智AI本身自带强大的对话聊天功能。同时,通过HomeAssistant平台搭建一个ESP32到ESP8266的控制,在局域网下的优势更明显,且可以集成别的有线、无线设备,例如小米智能家居等,一体化控制,且HomeAssistant有自己的APP,可以统一管理控制。即便是距离较远,只要在同一个WIFI下,都可以实现控制。并且不仅是在HomeAssistant平台上,通过修改,小智AI也可以同时控制局域网下的所有家居设备。
————————————————————————正文————————————————————————
一、下载安装、环境搭建、代码烧录流程:
参考资料
【ESP-IDF篇】搭建ESP-IDF软件开发环境,包括手动命令行和VSCode两种方式-CSDN博客
VSCode安装Esp-IDF开发环境(pip version)出错解决办法_如何彻底删除esp-idf-CSDN博客
参考这两个即可
如果你烧录过第一遍后,如果你想改代码再烧录,改完后直接依次点“彻底清理”、“ESP-IDF构建、烧录和监视”,这两个就行。
如果你烧录的过程中报错,改掉错误后,点击“ESP-IDF构建、烧录和监视”,就可以继续烧录。
二、语音控制点亮LED(小智AI自带的功能)
参考资料:
VScode下玩转ESP32S3并成功编译XIAOZHI-ESP32-MAIN代码_xiaozhi-esp32 编译-CSDN博客
【虾哥小智源码编译演示】 https://www.bilibili.com/video/BV1qqNceNE6d/?share_source=copy_web&vd_source=514c9373e6d25d0b426a7c7343964747
小智接入全屋智能HomeAssistant,家居控制,音乐播放完整演示_哔哩哔哩_bilibili
虾哥AI小智ESP32彩屏款编译及添加IOT教程-东阳马生DoYy-稍后再看-哔哩哔哩视频
[开源/教程]用小智闹钟,教你如何使用AI控制本地功能-东阳马生DoYy-稍后再看-哔哩哔哩视频
总结:主要步骤只有三步
①在小智源码(怎么下载自己百度就行)里面的iot文件夹下,创建一个.cc文件,模仿lamp文件里面的格式写。可以参考上述后两个视频,学习以下lamp.cc文件的结构,然后自己就可以写了。(在此感谢两位佬的帮助)
②写完之后去问自己的淘宝店家,自己的板子对应boards文件夹(在源码的main文件夹里)里面的什么型号,然后在对应型号的.h文件夹里面添加一个设备。例如我的是compact_wifi_board(买的是淘宝里面那个叫源地什么的店家的套件,人挺好,回复的也挺快。不是广告,纯觉得他人好,我都买回来快一个月了,问他问题他还会回复)
③在iot文件夹里,找到sample_interface.json,添加对应功能。
这样就能给小智AI添加一个功能了。
遇到报错1:
————————————————————————
Adding "set-target"'s dependency "fullclean" to list of commands with default set of options.
Executing action: fullclean
Executing action: set-target
Set Target to: esp32s3, new sdkconfig created. Existing sdkconfig renamed to sdkconfig.old.
Running cmake in directory d:\esp\esp\project\hello_world\build
Executing "cmake -G Ninja -DPYTHON_DEPS_CHECKED=1 -DESP_PLATFORM=1 -DIDF_TARGET=esp32s3 -DCCACHE_ENABLE=1 d:\esp\esp\project\hello_world"...
CMake Error at D:/ESP/ESP_IDE/esp-idf/tools/cmake/targets.cmake:19 (message):
IDF_TARGET in CMake cache does not match IDF_TARGET environment variable.
To change the target, clear the build directory and sdkconfig file, and
build the project again
Call Stack (most recent call first):
D:/ESP/ESP_IDE/esp-idf/tools/cmake/project.cmake:7 (__target_init)
CMakeLists.txt:5 (include)
-- Configuring incomplete, errors occurred!
cmake failed with exit code 1
————————————————————————————
解决办法:乖乖地执行上述两个参考文章的烧录步骤,点开左侧ESPIDF里面,第一步,选择当前项目的工作区的文件夹,选xiaozhi-main那个文件夹即可
还有一种可能,就是你已经是选中这个板子的状态了,直接编译就行,所以不让重复选择
遇到报错2:
esp-idf对头文件报错,无法跳转
解决办法:
有部分头文件不影响编译,把影响编译的用下面的解决办法处理。
ESP32的ESP-IDF在VScode工程下,头文件标红警告、报错、无法跳转_vscode esp32 找不到头文件-CSDN博客z可以把报错的都快速修复在include_path里面,但是要是外部库的话还没找到解决办法。(如何导入外部库可以参考下文的解决办法)
三、控制步进电机三个挡位旋转
电机驱动:UNL2003 电机:28BYJ-48
参考资料:
小智物联网开发:为小智安装“机械臂“(其实就是加个舵机进行语音控制)-CSDN博客
(在此感谢LS佬的帮助!)
遇到报错1:thing type not found
编译提示:thing: thing type not found : motor(这个motor是我自己创的文件)
解决办法:
把powershell右侧的都删了,然后把motor都统一了Motor,重新编译即可
遇到报错2:internal compiler error: Segmentation fault
D:/ESP-IDF/ESP-IDF/v5.3.2/esp-idf/components/esp_lcd/rgb/esp_lcd_panel_rgb.c:800:1: internal compiler error: Segmentation fault
800 | }
| ^
libbacktrace could not find executable to open
* 终端进程“d:\ESP-IDF\ESP-IDF-TOOL\tools\ninja\1.12.1\ninja.exe”已终止,退出代码: 1。
解决办法:系统内存不足,关掉一些没用的程序,还有,把VScode右侧的进程都终止了,重新编译。也可以再次点“ESP-IDF构建、烧录、监视”,继续编译。
遇到报错3:未找到ESP-IDF自带的库
D:/ESP-IDF/ESP-IDF/v5.3.2/esp-idf/components/esp_lcd/rgb/esp_lcd_panel_rgb.c:47:10: fatal error: lcd_periph.h: No such file or directory
47 | #include <lcd_periph.h>
| ^~~~~~~~~~~~~~
解决办法:如果这个缺失的文件,你可以在你电脑上找到,那说明只是路径设置不对,要么改include_path的路径,要么看看你哪份代码里面标了缺失这个文件,看看这个代码里,不缺失的文件在哪个位置,然后把找到的文件复制一份到这个不缺失的文件的文件夹,然后重启电脑,重新打开vscode。但如果是要导入外部库,目前我只能用改include_Path这个办法。
遇到报错4:未找到Arduino库
fatal error: Arduino.h: No such file or directory
10 | #include <Arduino.h>
| ^~~~~~~~~~~
compilation terminated.
解决办法:这是ESP-IDF自身不带Arduino库的原因,Releases · espressif/arduino-esp32 · GitHub
这个网站里面是不同版本的ESP-IDF(不知道版本的自己看ESP-IDF的安装位置,文件夹的名称就是版本号),下载source.zip然后解压到ESP-IDF的components里面即可
遇到报错5:如何导入外部库
fatal error: WiFi.h: No such file or directory
10 | #include <PubClient.h>
| ^~~~~~~~~~~
compilation terminated.
解决办法:(以下只针对单独的一个功能库,且不需要引用别的类似于Arduino等库的类型,如果要导入类似于WiFi.h这种,有很多依赖库的库,就需要把所有库都导入进来,做法和如下相同)
可以参考这个视频的源码(在视频简介)[开源/教程]用小智闹钟,教你如何使用AI控制本地功能-东阳马生DoYy-稍后再看-哔哩哔哩视频
步骤①:打开小智AI源代码的main文件夹,里面的CMakeLists,有一个添加IOT相关文件。
②在main文件夹里创建一个自己的文件夹,例如PubClient,放入自己要导入的外部库的.cc和.h文件
③模仿IOT函数,创建自己的库的引用,例如:
file(GLOB PubClient_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/PubClient/*.cc)
list(APPEND SOURCES ${PubClient_SOURCES})
list(APPEND INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/PubClient)
遇到报错6:ESP--IDF的SDK配置器(menuconfig)打开报错
-- DEBUG: Use esp-modbus component folder: C:/Users/Administrator/Desktop/xiaozhi-esp32-main/managed_components/espressif__esp-modbus. ---------------------------ERROR-------------------------- ESP Insights Project commit: HEAD-HASH-NOTFOUND -----------------------END OF ERROR----------------------- -- git rev-parse returned 'fatal: not a git repository (or any of the parent directories): .git' ---------------------------ERROR-------------------------- ESP RainMaker Project commit: HEAD-HASH-NOTFOUND -----------------------END OF ERROR----------------------- -- git rev-parse returned 'fatal: not a git repository (or any of the parent directories): .git' ---------------------------ERROR-------------------------- CMake Error at D:/ESP-IDF/ESP-IDF/v5.3.2/esp-idf/components/arduino-esp32-3.1.1/CMakeLists.txt:367 (message): esp32-arduino requires CONFIG_FREERTOS_HZ=1000 (currently 100) -----------------------END OF ERROR----------------------- -- Configuring incomplete, errors occurred! ---------------------------ERROR-------------------------- cmake failed with exit code 1, output of the command is in the C:\Users\Administrator\Desktop\xiaozhi-esp32-main\build\log\idf_py_stderr_output_21924 and C:\Users\Administrator\Desktop\xiaozhi-esp32-main\build\log\idf_py_stdout_output_21924 -----------------------END OF ERROR----------------------- ---------------------------ERROR-------------------------- SDK Configuration editor confserver process exited with code: 2 -----------------------END OF ERROR-----------------------
解决办法:打开左侧sdkconfig文件,找到:
CONFIG_FREERTOS_HZ=100 //原本是100,改成1000
四、小智ESP32S3+MQTT + HomeAssistant + ESP8266
这里介绍的是如何让小智AI连接上HomeAssistant,我之前已经实现了用ESP32-S3通过HomeAssistant上的MQTT(Mosquitto平台),控制、监视ESP8266上的设备(需要代码的话可以私信我,编译平台也是VScode)。我的虚拟机是Window自带的HyperV,Docker之前也用过,但是还是选择了HyperV。
参考资料:
【Homeassistant安装、小白也能上车】 https://www.bilibili.com/video/BV1ixXDYkEpa/?share_source=copy_web&vd_source=514c9373e6d25d0b426a7c7343964747
二、使用windows的hyper-v搭建homeassistant智能家居平台 - 哔哩哔哩
【HACS商店安装以及仪表盘卡片下载和使用【homeassistant教程1】】 https://www.bilibili.com/video/BV1jjAHepENu/?share_source=copy_web&vd_source=514c9373e6d25d0b426a7c7343964747
windows10-EMQX与MQTTX的安装及配置使用教程_emqx windows-CSDN博客
arduino esp32 通过wifi连接mqtt_eps32-CSDN博客(这个主要是参考代码怎么写的)
搭建个人智能家居 3 -第一个设备“点灯”_esphome-CSDN博客(这个主要是参考代码怎么写的)
(还有很多,CSDN、B站、瀚思彼岸等社区,还有问大佬,还有Deepseek帮我调代码等)
思路:仍然像给小智添加一个功能一样,在iot文件夹下添加一个.cc文件,叫做ESPController。
踩坑建议:最好还是使用ESP-IDF自带的WIFI库,即esp_wifi.h,Arduino的WiFi库有很多依赖,很麻烦,所以还是用ESP-IDF的库。
以下是小智iot文件夹里创建的代码
//ESPController文件
#include "iot/thing.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_mqtt.h"
#include "driver/uart.h"
#include <cstring>
#include <vector>
#define TAG "ESPController"
// MQTT 配置
#define MQTT_URI "mqtt://192.168.229.27:1883"
#define CLIENT_ID "ESP32-Controller"
// MQTT 主题
#define LED_CMD_TOPIC "home/livingroom/led/command"
#define FAN_CMD_TOPIC "home/livingroom/fan/command"
#define FAN_PRESET_TOPIC "home/livingroom/fan/preset_mode"
// UART 配置
#define UART_PORT UART_NUM_1 // 更换为其他可用UART端口
#define BUF_SIZE (1024)
static esp_mqtt_client_handle_t mqtt_client;
namespace iot {
class ESPController : public Thing {
private:
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) {
if (event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
esp_wifi_connect();
}
}
static void ip_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) {
if (event_id == IP_EVENT_STA_GOT_IP) {
esp_mqtt_client_start(mqtt_client);
}
}
static void mqtt_event_handler(void* handler_args, esp_event_base_t base,
int32_t event_id, void* event_data) {
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data;
if (event->event_id == MQTT_EVENT_CONNECTED) {
ESP_LOGI(TAG, "MQTT Connected");
}
}
static void uart_read_task(void* arg) {
uint8_t data[BUF_SIZE];
while (1) {
int len = uart_read_bytes(UART_PORT, data, BUF_SIZE - 1, pdMS_TO_TICKS(100)); // 增加超时时间
if (len > 0) {
data[len] = '\0';
std::string command((char*)data);
command.erase(command.find_last_not_of("\r\n") + 1);
if (command.find("打开客厅灯") != std::string::npos) {
send_mqtt_command(LED_CMD_TOPIC, "ON");
} else if (command.find("关闭客厅灯") != std::string::npos) {
send_mqtt_command(LED_CMD_TOPIC, "OFF");
} else if (command.find("打开风扇") != std::string::npos) {
send_mqtt_command(FAN_CMD_TOPIC, "ON");
} else if (command.find("关闭风扇") != std::string::npos) {
send_mqtt_command(FAN_CMD_TOPIC, "OFF");
} else if (command.find("一挡") != std::string::npos) {
send_mqtt_command(FAN_PRESET_TOPIC, "Low");
} else if (command.find("二挡") != std::string::npos) {
send_mqtt_command(FAN_PRESET_TOPIC, "Medium");
} else if (command.find("三挡") != std::string::npos) {
send_mqtt_command(FAN_PRESET_TOPIC, "High");
}
}else {
vTaskDelay(pdMS_TO_TICKS(10));}
}
}
static void send_mqtt_command(const char* topic, const char* payload) {
esp_mqtt_client_publish(mqtt_client, topic, payload, 0, 1, 0);
ESP_LOGI(TAG, "发送指令: %s -> %s", topic, payload);
}
public:
ESPController() : Thing("LivingRoomController", "客厅设备控制器") {
static bool netif_initialized = false;
if (!netif_initialized) {
esp_netif_init();
esp_event_loop_create_default();
netif_initialized = true;
}
// 初始化 MQTT(正确设置 client_id)
esp_mqtt_client_config_t mqtt_cfg = {};
mqtt_cfg.broker.address.uri = MQTT_URI;
mqtt_cfg.credentials.client_id = CLIENT_ID; // 嵌套在 credentials 中
mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
// 注册 MQTT 事件
esp_mqtt_client_register_event(mqtt_client, MQTT_EVENT_ANY, mqtt_event_handler, NULL);
// 直接启动 MQTT 客户端(假设网络已就绪)
esp_mqtt_client_start(mqtt_client);
// 初始化 UART
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
};
uart_param_config(UART_PORT, &uart_config);
uart_driver_install(UART_PORT, BUF_SIZE * 2, 0, 0, NULL, 0);
// 创建 UART 读取任务
xTaskCreate(uart_read_task, "uart_task", 4096, NULL, 2, NULL);
// 注册属性与方法
properties_.AddBooleanProperty("light_status", "客厅灯状态", [this]() -> bool {
return false;
});
// 使用完全限定的参数类型
methods_.AddMethod("ControlLight", "控制客厅灯开关",
ParameterList(std::vector<Parameter>{Parameter("state", "开关状态", kValueTypeString)}),
[this](const ParameterList& params) {
send_mqtt_command(LED_CMD_TOPIC, params["state"].string().c_str());
}
);
methods_.AddMethod("ControlFan", "控制风扇开关",
ParameterList(std::vector<Parameter>{Parameter("state", "开关状态", kValueTypeString)}),
[this](const ParameterList& params) {
std::string state = params["state"].string();
send_mqtt_command(FAN_CMD_TOPIC, state.c_str());
}
);
methods_.AddMethod("SetFanSpeed", "设置风扇档位",
ParameterList(std::vector<Parameter>{Parameter("speed", "档位(Low/Medium/High)", kValueTypeString)}),
[this](const ParameterList& params) {
std::string speed = params["speed"].string();
send_mqtt_command(FAN_PRESET_TOPIC, speed.c_str());
}
);
}
};
} // namespace iot
DECLARE_THING(ESPController);
注意事项:
一:UART端口别重复
iot文件夹里的用过的UART端口就不要再用了,不然会冲突。
#define UART_PORT UART_NUM_1 // 更换为其他可用UART端口
二:删掉注册WIFI的相关代码
当你实现了ESP32控制ESP8266时,你肯定已经在你代码里注册了WIFI,这里不需要,因为小智AI有自己的STA代码(意思就是,小智AI有“让你给他配置WIFI”这个功能),所以不要注册WIFI的相关代码了。只需要配置好MQTT地址、主题等就行。
三:注意小智AI发送的命令,是大写还是小写
后续想到什么我再补充吧,祝大家更上一层楼,祝中国AI事业更上一层楼。