本文档提供九章 MCP 验证板各外设的示例代码与接线说明。所有示例基于 emMCP v1.0.1 框架,使用 emMCP_AddToolToToolList 添加工具、emMCP_RegistrationTools 一次性注册后,AI 可通过语音指令直接控制外设或读取传感器数据。
TIP
完整工程源码请参考 emMCP 仓库 下 example/9Mod_MCPBoard/ 目录。
1. 板载 LED 控制
接线
板载 LED 连接 STM32 PC13,高电平点亮。
MCP 工具注册
// 工具回调 - 控制
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 工具注册
// 工具回调 - 控制
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 工具注册
// 工具回调 - 控制
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 引脚 |
|---|---|
| VCC | 3.3V |
| GND | GND |
| OUT | PA8(GPIO 输入) |
雷达检测到人体存在时 PA8 输出高电平,无人时输出低电平。
MCP 工具注册
// 工具回调 - 控制(雷达为只读传感器,无需控制逻辑)
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 SSD1306 | 0x3C |
| CH224K PD 诱骗 | 0x48 |
MCP 工具注册
// 工具回调 - 控制(温湿度为只读传感器,无需控制逻辑)
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 引脚 |
|---|---|
| VCC | 5V |
| GND | GND |
| TX | PB10 (USART3_TX) |
| RX | PB11 (USART3_RX) |
波特率 9600。模块内置格力、美的等主流空调码库。
MCP 工具注册
// 工具回调 - 控制
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 引脚 |
|---|---|
| SCL | PB6 |
| SDA | PB7 |
| I²C 地址 | 0x48 |
MCP 工具注册
// 工具回调 - 控制
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 连接:
| OLED | STM32 引脚 |
|---|---|
| SCL | PB6 |
| SDA | PB7 |
| I²C 地址 | 0x3C |
板载中文字库(GT20L61S),支持汉字、ASCII 字符和图片显示。
MCP 工具注册
// 工具回调 - 控制
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. 综合示例:注册所有外设
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 平台同步所有工具定义。
验证方法
- 编译检查:确保工程编译无报错
- 串口日志:烧录后连接调试串口(USART1, 115200),对模组说"你好小安",应看到
[DEBUG] emMCP_EventCallback: event:8等日志输出 - 工具同步:串口日志中出现
tool_list发送成功即表示工具已注册到 AI 平台 - 功能测试:对小安说出示例对应的语音指令,观察外设响应

