Skip to content

1. Download Source Code

It is recommended to clone the repository via Git:

bash
git clone https://github.com/Ai-Thinker-Open/emMCP.git

▫️Directory Structure

bash
emMCP
├── example               ------> Example code directory
   ├── 9Mod_MCPBorad     ------> 9Mod MCP Verification Board STM32F103 example
   ├── STM32F40xRTOS_XiaoZhiAI ------> STM32F407 FreeRTOS XiaoZhiAI example
   └── ...
├── port                  ------> emMCP porting interface directory
   ├── uartPort.h        ------> Porting header file (with config system)
   ├── uartPort.c        ------> Porting source file (double-buffer RX)
   ├── emMCP_port_config_example.h ------> Configuration example
   ├── emMCP_port_config_template.h ------> Configuration template
   └── README_PORT.md    ------> Porting guide
└── uart-mcp              ------> emMCP serial communication core
    ├── cJSON             ------> cJSON library (optimized variant)
   ├── cJSON.h       ------> cJSON header
   └── cJSON.c       ------> cJSON source
    ├── emMCP.h           ------> emMCP main header
    ├── emMCP.c           ------> emMCP main source
    └── emMCPLOG.h        ------> Logging header

2. Adding emMCP to Your Project

Copy the port and uart-mcp directories into your project, then include emMCP.h to use all emMCP resources.

v1.0.1 Changes

New configuration system introduced: port/port.h has been removed. Configuration macros are now in uartPort.h with three configuration methods (see below). Double-buffer RX added for improved UART data stability.

STM32F103 CMake example: 9Mod_MCPBorad

3. Porting Interface

emMCP is a library for serial communication. You only need to implement the low-level serial port functions.

▫️Configuration Macros (v1.0.1 New System)

emMCP v1.0.1 migrated platform-specific macros from port/port.h to uartPort.h with three configuration methods:

Method 1: Direct Macro Definition (Simple Projects) Define before including uartPort.h:

c
#define emMCP_printf    log_printf          // Print function
#define emMCP_malloc    pvPortMalloc        // Memory allocation
#define emMCP_free      vPortFree           // Memory free
#define emMCP_delay     osDelay             // Delay function
#define emMCP_uart_send HAL_UART_Transmit   // UART send

#include "uartPort.h"

Method 2: Create Config File (Recommended) Create emMCP_port_config.h in your project directory. uartPort.h auto-detects it via __has_include. See port/emMCP_port_config_example.h for reference.

Method 3: CMake Build Definition

cmake
target_compile_definitions(your_target PRIVATE
    emMCP_printf=log_printf
    emMCP_malloc=pvPortMalloc
    emMCP_free=vPortFree
    emMCP_delay=osDelay
    EMCP_USER_CONFIG_FILE="path/to/your_config.h"
)

▫️Implementing UART Send and Receive

Important

You must implement the MCU's UART driver yourself. The RX function should reliably receive data (DMA+IDLE mode recommended). The TX function must transmit complete data packets.

▫️Porting emMCP

  • Open port/uartPort.c
  • uartPortSendData() now uses the emMCP_uart_send(data, len) macro internally. Just ensure emMCP_uart_send is defined correctly:
c
#define emMCP_uart_send  HAL_UART_Transmit
  • Or write the implementation directly:
c
int uartPortSendData(char *data, int len)
{
    if (data == NULL || len <= 0) return -1;
    HAL_UART_Transmit(&huart2, (uint8_t *)data, len, 100);
    return 0;
}
  • In the MCU's UART RX interrupt/callback, call uartPortRecvData() (new version auto-stores with double buffer):
c
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) 
{
    if (huart->Instance == USART2) {
        HAL_UARTEx_ReceiveToIdle_DMA(huart, (uint8_t *)rxBuffer, sizeof(rxBuffer));
        uartPortRecvData((char *)rxBuffer, Size);
        __HAL_DMA_ENABLE_IT(&hdma_usart2_rx, DMA_IT_TC);
    }
}
  • In the main loop or task, call uartPortGetRxData() to retrieve received data (thread-safe):
c
char *data = uartPortGetRxData();
if (data != NULL) {
    // Process data...
    uartPortClearRxData();  // Mark as processed
}

4. Initializing emMCP

You can initialize emMCP before main() or in a separate init function:

c
static emMCP_t emMCP_dev;

int main(void)
{
    emMCP_Init(&emMCP_dev);
    while (1)
    {
        // Main loop
    }
}

5. Running emMCP_TickHandler() in a Loop

emMCP has a built-in state machine that needs periodic calls to emMCP_TickHandler() for data processing. Recommended to call it in the main loop:

c
static emMCP_t emMCP_dev;

int main(void)
{
    emMCP_Init(&emMCP_dev);
    while (1)
    {
        emMCP_TickHandler(10);
    }
}

6. Verifying a Successful Port

After completing the steps above, compile your project without errors and verify:

  • Flash the firmware to the MCU
  • Say "你好小安" to the AI module
  • Observe the serial output. If you see output like below, emMCP has been successfully ported:
bash
[DEBUG] emMCP_EventCallback:78: emMCP_EventCallback: event:8,type:4,param:2.WakeUP

7. Further Reading

👉How to Use

Released under the MIT License.