这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 国产MCU » 【2026AI硬件开发学习必看系列】通过MCP给AI语音助手加上温湿度测量播报功

共1条 1/1 1 跳转至

【2026AI硬件开发学习必看系列】通过MCP给AI语音助手加上温湿度测量播报功能

菜鸟
2026-04-17 18:16:14     打赏
【2026 AI硬件开发学习必看系列】通过MCPAI语音助手加上温湿度测量播报功能前言本文是一篇实战教程,旨在引导您通过自定义 MCP(Model Context Protocol) 工具,结合语音指令控制硬件设备,实现智能环境监测。

LS26Arcs-mini 开发板二次开发,展示如何通过语音触发 I2C 通信,驱动外接温湿度传感器,并实时返回环境数据。

演示视频https://docs2.listenai.com/zz/11585.mp4?shortId=BYshUSGvT

无论您是物联网开发者、嵌入式爱好者,还是希望将语音交互能力融入硬件项目的创客,本教程都将带您一步步构建一个完整的“语音—I2C—传感器数据”软硬件联动系统。您将学习到如何配置 MCP 工具、编写 I2C 传感器驱动,并实现从语音指令到实时数据采集的无缝交互。

使用的温湿度传感器是 AHT10 ,参考手册下载链接:https://docs2.listenai.com/zz/11282.pdf?shortId=BYshUSGvT

 目标:

当您说出“查询环境温湿度”,Arcs-mini 将通过 I2C(PA04/PA05)读取温湿度传感器数据,并语音回复当前温度值。实操之前,请确保已根据文档开发环境搭建与烧录 | 聆思文档中心 搭建开发环境。

固件下载

如果您不想重新编译代码而希望直接体验本固件,可点击下载。

固件下载链接:

https://docs2.listenai.com/zz/11821.lpk?shortId=BYshUSGvT

下载后,可以按照文档恢复出厂固件&升级固件教程 | 聆思文档中心(https://docs2.listenai.com/x/IMbN1kL5H) 进行烧录

代码示例

源码下载:apps.zip(https://docs2.listenai.com/zz/11820.zip?shortId=BYshUSGvT)

下载后,将其替换 arcs_mini 项目的 apps 文件夹

diff 文件下载链接:mcp_tool_AHT10.diff(https://docs2.listenai.com/zz/11587.diff?shortId=BYshUSGvT)

下载后,可以通过命令git apply ./mcp_tool_AHT10.diff 应用更改

一、硬件连接

AHT10 连接 Arcs_mini 的扩展 GPIO 接口

PA02 --> VIN

PA03 --> GND

PA04 --> SCL

PA05 --> SDA

二、初始化 温湿度计1.新增文件

在项目下目录apps/arcs-mini/services目录下添加文件service_aht10.c service_aht10.h,然后修改 CMakeLists.txt 文件使新增文件能够参与编译

service_aht10.c2.根据业务选择需要使用的外设

下表截取自数据手册的APPENDIX章节

 本文档演示使用 PA04 PA05 I2C0 外设, 可以找到 GPIOA_04 GPIOA_04I2C0 外设对应 Function 8

3.新增 温湿度计 驱动

 apps/arcs-mini/services/service_aht10.c 文件增加如下代码:

 


#include "service_aht10.h"
#include "IOMuxManager.h"
#include "Driver_I2C.h"
#include "lisa_log.h"
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
 
#define TAG "aht10"
 
#define AHT10_I2C_ADDR              (0x38)
#define AHT10_CMD_INIT              (0xE1)
#define AHT10_CMD_MEASURE           (0xAC)
#define AHT10_CMD_SOFT_RESET        (0xBA)
 
#define SERVICE_AHT10_SDA_PIN       (5U)
#define SERVICE_AHT10_SCL_PIN       (4U)
 
#define AHT10_POWERON_DELAY_MS      (40)
#define AHT10_INIT_DELAY_MS         (20)
#define AHT10_RESET_DELAY_MS        (400)
#define AHT10_MEASURE_DELAY_MS      (80)
#define AHT10_MAX_RETRY             (3)
 
static void *g_i2c_dev = NULL;
static volatile uint32_t g_i2c_event = 0;
static uint8_t g_aht10_addr = AHT10_I2C_ADDR;
static uint8_t g_sda_pin = 0xFF;
static uint8_t g_scl_pin = 0xFF;
 
static void aht10_i2c_cb(uint32_t event, void *workspace)
{
(void)workspace;
   g_i2c_event |= event;
}
 
static void aht10_delay_ms(uint32_t ms)
{
vTaskDelay(ms);
}
 
static int aht10_wait_event(void)
{
uint32_t timeout = 3000; // 3s timeout
while (g_i2c_event == 0 && timeout--) {
vTaskDelay(1);
}
return (g_i2c_event == 0) ? -1 : 0;
}
 
 
static int aht10_i2c_write(uint8_t addr, const uint8_t *data, uint32_t len)
{
LISA_LOGI(TAG, "I2C write: addr=0x%02X, len=%d", addr, len);
if (data && len > 0) {
LISA_LOGI(TAG, "  data[0]=0x%02X", data[0]);
if (len > 1) LISA_LOGI(TAG, "  data[1]=0x%02X", data[1]);
if (len > 2) LISA_LOGI(TAG, "  data[2]=0x%02X", data[2]);
}
 
   g_i2c_event = 0;
int32_t ret = I2C_MasterTransmit(g_i2c_dev, addr, (uint8_t *)data, len, 0);
LISA_LOGI(TAG, "I2C_MasterTransmit returned: %d", ret);
 
if (aht10_wait_event() != 0) {
LISA_LOGE(TAG, "I2C write timeout");
return -1;
}
 
return 0;
}
 
static int aht10_i2c_read(uint8_t addr, uint8_t *data, uint32_t len)
{
   g_i2c_event = 0;
I2C_MasterReceive(g_i2c_dev, addr, data, len, 0);
 
if (aht10_wait_event() != 0) {
LISA_LOGE(TAG, "I2C read timeout");
return -1;
}
 
return 0;
}
 
int service_aht10_init(void)
{
uint8_t sda_pin = SERVICE_AHT10_SDA_PIN;
uint8_t scl_pin = SERVICE_AHT10_SCL_PIN;
 
LISA_LOGI(TAG, "AHT10 I2C initialization starting, SDA=%d, SCL=%d", sda_pin, scl_pin);
 
IOMuxManager_PinConfigure(CSK_IOMUX_PAD_A, sda_pin, CSK_IOMUX_FUNC_ALTER8);
IOMuxManager_PinConfigure(CSK_IOMUX_PAD_A, scl_pin, CSK_IOMUX_FUNC_ALTER8);
 
   g_sda_pin = sda_pin;
   g_scl_pin = scl_pin;
 
   g_i2c_dev = I2C0();
if (!g_i2c_dev) {
LISA_LOGE(TAG, "I2C0 device not found");
return -1;
}
 
I2C_Initialize(g_i2c_dev, aht10_i2c_cb, NULL);
I2C_PowerControl(g_i2c_dev, CSK_POWER_FULL);
I2C_Control(g_i2c_dev, CSK_I2C_TRANSMIT_MODE, 0);
I2C_Control(g_i2c_dev, CSK_I2C_BUS_SPEED, CSK_I2C_BUS_SPEED_STANDARD);
I2C_Control(g_i2c_dev, CSK_I2C_BUS_CLEAR, 0);
 
aht10_delay_ms(AHT10_POWERON_DELAY_MS);
 
 
LISA_LOGI(TAG, "AHT10 I2C initialized, device addr=0x%02X", g_aht10_addr);
return 0;
}
 
int service_aht10_read_data(float *humidity, float *temperature)
{
if (!humidity || !temperature || !g_i2c_dev) {
return -1;
}
 
uint8_t status = 0;
uint8_t data[6] = {0};
 
// Step 1: Read status byte to check calibration bit[3]
LISA_LOGI(TAG, "Reading status byte...");
if (aht10_i2c_read(g_aht10_addr, &status, 1) != 0) {
LISA_LOGE(TAG, "Failed to read status byte");
return -1;
}
 
uint8_t calibrated = (status >> 3) & 0x01;
uint8_t busy = (status >> 7) & 0x01;
LISA_LOGI(TAG, "Status: 0x%02X, calibrated=%d, busy=%d", status, calibrated, busy);
 
// Step 2: Check calibration bit[3], send init command if not calibrated
if (calibrated == 0) {
LISA_LOGI(TAG, "Sensor not calibrated, sending init command (0xE1 0x08 0x00)...");
uint8_t init_cmd[3] = {AHT10_CMD_INIT, 0x08, 0x00};
if (aht10_i2c_write(g_aht10_addr, init_cmd, 3) != 0) {
LISA_LOGE(TAG, "Init command write failed");
return -1;
}
aht10_delay_ms(AHT10_INIT_DELAY_MS);
LISA_LOGI(TAG, "Init command sent and delayed");
} else {
LISA_LOGI(TAG, "Sensor already calibrated, skip init");
}
 
// Step 3: Send measure command (0xAC 0x33 0x00)
LISA_LOGI(TAG, "Sending measure command (0xAC 0x33 0x00)...");
uint8_t measure_cmd[3] = {AHT10_CMD_MEASURE, 0x33, 0x00};
if (aht10_i2c_write(g_aht10_addr, measure_cmd, 3) != 0) {
LISA_LOGE(TAG, "Measure command write failed");
return -1;
}
 
// Step 4: Wait 100ms for measurement
LISA_LOGI(TAG, "Waiting 100ms for measurement...");
aht10_delay_ms(100);
 
// Step 5: Poll status byte, check busy bit[7]
for (int retry = 0; retry < AHT10_MAX_RETRY; retry++) {
if (aht10_i2c_read(g_aht10_addr, &status, 1) != 0) {
LISA_LOGW(TAG, "Failed to read status in poll, retry %d", retry + 1);
aht10_delay_ms(AHT10_MEASURE_DELAY_MS);
continue;
}
 
       busy = (status >> 7) & 0x01;
LISA_LOGI(TAG, "Poll status: 0x%02X, busy=%d", status, busy);
 
if (busy == 0) {
// Step 6: Data ready, read 6 bytes
LISA_LOGI(TAG, "Data ready, reading 6 bytes...");
if (aht10_i2c_read(g_aht10_addr, data, 6) != 0) {
LISA_LOGE(TAG, "Failed to read measurement data");
return -1;
}
 
// Parse humidity from bytes 1-3 (20-bit value)
uint32_t raw_humi = ((uint32_t)(data[1]) << 12) | ((uint32_t)(data[2]) << 4) | ((data[3] & 0xF0) >> 4);
*humidity = (float)raw_humi * 100.0f / 1048576.0f;
 
// Parse temperature from bytes 3-5 (20-bit value)
uint32_t raw_temp = ((uint32_t)(data[3] & 0x0F) << 16) | ((uint32_t)(data[4]) << 8) | data[5];
*temperature = (float)raw_temp * 200.0f / 1048576.0f - 50.0f;
 
LISA_LOGI(TAG, "Humidity: %.2f%%, Temperature: %.2f°C", *humidity, *temperature);
return 0;
}
 
// Still busy, wait and retry
if (retry < AHT10_MAX_RETRY - 1) {
LISA_LOGI(TAG, "Measurement still busy, polling retry %d/%d", retry + 1, AHT10_MAX_RETRY);
aht10_delay_ms(AHT10_MEASURE_DELAY_MS);
}
}
 
LISA_LOGE(TAG, "Timeout waiting for measurement to complete");
return -1;
}
 
int service_aht10_get_temperature_humidity(float *humidity, float *temperature)
{
if (!humidity || !temperature) {
return -1;
}
 
// 调用 AHT10 驱动读取温湿度
int ret = service_aht10_read_data(humidity, temperature);
if (ret != 0) {
LOGE("Failed to read AHT10 sensor data, ret=%d", ret);
return -1;
}
 
LOGI("Sensor data - Humidity: %.2f%%, Temperature: %.2f°C",
*humidity, *temperature);
 
return 0;
}


 apps/arcs-mini/services/service_aht10.h 文件增加如下代码:

 


#ifndef SERVICE_AHT10_H
#define SERVICE_AHT10_H
 
#include <stdint.h>
 
#ifdef __cplusplus
extern "C" {
#endif
 
/**
* @brief 初始化 AHT10 传感器(I2C)
* 使用服务内固定引脚进行初始化。
*
* @return 0 成功, 非0 失败
*/
int service_aht10_init(void);
 
/**
* @brief 读取当前湿度和温度
*
* @param humidity 输出湿度百分比(0-100)
* @param temperature 输出温度(摄氏度)
* @return 0 成功, 非0 失败
*/
int service_aht10_read_data(float *humidity, float *temperature);
 
/**
* @brief 读取温湿度传感器数据
* @param humidity 湿度输出指针
* @param temperature 温度输出指针
* @return 0成功,-1失败
*/
int service_aht10_get_temperature_humidity(float *humidity, float *temperature);
 
#ifdef __cplusplus
}
#endif
 
#endif // SERVICE_AHT10_H


三、增加功能测试代码1. 修改文件

 引入头文件,在 apps/arcs-mini/main.c 中顶部添加代码:


#include "service_aht10.h"


 调用初始化函数,在 apps/arcs-mini/main.c 中的 main() 函数中添加代码:

 


service_aht10_init();


 按键触发温度获取,在 main.c 中的 button_changed() 函数中添加代码:


/* 四击: 读取并打印温湿度 */
float humidity = 0.0f;
float temperature = 0.0f;
extern int service_aht10_get_temperature_humidity(float *humidity, float *temperature);
int ret = service_aht10_get_temperature_humidity(&humidity, &temperature);
if (ret == 0) {
   LISA_LOGI(TAG, "四击: 当前温度: %.2f°C, 湿度: %.2f%%", temperature, humidity);
} else {
   LISA_LOGE(TAG, "四击: 读取温湿度失败");
}


2.测试功能

 按4下电源键,观察串口日志,预期应该有如下日志:

如果没有预期日志可以检查硬件连接是否正确

四、端侧注册 MCP 工具1.新增文件

 apps/arcs-mini/mcp_tools 文件夹下添加文件 mcp_tool_aht10.c

 修改 apps/arcs-mini/mcp_tools/CMakeLists.txt 添加代码: mcp_tool_aht10.c

2.添加 MCP 工具

apps/arcs-mini/mcp_tools/mcp_tool_aht10.c 文件下添加代码:

 


#include "cJSON.h"
#include "lisa_log.h"
#include "mcp.h"
#include <string.h>
#include <stdio.h>
#include "service_aht10.h"
 
#define TAG "mcp_tool_aht10"
 
 
/**
* @brief 温湿度传感器工具的列表信息创建函数
* @param name 工具名称
* @return 创建的cJSON对象,失败返回NULL
*/
static cJSON *mcp_tool_aht10_list(const char *name)
{
// 创建默认工具信息,描述温湿度查询功能
   cJSON *tool = mcp_tool_list_info_create_default(name, "获取环境温湿度传感器数据,包括当前温度(摄氏度)和湿度(百分比)");
if (!tool) {
return NULL;
}
 
// 该工具无入参,无需添加property
return tool;
}
 
/**
* @brief 温湿度传感器工具的调用处理函数
* @param id 工具ID
* @param name 工具名称
* @param args 调用参数(该工具无参数)
* @return 调用结果的cJSON对象,失败返回NULL
*/
static cJSON *mcp_tool_aht10_call(const char *id, const char *name, cJSON *args)
{
float humidity = 0.0f;
float temperature = 0.0f;
 
// 读取传感器数据
int ret = service_aht10_get_temperature_humidity(&humidity, &temperature);
if (ret != 0) {
LOGE("Read sensor data failed");
 
// 创建错误响应
       cJSON *result = mcp_tool_call_result_create(name);
if (!result) {
return NULL;
}
 
       cJSON *content_array = cJSON_CreateArray();
       cJSON *content_item = cJSON_CreateObject();
cJSON_AddStringToObject(content_item, "type", "text");
cJSON_AddStringToObject(content_item, "text", "读取传感器数据失败,请检查设备连接");
cJSON_AddItemToArray(content_array, content_item);
cJSON_AddItemToObject(result, "content", content_array);
cJSON_AddBoolToObject(result, "isError", 1);
 
return result;
}
 
// 构建成功响应
   cJSON *result = mcp_tool_call_result_create(name);
if (!result) {
return NULL;
}
 
// 拼接温湿度结果文本
char result_msg[256];
snprintf(result_msg, sizeof(result_msg),
"当前环境温度为 %.1f°C,湿度为 %.1f%%", temperature, humidity);
 
   cJSON *content_array = cJSON_CreateArray();
   cJSON *content_item = cJSON_CreateObject();
cJSON_AddStringToObject(content_item, "type", "text");
cJSON_AddStringToObject(content_item, "text", result_msg);
cJSON_AddItemToArray(content_array, content_item);
cJSON_AddItemToObject(result, "content", content_array);
cJSON_AddBoolToObject(result, "isError", 0);
 
return result;
}
 
MCP_TOOL_DEFINE(sensor_temperature_humidity, mcp_tool_aht10_list, mcp_tool_aht10_call);


3.测试 MCP 工具编译

 在终端按下列顺序执行编译、进入烧录模式、烧录、重启命令:(请确保已经根据文档开发环境搭建与烧录 | 聆思文档中心 下载烧录脚本并已安装 ADB 工具)


./build.sh -S  ./apps/arcs-mini


烧录

 


adb shell recovery


 


adb push res/arcs-mini/ap.bin /RAW/NAND/40000
adb push res/arcs-mini/tone.bin /RAW/NAND/100000
adb push res/arcs-mini/wake_word.bin /RAW/NAND/200000
adb push res/arcs-mini/respak.bin /RAW/NAND/400000
adb push build/arcs-mini.bin /RAW/NAND/600000


 


adb shell reboot hard


预期效果

 唤醒小聆,然后说 "环境温度湿度多少?" ,小聆回复环境温度就成功了

至此, MCP 工具已经可以调用温湿度计查询环境温度了

 

总结和信息补充

MCP协议在拓展智能硬件功能时带来很大便利性,不仅可以让智能硬件可以快捷的调用互联网服务,也可以让外设和感应器等外设接入更简单。

更多智能硬件接MCP的方式和示例会陆续分享,有需求的朋友可以直接关注或在评论区留言,我们会持续分享相关操作示例。

 

本文操作示例中使用的硬件是LS26Arcs-mini大模型开发板支持二次开发做更多个性化功能和DIY改造,需要了解硬件详细信息可以参考:https://docs2.listenai.com/x/IPiXdnAJg

如果还想进阶学习更多离线AI示例上手Zephyr 开发可以选择CSK6大模型视觉语音开发套件硬件详细信息可以参考:https://docs2.listenai.com/x/CNCwAs0Dv

 



共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]