Skip to content

Prerequisites

Ensure the following conditions are met

  • You have completed the porting as described in Porting to MCU
  • The emMCP project compiles successfully
  • The AI module can receive data correctly

1. Creating the emMCP Event Callback

▫️Event List

Events are defined as an enum in emMCP.h:

IndexEventDescription
0emMCP_EVENT_NONENo event
1emMCP_EVENT_CMD_OKCommand executed successfully
2emMCP_EVENT_CMD_ERRORCommand execution failed
3emMCP_EVENT_AI_STARTModule started
4emMCP_EVENT_AI_NETCFGNetwork configuration
5emMCP_EVENT_AI_NETERRNetwork error
6emMCP_EVENT_AI_WIFI_CONNNECTWi-Fi connecting
7emMCP_EVENT_AI_WIFI_CONNECTEDWi-Fi connected
8emMCP_EVENT_AI_WIFI_GOT_IPIP address obtained
9emMCP_EVENT_AI_WIFI_DISCONNECTWi-Fi disconnected
10emMCP_EVENT_AI_WAKEModule woken up
11emMCP_EVENT_AI_SLEEPModule sleeping
12emMCP_EVENT_AI_OTAUPDATEOTA update started
13emMCP_EVENT_AI_OTAOKOTA update successful
14emMCP_EVENT_AI_OTAERROTA update failed
15emMCP_EVENT_AI_MCP_CMDMCP command received
16emMCP_EVENT_AI_MCP_TextSubtitle text received
17emMCP_EVENT_AI_MCP_CHECKMCP check command received

v1.0.1 New Events

  • emMCP_EVENT_AI_WIFI_CONNECTED and emMCP_EVENT_AI_WIFI_GOT_IP provide finer-grained Wi-Fi status
  • emMCP_EVENT_AI_MCP_CHECK is used for tool health checks

▫️Event Callback Function

The emMCP event callback is defined as a weak function in emMCP.c and can be overridden:

c
__emMCPWeak void emMCP_EventCallback(emMCP_event_t event, mcp_server_tool_type_t type, void *param)
{
  char *param_str = (char *)param;
  emMCP_log_debug("emMCP_EventCallback: event:%d,type:%d,param:%s", event, type, param_str);
}

Parameters:

  • event: Event type, refer to the event list
  • type: Parameter type (refers to mcp_server_tool_type_t enum, currently only string type)
  • param: Event parameter (typically a string)

Override example:

c
void emMCP_EventCallback(emMCP_event_t event, mcp_server_tool_type_t type, void *param)
{
  switch (event) {
  case emMCP_EVENT_CMD_OK:
    log_info("emMCP_EVENT_CMD_OK");
    break;
  case emMCP_EVENT_CMD_ERROR:
    log_error("emMCP_EVENT_CMD_ERROR");
    break;
  case emMCP_EVENT_AI_START:
    log_info("emMCP_EVENT_AI_START");
    break;
  case emMCP_EVENT_AI_WAKE:
    log_info("emMCP_EVENT_AI_WAKE");
    break;
  case emMCP_EVENT_AI_MCP_CMD:
    log_info("emMCP_EVENT_AI_MCP_CMD:%s", (char *)param);
    break;
  case emMCP_EVENT_AI_MCP_Text:
    log_info("emMCP_EVENT_AI_MCP_Text:%s", (char *)param);
    break;
  default:
    break;
  }
}

2. Creating emMCP Tools

▫️Tool Creation Flow

▫️Step 1: Create emMCP_tool_t Variable

The emMCP_tool_t struct is defined as:

c
typedef struct emMCP_tool
{
    char *name;                          // Tool name
    char *description;                   // Tool description
    void (*setRequestHandler)(void *);   // Set callback
    void (*checkRequestHandler)(void *); // Check callback
    inputSchema_t inputSchema;           // Input parameters
    struct emMCP_tool *next;             // Next tool
} emMCP_tool_t;

The inputSchema_t struct:

c
typedef struct
{
    properties_t properties[MCP_SERVER_TOOL_PROPERTIES_NUM]; // Properties
    methods_t methods[MCP_SERVER_TOOL_METHODS_NUM];          // Methods
} inputSchema_t;
  • name: Tool name, unique identifier
  • description: Description of what the tool does
  • setRequestHandler: Callback for control commands (e.g., "turn on the light")
  • checkRequestHandler: Callback for query commands (e.g., "is the light on?")
  • inputSchema: Input parameter description

▫️Step 2: Create Tool Callbacks

c
// Control callback
static void emMCP_SetLEDHandler(void *arg) { }
// Query callback
static void emMCP_GetLEDHandler(void *arg) { }

emMCP_t emMCP;
emMCP_tool_t led;

int main(void)
{
    emMCP_Init(&emMCP);
    led.name = "LED";
    led.description = "Controls the LED on/off";
    led.inputSchema.properties[0].name = "enable";
    led.inputSchema.properties[0].description = "true=on, false=off, null=query";
    led.inputSchema.properties[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN;

    led.setRequestHandler = emMCP_SetLEDHandler;
    led.checkRequestHandler = emMCP_GetLEDHandler;
    // ...
}

▫️Step 3: Add Tool to emMCP

c
emMCP_AddToolToToolList(&led);

▫️Step 4: Register Tools with XiaoAn AI

c
emMCP_RegistrationTools();

Full example:

c
int main(void)
{
    emMCP_Init(&emMCP);
    led.name = "LED";
    led.description = "Controls the LED on/off";
    led.inputSchema.properties[0].name = "enable";
    led.inputSchema.properties[0].description = "true=on, false=off, null=query";
    led.inputSchema.properties[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN;
    led.setRequestHandler = emMCP_SetLEDHandler;
    led.checkRequestHandler = emMCP_GetLEDHandler;

    emMCP_AddToolToToolList(&led);
    emMCP_RegistrationTools();
    while (1) {
        emMCP_TickHandle(10);
    }
}

3. Handling MCP Commands

The tool callbacks are invoked when an MCP command arrives. Use cJSON to parse parameters:

c
static void emMCP_SetLEDHandler(void *arg)
{
    cJSON *param = (cJSON *)arg;
    cJSON *enable = cJSON_GetObjectItemCaseSensitive(param, "enable");
    if (enable != NULL) {
        if (enable->valueint == 1) {
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
        } else {
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
        }
        emMCP_ResponseValue(emMCP_CTRL_OK);
    } else {
        emMCP_ResponseValue(emMCP_CTRL_ERROR);
    }
}

Note

v1.0.1 uses cJSON_GetObjectItemCaseSensitive() (case-sensitive JSON key lookup). Ensure your JSON keys match exactly.

4. Multi-Parameter Control

For tools with multiple parameters (e.g., RGB LED), define multiple properties:

c
rgb.inputSchema.properties[0].name = "enable";
rgb.inputSchema.properties[0].description = "RGB LED on/off";
rgb.inputSchema.properties[0].type = MCP_SERVER_TOOL_TYPE_BOOLEAN;

rgb.inputSchema.properties[1].name = "red";
rgb.inputSchema.properties[1].description = "Red value, 0-255";
rgb.inputSchema.properties[1].type = MCP_SERVER_TOOL_TYPE_NUMBER;

rgb.inputSchema.properties[2].name = "green";
rgb.inputSchema.properties[2].description = "Green value, 0-255";
rgb.inputSchema.properties[2].type = MCP_SERVER_TOOL_TYPE_NUMBER;

rgb.inputSchema.properties[3].name = "blue";
rgb.inputSchema.properties[3].description = "Blue value, 0-255";
rgb.inputSchema.properties[3].type = MCP_SERVER_TOOL_TYPE_NUMBER;

5. Memory Configuration (v1.0.1)

Define these macros to control memory usage (default values shown below; override before including emMCP.h):

c
#define MCP_SERVER_TOOL_NUMBLE_MAX 4              // Max tools
#define MCP_SERVER_TOOL_PROPERTIES_NUM 4           // Max properties per tool
#define MCP_SERVER_TOOL_METHODS_NUM 2               // Max methods per tool
#define MCP_SERVER_TOOL_METHODS_PARAMETERS_NUM 3    // Max parameters per method

v1.0.1 Memory Optimization

Defaults changed from (4, 6, 5, 5) to (4, 4, 2, 3), saving ~40% RAM. Override if needed.

6. Helper API (v1.0.1 New)

▫️Get Parameter

emMCP_GetParam(cJSON *params, char *param_name) safely extracts a field from the callback argument:

c
static void emMCP_SetLEDHandler(void *arg) {
    cJSON *param = (cJSON *)arg;
    cJSON *enable = emMCP_GetParam(param, "enable");
    if (enable != NULL && cJSON_IsTrue(enable)) {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
    }
    emMCP_ResponseValue(emMCP_CTRL_OK);
}

▫️UART Status

  • emMCP_CheckUartSendStatus() — Check UART send completion
  • emMCP_UpdateUartRecv(bool isRecv) — Update RX status

7. Extra Features

v1.0.1 Change

The following extra features (wake-up, volume, baud rate) are now controlled by the EMCP_ENABLE_EXTRA_CMDS macro, disabled by default to save memory. Enable by defining before including emMCP.h:

c
#define EMCP_ENABLE_EXTRA_CMDS

▫️Wake-up Control

c
emMCP_SetAiWakeUp(20);  // Wake up for 20 seconds

▫️Volume Control

c
emMCP_SetAiVolume(50);  // Set volume to 50

▫️Query Volume (v1.0.1 New)

c
uint8_t vol = emMCP_CheckAiVolume();

▫️Baud Rate

c
emMCP_SetBaudrate(115200);  // Set baud rate (takes effect immediately)

TIP

After changing the baud rate via emMCP, you must update the MCU's UART baud rate accordingly.

Released under the MIT License. Build Time 2026-06-05 16:21:50