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.