本文档提供九章 MCP 验证板各外设的示例代码与接线说明。所有示例基于 emMCP v1.0.1 框架,使用 emMCP_AddToolToToolList 添加工具、emMCP_RegistrationTools 一次性注册后,AI 可通过语音指令直接控制外设或读取传感器数据。
TIP
完整工程源码请参考 emMCP 仓库 下 example/9Mod_MCPBoard/ 目录。
1. 继电器控制
接线
| 继电器端子 | 说明 |
|---|---|
| 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();语音指令
「小安,打开继电器」 「小安,关闭继电器」
2. 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%」 「小安,关闭灯条」
3. SHT30 温湿度读取
接线
SHT30 温湿度传感器与 PD 诱骗共享 GPIO 位操作模拟的 I²C 总线(PB6(SDA) / PB7(SCL)),通过设备地址区分:
| 设备 | I²C 地址 |
|---|---|
| SHT30 温湿度 | 0x44 |
| CH224K PD 诱骗 | 0x48 |
OLED 使用 SPI 接口(独立片选),板载 GT20L61S 中文字库芯片。
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();语音指令
「小安,查一下现在的温湿度」 「小安,当前温度是多少?」
4. PD 诱骗供电控制
接线
PD 诱骗使用 CH224K 芯片,通过 I²C 配置输出电压档位。使用 Type-C 接口连接 PD 充电器。
⚠️ 高压警告: PD 诱骗输出最高 20V/3A (60W)。接线前请确认外接负载的额定电压,切勿短路。请勿在通电状态下插拔高压负载端。初次使用建议从 5V 开始测试。
| PD 诱骗 | STM32 引脚 |
|---|---|
| SDA | PB6 |
| SCL | 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」
5. OLED 显示控制
接线
0.96 寸 OLED(SSD1306,128×64)使用 SPI1 接口,板载 GT20L61S 中文字库芯片:
| OLED 引脚 | STM32 引脚 | 说明 |
|---|---|---|
| OLED_DC | PA1 | 数据/命令选择 |
| OLED_CS1 | PA4 | OLED 片选 |
| CS2 | PB0 | GT20L61S 字库片选 |
| SCLK | PA5 (SPI1_SCK) | SPI 时钟 |
| MISO | PA6 (SPI1_MISO) | SPI 数据输入(字库读) |
| MOSI | PA7 (SPI1_MOSI) | SPI 数据输出 |
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 显示「你好」」 「小安,在屏幕上显示当前温度」
6. 综合示例:注册所有外设
void RegisterAllTools(void)
{
// LED
emMCP_AddToolToToolList(&led);
// 继电器
emMCP_AddToolToToolList(&relay);
// WS2812 灯条
emMCP_AddToolToToolList(&ws2812);
// 温湿度
emMCP_AddToolToToolList(&sht30);
// PD 诱骗
emMCP_AddToolToToolList(&pd);
// OLED 显示
emMCP_AddToolToToolList(&oled);
// 一次性注册所有工具到小安AI
emMCP_RegistrationTools();
}将所有工具注册后,只需调用一次 emMCP_RegistrationTools() 即可向 AI 平台同步所有工具定义。
验证方法
- 编译检查:确保工程编译无报错
- 串口日志:烧录后连接调试串口(USART1, 1500000),对模组说"你好小安",应看到
[DEBUG] emMCP_EventCallback: event:8等日志输出 - 工具同步:串口日志中出现
tool_list发送成功即表示工具已注册到 AI 平台 - 功能测试:对小安说出示例对应的语音指令,观察外设响应

