# AI聊天机器人 ***Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.*** --- AI聊天机器人方案是一个基于UniRTOS与Coze平台构建的智能语音交互与端侧工具调用方案。 本项目以UniRTOS作为端侧运行基础,结合Coze平台完成模型对话与云端能力接入,在移远EG800Z模块上实现按键交互、语音对话、模型问答、端侧工具调用及音频链路处理。系统支持通过按键和语音两种方式发起模型对话,并可在对话过程中调用端侧工具,例如控制LED打开/关闭、打印端侧日志等;同时支持灵活扩展,用户可自行添加端侧或云端工具调用、更改音频编码等。依托API接口丰富的UniRTOS,方案具备良好的跨模组适配能力,便于在不同移远模组上运行,适用于智能语音助手、AI交互终端、端侧工具控制及移远模组快速集成等场景。 该项目重点展示UniRTOS与Coze平台在语音交互、模型对话、端侧工具调用、跨移远模组部署和功能扩展方面的能力,可作为AI聊天机器人、智能语音终端和端云协同交互系统的参考示例。 # 开发资源 | **资源名称** | **数量** | **简要描述** | **获取方法** | | --- | --- | --- | --- | | QuecDuino开发板 | 1 | 项目运行硬件平台。 | [点此购买]() | | USB数据线 | 1 | 用于连接PC和开发板,非“仅充电”类型的USB-A转USB-C。 | [点此购买]() | | PC | 1 | 系统为Windows 10/Windows 11。 | 自行准备 | | SIM卡 | 1 | 可正常上网的USIM卡。 | 自行准备 | | 喇叭 | 1 | 功率 2-5 W的喇叭 | [点此购买]() | | LED模块 | 1 | GPIO驱动的LED模块 | [点此购买]() | | ASRPRO | 1 | 可选,用于语音唤醒 | 自行准备 | # 快速上手 ## 硬件连接 ```{image} images/image_XjwJbPTsLophB8xCt3fc7aNcnbh.webp :width: 942px :height: 615px :align: center ``` 1. 使用跳线帽短接开发板的PA引脚。 2. 将LTE天线安装到开发板的天线底座。 3. 将SIM卡插入SIM卡卡槽。 4. 将喇叭接入扬声器接口。 5. 使用USB数据线连接开发板和PC。 6. (可选)将ASRPRO模块连接至UART1,UART交叉连接:模块TX接主控RX,模块RX接主控TX。 ## 软件部署 ### 开发环境搭建 参考 [UniRTOS快速上手]() 完成开发环境安装,并确认 **unirtos-cli** 可在PowerShell中使用。 ### 代码拉取 新开启PowerShell窗口,执行以下命令: ```powershell unirtos-cli new -r unirtos-maker-examples cd unirtos-maker-examples/ai_chatbot ``` ### 修改配置参数 项目配置集中在 *main/inc/chatbot_config.h*。 | **配置类别** | **配置项** | **说明** | | --- | --- | --- | | 唤醒模块 | *CHATBOT_WAKE_UART_PORT、CHATBOT_WAKE_UART_BAUDRATE* | ASRPRO的UART端口与波特率,当前为UART1、9600 bps。 | | 唤醒关键字 | *CHATBOT_WAKE_KEYWORD* | ASRPRO上报并用于匹配的关键字,当前示例为“WAKE”。 | | 状态LED | *CHATBOT_LED_PIN_NUM* | 状态指示灯物理引脚,当前默认为 55。 | | PTT按键 | *CHATBOT_TALK_BUTTON_PIN_NUM、CHATBOT_TALK_BUTTON_GPIO* | 按键物理引脚与GPIO映射,当前为 50 号引脚对应GPIO37。 | | Coze接入 | *CHATBOT_COZE_BOT_ID、CHATBOT_COZE_AUTH_TOKEN* | 分别配置Bot ID和访问令牌。令牌属于敏感信息,必须使用自己的有效令牌。 | | 音频编码 | *CHATBOT_AUDIO_CODEC_G711A* | 设为 1 时使用G711A、8 kHz;设为 0 时使用PCM16、16 kHz。 | | 音频帧 | *CHATBOT_AUDIO_FRAME_MS* | 单帧时长,当前为 100 ms。 | | 会话参数 | *CHATBOT_SESSION_IDLE_TIMEOUT_MS、CHATBOT_RECONNECT_BACKOFF_MS* | 分别配置空闲超时和重连退避,当前为 60 s和 5 s。 | ### 构建项目 在PowerShell中执行: ```powershell unirtos-cli env-setup unirtos-cli build -m EG800ZCN_LA ``` 构建成功后,命令行末尾将显示: ```plaintext SUCCESS: Unirtos project built successfully! ``` 通过QFlash工具将生成的固件烧录至开发板。 ### 验证项目 1. 长按开发板电源键(S1)开机,打开EPAT工具,根据 [日志调试教程]() 进行配置,配置完成后重启开发板,从模块上电开始抓取模块日志,确保日志输出完整。 ```{image} images/image_WFyMbRcBToyACcxpLC6c2246nrh.webp :width: 1600px :height: 877px :align: center ``` 2. 等待项目初始化完成,长按唤醒键(S2)唤醒Agent建立WebSocket连接,连接建立后将播放Agent下发的开场白,项目源码中指定开场白为“你好,有什么可以帮到您的吗?” 3. 待开场白播放结束,再次长按唤醒键(S2)开始说话,说完后松开按键,等待Agent回复即可。 # 软件设计详解 ## 方案框架 软件划分为六层: 1. 核心逻辑层:*core/chat_core.c* 维护会话状态机,是业务决策中心。 2. 中介模块:*bus/chat_bus.c* 仅传递低频控制事件,不传递音频数据。 3. 音频层:*audio/audio_capture.c*、*audio/audio_player.c* 和 *audio/vad_lite.c* 提供采集、播放和语音段判定。 4. 通信层:*network/* 负责PDP数据连接、WebSocket协议及音频上行/下行任务。 5. 外设层:*peripherals/* 负责ASRPRO、按键、ES8311和状态指示。 6. 工具层:*tools/* 负责Function注册、分发和结果回传。 ## 控制面与音频数据面 控制面承载唤醒、按键、网络、建连、文本、工具调用和错误等小型事件。所有控制事件都封装为 *chat_event_t*,再投递到 *bus/chat_bus.c* 创建的消息队列,由核心任务按顺序消费。 音频数据面不通过消息队列。上行任务直接从采集流读取一帧音频,完成VAD或PTT门控、G711A编码和Base64封装后发送;下行回调解析Base64音频,交由下行任务直接写入播放流。这种分离避免大音频帧占满长度为 16 的控制队列。 ```{image} images/board_RTnJwQ3Tlhwi1CbD5eacGtIxnd4.jpg :width: 820px :height: 635px :align: center ``` ```{image} images/board_AH7AwWgAihzpQ7bgz03cuOUunVh.jpg :width: 820px :height: 635px :align: center ``` ## 业务流程 ### VAD对话流程 ```{image} images/board_JUf9wf6TXhc25rbXqohcoeyknCf.jpg :width: 820px :height: 656px :align: center ``` ### 按键对话流程 ```{image} images/board_O5dwwKfIShRu4rb9Tk6cmJcBnzd.jpg :width: 820px :height: 629px :align: center ``` ## 代码讲解 ### 项目结构 ```plaintext main/ ├── inc/ │ ├── chatbot_app.h # 应用公共初始化接口 │ ├── chatbot_config.h # 引脚、音频、Coze、任务和超时配置 │ ├── chatbot_types.h # 状态、子状态、会话模式和事件类型 │ ├── chat_bus.h # 控制面事件总线接口 │ ├── chat_core.h # 核心状态机接口 │ ├── audio_pipeline.h # 音频采集和播放接口 │ ├── coze_network.h # 网络与Coze会话接口 │ ├── peripherals.h # 唤醒、按键、Codec和指示灯接口 │ └── chatbot_tool.h # 工具注册与分发接口 └── src/ ├── main.c # 应用注册入口unir_chatbot_demo_init └── chatbot/ ├── chatbot_app.c # 分层初始化和能力降级 ├── audio/ │ ├── audio_capture.c # 麦克风采集流 │ ├── audio_player.c # 扬声器播放流 │ ├── vad_lite.c # 轻量VAD状态机 │ └── g711.c # G711A编解码 ├── bus/ │ └── chat_bus.c # 控制面消息队列 ├── core/ │ └── chat_core.c # 会话状态机和业务决策 ├── network/ │ ├── net_mgr.c # PDP数据连接管理 │ ├── coze_client.c # Coze WebSocket客户端 │ ├── coze_protocol.c # Coze事件编解码与分发 │ ├── coze_session.c # 会话生命周期管理 │ └── coze_audio_task.c # 音频上行和下行任务 ├── peripherals/ │ ├── wake_uart.c # ASRPRO串口唤醒 │ ├── talk_button.c # PTT按键和防抖 │ ├── codec_es8311.c # ES8311寄存器配置 │ └── indicator.c # 会话状态指示灯 └── tools/ ├── tool_registry.c # 工具注册表和分发 ├── tool_led.c # led_control工具 └── tool_log.c # log_print工具 ``` 其中,*chatbot_config.h* 集中管理需要适配硬件和云端账号的配置;*core* 是全局业务决策中心;*network* 负责网络、协议和音频数据传输;*audio* 负责本地音频处理;*peripherals* 负责硬件适配;*tools* 负责Function Calling工具扩展。 ### 初始化 项目入口位于 *src/main.c* 中的 *unir_chatbot_demo_init()*。这个函数负责完成三件事: 1. 设置GPIO电压。 2. 打印启动日志。 3. 调用 *chatbot_init()* 创建聊天应用分层初始化。 ```c void unir_chatbot_demo_init(void) { if(qosa_gpio_set_voltage(VOL_3_30V) != (qosa_gpio_error_e)QOSA_OK) { QLOGE("[chatbot] failed to set GPIO voltage"); } QLOGV("[chatbot] enter AI chatbot demo !!!"); QLOGV("[chatbot] sdk version(tag): %s", qosa_sdk_get_version()); QLOGV("[chatbot] fw version: %s", qosa_get_fw_version()); chatbot_init(); qosa_task_sleep_sec(8); chat_bus_post_event(&(chat_event_t){ .type = CHAT_EVT_WAKE_DETECTED, .text = "" }); } UNIRTOS_APP_EXPORT(700, "unir_chatbot_demo", unir_chatbot_demo_init); ``` 此处使用UniRTOS的应用注册机制 **UNIRTOS_APP_EXPORT**,把应用启动函数注册到系统初始化列表中,由框架统一调度。 *src/chatbot/chatbot_app.c* 中的 *chatbot_init()* 按网络、唤醒、LED、工具、Codec、音频、核心和按键的顺序初始化模块。除核心状态机创建失败外,其他模块失败均仅记录降级日志并继续初始化,使设备可以保留可用能力。 ```c void chatbot_init(void) { QLOGI("[chatbot] init start"); if (coze_network_init() != 0) { QLOGW("[chatbot] coze_network_init failed, degraded: no network readiness detection"); } if (wake_uart_init() != 0) { QLOGW("[chatbot] wake_uart_init failed, degraded: no voice wake capability"); } if (indicator_init() != 0) { QLOGW("[chatbot] indicator_init failed, degraded: no LED feedback"); } if (tool_registry_init_all() != 0) { QLOGW("[chatbot] tool_registry_init_all failed, degraded: no tool-calling capability"); } if (codec_es8311_init() != 0) { QLOGW("[chatbot] codec_es8311_init failed at startup, will retry when session starts"); } (void)audio_capture_init(); (void)audio_player_init(); if (chat_core_init() != 0) { QLOGE("[chatbot] chat_core_init failed, application cannot run"); return; } if (talk_button_init() != 0) { QLOGW("[chatbot] talk_button_init failed, degraded: no push-to-talk capability"); } QLOGI("[chatbot] init done, waiting for wake word"); } ``` “尽力初始化”模式使单一外设故障不会阻塞整机启动。例如ASRPRO模块未接或UART配错时,设备仍可依赖PTT按键正常对话。 ### 状态机与事件总线 *inc/chatbot_types.h* 定义五个主状态: - *CHAT_STATE_IDLE*:待机,只保持唤醒和按键输入可用。 - *CHAT_STATE_WAKING*:收到触发,正在确定会话入口。 - *CHAT_STATE_CONNECTING*:正在激活数据连接或建立Coze会话。 - *CHAT_STATE_ACTIVE*:会话存活,音频任务持续运行。 - *CHAT_STATE_ERROR*:发生连接或服务端错误,等待退避。 *CHAT_STATE_ACTIVE* 还派生 *CHAT_SUBSTATE_LISTENING*、*CHAT_SUBSTATE_THINKING* 和 *CHAT_SUBSTATE_SPEAKING* 三个显示子态。子态只驱动反馈,不会重复创建或销毁音频任务。 状态机位于 *src/chatbot/core/chat_core.c*,核心逻辑是处理事件并推进会话状态。其入口函数是 *chat_core_handle_event()*,真正的会话入口是唤醒事件处理分支: ```c static void chat_core_handle_event(const chat_event_t *event) { switch (event->type) { case CHAT_EVT_WAKE_DETECTED: if (g_state == CHAT_STATE_IDLE) { g_state = CHAT_STATE_CONNECTING; g_session_mode = CHAT_SESSION_MODE_VOICE; indicator_apply_state(g_state, g_substate); (void)coze_session_request_start(g_session_mode); } break; case CHAT_EVT_WS_CONNECTED: if (g_state == CHAT_STATE_CONNECTING) { g_state = CHAT_STATE_ACTIVE; g_idle_elapsed_ms = 0; indicator_apply_state(g_state, g_substate); (void)coze_audio_tasks_start(); } break; case CHAT_EVT_BUTTON_PRESSED: if (g_state == CHAT_STATE_IDLE) { g_button_press_start_ms = qosa_get_system_tick_cnt(); } else if (g_state == CHAT_STATE_ACTIVE) { coze_audio_set_ptt_pressed(QOSA_TRUE); coze_audio_flush_downlink(); if (coze_protocol_is_chat_busy()) { (void)coze_session_send_cancel(); } } break; case CHAT_EVT_BUTTON_RELEASED: if (g_state == CHAT_STATE_IDLE) { qosa_uint32_t press_duration_ms = qosa_get_system_tick_cnt() - g_button_press_start_ms; if (press_duration_ms >= CHATBOT_TALK_BUTTON_LONG_PRESS_MS) { g_state = CHAT_STATE_CONNECTING; g_session_mode = CHAT_SESSION_MODE_BUTTON; indicator_apply_state(g_state, g_substate); (void)coze_session_request_start(g_session_mode); } } else if (g_state == CHAT_STATE_ACTIVE && g_session_mode == CHAT_SESSION_MODE_BUTTON) { coze_audio_set_ptt_pressed(QOSA_FALSE); (void)coze_session_send_audio_complete(); } break; default: break; } } ``` 这里体现了聊天机器人的关键策略: - 语音唤醒直接进入语音模式建连。 - 按键在待机时短按无效,长按超过 800 ms才进入按键模式建连。 - 按键在会话中按下时立即执行打断逻辑,松开时提交本轮音频。 - 所有触发都会先更新状态、应用反馈,再调用会话接口。 *src/chatbot/bus/chat_bus.c* 中的 *chat_bus_init()* 创建深度为 16 的消息队列;*chat_bus_post_event()* 使用非阻塞方式投递,队列满时记录告警并丢弃控制事件,避免阻塞UART、GPIO或网络回调。核心任务通过 *chat_bus_wait_event()* 以 1000 ms周期等待事件,同时执行会话空闲检查和重连计时: ```c static void chat_core_task_process(void *argv) { (void)argv; chat_event_t event = {0}; while (1) { int ret = chat_bus_wait_event(&event, CHATBOT_CORE_TICK_PERIOD_MS); if (ret == 0) { chat_core_handle_event(&event); } else { chat_core_tick(); } } } ``` ### 网络与Coze会话 *src/chatbot/network/net_mgr.c* 负责网络就绪状态。*net_mgr_request_ready()* 先查询当前PDP连接;若未就绪且SIM已附着,则异步激活配置的SIM0/PDP1数据连接。PDP激活成功后投递 *CHAT_EVT_NET_READY*,由核心状态机继续会话流程。 ```c int net_mgr_request_ready(void) { qosa_datacall_conn_t conn = QOSA_DATACALL_CONN_INVALID; qosa_datacall_errno_e ret = QOSA_DATACALL_OK; if (net_mgr_refresh_ready() == QOSA_TRUE) { return 0; } if (g_net_activating == QOSA_TRUE) { return 0; } if (qosa_datacall_wait_attached(CHATBOT_COZE_SIM_CID, 0) == QOSA_FALSE) { return -1; } conn = qosa_datacall_conn_new(CHATBOT_COZE_SIM_CID, CHATBOT_COZE_PDP_CID, QOSA_DATACALL_CONN_TCPIP); if (conn == QOSA_DATACALL_CONN_INVALID) { return -1; } ret = qosa_datacall_start_async(conn, 60, (datacall_callback_cb_ptr)net_mgr_datacall_act_cb, QOSA_NULL); if (ret != QOSA_DATACALL_OK) { return -1; } g_net_activating = QOSA_TRUE; return 0; } ``` *src/chatbot/network/coze_session.c* 是会话生命周期接口。*coze_session_request_start()* 记录语音或按键模式并请求连接;收到Coze会话建立事件后,核心调用 *coze_session_on_connected()* 启动音频上行和下行任务;结束会话时,*coze_session_stop()* 先停止音频任务,再关闭WebSocket。 *src/chatbot/network/coze_protocol.c* 负责构造和解析协议消息,包括: - *coze_protocol_build_update_event()*:发送会话音频与事件订阅配置。 - *coze_protocol_build_audio_append_event()*:将编码后的单帧音频封装为上行事件。 - *coze_protocol_build_audio_complete_event()*:通知云端本轮输入结束。 - *coze_protocol_build_cancel_event()*:取消正在生成的旧回复。 - *coze_protocol_build_tool_result_event()*:将本地工具执行结果提交给Coze。 - *coze_protocol_dispatch_downlink()*:将下行文本、音频、工具请求、完成和错误消息转换为本地动作或控制事件。 ### 音频采集、编码与播放 *src/chatbot/audio/audio_capture.c* 和 *src/chatbot/audio/audio_player.c* 均按需打开音频流。*audio_capture_start()* 和 *audio_player_start()* 是幂等接口,已经打开时直接返回;会话结束或打断时关闭对应句柄,释放底层资源。 当前 *CHATBOT_AUDIO_CODEC_G711A* 默认值为 1,因此采集和播放流使用 8 kHz、单声道、16 bit PCM;上行发送前由 *chatbot/network/coze_audio_task.c* 转换为G711A,下行接收后转换回PCM再播放。每帧时长为 100 ms,当前配置下每个PCM帧为 1600 字节。 语音模式中,*src/chatbot/audio/vad_lite.c* 使用轻量RMS门限状态机识别语音开始、持续和结束。*coze_audio_task.c* 还按 *CHATBOT_VOICE_MIC_SOFT_GAIN* 对语音模式采集PCM施加 8 倍软件增益,提高低音量语音的起声识别率。按键模式不经过VAD门控。 下行播放由独立任务串行写入。*audio_player_write_frame()* 正确处理底层写入返回 0 或部分写入的情况:最多等待 2 s,持续满缓冲则放弃当前帧并输出告警,避免播放异常永久阻塞通信任务。 ```c int audio_player_write_frame(const qosa_uint8_t *pcm, qosa_uint32_t len) { if ((g_player_handle == QOSA_NULL) || (pcm == QOSA_NULL)) { return -1; } qosa_uint32_t written = 0; qosa_uint32_t full_retry = 0; while (written < len) { qosa_int32_t ret = qcm_aud_stream_write(g_player_handle, (qosa_uint8_t *)(pcm + written), len - written); if (ret < 0) { QLOGW("[chat_audio_player] write failed, ret=%d", ret); return -1; } if (ret == 0) { full_retry++; if (full_retry >= 400) /* 400 * 5ms ≈ 2s */ { QLOGW("[chat_audio_player] write timeout: sink full for 2s"); return -1; } qosa_task_sleep_ms(5); continue; } full_retry = 0; written += (qosa_uint32_t)ret; } return (int)written; } ``` ### 外设与状态反馈 *src/chatbot/peripherals/wake_uart.c* 配置UART1、9600 bps、8 数据位、1 停止位、无校验和无流控。接收回调读取不超过 64 字节的缓存,命中配置关键字后投递唤醒事件。 *src/chatbot/peripherals/talk_button.c* 使用双边沿中断区分按下和松开事件,并以 50 ms软件窗口过滤抖动。核心状态机负责长按 800 ms的会话建立判定,因此驱动层不依赖具体业务状态。 *src/chatbot/peripherals/indicator.c* 使用物理引脚 55 的默认GPIO配置初始化状态灯。当前硬件高电平点亮:待机和异常熄灭,唤醒中、建连中和活动会话点亮。*substate* 参数保留给后续“闪烁/呼吸”等差异化反馈使用。 ```c void indicator_apply_state(chat_state_e state, chat_substate_e substate) { (void)substate; if (g_indicator_gpio_num == QOSA_GPIO_MAX) { return; } qosa_gpio_level_e level = CHATBOT_LED_INACTIVE_LEVEL; switch (state) { case CHAT_STATE_WAKING: case CHAT_STATE_CONNECTING: case CHAT_STATE_ACTIVE: level = CHATBOT_LED_ACTIVE_LEVEL; break; case CHAT_STATE_IDLE: case CHAT_STATE_ERROR: default: level = CHATBOT_LED_INACTIVE_LEVEL; break; } (void)qosa_gpio_set_level(g_indicator_gpio_num, level); } ``` ### 工具调用 工具层采用注册表模式。*src/chatbot/tools/tool_registry.c* 最多登记 8 个工具,每个工具遵循 *tool_handler_t* 处理函数原型。 ```c typedef int (*tool_handler_t)(const char *args_json, char *result_json, int result_buf_len); int tool_registry_dispatch(const char *tool_name, const char *args_json, char *result_json, int result_buf_len) { for (int i = 0; i < g_tool_count; i++) { if (qosa_strcmp(g_tool_registry[i].name, tool_name) == 0) { return g_tool_registry[i].handler(args_json, result_json, result_buf_len); } } qosa_snprintf(result_json, result_buf_len, "{\"error\":\"tool not found\"}"); return -1; } ``` 工具查找采用线性遍历,工具数量较少时性能可接受。注册表本身在 *tool_registry_init* 中初始化,依次调用各工具模块的初始化函数。 以 *led_control* 为例,其处理函数 *tool_led_control_handler* 解析JSON参数中的action字段,控制物理引脚56对应的GPIO电平: ```c static int tool_led_control_handler(const char *args_json, char *result_json, int result_buf_len) { qosa_cjson_t *root = qosa_cjson_parse(args_json); if (root == QOSA_NULL) { qosa_snprintf(result_json, result_buf_len, "{\"error\":\"invalid json\"}"); return -1; } const char *action = qosa_cjson_get_string(root, "action"); qosa_gpio_level_e level; if (action != QOSA_NULL && qosa_strcmp(action, "on") == 0) { level = CHATBOT_LED_ACTIVE_LEVEL; /* 高电平点亮 */ } else { level = CHATBOT_LED_INACTIVE_LEVEL; /* 低电平熄灭 */ } qosa_gpio_set_level(TOOL_LED_GPIO_NUM, level); qosa_cjson_delete(root); qosa_snprintf(result_json, result_buf_len, "{\"result\":\"ok\"}"); return 0; } ``` 工具执行完成后,返回JSON格式的结果字符串。核心状态机收到 *CHAT_EVT_TOOL_CALL* 事件时,会将解析出的 *chat_id*、*tool_call_id* 和工具结果打包,通过控制面通道发送回Coze服务端。 *tool_registry_init_all()* 初始化注册表并依次加载内置工具;单个工具初始化失败不会影响其他工具。 内置工具如下: | **工具名称** | **实现文件** | **输入** | **行为** | | --- | --- | --- | --- | | led_control | *src/chatbot/tools/tool_led.c* | action为on或off | 控制物理引脚 56 对应的LED灯的亮灭状态。 | | log_print | *src/chatbot/tools/tool_log.c* | 非空text字符串 | 将文本写入UniRTOS日志,最长保留 128 字节。 | 当Coze下发工具请求时,协议层将请求事件投递至核心状态机。核心调用 *tool_registry_dispatch()* 执行匹配工具,再通过 *coze_session_send_tool_result()* 回传JSON结果。工具执行与音频收发位于不同链路,不会阻塞实时语音。 新增工具时,执行以下步骤: 1. 在Coze Bot中创建名称、说明和JSON参数Schema与本地工具一致的Function。 2. 创建 *src/chatbot/tools/tool_xxx.c*,实现符合 *tool_handler_t* 的处理函数和 *tool_xxx_init()*。 3. 在 *src/chatbot/tools/tool_registry.c* 的 *tool_registry_init_all()* 中调用 *tool_xxx_init()*。 4. 构建、烧录并通过Coze Function Calling验证输入参数、设备动作与JSON结果。 不需要修改核心状态机、事件总线或通信层。 # 应用演示 修改项目Coze平台相关信息(访问令牌、bot_id等),其他端侧外设可自由修改配置(唤醒键、状态灯等),以下演示使用默认配置。 ## 上电开机 长按开发板上的按键S1,此时编号为D1的开机指示灯(红色)亮起,表示模块已开机。 ```{image} images/image_DKBPb1D9yoS0SVxIc6pckHuynKg.webp :width: 1003px :height: 605px :align: center ``` ## 唤醒连接 长按开发板上的按键S2,此时编号为D3的状态指示灯(绿色)亮起,松开按键,模块开始连接Coze平台,等待喇叭播放固定开场白,表示已成功连接Coze平台。 ```{image} images/image_Nfecbzmfio0m6CxYFQJc8WMwnZb.webp :width: 943px :height: 620px :align: center ``` ## 与智能体对话 再次长按开发板上的按键S2,对着板载麦克风说出问题,完成之后松开按键,智能体会对识别的问题进行回复,继续对话重复上述操作即可。在智能体输出回复期间也可长按打断本轮对话,开始下一轮新对话。 ```{image} images/image_IZz4b9azsounRpxU7uZcLCMOnug.webp :width: 1900px :height: 968px :align: center ``` ## 断开连接 在无对话空闲状态下,60 s后模块将主动断开与Coze平台的连接,此时状态指示灯D3将会熄灭。 ```{image} images/image_F5bdbJ2hsodcFwxNU60cbbIBnSb.webp :width: 988px :height: 566px :align: center ```