# Anomaly Detection & Motion Classification with XIAO ESP32S3

<figure><img src="https://914643686-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff4RN0N7yyVMCwIlMz8jq%2Fuploads%2F9suxa9J0BtWXNBilR5Yv%2Fportada-mov.png?alt=media&#x26;token=e344ef36-e05e-42d1-acae-6673c6121e93" alt=""><figcaption></figcaption></figure>

### Introduction

In my last tutorial, [TinyML Made Easy: Image Classification](https://www.hackster.io/mjrobot/tinyml-made-easy-image-classification-cb42ae), we explored Image Classification on the new tiny device of the Seeed XIAO family, the ESP32S3 Sense. The Sense has a camera and a mic incorporated, but what happens if you want another type of sensor as an IMU? No problem! One great advantage of the XIAO ESP32S3 is its several pins available as an I2C bus (SDA/SCL pins).

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590599/image_GstFLMyDUy.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

### Installing the XIAO ESP32S3 Sense on Arduino IDE

Following my last tutorial, you should have the device installed on the Arduino IDE. If not, let's do a quick review:

On Arduino IDE, navigate to File > Preferences, and fill in the URL:

<https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json>

on the field ==> Additional Boards Manager URLs

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590600/pasted_graphic_JkHMkNOmPR.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt=""><figcaption></figcaption></figure>

Next, open boards manager. Go to Tools > Board > Boards Manager… and enter with esp32. Select and install the most updated package:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590601/pasted_graphic_2_OtwAIVm5cJ.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt=""><figcaption></figcaption></figure>

On Tools, select the Board (XIAO ESP32S3):

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590602/pasted_graphic_4_srwnXRNO0l.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt=""><figcaption></figcaption></figure>

Last but not least, select the Port where the ESP32S3 is connected.

That is it! The device should be OK. To be sure, run the Blink sketch.

#### Installing the IMU

You could select your IMU from several devices found in the market, such as ADXL362 (3-axis), MAX21100 (6-axis), MPU6050 (6-axis), LIS3DHTR (3-axis), or the LCM20600 (6-axis), which is part of the Seeed Grove - IMU 9DOF (lcm20600+AK09918).

For this project, we will use an IMU, the MPU6050 (or 6500), a low-cost (less than 2.00 USD) 6-Axis Accelerometer/Gyroscope unit.

> At the conclusion, I will also comment about using the LCM20600

The [MPU-6500](https://invensense.tdk.com/download-pdf/mpu-6500-datasheet/) is a 6-axis Motion Tracking device that combines a 3-axis gyroscope, 3-axis accelerometer, and a Digital Motion ProcessorTM (DMP) in a small 3x3x0.9mm package. It also features a 4096-byte FIFO that can lower the traffic on the serial bus interface and reduce power consumption by allowing the system processor to burst read sensor data and then go into a low-power mode.

With its dedicated I2C sensor bus, the MPU-6500 directly accepts inputs from external I2C devices. MPU-6500, with its 6-axis integration, on-chip DMP, and run-time calibration firmware, enables manufacturers to eliminate the costly and complex selection, qualification, and system-level integration of discrete devices, guaranteeing optimal motion performance for consumers. MPU-6500 is also designed to interface with multiple non-inertial digital sensors, such as pressure sensors, on its auxiliary I2C port.

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590608/image_ZFuJgZIdRi.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

> Usually the libraries available are for MPU6050, but they work for both devices.

Connecting the HWConnect the IMU to the XIAO according to the below diagram:

* MPU6050 SCL --> XIAO D5
* MPU6050 SDA --> XIAO D4
* MPU6050 VCC --> XIAO 3.3V
* MPU6050 GND --> XIAO GND

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590645/drawing_Vp4G8xChAB.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="Drawing.png"><figcaption></figcaption></figure>

#### Install the Library

Go to Arduino Library Manager and type MPU6050. Install the latest version.

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590642/pasted_graphic_16_CH1rHB6s2M.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="Pasted Graphic 16.png"><figcaption></figcaption></figure>

Download the sketch [MPU6050\_Acc\_Data\_Acquisition.in](https://github.com/Mjrovai/XIAO-ESP32S3-Sense/tree/main/IMU/MPU6050_Acc_Data_Acquisition):

```
/*
 * Based on I2C device class (I2Cdev) Arduino sketch for MPU6050 class by Jeff Rowberg <jeff@rowberg.net>
 * and Edge Impulse Data Forwarder Exampe (Arduino) - https://docs.edgeimpulse.com/docs/cli-data-forwarder
 * 
 * Developed by M.Rovai @11May23
 */

#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"

#define FREQUENCY_HZ        50
#define INTERVAL_MS         (1000 / (FREQUENCY_HZ + 1))
#define ACC_RANGE           1 // 0: -/+2G; 1: +/-4G

// convert factor g to m/s2 ==> [-32768, +32767] ==> [-2g, +2g]
#define CONVERT_G_TO_MS2    (9.81/(16384.0/(1.+ACC_RANGE))) 

static unsigned long last_interval_ms = 0;

MPU6050 imu;
int16_t ax, ay, az;

void setup() {
  
    Serial.begin(115200);

    
    // initialize device
    Serial.println("Initializing I2C devices...");
    Wire.begin();
    imu.initialize();
    delay(10);
    
//    // verify connection
//    if (imu.testConnection()) {
//      Serial.println("IMU connected");
//    }
//    else {
//      Serial.println("IMU Error");
//    }
    delay(300);
    
    //Set MCU 6050 OffSet Calibration 
    imu.setXAccelOffset(-4732);
    imu.setYAccelOffset(4703);
    imu.setZAccelOffset(8867);
    imu.setXGyroOffset(61);
    imu.setYGyroOffset(-73);
    imu.setZGyroOffset(35);
    
    /* Set full-scale accelerometer range.
     * 0 = +/- 2g
     * 1 = +/- 4g
     * 2 = +/- 8g
     * 3 = +/- 16g
     */
    imu.setFullScaleAccelRange(ACC_RANGE);
}

void loop() {

      if (millis() > last_interval_ms + INTERVAL_MS) {
        last_interval_ms = millis();
        
        // read raw accel/gyro measurements from device
        imu.getAcceleration(&ax, &ay, &az);

        // converting to m/s2
        float ax_m_s2 = ax * CONVERT_G_TO_MS2;
        float ay_m_s2 = ay * CONVERT_G_TO_MS2;
        float az_m_s2 = az * CONVERT_G_TO_MS2;

        Serial.print(ax_m_s2); 
        Serial.print("\t");
        Serial.print(ay_m_s2); 
        Serial.print("\t");
        Serial.println(az_m_s2); 
      }
}
```

**Some comments about the code:**

Note that the values generated by the accelerometer and gyroscope have a range: \[-32768, +32767], so for example, if the default accelerometer range is used, the range in Gs should be: \[-2g, +2g]. So, "1G" means 16384.

For conversion to m/s2, for example, you can define the following:

```
#define CONVERT_G_TO_MS2 (9.81/16384.0)
```

In the code, I left an option (ACC\_RANGE) to be set to 0 (+/-2G) or 1 (+/- 4G). We will use +/-4G; that should be enough for us. In this case.

We will capture the accelerometer data on a frequency of 50Hz, and the acceleration data will be sent to the Serial Port as meters per squared second (m/s2).

When you ran the code with the IMU resting over your table, the accelerometer data shown on the Serial Monitor should be around 0.00, 0.00, and 9.81. If the values are a lot different, you should calibrate the IMU.

The MCU6050 can be calibrated using the sketch: [mcu6050-calibration.ino](https://github.com/Mjrovai/XIAO-ESP32S3-Sense/tree/main/IMU/mcu6050-calibration).Run the code. The following will be displayed on the Serial Monitor:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590654/pasted_graphic_19_FhU4qX0dLU.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="Pasted Graphic 19.png"><figcaption></figcaption></figure>

Send any character (in the above example, "x"), and the calibration should start.

> Note that A message MPU6050 connection failed. Ignore this message. For some reason, imu.testConnection() is not returning a correct result.

In the end, you will receive the offset values to be used on all your sketches:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590656/pasted_graphic_20_Tui5mRNqOL.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="Pasted Graphic 20.png"><figcaption></figcaption></figure>

Take the values and use them on the setup:

```
//Set MCU 6050 OffSet Calibration 
imu.setXAccelOffset(-4732);
imu.setYAccelOffset(4703);
imu.setZAccelOffset(8867);
imu.setXGyroOffset(61);
imu.setYGyroOffset(-73);
imu.setZGyroOffset(35);
```

Now, run the sketch [MPU6050\_Acc\_Data\_Acquisition.in:](https://github.com/Mjrovai/XIAO-ESP32S3-Sense/tree/main/IMU/MPU6050_Acc_Data_Acquisition)Once you run the above sketch, open the Serial Monitor:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590659/pasted_graphic_21_DTRap3UbE7.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="Pasted Graphic 21.png"><figcaption></figcaption></figure>

Or check the Plotter:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590660/pasted_graphic_23_hM0BpXdmeI.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="Pasted Graphic 23.png"><figcaption></figcaption></figure>

Move your device in the three axes. You should see the variation on Plotter:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590661/pasted_graphic_22_qOS34YmKic.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="Pasted Graphic 22.png"><figcaption></figcaption></figure>

### The TinyML Motion Classification model

For our tutorial, we will simulate mechanical stresses in transport. Our problem will be to classify four classes of movement:

* Maritime (pallets in boats)
* Terrestrial (palettes in a Truck or Train)
* Lift (Palettes being handled by Fork-Lift)
* Idle (Palettes in Storage houses)

So, to start, we should collect data. Then, accelerometers will provide the data on the palette (or container).

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590536/data1_sg5MS6KfkM.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt=""><figcaption></figcaption></figure>

From the above images, we can see that primarily horizontal movements should be associated with the "Terrestrial class, " Vertical movements with the "Lift Class, " no activity with the "Idle class, " and movement on all three axes to [Maritime class.](https://www.containerhandbuch.de/chb_e/stra/index.html?/chb_e/stra/stra_02_03_03.htm)

#### Connecting the device to Edge Impulse

For data collection, we should first connect our device to the Edge Impulse Studio, which will also be used for data pre-processing, model training, testing, and deployment.

> Follow the instructions [here ](https://docs.edgeimpulse.com/docs/edge-impulse-cli/cli-installation)to install the [Node.js ](https://nodejs.org/en/)and Edge Impulse CLI on your computer.

Once the XIAO ESP32S3 is not a fully supported development board by Edge Impulse, we should, for example, use the [CLI Data Forwarder t](https://docs.edgeimpulse.com/docs/edge-impulse-cli/cli-data-forwarder)o capture data from our sensor and send it to the Studio, as shown in this diagram:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590537/image_PHK0GELEYh.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt=""><figcaption></figcaption></figure>

> You can also capture your data "offline", storing them on a SD card or send data via Bluetouth or Wifi for your computer. On this [video](https://youtu.be/2KBPq_826WM), you can learn alternative ways to send data to the Edge Impulse Studio.

Connect your device to the serial port and run the previous code to capture IMU (Accelerometer) data, "printing them" on the serial. This will allow the Edge Impulse Studio to "capture" them.

Go to the Edge Impulse page and create a project.

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590663/image_xUyC0uWhnG.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

> The maximum length for an Arduino library name is 63 characters. Note that the Studio will name the final library using your project name and include “\_inference” to it. The name I chose initially did not work when I tried to deploy the Arduino library because it resulted in 64 characters. So, I need to change it, taking out the “-anomaly-detection” part.

Start the [CLI Data Forwarder ](https://docs.edgeimpulse.com/docs/edge-impulse-cli/cli-data-forwarder)on your terminal, entering (if it is the first time) the following command:$ edge-impulse-data-forwarder --clean

```
$ edge-impulse-data-forwarder --clean
```

Next, enter your EI credentials and choose your project, variables, and device names:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590664/image_qkRsm7A981.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

Go to your EI Project and verify if the device is connected (the dot should be green):

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590667/image_a5J303wHbE.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

#### Data Collection

As discussed before, we should capture data from all four Transportation Classes. Imagine that you have a container with a built-in accelerometer:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1591091/boat_aOqDzqArqs.jpg?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="boat.jpg"><figcaption></figcaption></figure>

Now imagine your container is on a boat, facing an angry ocean, on a truck, etc.:

* **Maritime** (pallets in boats)

<figure><img src="https://914643686-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff4RN0N7yyVMCwIlMz8jq%2Fuploads%2FLE0URieu6xTfhLVMMJ7A%2Fmaritime.gif?alt=media&#x26;token=ee8a8e97-6bc1-4973-a761-42d4810397bd" alt=""><figcaption></figcaption></figure>

* **Terrestrial** (palettes in a Truck or Train)

<figure><img src="https://914643686-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff4RN0N7yyVMCwIlMz8jq%2Fuploads%2FwauYVwRa2BPmumROkcbx%2Fterrestrial.gif?alt=media&#x26;token=9bcc7895-14fe-4d7c-ac82-7ac055ed2ce3" alt=""><figcaption></figcaption></figure>

* **Lift** (Palettes being handled by

<figure><img src="https://914643686-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff4RN0N7yyVMCwIlMz8jq%2Fuploads%2FDCFHomlQ6jubfmMZrqrc%2Flift.gif?alt=media&#x26;token=c582789d-328a-413c-8d72-86c0a2b77070" alt=""><figcaption></figcaption></figure>

* **Idle** (Palettes in Storage houses)

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590677/idle_OiZWwciVVh.jpg?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="idle.jpg"><figcaption></figcaption></figure>

Below is one sample (raw data) of 10 seconds:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590541/image_E3mFL7tvSh.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt=""><figcaption></figcaption></figure>

You can capture, for example, 2 minutes (twelve samples of 10 seconds each) for the four classes. Using the "3 dots" after each one of the samples, select 2, moving them for the Test set (or use the automatic Train/Test Split tool on the Danger Zone of Dashboard tab). Below, you can see the result datasets:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590679/image_WB3eKzzN6R.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

#### Data Pre-Processing

The raw data type captured by the accelerometer is a "time series" and should be converted to "tabular data". We can do this conversion using a sliding window over the sample data. For example, in the below figure,

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590693/image_KQNIPcxqXV.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

We can see 10 seconds of accelerometer data captured with a sample rate (SR) of 50Hz. A 2-second window will capture 300 data points (3 axis x 2 seconds x 50 samples). We will slide this window each 200ms, creating a larger dataset where each instance has 300 raw features.

> You should use the best SR for your case, considering Nyquist's theorem, which states that a periodic signal must be sampled at more than twice the signal's highest frequency component.

Data preprocessing is a challenging area for embedded machine learning. Still, Edge Impulse helps overcome this with its digital signal processing (DSP) preprocessing step and, more specifically, the Spectral Features.

On the Studio, this dataset will be the input of a Spectral Analysis block, which is excellent for analyzing repetitive motion, such as data from accelerometers. This block will perform a DSP (Digital Signal Processing), extracting features such as "FFT" or "Wavelets".&#x20;

In the most common case, FFT, the **Time Domain Statistical features** per axis/channel are:

* RMS
* Skewness
* Kurtosis

And the **Frequency Domain Spectral features** per axis/channel are:

* Spectral Power
* Skewness
* Kurtosis

So, for example, for an FFT length of 32 points, the resulting output of the Spectral Analysis Block will be 21 features per axis (a total of 63 features).

Those 63 features will be the Input Tensor of a Neural Network Classifier and the Anomaly Detection model (K-Means).

> You can learn more by digging into the tutorial [TinyML under the hood: Spectral Analysis](https://www.hackster.io/mjrobot/tinyml-under-the-hood-spectral-analysis-94676c).

#### Model Design

Our classifier will be a Dense Neural Network (DNN) that will have 63 neurons on its input layer, two hidden layers with 20 and 10 neurons, and an output layer with four neurons (one per each class), as shown here:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590702/image_ojSbkXrKse.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

#### Impulse Design

An impulse takes raw data, uses signal processing to extract features, and then uses a learning block to classify new data.

We also take advantage of a second model, the K-means, that can be used for Anomaly Detection. If we imagine that we could have our known classes as clusters, any sample that could not fit on that could be an outlier, an anomaly (for example, a container rolling out of a ship on the ocean).

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590547/image_pFnNVK4Wjc.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt=""><figcaption></figcaption></figure>

<figure><img src="https://914643686-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff4RN0N7yyVMCwIlMz8jq%2Fuploads%2F9Nif1Bs8fuu04OW4JYjG%2Fanomaly.gif?alt=media&#x26;token=72c5958f-8f38-40eb-9ab0-2bd664425231" alt=""><figcaption></figcaption></figure>

For that, we can use the same input tensor that goes to the NN Classifier as the input of a K-means model:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590548/image_iW1ygppsHi.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt=""><figcaption></figcaption></figure>

Below is our final Impulse design:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590696/image_W8xMffuTwP.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

#### Generating features

At this point in our project, we have defined the pre-processing method and the model designed.&#x20;

Now it is time to finish the job. First, let's take the raw data (time-series type) and convert it to tabular data. Go to the Spectral Features tab, and select Save Parameters:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590697/image_bsHjHtleGs.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

At the top menu, select the Generate Features option and the Generate Features button. Each 2-second window data will be converted into one data point of 63 features.

> The Feature Explorer will show those data in 2D using [UMAP.](https://umap-learn.readthedocs.io/en/latest/) Uniform Manifold Approximation and Projection (UMAP) is a dimension reduction technique that can be used for visualization similarly to t-SNE but also for general non-linear dimension reduction.

The visualization makes it possible to verify that the classes present an excellent separation, which indicates that the classifier should work well.

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590706/image_fyynJu1laN.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

Optionally, you can analyze how important each one of the features is for one class compared with other classes.

#### Training

Our model has four layers, as shown below:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590707/image_0M4u1e4dJI.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

As hyperparameters, we will use a Learning Rate of 0.005 and 20% of data for validation for 30 epochs. After training, we can see that the accuracy is 97%.

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590709/image_cCscB5HMw9.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

For Anomaly Detection, we should choose the suggested features that are precisely the most important ones in the Feature Extraction. The number of clusters will be 32, as suggested by the Studio:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590710/image_8IOqOw1yoX.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

#### Testing

Using 20% of the data left behind during the data capture phase, we can verify how our model will behave with unknown data; if not 100% (what is expected), the result was not that good (8%), mainly due to the terrestrial class. Once we have four classes (which output should add 1.0), we can set up a lower threshold for a class to be considered valid (for example, 0.4):

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590714/image_ecSV5fIlPu.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

Now, the Test accuracy will go up to 97%.

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590715/image_TnLYYt60Vc.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

You should also use your device (which is still connected to the Studio) and perform some Live Classification.

> Just to let you know, here you will capture actual data with your device and upload it to the Studio, where an inference will be taken using the trained model (But the model is NOT in your device).

#### Deploy

Now it is time for magic˜! The Studio will package all the needed libraries, preprocessing functions, and trained models, downloading them to your computer. You should select the option Arduino Library, and at the bottom, choose Quantized (Int8) and Build. A Zip file will be created and downloaded to your computer.

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590716/image_d5jrYgBErG.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

On your Arduino IDE, go to the Sketch tab select the option Add.ZIP Library, and Choose the.zip file downloaded by the Studio:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590717/image_6w7t1NYsBV.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

#### Inference

Now, it is time for a real test. We will make inferences wholly disconnected from the Studio. Let's change one of the code examples created when you deploy the Arduino Library.

In your Arduino IDE, go to the File/Examples tab and look for your project, and on examples, select nano\_ble\_sense\_accelerometer:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590718/image_M3k3wqDRto.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

Of course, this is not your board, but we can have the code working with only a few changes.For example, at the beginning of the code, you have the library related to Arduino Sense IMU:

```
/* Includes --------------------------------------------------------------- */
#include <XIAO-ESP32S3-Motion-Classification_inferencing.h>
#include <Arduino_LSM9DS1.h>
```

Change the "includes" portion with the code related to the IMU:

```
#include <XIAO-ESP32S3-Motion-Classification_inferencing.h>
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"
```

Change the Constant Defines

```
/* Constant defines ------------------------------------------------------- */
MPU6050 imu;
int16_t ax, ay, az;

#define ACC_RANGE           1 // 0: -/+2G; 1: +/-4G
#define CONVERT_G_TO_MS2    (9.81/(16384/(1.+ACC_RANGE)))
#define MAX_ACCEPTED_RANGE  (2*9.81)+(2*9.81)*ACC_RANGE
```

On the setup function, initiate the IMU, set the off set values and range:

```
// initialize device
Serial.println("Initializing I2C devices...");
Wire.begin();
imu.initialize();
delay(10);

//Set MCU 6050 OffSet Calibration 
imu.setXAccelOffset(-4732);
imu.setYAccelOffset(4703);
imu.setZAccelOffset(8867);
imu.setXGyroOffset(61);
imu.setYGyroOffset(-73);
imu.setZGyroOffset(35);

imu.setFullScaleAccelRange(ACC_RANGE);
```

At the loop function, the buffers buffer\[ix], buffer\[ix + 1], and buffer\[ix + 2] will receive the 3-axis data captured by the accelerometer. On the original code, you have the line:

```
IMU.readAcceleration(buffer[ix], buffer[ix + 1], buffer[ix + 2]);
```

Change it with this block of code:

```
imu.getAcceleration(&ax, &ay, &az);       
buffer[ix + 0] = ax;
buffer[ix + 1] = ay;
buffer[ix + 2] = az;
```

You should change the order of the following two blocks of code. First, you make the conversion to raw data to "Meters per squared second (ms2)", followed by the test regarding the maximum acceptance range (that here is in ms2, but on Arduino, was in Gs):

```
buffer[ix + 0] *= CONVERT_G_TO_MS2;
buffer[ix + 1] *= CONVERT_G_TO_MS2;
buffer[ix + 2] *= CONVERT_G_TO_MS2;

for (int i = 0; i < 3; i++) {
     if (fabs(buffer[ix + i]) > MAX_ACCEPTED_RANGE) {
        buffer[ix + i] = ei_get_sign(buffer[ix + i]) * MAX_ACCEPTED_RANGE;
     }
}
```

And that is it! You can now upload the code to your device and proceed with the inferences. The complete code is available on the [project's GitHub](https://github.com/Mjrovai/XIAO-ESP32S3-Sense/tree/main/IMU).If you get an error trying to upload the code to the XIAO ESP32S3 as below, you should switch off ESP NN acceleration.

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590724/image_cOygQbO5pG.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="image.png"><figcaption></figcaption></figure>

To do that, locate ei\_classifier\_config.h in exported Arduino library folder: /scr/edge-impulse-sdk/classifier/:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590720/pasted_graphic_39_CKz4qrWyyI.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt=""><figcaption></figcaption></figure>

Locate the line with #define EI\_CLASSIFIER\_TFLITE\_ENABLE\_ESP\_NN 1, and change it from 1 to 0:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590721/image_DscGI2Bt42.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt=""><figcaption></figcaption></figure>

Now you should try your movements, seeing the result of the inference of each class on the images:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1590736/idle-inference_eCdioWmlzz.png?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="idle-inference.png"><figcaption></figcaption></figure>

#### Conclusion

The Seeed XIAO ESP32S is a giant tiny device! It is powerful, not expensive, low power, and suitable for use on the most common embedded machine learning applications. Even though Edge Impulse does not officially support XIAO BLE Sense, we realized it could be easily connected with the Studio.

Regarding the IMU, this project used the low-cost MPU6050 but could also use other IMUs, for example, the LCM20600 (6-axis), which is part of the [Seeed Grove - IMU 9DOF (lcm20600+AK09918)](https://wiki.seeedstudio.com/Grove-IMU_9DOF-lcm20600+AK09918/).

One advantage of the last device is that it has integrated a Grove connector, which can be helpful in teaching in the case you are using the XIAO with an extension board, as shown below:

<figure><img src="https://hackster.imgix.net/uploads/attachments/1591025/grove-icm2060-small_plZuu0oQ5W.jpg?auto=compress%2Cformat&#x26;w=740&#x26;h=555&#x26;fit=max" alt="Grove-ICM2060-small.jpg"><figcaption></figcaption></figure>

You can just follow the instructions here to connect the IMU with the MCU if you don't mind. Just so you know, for using the Grove ICM20600 Accelerometer, it is essential to update the files I2Cdev.cpp and I2Cdev.h that you will download from the [library provided by Seeed Studio](https://github.com/Seeed-Studio/Seeed_ICM20600_AK09918). For that, could you replace both files from this link? You can find on the GitHub project a sketch for testing the IMU: [accelerometer\_test.ino](https://github.com/Mjrovai/XIAO-ESP32S3-Sense/tree/main/IMU/accelerometer_test).

> On the projet's GitHub repository, you will find the last version of all codes and other docs: [XIAO-ESP32S3 - IMU](https://github.com/Mjrovai/XIAO-ESP32S3-Sense/tree/main/IMU).

#### Knowing more

If you want to learn more about Embedded Machine Learning (TinyML), please see these references:

* "[TinyML - Machine Learning for Embedding Devices"](https://github.com/Mjrovai/UNIFEI-IESTI01-TinyML-2022.1) - UNIFEI
* "[Professional Certificate in Tiny Machine Learning (TinyML)"](https://www.edx.org/professional-certificate/harvardx-tiny-machine-learning) – edX/Harvard
* "[Introduction to Embedded Machine Learning"](https://www.coursera.org/learn/introduction-to-embedded-machine-learning) - Coursera/Edge Impulse
* "[Computer Vision with Embedded Machine Learning"](https://www.coursera.org/learn/computer-vision-with-embedded-machine-learning) - Coursera/Edge Impulse
* "[Deep Learning with Python"](https://www.manning.com/books/deep-learning-with-python) by François Chollet
* “[TinyML”](https://www.oreilly.com/library/view/tinyml/9781492052036/) by Pete Warden, Daniel Situnayake
* "[TinyML Cookbook"](https://github.com/PacktPublishing/TinyML-Cookbook) by Gian Marco Iodice
* "[AI at the Edge](https://www.oreilly.com/library/view/ai-at-the/9781098120191/)" by Daniel Situnayake, Jenny Plunkett

> On the [TinyML4D website](https://tinyml.seas.harvard.edu/courses/), You can find lots of educational materials on the TinyML website. They are all free and open-source for educational uses – we ask that if you use the material, please cite them! TinyML4D is an initiative to make TinyML education available to everyone globally.
