Motion Sense Demonstration
1 NanoEdgeAI. Studio
Demo Developed in a B-U585-IOT2A Board
2 STM32CubeMX
3 STM32CubeIDE
4 Debug and Results
NanoEdgeAI. Studio
Data Logger Creation
Let’s start by creating a Data Logger for collecting the sensor’s data from our board.
We can generate binaries for a variety of boards. For now, let’s choose the B-U585I-IOT02A:
Let’s configure the ISM330DHCX Accelerometer. We will set up the sampling configurations and
activate the acquisitions in the 3-axis. Then, we can generate our Data Logger binary:
If using custom application*
* In case of custom board/sensors application, check the Datalogger source code to proceed with your code portability
Moving to File Explorer
Now, you can flash your board with the Generated Datalogger. Follow the steps below:
1. Click on the
Generated Datalogger
2. Select the .bin file
3. Just drag and
drop it in the Board
Validation with Tera Term
When the binary flashed onto the board, the COM LED will blink and it will collect raw data from the selected
sensor and send it to the Serial Port. You can verify the results with TeraTerm, by enabling and setting up the
COM Port communication in [Setup] > [Serial port…]. The Serial window should look like the image below:
Back to NanoEdge AI Studio
Go back to the home page in NanoEdge AI Studio by clicking on the house icon, and let’s create a n-Class
Classification project. Select the project type and click on “Create new project”:
Project Settings
For the first step of the project, you need to setup your project. Choose a name for your project, select the Target
Board (B-U585I-IOT02A) and which Sensor Type we are using. Then, click on “Save & Next” for the next step:
Import Signals
The next step is adding the signals. First, click on “Add signal” and then select “From serial (USB)” in the poped-
up window.
Import Signals
The next tab is to collect the signals. In this tab, you need to configure the COM Port and Baudrate of your serial
communication. You can also set the maximum number of lines. Keep in mind the minimal number of lines is 20
lines per axis. WARNING: Close the TeraTerm window to proceed with this step!
When you press the “Start/Stop” button and the
“Maximum number of lines” is not selected, the
sensor will keep collecting the signals until the
“Start/Stop” button is pressed once again.
You can check how many lines were collected
by the right side, in “Number of lines”.
After collecting the signals, you can press the
“Continue” button at the left bottom corner.
Signal Classification
For this project, we need to collect 3 different classes of signals, them being: STOP, WALK, and RUN. You can
choose how you want to differentiate each one of them (i.e. by the hands movements on each class).
Here are the signals values and its Fast Fourier Transformation (FFT) observed in the three classes:
STOP Class WALK Class RUN Class
Benchmark
After importing the signals, we can proceed with the benchmark step. The benchmark is responsible to
analyze, choose and validate the best ML processing for your project. To do this, you can click on the button
"Run new benchmark" and wait for the desired score to stop the process.
Validation (Optional)
For the Validation step (optional), you can click on “+ New Experiment” button and include a file containing a
data vector to validate the generated library and benchmark.
Select the Library version
and the Benchmark
Emulator (Optional)
For the Emulation step (optional), click on “Initialize Emulator” button and select “From Serial (USB)” to enable
the Live Analysis.
Deployment
To conclude our project on NanoEdge AI Studio, on the Deployment step, click on "Compile Library" and save
the precompiled library as *.zip file in your preferred location.
Note that the software also generates a "Hello World" C code as reference.
STM32Cube MX
Create a Project
Create a new STM32 Project within STM32CubeIDE by going to [File] > [New] > [STM32 Project]. Then go
to [Board Selector], type the B-U585I-IOT02A board, select it and click on [Next]:
Create a Project
Finally, you can name your project and click on the [Finish] button. Then, select [No] for the next pop-up
window:
Manage Software Packs
At the STM32CubeMX *.ioc file, we need to include the X-CUBE-MEMS1 software package, which contains
the necessary files and codes for configuring the accelerometer sensor. Go to [Software Packs] > [Manage
Software Packs] and find the X-CUBE-MEMS1 at “STMicroelectronics” tab. Select the latest version
available and click on [Install] button.
2
Select Components
After installing the software package, go to [Software Packs] > [Select Components] and select the
accelerometer via I2C interface. Within the Component Selector, go to [STMicroelectronics X-CUBE-
MEMS1] > [Board Part AccGyr] > [ISM330DHCX] and select “I2C”, then click on [Ok] button.
I2C2 Pinout & Configuration
Going back to the *.ioc file Pinout & Configuration tab, we first need to configure the I2C. Go to the
[Connectivity] tab and select the [I2C2]. At the [Parameter Settings] select the I2C speed mode as “Fast
Mode”:
I2C2 Pinout & Configuration
At the [GPIO Settings] tab, change the I2C pinout to the following pins, according to the MCU Schematic:
Middleware and Software Packs
We can now select the Software Pack for our project. Go to [Middleware and Software Packs] > [X-CUBE-
MEMS1] and check the “Board Part AccGyr” box. Then, select “I2C2” as [Found Solutions] at [Platform
Settings]:
ISM330DHCX Interruption Pin
The next step is to set the ISM330DHCX Interruption pin, also through the MCU Schematic:
Looking for the INT1 interruption pin, we have the PE11 as final pinout:
ISM330DHCX Interruption Pin
Then, select the PE11 pin and set it as GPIO_EXTI11. After this, go to [System Core] > [NVIC] and check
the “EXTI Line11 interrupt” box:
USART1 Pinout & Configuration
The last peripheral to be set is the USART1, so the result can be printed through the serial communication.
Go to [Connectivity] > [USART1] and configure it as the figure below:
USART1 Pinout & Configuration
Make sure to change the USART1 pinout as well for PA9 and PA10, like the figure below:
Code Generation
By the end of the configuration, the MCU UFBGA169
package graphical interface should look like the figure at
the left side.
We can now generate the code by clicking on the gear icon
or by the [Alt] + [K] keyboard shortcut:
STM32Cube IDE
Adding the NEAI Library
For the next steps, you need to unzip the compiled library file downloaded through NanoEdge AI Studio. The
unzipped folder should look like the figure below:
We need to add the *.h and *.a files to our project, so the first step is to create a folder within your project for
the NanoEdge AI Studio Library:
Adding the NEAI Library
Name the new folder as “NEAI_Lib”, then create two more folder within the NEAI_Lib called “Inc” and “Lib”,
just as the figure below shows:
With this, drag and drop the compiled library “knowledge.h” and “NanoEdgeAI.h” files into the [NEAI_Lib] >
[Inc] folder and the “libneai.a” file into the [NEAI_Lib] > [Lib] folder:
Adding the NEAI Library
After adding the generated library to your project, right click on your project name and click on “Properties”.
Then go to [C/C++ General] > [Paths and Sysmbols] > [Includes] and add the “NEAI_Lib/Inc” path then click
on “Apply”.
Adding the NEAI Library
Move to the [Libraries] tab and add the “neai” library, then click on “Apply”:
Adding the NEAI Library
Finally, move to the [Library Paths] tab and add the “NEAI_Lib/Lib” library path, then click on “Apply and
Close”:
Coding
Now, we can go to the coding part. At the project’s “main.c” file, add the following instructions:
• Private Includes • Private Defines: • Private Macros:
/* USER CODE BEGIN Includes */ /* USER CODE BEGIN PD */ /* USER CODE BEGIN PM */
//Accelerometer header //Declare the accelerometer data static void MEMS_Init(void);
#include "ism330dhcx.h" struct /* USER CODE END PM */
ISM330DHCX_Object_t MotionSensor;
//I2C2 Bus and BSP header
#include "b_u585i_iot02a_bus.h" //Data reception Indicator
volatile uint32_t dataRdyIntReceived;
//Standart IO library
#include <stdio.h> #define SIGNAL_SIZE
(uint32_t)(DATA_INPUT_USER *
//NanoEdgeAI Headers AXIS_NUMBER)
#include "NanoEdgeAI.h" /* USER CODE END PD */
#include "knowledge.h"
/* USER CODE END Includes */
Coding
Note: These variables declarations can be found into the “NanoEdgeAI.h” file’s last comment.
• Private Variables
/* USER CODE BEGIN PV */
uint16_t id_class = 0; // Point to id class (see argument of neai_classification fct)
float input_user_buffer[DATA_INPUT_USER * AXIS_NUMBER]; // Buffer of input values
float output_class_buffer[CLASS_NUMBER]; // Buffer of class probabilities
const char *id2class[CLASS_NUMBER + 1] = { // Buffer for mapping class id to class name
"unknown",
"WALK",
"RUN",
"STOP",
};
/* USER CODE END PV */
Coding
We need to add a function to fill the buffer with the sensor’s data, which later will be the inputs for the NEAI
functions. Add the function to the “private user code” section:
• Private User Code (USER CODE BEGIN 0)
/* USER CODE BEGIN 0 */
void fill_buffer(float input_buffer[])
{
uint16_t i;
ISM330DHCX_Axes_t acc_axes;
for(i=0;i<(DATA_INPUT_USER*AXIS_NUMBER);i+=3)
{
while (dataRdyIntReceived == 0);
dataRdyIntReceived = 0;
ISM330DHCX_ACC_GetAxes(&MotionSensor, &acc_axes);
printf("X = %5d, Y = %5d, Z = %5d\r\n", (int) acc_axes.x, (int) acc_axes.y, (int) acc_axes.z);
input_buffer[i] = (int) acc_axes.x;
input_buffer[i+1] = (int) acc_axes.y;
input_buffer[i+2] = (int) acc_axes.z;
}
}
Coding
Still at the “Private User Code”, we need to add the MEMS_Init() function to initialize, configure, and connect
the ISM330DHCX sensor to the STM32 MCU:
• Private User Code (USER CODE BEGIN 0)
static void MEMS_Init(void) /* (MEMS_Init continuation...) */
{
ISM330DHCX_IO_t io_ctx; /* Initialize the ISM330DHCX sensor */
uint8_t id; ISM330DHCX_Init(&MotionSensor);
ISM330DHCX_AxesRaw_t axes;
/* Configure the ISM330DHCX accelerometer (ODR, scale and interrupt) */
/* Link I2C functions to the ISM330DHCX driver */ ISM330DHCX_ACC_SetOutputDataRate(&MotionSensor, 26.0f); /* 26 Hz */
io_ctx.BusType = ISM330DHCX_I2C_BUS; ISM330DHCX_ACC_SetFullScale(&MotionSensor, 4); /* [-4000mg; +4000mg] */
io_ctx.Address = ISM330DHCX_I2C_ADD_H; ISM330DHCX_Set_INT1_Drdy(&MotionSensor, ENABLE); /* Enable DRDY */
io_ctx.Init = BSP_I2C2_Init; ISM330DHCX_ACC_GetAxesRaw(&MotionSensor, &axes); /* Clear DRDY */
io_ctx.DeInit = BSP_I2C2_DeInit;
io_ctx.ReadReg = BSP_I2C2_ReadReg; /* Start the ISM330DHCX accelerometer */
io_ctx.WriteReg = BSP_I2C2_WriteReg; ISM330DHCX_ACC_Enable(&MotionSensor);
io_ctx.GetTick = BSP_GetTick; }
ISM330DHCX_RegisterBusIO(&MotionSensor, &io_ctx); /* USER CODE END 0 */
/* Read the ISM330DHCX WHO_AM_I register */
ISM330DHCX_ReadID(&MotionSensor, &id);
if (id != ISM330DHCX_ID) {
Error_Handler();
}
Coding
In the USER CODE 2 section, we call the MEMS_Init() function. Additionally, we initialize the
neai_classification_init(knowledge) function, which starts the use of the NanoEdge AI library resources in
our application.
Note: ‘Knowledge' refers to the data vector that was collected in NanoEdge AI Studio and serves as the function dataset.
• USER CODE 2
/* USER CODE BEGIN 2 */
dataRdyIntReceived = 0;
MEMS_Init();
enum neai_state error_code = neai_classification_init(knowledge);
if(error_code != NEAI_OK)
{
printf("Knowledge Initialization ERROR: %d\r\n", error_code);
}
else
{
printf("Knowledge Initialization Done\r\n");
}
/* USER CODE END 2 */
Coding
Within the infinite while(1) loop, we repeatedly call the fill_buffer() function to populate the data vector that
will undergo analysis in the application. The _classification( ) macro, in turn, performs a comparison
between the accelerometer data and its previously stored datasets.
• USER CODE 3
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
fill_buffer(input_user_buffer);
neai_classification(input_user_buffer, output_class_buffer, &id_class);
printf("Class: %s [%u%%]\r\n",id2class[id_class],(uint16_t)(output_class_buffer[id_class-1]*100));
}
/* USER CODE END 3 */
Coding
Finally, at the USER CODE 4, we will insert a callback function that updates the dataRdyIntReceived
variable to handle the interrupts. We also have a _write() macro that enables the use of the printf( ) function
in the Terminal.
• USER CODE 4
/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_11)
dataRdyIntReceived++;
}
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 */
Debug and Results
Debug with STM32CubeIDE
1. Click on the
“Debug” icon
3. Add the buffer
and monitor its
values as the
application runs
2. Click on the
“Live Expressions”
Debug with TeraTerm
After opening the correct VCOM in TeraTerm, we can see two types of printed messages:
Without commenting out the printf( ) Commenting out the printf( ) in
in the fill_buffer() function the fill_buffer() function