Getting started with STM32Cube.AI and motion sensing on the STM32L4 IoT node

In this guide, you will learn how to create a motion sensing application to recognize human activities using machine learning on an STM32 microcontroller.

The model used classifies activities such as stationary, walking, or running from accelerometer data provided by the LSM6DSL sensor.

We will be creating a Human Activity Recognition (HAR) application for the STM32L4 Discovery kit IoT node B-L475E-IOT01A development board.

The board is powered by an STM32L475VG microcontroller (Arm® Cortex®-M4F MCU operating at 80 MHz with 1 Mbyte of Flash memory and 128 Kbytes of SRAM).

HAR

1. What you will learn

  • How to read motion sensor data.
  • How to generate neural network code for STM32 using X-CUBE-AI.
  • How to input sensor data into a neural network code.
  • How to output inference results.

2. What you will need

  • B-L475E-IOT01A: STM32L4 Discovery kit IoT node.
  • STM32CubeMX (v5.6.0 or later) with:
    • X-CUBE-MEMS1 (v7.1.0 or later) - for motion sensor component drivers.
    • X-CUBE-AI (v5.0.0 or later) - for the neural network conversion tool & network runtime library.
    • Note: both X-CUBE-AI and X-CUBE-MEMS1 Expansion Packages can be installed from within STM32CubeMX (see UM1718 - Section 3.4.4 Installing embedded software packs).
  • STM32CubeProgrammer (STM32CubeProg) with <STM32CubeProgrammer install directory>/bin added to your PATH environment variable.
  • A compatible IDE or toolchain. In this guide, instructions are given for GNU Make & GNU ARM Embedded Toolchain but similar steps can easily be adapted to other development environments (STM32CubeIDE, IAR Embedded Workbench®, Keil® MDK-ARM).
  • A serial terminal application (such as Tera Term, PuTTY, GNU Screen or others).
  • A Keras model. You can either download the pre-trained model.h5 or create your own model using your own data captures.
B-L475E-IOT01Ax

3. Part 1 - Getting started - Project creation using STM32CubeMX (5 min)

This first section will guide you through the basic steps to create an STM32 project template using STM32CubeMX.

We are going to blink an LED and output some messages on a serial terminal.

The goal of this project is to make sure you have a working environment with your development platform.

Info white.png Information
Users already familiar with STM32 development can skip this section.

3.1. Loading a new MCU into STM32CubeMX

  • Go to File > New project… in the top menu bar.
  1. Open the Board Selector tab.
  2. Search for B-L475E-IOT01A.
  3. Select the board in the bottom-right board list table.
  4. Click on Start Project to bring up the MCU configuration window.
  5. When prompted to initialize all peripherals with their default mode, select No to avoid generating unused code.

3.2. Configuring the UART communication interface

Configure the USART1 interface in asynchronous mode and with the default parameters (115200 bauds, 8-bit, no parity and 1 stop bit). You can also check the GPIO settings to view the associated USART1 GPIO. PB6 and PB7 should be configured as Alternate Function Push-Pull.

  1. Select the USART1 Connectivity interface.
  2. Enable Asynchronous mode.
  3. If not already configured, set the Baud Rate to 115200 bit/s.
  4. In the Pinout view or under the GPIO Settings tab, ensure PB6 and PB7 are associated with USART1.


3.3. Generating the project and application code

  1. Go to the Project Manager tab.
  2. Enter a Project Name and Location.
  3. Select your desired Toolchain / IDE. In this example, we will be creating a GCC/Makefile based project. Other settings can be left to their default values for now.
  4. Click GENERATE CODE.

STM32CubeMX generates the basic project structure for an STM32 application. The user application entry point is found in Src/main.c. The MCU hardware abstraction layer (HAL) drivers and CMSIS components are copied under the Drivers/ directory.

3.4. Adding some user application code (update Src/main.c file)

3.4.1. Include stdio.h

This snippet is provided AS IS, and by taking it, you agree to be bound to the license terms that can be found here for the component: Application.
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */

3.4.2. Insert testing code

In the main() function, after all system initialization routines, insert some testing code to make sure that both the serial output and error handler are working as expected.

This snippet is provided AS IS, and by taking it, you agree to be bound to the license terms that can be found here for the component: Application.
int main(void)
{
  /* ... */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  printf("STM32 is working\r\n"); // Remove this line when moving on to Part 2
  Error_Handler();                // Remove this line when moving on to Part 2

  /* USER CODE END 2 */
Info white.png Information
To preserve changes when re-generating code using STM32CubeMX, code must only be entered within the /* USER CODE BEGIN */ and /* USER CODE END */ template tags in the Src/ and Inc/ directories. Any modifications outside of theses tags might be lost during an STM32CubeMX code generation procedure.

3.4.3. Retargeting printf to a UART serial port

  • Retarget the printf output to the serial UART interface. Insert the following code snippet if you are working with a GCC toolchain:
This snippet is provided AS IS, and by taking it, you agree to be bound to the license terms that can be found here for the component: Application.
/* USER CODE BEGIN 4 */
int _write(int fd, char * ptr, int len)
{
  HAL_UART_Transmit(&huart1, (uint8_t *) ptr, len, HAL_MAX_DELAY);
  return len;
}
/* USER CODE END 4 */
Info white.png Information
stdout redirection is toolchain dependent. Example implementations for other compilers can be found in aiSystemPerformance.c from the SystemPerformance application.

3.4.4. Implement an Error_Handler()

To protect your application of any potential issues, it is recommended to implement a trapping mechanism in the the Error_Handler() function.

  • Create an infinite while loop to trap errors and blink the board’s LED.
This snippet is provided AS IS, and by taking it, you agree to be bound to the license terms that can be found here for the component: Application.
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  while(1) {
    HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
    HAL_Delay(50); /* wait 50 ms */
  }
  /* USER CODE END Error_Handler_Debug */
}

3.5. Compile, download and run

  • Build your project using the make command.
$ make -j
...
arm-none-eabi-size build/MotionSensing.elf
   text    data     bss     dec     hex filename
  12712     128    1728   14568    38e8 build/MotionSensing.elf
arm-none-eabi-objcopy -O ihex build/MotionSensing.elf build/MotionSensing.hex
arm-none-eabi-objcopy -O binary -S build/MotionSensing.elf build/MotionSensing.bin
$ STM32_Programmer_CLI -c port=swd mode=UR reset=HWrst -d build/MotionSensing.elf -s
  • Open a serial monitor application (such as Tera Term, PuTTY or GNU Screen) and connect to the ST-LINK Virtual COM port (COM port number may differ on your machine).
TeraTerm: New Connection
  • Configure the communication settings to 115200 baud, 8-bit, no parity.
TeraTerm Serial port settings
  • Press the black RESET button. The “STM32 is working” message should appear each time you press RESET and you should be able to see LED2 blinking on the board. When holding down the reset button, the program should be halted and LED2 should be turned off.

4. Part 2 - Reading accelerometer data using the LSM6DSL and X-CUBE-MEMS1 (15 min)

4.1. Add X-CUBE-MEMS1 components

  • In the Pinout & Configuration tab, click on Additional Software
  • Under the STMicroelectronics.X-CUBE-MEMS1 bundle, select Boards Components > AccGyr / LSM6DSL I2C and click on OK.

4.2. Configure the I2C bus interface

  1. Back in STM32CubeMX, choose the I2C2 Connectivity interface.
  2. Enable the I2C bus interface by choosing mode I2C.
  3. Set the I2C mode to Fast Mode and speed to 400 kHz (LSM6DSL maximum I2C clock frequency)
  4. Under the GPIO settings tab, associate the PB10 and PB11 pins with the I2C2 interface.
  5. Clear pin user labels for PB10 and PB11. In the "'Pinout"' view:
    1. Right-click on the pin.
    2. Click on Enter User Label.
    3. Clear the label (press backspace to remove any existing text label).


4.3. Configure the LSM6DSL interrupt line

To receive INT1 signals from the LSM6DSL sensor, check your MCU GPIO pin is correctly configured to receive external interrupts on PD11.

  1. In the GPIO settings (under System Core), set PD11 to External Interrupt Mode with Rising edge trigger detection (EXTI_RISING)
  2. In the NVIC settings (under System Core), enable EXTI line[15:10] interrupts for EXTI11 interrupt.

4.4. Configure X-CUBE-MEMS1

  1. Expand Additional Software to select STMicroelectronics.X-CUBE-MEMS1.7.0.0.
  2. Enable Board Component MEMS.
  3. Configure the Platform settings with I2C2 from the dropdown menu.
  4. You can now click on GENERATE CODE.

The STM32CubeMX code generation will copy over the LSM6DSL component driver into the BSP/ directory and generate an I2C bus I/O interface under Src/ and Inc/:

4.5. Initialize and read values from the sensor (update Src/main.c file)

4.5.1. Include the LSM6DSL driver and I2C bus header files

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lsm6dsl.h"
#include "b_l475e_iot01a1_bus.h"
#include <stdio.h>
/* USER CODE END Includes */

4.5.2. Create a global LSM6DSL motion sensor instance and data available status flag

The variable is marked as volatile because it is going to be modified by an interrupt service routine:

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */
LSM6DSL_Object_t MotionSensor;
volatile uint32_t dataRdyIntReceived;
/* USER CODE END PV */
  • Add a MEMS_Init() function declaration:
/* USER CODE BEGIN PFP */
static void MEMS_Init(void);
/* USER CODE END PFP */

4.5.3. Define the MEMS_Init() function to configure the LSM6DSL motion sensor

  • Range: ±4 g
  • Output Data Rate (ODR): 26 Hz
  • Linear acceleration sensitivity: 0.122 mg/LSB (FS = ±4)
  • Resolution: 16 bits (little endian by default)
/* USER CODE BEGIN 4 */
int _write(int fd, char * ptr, int len)
{
  HAL_UART_Transmit(&huart1, (uint8_t *) ptr, len, HAL_MAX_DELAY);
  return len;
}

static void MEMS_Init(void)
{
  LSM6DSL_IO_t io_ctx;
  uint8_t id;
  LSM6DSL_AxesRaw_t axes;

  /* Link I2C functions to the LSM6DSL driver */
  io_ctx.BusType     = LSM6DSL_I2C_BUS;
  io_ctx.Address     = LSM6DSL_I2C_ADD_L;
  io_ctx.Init        = BSP_I2C2_Init;
  io_ctx.DeInit      = BSP_I2C2_DeInit;
  io_ctx.ReadReg     = BSP_I2C2_ReadReg;
  io_ctx.WriteReg    = BSP_I2C2_WriteReg;
  io_ctx.GetTick     = BSP_GetTick;
  LSM6DSL_RegisterBusIO(&MotionSensor, &io_ctx);

  /* Read the LSM6DSL WHO_AM_I register */
  LSM6DSL_ReadID(&MotionSensor, &id);
  if (id != LSM6DSL_ID) {
    Error_Handler();
  }

  /* Initialize the LSM6DSL sensor */
  LSM6DSL_Init(&MotionSensor);

  /* Configure the LSM6DSL accelerometer (ODR, scale and interrupt) */
  LSM6DSL_ACC_SetOutputDataRate(&MotionSensor, 26.0f); /* 26 Hz */
  LSM6DSL_ACC_SetFullScale(&MotionSensor, 4);          /* [-4000mg; +4000mg] */
  LSM6DSL_ACC_Set_INT1_DRDY(&MotionSensor, ENABLE);    /* Enable DRDY */
  LSM6DSL_ACC_GetAxesRaw(&MotionSensor, &axes);        /* Clear DRDY */

  /* Start the LSM6DSL accelerometer */
  LSM6DSL_ACC_Enable(&MotionSensor);
}
/* USER CODE END 4 */
Info white.png Information
For code simplicity and readability, status code return value checking has been omitted. It is strongly advised to check if the return status is equal to LSM6DSL_OK.

4.5.4. Add a callback to the LSM6DSL sensor interrupt line (INT1 signal on GPIO PD11)

It is used to set the dataRdyIntReceived status flag when a new set of measurement data is available to be read:

/* USER CODE BEGIN 4 */
/*...*/

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if (GPIO_Pin == GPIO_PIN_11) {
    dataRdyIntReceived++;
  }
}
/* USER CODE END 4 */

4.5.5. Call the previously implemented MEMS_Init() function

int main(void)
{
  /* ... */

  /* USER CODE BEGIN 2 */

  dataRdyIntReceived = 0;
  MEMS_Init();

  /* USER CODE END 2 */

4.5.6. Add code to read acceleration data

int main(void)
{
  /* ... */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if (dataRdyIntReceived != 0) {
      dataRdyIntReceived = 0;
      LSM6DSL_Axes_t acc_axes;
      LSM6DSL_ACC_GetAxes(&MotionSensor, &acc_axes);
      printf("% 5d, % 5d, % 5d\r\n",  (int) acc_axes.x, (int) acc_axes.y, (int) acc_axes.z);
    }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

4.6. Compile, download and run

At this point, you can compile, download and run your application.

Warning white.png Warning
If you get a build error: ‘INTERNAL_I2C2_SCL_Pin’ undeclared. This error can be fixed by clearing the pin user labels in STM32CubeMX for PB10 and PB11. In the "'Pinout view:
  • Right-click on the pin.
  • Click Enter User Label.
  • Clear the label.


Warning white.png Warning
If you get a build error “No rule to make target ‘build/lsm6dsl_reg.o’” or a link error, it is probably because the LSM6DSL component drivers have not been correctly added to the list of C_SOURCES files in the Makefile. Check your C_SOURCES and C_INCLUDES configurations:
C_SOURCES =  \
Src/b_l475e_iot01a1_bus.c \
Drivers/BSP/Components/lsm6dsl/lsm6dsl_reg.c \
Drivers/BSP/Components/lsm6dsl/lsm6dsl.c \
...

C_INCLUDES =  \
-IDrivers/BSP/Components/lsm6dsl \
...


Warning white.png Warning
If using STM32CubeMX through STM32CubeIDE, X-CUBE-MEMS1 might fail to generate the b_l475e_iot01a1_bus.h & b_l475e_iot01a1_bus.c files (fatal error: b_l475e_iot01a1_bus.h: No such file or directory).

Possible workarounds:

  • Use the standalone the version of STM32CubeMX.
  • Create a project using the MCU Selector instead of the Board Selector to create a custom bus driver. All pins, clocks and peripherals must be configured manually.

4.7. Visualize and capture live sensor data

When running your application, a new set of 24 acceleration axis values (x, y, z) should be displayed in the serial output every 1/26 Hz = 38 ms. When the board is sitting upright on your desk (not moving), x, y values should be close to 0 g and z should be close to 1 g (1000 mg).

TeraTerm xyz output
  • To capture data, you can copy paste the serial output into a .csv text file or use the following command on a Linux machine:
$ cat /dev/ttyXXXX > capture.csv

With every new capture, you can plot the data using matplotib or any other tool capable of reading csv data. Below is an example of a simple python script to plot (x, y, z data captures):

# plotter.py
import argparse
import numpy as np
import matplotlib.pyplot as plt

parser = argparse.ArgumentParser()
parser.add_argument('filename')
args = parser.parse_args()

data = np.loadtxt(args.filename, delimiter=',')

plt.figure(figsize=(10, 3))
plt.plot(data[:, 0], label='x')
plt.plot(data[:, 1], label='y')
plt.plot(data[:, 2], label='z')
plt.legend()
plt.show()
  • You can then visualize the captured data using with the following command:
$ python plotter.py capture.csv
Motion data plot

If desired, the .csv file can be manually edited for cleanup using a text editor for example. Once you are satisfied with you captures, they can be used for a machine learning model training.

5. Part 3 - Creating an STM32Cube.AI application using X-CUBE-AI (15 min)

5.1. Creating your model

For this example, we will be using a Keras model trained using a small dataset created specifically for this example. Either download the pre-trained model.h5 or create your own model using your own data captures.

  • dataset.zip Is a ready-to-use dataset of 3-axis acceleration data for various human activities.
Info white.png Information
The model will be using unprocessed data. For increased accuracy, data preprocessing such as gravity rotation and suppression can be added. This requires a model re-training to include the data preprocessing. Data preprocessing usage and alternative models can be found in FP-AI-SENSING1. If using a different model, make sure to adjust sensor settings (data rate and scale) accordingly to match the training dataset.

5.2. Add STM32Cube.AI to your STM32CubeMX project.

  • Back into STM32CubeMX, add the X-CUBE-AI Core and Application template components:
  1. Use the Artificial Intelligence filter.
  2. Enable Core and ApplicationTemplate
  3. Click OK
  • Configure the X-CUBE-AI component and re-generate the code:
  1. Expand Additional Software to select STMicroelectronics.X-CUBE-AI.5.0.0
  2. Check to make sure both X-CUBE-AI and Application components are selected
  3. Click Add network
  4. Change the Network Type to Keras
  5. Browse to select the model
  6. (optional) Click on Analyze to view the model memory footprint, occupation and complexity.
  7. GENERATE CODE
Info white.png Information
For complex models, it is recommended to increase the application stack size. Stack size usage can be found using the X-CUBE-AI System Performance application component.

The code generator copied over the AI middleware library and created the following additional user files:

5.3. Running an inference (update Src/main.c file)

5.3.1. Declare neural-network buffers

Declare a neural-network input and output buffer (aiInData and aiOutData). The corresponding output labels ;ust also be added to activities.

/* USER CODE BEGIN PV */
LSM6DSL_Object_t MotionSensor;
volatile uint32_t dataRdyIntReceived;
float aiInData[AI_NETWORK_IN_1_SIZE];
float aiOutData[AI_NETWORK_OUT_1_SIZE];
const char* activities[AI_NETWORK_OUT_1_SIZE] = {
  "stationary", "walking", "running"
};
/* USER CODE END PV */

5.3.2. Create an argmax function

Create an argmax function to return the index of the highest scored output.

/* USER CODE BEGIN 0 */
static uint32_t argmax(const float * values, uint32_t len)
{
  float max_value = values[0];
  uint32_t max_index = 0;
  for (uint32_t i = 1; i < len; i++) {
    if (values[i] > max_value) {
      max_value = values[i];
      max_index = i;
    }
  }
  return max_index;
}
/* USER CODE END 0 */

5.3.3. Update the main while loop

Finally, put everything together with the following changes in your main while loop:

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  uint32_t write_index = 0;
  while (1)
  {
    if (dataRdyIntReceived != 0) {
      dataRdyIntReceived = 0;
      LSM6DSL_Axes_t acc_axes;
      LSM6DSL_ACC_GetAxes(&MotionSensor, &acc_axes);
      // printf("% 5d, % 5d, % 5d\r\n",  (int) acc_axes.x, (int) acc_axes.y, (int) acc_axes.z);

      /* Normalize data to [-1; 1] and accumulate into input buffer */
      /* Note: window overlapping can be managed here */
      aiInData[write_index + 0] = (float) acc_axes.x / 4000.0f;
      aiInData[write_index + 1] = (float) acc_axes.y / 4000.0f;
      aiInData[write_index + 2] = (float) acc_axes.z / 4000.0f;
      write_index += 3;

      if (write_index == AI_NETWORK_IN_1_SIZE) {
        write_index = 0;

        printf("Running inference\r\n");
        if (aiRun(aiInData, aiOutData) != 0) {
          Error_Handler();
        }

        /* Output results */
        for (uint32_t i = 0; i < AI_NETWORK_OUT_1_SIZE; i++) {
          printf("%8.6f ", aiOutData[i]);
        }
        uint32_t class = argmax(aiOutData, AI_NETWORK_OUT_1_SIZE);
        printf(": %d - %s\r\n", (int) class, activities[class]);
      }
    }
    /* Bypass MX_X_CUBE_AI_Process() as aiRun() has been called manually */
  #if 0
  /* USER CODE END WHILE */
  MX_X_CUBE_AI_Process();
  /* USER CODE BEGIN 3 */
  #endif
  }
  /* USER CODE END 3 */
}

5.4. Compile, download and run

You can now compile, download and run your project to test the application using live sensor data. Try to move the board around at different speeds to simulate human activities.

  • At idle, when the board is at rest, the serial output should display “stationary”.
  • If you move the board up and down slowly to moderately fast, the serial output should display “walking”.
  • If you shake the board quickly, the serial output should display “running”.
TeraTerm: HAR Output


Warning white.png Warning
If you get build errors, check your Makefile to see if the AI middleware library, include directory and generated source files have been correctly added.
C_SOURCES =  \
Src/network_data.c \
Src/network.c \
...

C_INCLUDES =  \
-IInc \
-IMiddlewares/ST/AI/Inc \
...

LIBS += -l:NetworkRuntime500_CM4_GCC.a
LIBDIR = -LMiddlewares/ST/AI/Lib


Warning white.png Warning
When using GCC and Newlib-nano, formatted input/output of floating-point number are implemented as weak symbol. If you want to use %f, you have to pull in the symbol by explicitly specifying -u _printf_float command option in the GCC Linker flags. This option can be added to the project Makefile in the LDFLAGS variable:
LDFLAGS += -u _printf_float

5.5. Real-time scheduling considerations

In order to provide real-time results and allow the STM32 application to read accelerometer data without interrupting the model inference, all tasks inside the main while loop should be able to execute under 38 ms (1/26 Hz). For example, we can have the following scheduling breakdown on the STM32L4 @ 80 MHz:

  • 0.33 ms - Sensor data acquisition over I2C (3x2 bytes of data (16-bit x, y, z values) + 1 byte for sensitivity read).
  • 4 ms - Sensor data output UART printf (40 bytes; 3-axis values in ASCII)
  • < 0.1 ms - Preprocessing (data normalization)
  • AI model inference time:
    • ~6 ms for the provided model.h5 (used here)
    • ~4 ms for the IGN_WSDM model
    • ~11 ms for the GMP model
  • 4 ms - Output inference results over UART(40 bytes in ASCII)
logic mems.png
Info white.png Information
To provide more breathing space for time consuming task such as running a neural network inference and blocking serial output messages using printf, the LSM6DSL sensor can be configured to use its internal FIFO to accumulate accelerometer data capture and wake-up the MCU once all required acceleration values have been captured for the model inference. The FIFO threshold size can be adjusted to the neural network model input size.
Info white.png Information
Inference time and memory footprint can also be reduced when using a quantized model.
5.5.1. Overrun protection

If you want to guarantee real-time execution of your model and application, you can add sensor reading overrun detection by changing the LSM6DSL Data-Ready interrupt signal to pulse even if it values have not yet been read. The dataRdyIntReceived counter will be incremented each time the STM32 sees a a pulse from the sensor; even if the main thread is locked in a time consuming task. The counter can then be check ed prior to each data read to make sure there has not been more than one pulse since the last reading.

  • Change the sensor initialization routine to configure DRDY INT1 signal in pulse mode:
static void MEMS_Init(void)
{
  /* ... */

  /* Configure the LSM6DSL accelerometer (ODR, scale and interrupt) */
  LSM6DSL_ACC_SetOutputDataRate(&MotionSensor, 26.0f); /* 26 Hz */
  LSM6DSL_ACC_SetFullScale(&MotionSensor, 4);          /* [-4000mg; +4000mg] */
  LSM6DSL_Set_DRDY_Mode(&MotionSensor, 1);             /* DRDY pulsed mode */
  LSM6DSL_ACC_Set_INT1_DRDY(&MotionSensor, ENABLE);    /* Enable DRDY */
  LSM6DSL_ACC_GetAxesRaw(&MotionSensor, &axes);        /* Clear DRDY */

  /* Start the LSM6DSL accelerometer */
  LSM6DSL_ACC_Enable(&MotionSensor);
}
  • And check that there has not been more than one DRDY pulse since the last sensor reading:
  while (1)
  {

    if (dataRdyIntReceived != 0) {
      if (dataRdyIntReceived != 1) {
        printf("Overrun error: new data available before reading previous data.\r\n");
        Error_Handler();
      }
      LSM6DSL_Axes_t acc_axes;
      LSM6DSL_ACC_GetAxes(&MotionSensor, &acc_axes);
      dataRdyIntReceived = 0;

      /* ... */

    }
  }

If you don't get any errors, your are good to go. Otherwise, you might want to use a smaller model, limit the number of printf or even consider some other approach like the usage of a FIFO or an RTOS for example. To check that your overrun capture is working correctly, you can try adding a simple HAL_Delay() to trigger an overrun error.

6. Closing thoughts

Now that you have seen how to capture and record data, you can:

  • create additional data captures to increase the dataset robustness against model over fitting. It is a good idea to vary the sensor position and user.
  • capture new classes of activities (such as cycling, automotive, skiing and others) to enrich the dataset.
  • experiment with different model architectures for other use-cases.

The boards offers many other sensing and connectivity options:

  • MEMS microphones: for audio and voice applications
  • Other motion sensors (gyroscope, magnetometer)
  • Environmental sensors (temperature & humidity)
  • VL53L0X Time-of-Flight (ToF) proximity sensor
  • Connectivity (Bluetooth® Low Energy, Wi-Fi® and Sub-GHz)
  • ARDUINO® and Pmod™ connectors

7. References


[[Category:Artifical Intelligence]]