FastBond3挑战部分-基于FireBeetle ESP32-S3与ESP32-S3-LCD-EV-BOARD完成智能家居系统
该项目使用了DFRobot FireBeetle ESP32-S3与ESP32-S3-LCD-EV-BOARD,实现了基于大语言模型的智能家居系统的设计,它的主要功能为:智能家居、大语言模型智能决策、人机交互。
标签
嵌入式系统
SyntaxError
更新2024-10-31
76

项目背景

智能家居市场近年来蓬勃发展,引领着未来生活方式的变革。在这片充满活力的市场中,各类智能家居设备如雨后春笋般涌现,从智能音箱的悦耳旋律到智能电视的沉浸式体验,从智能冰箱的智能管理到智能门锁的安全守护,再到智能照明的温馨氛围营造,无一不展现着科技如何让居家生活变得更加智能与便捷。

在此基础上,本项目推出的智能家居系统集成了上述智能家居设备的核心功能,可以融合多媒体显示等功能,为用户带来直观与便捷的交互体验。更为重要的是,本项目创新性地将大语言模型深度融入该系统平台之中,这一举措不仅极大地提升了设备的智能化水平,更赋予了其强大的决策辅助能力。

所用主要核心器件介绍

1.(来自DFROBOT)FireBeetle 2 ESP32-S3是一款基于ESP32-S3-WROOM-1-N16R8模组设计的主控板。ESP32-S3-WROOM-1-N16R8模组拥有16MB Flash和8MB PSRAM,可以存储更多的代码和数据,模组搭载的ESP32-S3芯片拥有强大的神经网络运算能力和信号处理能力,适用于图像识别、语音识别等项目。FireBeetle 2 ESP32-S3板载摄像头接口,可以方便的连接摄像头,独立的摄像头供电电路,减少了其他信号对摄像头的干扰。开发板附带了一个OV2640摄像头,该摄像头拥有200万像素和68°视场角,最高支持1600*1200分辨率。FireBeetle 2 ESP32-S3板载GDI屏幕接口,解决使用屏幕时的接线烦恼,集成电源管理功能,支持锂电池充电和硬件开关机。

2.(来自乐鑫)ESP32-S3-LCD-EV-Board 是一款基于 ESP32-S3 芯片的屏幕交互开发板,通过搭配不同类型的 LCD 子板,可以驱动 IIC、SPI、8080 以及 RGB 接口的 LCD 显示屏。同时它还搭载双麦克风阵列,支持语音识别和近/远场语音唤醒,具有触摸屏交互和语音交互功能,满足用户对多种不同分辨率以及接口的触摸屏应用产品的开发需求。

3. (来自DFROBOT)TCS3200颜色传感器(SEN0101)是一款全彩的颜色检测器,包括了一块TAOS TCS3200RGB感应芯片和4个白色LED灯,TCS3200能在一定的范围内检测和测量几乎所有的可见光。TCS3200有大量的光检测器,每个都有红绿蓝和清除4种滤光器。每6种颜色滤光器均匀地按数组分布来清除颜色中偏移位置的颜色分量。内置的振荡器能输出方波,其频率与所选择的光的强度成比例关系。

4.Adafruit的两个板卡,其中一款Feather板也是基于ESP32-S3,拥有不俗的性能,板载一块小屏幕,方便调试,另一块则是15x7的矩阵灯板。

方案框图与设计思路

本项目的设计框图如下所示,其中大致可以分为三部分,首先是PC机上的云端服务,负责提供flask的总体通信交互框架,用于不同板卡间以及与上位机的通信交互,同时也在上位机上部署了大语言模型的推理服务,在通过ESP32-S3-LCD-EV-BOARD的LVGL界面完成与用户的人机交互,收集到用户的意向(比如通过文本输入等)后,经由GPT-4o模型的分析,通过Function Call等技术完成推理,进行决策,下发需要完成的任务,如开灯、开启空调等,而这些细节任务的执行则由Adafruit的ESP32板辅助完成,其中信息交互中枢的任务则有FireBeetle2 ESP32 S3承担任务。

image.png

功能演示

首先在上位机pc上启动flask服务以及大语言模型推理服务的接口,启动成功如图所示:

启动FireBeetle板的中间通信服务,然后将Adafruit的ESP32S3板卡与矩阵灯板以及SEN0101传感器相连(本项目主要使用其4个闪亮的白色LED灯,用以模拟家庭环境中的开灯如白炽灯场景),连接对应引脚,配置完I2C地址后,上电,可以看到默认情况下,灯板以及LED灯都是熄灭状态,而ESP32由于烧录的CPY固件,因此板载的小屏幕上会显示输出信息,可以看到板子上电后连接到wifi,并且自动显示地址,并且已经在监听5005端口,等待大语言模型下发的指令传递至此,从而继续操作其他两个外设。

将ESP32的EV板上电,可以看到基于LVGL构建的交互界面,最上方显示连接wifi后分配的IP地址,中间则是一个文本输入框,用来接收用户输入的文字文本信息,下方一个按钮,用来确认用户输入的信息。

点击文本输入框,界面会自动弹出一个软键盘,方便用户直接输入信息,免去要外接键盘的麻烦。

然后用户可以自由输入感受、想法等等一切文本信息,借由大语言模型的出色的理解能力,可以与用户共情,懂你所想,从而完成用户需要的任务。比如这里输入“it is night already”,并点击确认,首先会回显确认信息,然后借由flask服务将文本信息转发至PC机,基于Function Call技术进行大语言模型处理。

这里查看大语言模型输出可以发现,模型成功意识到现在天色已晚,潜台词需要开灯照明了

然后查看“白炽灯”状态,果然已经由大语言模型智能处理,打开了灯泡开始照明,“Enjoy your brightness”!

同理,可以输入如“I am hot”之类的主观感受,大语言模型也会懂你意思,指挥开启空调服务

而且也可以输入比如“I am so bored”让大模型帮忙找点乐子,然后就会发现大模型用灯板摆了些字符图案什么的逗你开心

具体细节可以通过视频观看了解,总体来说,智能性以及可拓展性还是很强的。

关键代码说明

在大语言模型的实际使用中,我个人基于Langchain框架进行开发,也推荐初学者采用此框架。Langchain是一个流行的开源大语言模型操作框架,为开发者提供了一系列的工具和组件,使得与语言模型中的各种数据(如SQL、PDF、CSV)等的连接、语言模型的应用和优化直接简洁。

下面代码是使用langchain框架调用工具的一个示例,主要部分有两处,一处是@tool下的Function的功能定义说明以及具体实现,另一处则是基于OpenAI的chat框架,应用langchain提供的Agent进行Function Call的效果实现。

复制import httpx

from langchain import hub
from langchain.agents import AgentExecutor, create_react_agent, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate

api_key = "Your_API_Key"


# Define tools to be used with the agent
@tool
def call_ambulance(position: str) -> str:
"""Call ambulance in terms of emergency like accident to the given position """
return "Ambulance called successfully."

@tool
def call_police(position: str) -> str:
"""Call police in terms of heavy emergency to maintain situation to the given position"""
return "Police called successfully."

tools = [call_ambulance, call_police]
llm = ChatOpenAI(

openai_api_key=api_key,
model="gpt-4o")
question = "发生交通事故了"

prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)

# Invoke the agent with the combined input
result = agent_executor.invoke(
{"input": question, "tools": tools}).values()

ESP32的LCD板上的LVGL界面也是项目核心,按官方文档配置arduino环境后,即可使用下面我提供的代码构建视频中显示的界面,其中每段功能的注释也已加入。

#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ESP_Panel_Library.h>
#include <lvgl.h>
#include "lvgl_port_v8.h"


// WiFi信息
const char* ssid = "Iridescent";
const char* password = "20020120";
const char* serverUrl = "http://172.20.10.4:5000/api/data";  // 替换为PC的IP地址


lv_obj_t *textarea;
lv_obj_t *keyboard;
lv_obj_t *label_status;


// 按钮事件处理函数,发送文本内容到PC上的Flask服务器
static void btn_event_cb(lv_event_t * e)
{
    const char * text = lv_textarea_get_text(textarea);


    // 在串口输出文本框中的内容
    Serial.print("Text input: ");
    Serial.println(text);


    // 发送数据到Flask服务器
    if (WiFi.status() == WL_CONNECTED) {
        HTTPClient http;
        http.begin(serverUrl);
        http.addHeader("Content-Type", "application/json");


        // 创建JSON数据
        String jsonData = "{\"data\": \"" + String(text) + "\"}";


        // 发送POST请求
        int httpResponseCode = http.POST(jsonData);


        if (httpResponseCode > 0) {
            Serial.print("POST Response Code: ");
            Serial.println(httpResponseCode);
            if (httpResponseCode == HTTP_CODE_OK) {
                String response = http.getString();
                Serial.println("Server response: " + response);
            }
        } else {
            Serial.print("Error on sending POST: ");
            Serial.println(httpResponseCode);
        }


        http.end();
    } else {
        Serial.println("Error in WiFi connection");
    }


    // 弹出提示框显示输入内容
    lv_obj_t *msg = lv_msgbox_create(NULL, "Input", text, NULL, true);
    lv_obj_align(msg, LV_ALIGN_CENTER, 0, 0);
}


// 文本框事件处理函数,点击文本框时弹出键盘
static void textarea_event_cb(lv_event_t * e)
{
    lv_keyboard_set_textarea(keyboard, textarea);  // 关联键盘和文本输入框
    lv_obj_clear_flag(keyboard, LV_OBJ_FLAG_HIDDEN);  // 显示键盘
}


// 键盘事件处理函数
static void keyboard_event_cb(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t *kb = lv_event_get_target(e);
    if(code == LV_EVENT_CANCEL) {
        lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);  // 隐藏键盘
    }
}


// 初始化WiFi并显示状态
void initWiFi()
{
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi...");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }


    Serial.println("\nConnected to WiFi!");
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());


    // 在屏幕上显示WiFi连接成功和IP地址
    String status_text = "Connected: " + WiFi.localIP().toString();
    lv_label_set_text(label_status, status_text.c_str());
}


void setup()
{
    Serial.begin(115200);
    String title = "LLM Based HomeAssistant Platform";
    Serial.println(title + " start");


    // 初始化显示屏
    ESP_Panel *panel = new ESP_Panel();
    panel->init();
#if LVGL_PORT_AVOID_TEAR
    ESP_PanelBus_RGB *rgb_bus = static_cast<ESP_PanelBus_RGB *>(panel->getLcd()->getBus());
    rgb_bus->configRgbFrameBufferNumber(LVGL_PORT_DISP_BUFFER_NUM);
    rgb_bus->configRgbBounceBufferSize(LVGL_PORT_RGB_BOUNCE_BUFFER_SIZE);
#endif
    panel->begin();
    // 初始化LVGL
    lvgl_port_init(panel->getLcd(), panel->getTouch());
    lvgl_port_lock(-1);


    // 创建标题标签
    lv_obj_t *title_label = lv_label_create(lv_scr_act());
    lv_label_set_text(title_label, title.c_str());
    lv_obj_align(title_label, LV_ALIGN_TOP_MID, 0, 5);

    // 创建WiFi状态标签
    label_status = lv_label_create(lv_scr_act());
    lv_label_set_text(label_status, "Connecting to WiFi...");
    lv_obj_align(label_status, LV_ALIGN_TOP_MID, 0, 30);

    // 初始化WiFi连接
    initWiFi();

    // 创建文本输入框
    textarea = lv_textarea_create(lv_scr_act());
    lv_obj_set_width(textarea, 200);
    lv_obj_align(textarea, LV_ALIGN_CENTER, 0, -40);
    lv_textarea_set_placeholder_text(textarea, "Enter text here");
    lv_obj_add_event_cb(textarea, textarea_event_cb, LV_EVENT_CLICKED, NULL);

    // 创建按钮
    lv_obj_t *btn = lv_btn_create(lv_scr_act());
    lv_obj_align(btn, LV_ALIGN_CENTER, 0, 40);
    lv_obj_set_size(btn, 100, 50);
    lv_obj_t *btn_label = lv_label_create(btn);
    lv_label_set_text(btn_label, "Submit");
    lv_obj_center(btn_label);
    lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
    // 创建并隐藏虚拟键盘
    keyboard = lv_keyboard_create(lv_scr_act());
    lv_obj_add_flag(keyboard, LV_OBJ_FLAG_HIDDEN);
    lv_obj_add_event_cb(keyboard, keyboard_event_cb, LV_EVENT_CANCEL, NULL);
    lvgl_port_unlock();
    Serial.println(title + " end");
}

void loop()
{
    lv_task_handler();
    delay(5);
}

PCB以及其他说明

很可惜,基于Adafruit的Feather系列修改画了一版pcb板,准备用来容纳不同小ESP32板之间的交互,然而打板有了一些延误,不能在项目提交截止前完全展示了,因此用面包板替代展示了所构建的功能。修改的一版PCB板可以在附件看到,后面PCB到了,也会继续尝试完美完成哈哈哈,如果效果好,也会再传上来。

心得体会

非常荣幸能够参与FastBond活动,通过这次活动,我深刻地体验到了电子电路设计的乐趣和挑战,并且对电子器件有了更深入的了解。我希望能够进一步探索更智能、人性化的方式,让用户更加轻松地体验到良好的家庭生活。为了实现这个目标,最后,再次感谢FastBond活动的组织者和主办方提供了一个如此棒的学习和交流平台,祝愿FastBond活动越来越成功!

软硬件
电路图
附件下载
PCB_V2.json
代码.zip
团队介绍
个人项目
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号