Skip to main content
Associate II
July 13, 2025
Solved

How to Feed Features and Get Predictions with X-CUBE-AI on STM32?

  • July 13, 2025
  • 1 reply
  • 412 views

Hi everyone,
I'm working on deploying an MLP model on STM32F746 using X-CUBE-AI. The model was converted from ONNX format, and I’m running into trouble feeding the input features and retrieving the prediction result

I want to run inference with the following flow:

float input_data[60] = { /* preprocessed features */ };
float output_data[1]; // expecting softmax probabilities
int output_label; // expecting predicted class label

My main confusion:

I'm unclear on how to actually use the interface generated by Cube.AI:

  • What are the correct steps to initialize and fill the ai_buffer structs?

  • Do I need to manually align my input/output arrays, or use the data_ins[] / data_outs[] generated by default?

  • What is the purpose of macros like AI_HANDLE_PTR(...) and AI_MNETWORK_IN?

  • I tried calling ai_mlp_run(...) like this:

     
    ai_mlp_run(network, ai_input, ai_output);

    but I get build errors or invalid pointer types

 



model information :

problem.png

app_X-cube-ai.h:
problem2.png

Best answer by Julian E.

Hello @Stone_chan,

 

When you create your project in CubeMX, select the last version of X Cube AI and the application Template.

 

Then in the generated code, you will find app_x-cube-ai.c in /X-Cube-AI-App.

The main functions here are the MX_X_CUBE_AI_Init and MX_X_CUBE_AI_Process.

 

In the MX_X_CUBE_AI_Process you will find 3 parts:

  • acquire_and_process_data(data_ins)
  • ai_run()
  • post_process(data_outs)

 

The way to pass data is through the function acquire_and_process_data(data_ins). By default, it looks like this:

int acquire_and_process_data(ai_i8* data[])
{
 /* fill the inputs of the c-model
 for (int idx=0; idx < AI_NETWORK_IN_NUM; idx++ )
 {
 data[idx] = ....
 }

 */
 return 0;
}

The for loop here is used if you have multiple input to give your model. In most case you have one, so you will only pass 1 time and fill data[0].

 

Same for the post_process(data_outs) function.

 

The data that you have to pass are 1D array of size of your input/output and be careful, by default the data_ins and data_outs are in int8 (ai_i8). So if you are using float32 for example, make sure to adapt the code.

 

For your board in particular, the MX_X_CUBE_AI_Process is not called in the main.c.

You can add it in the while(1) in the main function:

int main(void)
{

 /* USER CODE BEGIN 1 */

 /* USER CODE END 1 */

 /* Enable the CPU Cache */

 /* Enable I-Cache---------------------------------------------------------*/
 SCB_EnableICache();

 /* Enable D-Cache---------------------------------------------------------*/
 SCB_EnableDCache();

 /* MCU Configuration--------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();

 /* USER CODE BEGIN Init */

 /* USER CODE END Init */

 /* Configure the system clock */
 SystemClock_Config();

 /* Configure the peripherals common clocks */
 PeriphCommonClock_Config();

 /* USER CODE BEGIN SysInit */

 /* USER CODE END SysInit */

 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_ADC3_Init();
 MX_CRC_Init();
 MX_DCMI_Init();
 MX_DMA2D_Init();
 MX_ETH_Init();
 MX_FMC_Init();
 MX_I2C1_Init();
 MX_I2C3_Init();
 MX_LTDC_Init();
 MX_QUADSPI_Init();
 MX_RTC_Init();
 MX_SAI2_Init();
 MX_SDMMC1_SD_Init();
 MX_SPDIFRX_Init();
 MX_SPI2_Init();
 MX_TIM1_Init();
 MX_TIM2_Init();
 MX_TIM3_Init();
 MX_TIM5_Init();
 MX_TIM8_Init();
 MX_TIM12_Init();
 MX_USART1_UART_Init();
 MX_USART6_UART_Init();
 MX_FATFS_Init();
 MX_X_CUBE_AI_Init();
 /* USER CODE BEGIN 2 */

 /* USER CODE END 2 */

 /* USER CODE BEGIN RTOS_MUTEX */
 /* add mutexes, ... */
 /* USER CODE END RTOS_MUTEX */

 /* USER CODE BEGIN RTOS_SEMAPHORES */
 /* add semaphores, ... */
 /* USER CODE END RTOS_SEMAPHORES */

 /* USER CODE BEGIN RTOS_TIMERS */
 /* start timers, add new ones, ... */
 /* USER CODE END RTOS_TIMERS */

 /* USER CODE BEGIN RTOS_QUEUES */
 /* add queues, ... */
 /* USER CODE END RTOS_QUEUES */

 /* Create the thread(s) */
 /* definition and creation of defaultTask */
 osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 4096);
 defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

 /* USER CODE BEGIN RTOS_THREADS */
 /* add threads, ... */
 /* USER CODE END RTOS_THREADS */

 /* Start scheduler */
 osKernelStart();

 /* We should never get here as control is now taken by the scheduler */

 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
 /* USER CODE END WHILE */
	 MX_X_CUBE_AI_Process();
 /* USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */
}

 

Have a good day,

Julian

1 reply

Julian E.
Julian E.Best answer
Technical Moderator
July 21, 2025

Hello @Stone_chan,

 

When you create your project in CubeMX, select the last version of X Cube AI and the application Template.

 

Then in the generated code, you will find app_x-cube-ai.c in /X-Cube-AI-App.

The main functions here are the MX_X_CUBE_AI_Init and MX_X_CUBE_AI_Process.

 

In the MX_X_CUBE_AI_Process you will find 3 parts:

  • acquire_and_process_data(data_ins)
  • ai_run()
  • post_process(data_outs)

 

The way to pass data is through the function acquire_and_process_data(data_ins). By default, it looks like this:

int acquire_and_process_data(ai_i8* data[])
{
 /* fill the inputs of the c-model
 for (int idx=0; idx < AI_NETWORK_IN_NUM; idx++ )
 {
 data[idx] = ....
 }

 */
 return 0;
}

The for loop here is used if you have multiple input to give your model. In most case you have one, so you will only pass 1 time and fill data[0].

 

Same for the post_process(data_outs) function.

 

The data that you have to pass are 1D array of size of your input/output and be careful, by default the data_ins and data_outs are in int8 (ai_i8). So if you are using float32 for example, make sure to adapt the code.

 

For your board in particular, the MX_X_CUBE_AI_Process is not called in the main.c.

You can add it in the while(1) in the main function:

int main(void)
{

 /* USER CODE BEGIN 1 */

 /* USER CODE END 1 */

 /* Enable the CPU Cache */

 /* Enable I-Cache---------------------------------------------------------*/
 SCB_EnableICache();

 /* Enable D-Cache---------------------------------------------------------*/
 SCB_EnableDCache();

 /* MCU Configuration--------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();

 /* USER CODE BEGIN Init */

 /* USER CODE END Init */

 /* Configure the system clock */
 SystemClock_Config();

 /* Configure the peripherals common clocks */
 PeriphCommonClock_Config();

 /* USER CODE BEGIN SysInit */

 /* USER CODE END SysInit */

 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_ADC3_Init();
 MX_CRC_Init();
 MX_DCMI_Init();
 MX_DMA2D_Init();
 MX_ETH_Init();
 MX_FMC_Init();
 MX_I2C1_Init();
 MX_I2C3_Init();
 MX_LTDC_Init();
 MX_QUADSPI_Init();
 MX_RTC_Init();
 MX_SAI2_Init();
 MX_SDMMC1_SD_Init();
 MX_SPDIFRX_Init();
 MX_SPI2_Init();
 MX_TIM1_Init();
 MX_TIM2_Init();
 MX_TIM3_Init();
 MX_TIM5_Init();
 MX_TIM8_Init();
 MX_TIM12_Init();
 MX_USART1_UART_Init();
 MX_USART6_UART_Init();
 MX_FATFS_Init();
 MX_X_CUBE_AI_Init();
 /* USER CODE BEGIN 2 */

 /* USER CODE END 2 */

 /* USER CODE BEGIN RTOS_MUTEX */
 /* add mutexes, ... */
 /* USER CODE END RTOS_MUTEX */

 /* USER CODE BEGIN RTOS_SEMAPHORES */
 /* add semaphores, ... */
 /* USER CODE END RTOS_SEMAPHORES */

 /* USER CODE BEGIN RTOS_TIMERS */
 /* start timers, add new ones, ... */
 /* USER CODE END RTOS_TIMERS */

 /* USER CODE BEGIN RTOS_QUEUES */
 /* add queues, ... */
 /* USER CODE END RTOS_QUEUES */

 /* Create the thread(s) */
 /* definition and creation of defaultTask */
 osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 4096);
 defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

 /* USER CODE BEGIN RTOS_THREADS */
 /* add threads, ... */
 /* USER CODE END RTOS_THREADS */

 /* Start scheduler */
 osKernelStart();

 /* We should never get here as control is now taken by the scheduler */

 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
 /* USER CODE END WHILE */
	 MX_X_CUBE_AI_Process();
 /* USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */
}

 

Have a good day,

Julian

​In order to give better visibility on the answered topics, please click on 'Accept as Solution' on the reply which solved your issue or answered your question.