Skip to content

本文档提供九章 MCP 验证板各外设的示例代码与接线说明。所有示例基于 emMCP v1.0.1 框架,使用 emMCP_AddToolToToolList 添加工具、emMCP_RegistrationTools 一次性注册后,AI 可通过语音指令直接控制外设或读取传感器数据。

TIP

完整工程源码请参考 emMCP 仓库example/9Mod_MCPBoard/ 目录。


1. 板载 LED 控制

接线

板载 LED 连接 STM32 PC13,高电平点亮。

MCP 工具注册

c
// 工具回调 - 控制
static void emMCP_SetLEDHandler(void *arg)
{
    cJSON *param = (cJSON *)arg;
    cJSON *enable = cJSON_GetObjectItem(param, "enable");
    if (enable != NULL) {
        if (enable->valueint == 1) {
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
        } else {
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
        }
        emMCP_ResponseValue(emMCP_CTRL_OK);
    } else {
        emMCP_ResponseValue(emMCP_CTRL_ERROR);
    }
}

// 工具回调 - 查询
static void emMCP_GetLEDHandler(void *arg)
{
    emMCP_ResponseValue(emMCP_CTRL_OK);
}

// 在 main() 中注册
emMCP_tool_t led;
led.name = "LED灯";
led.description = "用来控制板载LED灯的亮灭";
led.inputSchema.properties[0].name = "enable";
led.inputSchema.properties[0].description = "是否打开LED灯,true表示打开,false表示关闭,查询时为null";
led.inputSchema.properties[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN;
led.setRequestHandler = emMCP_SetLEDHandler;
led.checkRequestHandler = emMCP_GetLEDHandler;
emMCP_AddToolToToolList(&led);
emMCP_RegistrationTools();

语音指令

「小安,打开板载 LED」 「小安,关闭板载 LED」


2. 继电器控制

接线

继电器端子说明
NO (常开)默认断开,高电平吸合
COM (公共)外接设备火线
GND / VCC板载已连接,无需额外接线

STM32 控制引脚:PB5(高电平触发)。

MCP 工具注册

c
// 工具回调 - 控制
static void emMCP_SetRelayHandler(void *arg)
{
    cJSON *param = (cJSON *)arg;
    cJSON *enable = cJSON_GetObjectItem(param, "enable");
    if (enable != NULL) {
        if (enable->valueint == 1) {
            HAL_GPIO_WritePin(RELAY_GPIO_Port, RELAY_Pin, GPIO_PIN_SET);
        } else {
            HAL_GPIO_WritePin(RELAY_GPIO_Port, RELAY_Pin, GPIO_PIN_RESET);
        }
        emMCP_ResponseValue(emMCP_CTRL_OK);
    } else {
        emMCP_ResponseValue(emMCP_CTRL_ERROR);
    }
}

// 工具回调 - 查询
static void emMCP_GetRelayHandler(void *arg)
{
    emMCP_ResponseValue(emMCP_CTRL_OK);
}

// 在 main() 中注册
emMCP_tool_t relay;
relay.name = "继电器";
relay.description = "用来控制继电器开关";
relay.inputSchema.properties[0].name = "enable";
relay.inputSchema.properties[0].description = "是否打开继电器,true表示打开,false表示关闭,查询时为null";
relay.inputSchema.properties[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN;
relay.setRequestHandler = emMCP_SetRelayHandler;
relay.checkRequestHandler = emMCP_GetRelayHandler;
emMCP_AddToolToToolList(&relay);
emMCP_RegistrationTools();

语音指令

「小安,打开继电器」 「小安,关闭继电器」


3. WS2812 灯条控制

接线

WS2812 灯条通过板载 4Pin 接口连接:

WS2812 接口说明
5V电源正极(板载 5V 输出)
GND电源地
DIN数据输入 — PA11
DOUT数据输出(级联下一颗)

支持 RGB 颜色控制和亮度调节。

MCP 工具注册

c
// 工具回调 - 控制
static void emMCP_SetWS2812Handler(void *arg)
{
    cJSON *param = (cJSON *)arg;

    cJSON *red = cJSON_GetObjectItem(param, "red");
    cJSON *green = cJSON_GetObjectItem(param, "green");
    cJSON *blue = cJSON_GetObjectItem(param, "blue");
    cJSON *brightness = cJSON_GetObjectItem(param, "brightness");

    uint8_t r = red ? red->valueint : 0;
    uint8_t g = green ? green->valueint : 0;
    uint8_t b = blue ? blue->valueint : 0;
    uint8_t bri = brightness ? brightness->valueint : 255;

    WS2812_SetColor(r, g, b, bri);
    emMCP_ResponseValue(emMCP_CTRL_OK);
}

// 工具回调 - 查询
static void emMCP_GetWS2812Handler(void *arg)
{
    emMCP_ResponseValue(emMCP_CTRL_OK);
}

// 在 main() 中注册
emMCP_tool_t ws2812;
ws2812.name = "WS2812灯条";
ws2812.description = "用来控制WS2812灯条的颜色和亮度";
ws2812.inputSchema.properties[0].name = "red";
ws2812.inputSchema.properties[0].description = "红色值,范围0-255,查询时为null";
ws2812.inputSchema.properties[0].type = MCP_SERVER_TOOL_TYPE_NUMBLE;
ws2812.inputSchema.properties[1].name = "green";
ws2812.inputSchema.properties[1].description = "绿色值,范围0-255,查询时为null";
ws2812.inputSchema.properties[1].type = MCP_SERVER_TOOL_TYPE_NUMBLE;
ws2812.inputSchema.properties[2].name = "blue";
ws2812.inputSchema.properties[2].description = "蓝色值,范围0-255,查询时为null";
ws2812.inputSchema.properties[2].type = MCP_SERVER_TOOL_TYPE_NUMBLE;
ws2812.inputSchema.properties[3].name = "brightness";
ws2812.inputSchema.properties[3].description = "亮度值,范围0-255,查询时为null";
ws2812.inputSchema.properties[3].type = MCP_SERVER_TOOL_TYPE_NUMBLE;
ws2812.setRequestHandler = emMCP_SetWS2812Handler;
ws2812.checkRequestHandler = emMCP_GetWS2812Handler;
emMCP_AddToolToToolList(&ws2812);
emMCP_RegistrationTools();

语音指令

「小安,把灯条调成红色」 「小安,灯条亮度调到 50%」 「小安,关闭灯条」


4. 雷达人体检测

接线

Rd-03L_V2 毫米波雷达模块通过 4Pin 排针连接:

雷达模块STM32 引脚
VCC3.3V
GNDGND
OUTPA8(GPIO 输入)

雷达检测到人体存在时 PA8 输出高电平,无人时输出低电平。

MCP 工具注册

c
// 工具回调 - 控制(雷达为只读传感器,无需控制逻辑)
static void emMCP_SetRadarHandler(void *arg)
{
    emMCP_ResponseValue(emMCP_CTRL_ERROR);
}

// 工具回调 - 查询
static void emMCP_GetRadarHandler(void *arg)
{
    uint8_t detected = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8);

    cJSON *result = cJSON_CreateObject();
    cJSON_AddBoolToObject(result, "detected", detected);
    char *json_str = cJSON_PrintUnformatted(result);
    emMCP_ResponseValue(json_str);
    cJSON_Delete(result);
    cJSON_free(json_str);
}

// 在 main() 中注册
emMCP_tool_t radar;
radar.name = "雷达传感器";
radar.description = "用来检测人体是否存在";
radar.inputSchema.properties[0].name = "detected";
radar.inputSchema.properties[0].description = "是否检测到人体,true表示有人,false表示无人,控制时为null";
radar.inputSchema.properties[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN;
radar.setRequestHandler = emMCP_SetRadarHandler;
radar.checkRequestHandler = emMCP_GetRadarHandler;
emMCP_AddToolToToolList(&radar);
emMCP_RegistrationTools();

语音指令

「小安,房间里有人吗?」 「小安,查询雷达状态」


5. SHT30 温湿度读取

接线

SHT30 温湿度传感器与 OLED、PD 诱骗共享 I²C 总线(PB6(SCL) / PB7(SDA)),通过设备地址区分:

设备I²C 地址
SHT30 温湿度0x44
OLED SSD13060x3C
CH224K PD 诱骗0x48

MCP 工具注册

c
// 工具回调 - 控制(温湿度为只读传感器,无需控制逻辑)
static void emMCP_SetSHT30Handler(void *arg)
{
    emMCP_ResponseValue(emMCP_CTRL_ERROR);
}

// 工具回调 - 查询
static void emMCP_GetSHT30Handler(void *arg)
{
    float temp = 0, humi = 0;
    SHT30_ReadData(&temp, &humi);

    cJSON *result = cJSON_CreateObject();
    cJSON_AddNumberToObject(result, "temperature", temp);
    cJSON_AddNumberToObject(result, "humidity", humi);
    cJSON_AddStringToObject(result, "unit_temp", "°C");
    cJSON_AddStringToObject(result, "unit_humi", "%");
    char *json_str = cJSON_PrintUnformatted(result);
    emMCP_ResponseValue(json_str);
    cJSON_Delete(result);
    cJSON_free(json_str);
}

// 在 main() 中注册
emMCP_tool_t sht30;
sht30.name = "温湿度传感器";
sht30.description = "用来获取环境温度和湿度数据";
sht30.inputSchema.properties[0].name = "temperature";
sht30.inputSchema.properties[0].description = "当前温度值,单位°C,控制时为null";
sht30.inputSchema.properties[0].type = MCP_SERVER_TOOL_TYPE_NUMBLE;
sht30.inputSchema.properties[1].name = "humidity";
sht30.inputSchema.properties[1].description = "当前湿度值,单位%,控制时为null";
sht30.inputSchema.properties[1].type = MCP_SERVER_TOOL_TYPE_NUMBLE;
sht30.setRequestHandler = emMCP_SetSHT30Handler;
sht30.checkRequestHandler = emMCP_GetSHT30Handler;
emMCP_AddToolToToolList(&sht30);
emMCP_RegistrationTools();

语音指令

「小安,查一下现在的温湿度」 「小安,当前温度是多少?」


6. 红外空调控制

接线

HXD039B2 红外发射模块通过 5Pin 排母连接:

红外模块STM32 引脚
VCC5V
GNDGND
TXPB10 (USART3_TX)
RXPB11 (USART3_RX)

波特率 9600。模块内置格力、美的等主流空调码库。

MCP 工具注册

c
// 工具回调 - 控制
static void emMCP_SetIRHandler(void *arg)
{
    cJSON *param = (cJSON *)arg;

    cJSON *brand = cJSON_GetObjectItem(param, "brand");       // 品牌: gree/midea
    cJSON *action = cJSON_GetObjectItem(param, "action");     // 动作: on/off/temp_up/temp_down
    cJSON *temp = cJSON_GetObjectItem(param, "temperature");  // 温度: 16-30

    // 组装红外指令并发送到 USART3
    char cmd[64];
    snprintf(cmd, sizeof(cmd), "%s_%s_%d",
        brand ? brand->valuestring : "gree",
        action ? action->valuestring : "on",
        temp ? temp->valueint : 26);
    IR_SendCommand(cmd);
    emMCP_ResponseValue(emMCP_CTRL_OK);
}

// 工具回调 - 查询
static void emMCP_GetIRHandler(void *arg)
{
    emMCP_ResponseValue(emMCP_CTRL_OK);
}

// 在 main() 中注册
emMCP_tool_t ir;
ir.name = "红外空调";
ir.description = "用来控制红外空调的开关和温度";
ir.inputSchema.properties[0].name = "brand";
ir.inputSchema.properties[0].description = "空调品牌,可选: gree/midea,查询时为null";
ir.inputSchema.properties[0].type = MCP_SERVER_TOOL_TYPE_STRING;
ir.inputSchema.properties[1].name = "action";
ir.inputSchema.properties[1].description = "控制动作,可选: on/off/temp_up/temp_down,查询时为null";
ir.inputSchema.properties[1].type = MCP_SERVER_TOOL_TYPE_STRING;
ir.inputSchema.properties[2].name = "temperature";
ir.inputSchema.properties[2].description = "目标温度,范围16-30,查询时为null";
ir.inputSchema.properties[2].type = MCP_SERVER_TOOL_TYPE_NUMBLE;
ir.setRequestHandler = emMCP_SetIRHandler;
ir.checkRequestHandler = emMCP_GetIRHandler;
emMCP_AddToolToToolList(&ir);
emMCP_RegistrationTools();

语音指令

「小安,打开空调」 「小安,空调温度调到26度」 「小安,关闭空调」


7. PD 诱骗供电控制

接线

PD 诱骗使用 CH224K 芯片,通过 I²C 配置输出电压档位。使用 Type-C 接口连接 PD 充电器。

PD 诱骗STM32 引脚
SCLPB6
SDAPB7
I²C 地址0x48

MCP 工具注册

c
// 工具回调 - 控制
static void emMCP_SetPDHandler(void *arg)
{
    cJSON *param = (cJSON *)arg;
    cJSON *voltage = cJSON_GetObjectItem(param, "voltage");

    if (voltage) {
        CH224K_SetVoltage(voltage->valueint);
        emMCP_ResponseValue(emMCP_CTRL_OK);
    } else {
        emMCP_ResponseValue(emMCP_CTRL_ERROR);
    }
}

// 工具回调 - 查询
static void emMCP_GetPDHandler(void *arg)
{
    emMCP_ResponseValue(emMCP_CTRL_OK);
}

// 在 main() 中注册
emMCP_tool_t pd;
pd.name = "PD诱骗供电";
pd.description = "用来设置PD诱骗输出电压";
pd.inputSchema.properties[0].name = "voltage";
pd.inputSchema.properties[0].description = "目标电压,可选: 5/9/12/15/20,查询时为null";
pd.inputSchema.properties[0].type = MCP_SERVER_TOOL_TYPE_NUMBLE;
pd.setRequestHandler = emMCP_SetPDHandler;
pd.checkRequestHandler = emMCP_GetPDHandler;
emMCP_AddToolToToolList(&pd);
emMCP_RegistrationTools();

语音指令

「小安,设置 PD 输出 12V」 「小安,PD 升压到 20V」


8. OLED 显示控制

接线

0.96 寸 OLED(SSD1306,128×64)通过 I²C 连接:

OLEDSTM32 引脚
SCLPB6
SDAPB7
I²C 地址0x3C

板载中文字库(GT20L61S),支持汉字、ASCII 字符和图片显示。

MCP 工具注册

c
// 工具回调 - 控制
static void emMCP_SetOLEDHandler(void *arg)
{
    cJSON *param = (cJSON *)arg;
    cJSON *text = cJSON_GetObjectItem(param, "text");

    if (text) {
        OLED_Clear();
        OLED_ShowString(0, 0, text->valuestring);
        OLED_Refresh();
        emMCP_ResponseValue(emMCP_CTRL_OK);
    } else {
        emMCP_ResponseValue(emMCP_CTRL_ERROR);
    }
}

// 工具回调 - 查询
static void emMCP_GetOLEDHandler(void *arg)
{
    emMCP_ResponseValue(emMCP_CTRL_OK);
}

// 在 main() 中注册
emMCP_tool_t oled;
oled.name = "OLED屏幕";
oled.description = "用来在OLED屏幕上显示文字";
oled.inputSchema.properties[0].name = "text";
oled.inputSchema.properties[0].description = "要显示的文字内容,查询时为null";
oled.inputSchema.properties[0].type = MCP_SERVER_TOOL_TYPE_STRING;
oled.setRequestHandler = emMCP_SetOLEDHandler;
oled.checkRequestHandler = emMCP_GetOLEDHandler;
emMCP_AddToolToToolList(&oled);
emMCP_RegistrationTools();

语音指令

「小安,OLED 显示「你好」」 「小安,在屏幕上显示当前温度」


9. 综合示例:注册所有外设

c
void RegisterAllTools(void)
{
    // LED
    emMCP_AddToolToToolList(&led);
    // 继电器
    emMCP_AddToolToToolList(&relay);
    // WS2812 灯条
    emMCP_AddToolToToolList(&ws2812);
    // 雷达
    emMCP_AddToolToToolList(&radar);
    // 温湿度
    emMCP_AddToolToToolList(&sht30);
    // 红外
    emMCP_AddToolToToolList(&ir);
    // PD 诱骗
    emMCP_AddToolToToolList(&pd);
    // OLED 显示
    emMCP_AddToolToToolList(&oled);

    // 一次性注册所有工具到小安AI
    emMCP_RegistrationTools();
}

将所有工具注册后,只需调用一次 emMCP_RegistrationTools() 即可向 AI 平台同步所有工具定义。

验证方法

  1. 编译检查:确保工程编译无报错
  2. 串口日志:烧录后连接调试串口(USART1, 115200),对模组说"你好小安",应看到 [DEBUG] emMCP_EventCallback: event:8 等日志输出
  3. 工具同步:串口日志中出现 tool_list 发送成功即表示工具已注册到 AI 平台
  4. 功能测试:对小安说出示例对应的语音指令,观察外设响应

更多资料

Released under the MIT License.