1. Download Source Code
It is recommended to clone the repository via Git:
git clone https://github.com/Ai-Thinker-Open/emMCP.git▫️Directory Structure
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 header2. 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:
#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
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 theemMCP_uart_send(data, len)macro internally. Just ensureemMCP_uart_sendis defined correctly:
#define emMCP_uart_send HAL_UART_Transmit- Or write the implementation directly:
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):
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):
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:
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:
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:
[DEBUG] emMCP_EventCallback:78: emMCP_EventCallback: event:8,type:4,param:2.WakeUP
