In my last tutorials using the Seed XIAO BLE Sense, we explored embedded machine learning applications, using data generated by its sensors as the accelerometer: TinyML Made Easy: Anomaly Detection & Motion Classification, and microphone: TinyML Made Easy: Sound Classification (KWS).
In the accelerometer case, the data was collected by connecting the XIAO directly to Edge Impulse Studio via a serial port using the computer.
But this method is not ideal because you should have the MCU connected to a computer via a Serial cable. An alternative is to collect data in an "off-line" way, as we did with the microphone, saving recorded audio on an SD card:
But there is an even better alternative to collect low-frequency data from the XIAO without adding an Expansion Board, which uses its onboard Bluetooth capability. For that, we should also develop a kind of "bridge", where an Android App will receive the data generated by the XIAO sensor (via BLE) and store them remotely on a Google Sheet, as shown on the diagram:
This is our goal with this project. Collect the accelerometer data, creating a DataLogger file on Google Sheets file.
The Accelerometer dataThe XIAO BLE Sense has integrated a 6-Axis IMU, the LSM6DS3TR-C, a system-in-package 3D digital accelerometer, and a 3D digital gyroscope. To use it, you should have installed the library 'Seeed Arduino LSM9DS1':
If it is your first time installing the IMU library on the Arduino IDE, optionally you can test the sensor with a simple code as below:
#include "LSM6DS3.h"
#include "Wire.h"
//Create an instance of class LSM6DS3
LSM6DS3 myIMU(I2C_MODE, 0x6A); //I2C device address 0x6A
void setup() {
Serial.begin(9600);
while (!Serial);
if (myIMU.begin() != 0) {
Serial.println("Device error");
} else {
Serial.println("Device OK!");
}
}
void loop() {
//Accelerometer
Serial.print("\nAccelerometer:\n");
Serial.print(" X1 = ");
Serial.println(myIMU.readFloatAccelX(), 4);
Serial.print(" Y1 = ");
Serial.println(myIMU.readFloatAccelY(), 4);
Serial.print(" Z1 = ");
Serial.println(myIMU.readFloatAccelZ(), 4);
delay(1000);
}
You should see the accelerometer data on the Serial monitor.
A quick introduction to BLE (by Arduino)
The Seeed XIAO Sense carries BLE 5.0 wireless capability and can operate with low powerconsumption. Bluetooth® Low Energy is optimized for low power use at low data rates and was designed to operate from simple lithium coin cell batteries.
Unlike standard Bluetooth® communication, based on an asynchronous serial connection (UART) a Bluetooth® LE radio acts like a community bulletin board. The computers connected to it are like community members reading the bulletin board. Each radio acts as either the bulletin board or the reader. If your radio is a bulletin board (called a peripheral device in Bluetooth® LE parlance), it posts data for all radios in the community to read. If your radio is a reader (called a central device in Bluetooth LE terms), it reads from any bulletin boards (peripheral devices) with information about which it cares. You can also think of peripheral devices as the servers in a client-server transaction because they contain the information that reader radios ask for. Similarly, central devices are the clients of the Bluetooth® LE world because they read the information from the peripherals.
The peripheral device can provide one or more services,and each service can have one or more characteristics. For example, a weather station could have a Weather service consisting of the characteristics of Temperature, Pressure, and Humidity. In turn, the central devices, always keeping in mind the example of the weather station, can subscribe to receive notifications when, for example, the value of temperature, humidity, etc., changes.
The structures designed in this way do not require the pairing of the devices as each service and feature has its unique identification code, and therefore that is enough; it does not require the constant presence of the connection between the two devices, and in addition, it allows the central device to subscribe only to what is of interest, receiving a notification when appropriate. The peripheral device delivers services to multiple listening devices as it is not bound to pairing.
In our project, the Service will be the data generated by the XIAO, being as characteristics, the 3-axis data generated by the Accelerometer (one characteristic for each axis). The central device will be the Android app.
This project was inspired and based on the great John Bernard tutorial, which I strongly recommend a visit.
ArduinoBLE LibraryTo use the BLE on the XIAO BLE Sense, we should first install the ArduinoBLE Library:
Next, let's verify if the BLE is properly working. Go to Arduino IDE Menu and open the sketch LED.ico:
File -> Example -> ArduinoBLE -> Peripheral -> LED
This example creates a Bluetooth® Low Energy peripheral with a service that contains a characteristic to control an LED. You can use a generic Bluetooth® Low Energy central app, like LightBlue (iOS and Android) or nRF Connect (Android), to interact with the services and characteristics created in this sketch.
Once this code was created originally to use with Arduino Boards, we should change the line:
const int ledPin = LED_BUILTIN; // pin to use for the LED
to:
const int ledPin = LEDR; // pin to use for the XIAO Red LED
Run the sketch and open the serial monitor. If the BLE service started properly, you should see the info; "BLE LED Peripheral" :
Now, go to your App Store and download, for example, the LightBlue App:
Open the app and see several Bluetooth devices in your area. Select the Device named "Arduino" (Note that one service was also detected). You can confirm that the UUID for the advertisement (LED Control) is the same as you entered in your code.
Click on it to Write a value to be sent to the device.
Note that the RGB LED in the XIAO works with reverse logic (0 -> ON and 1 -> OFF)
You can confirm the onboard XIAO Red LED turned ON/OFF and also at the Serial Monitor:
That's it! Our XIAO BLE Sense is broadcasting advertisements to the world, and now it is time to include real sensor data.
Sending Sensor data via BLE
As we saw in the previous test sketch, we should define the UUid for Service and characteristics (in this case, one for each accelerometer axis).
The UUid should be a unique number, and this UUID Generator can help.
GUID (aka UUID) is an acronym for 'Globally Unique Identifier' (or 'Universally Unique Identifier'). It is a 128-bit integer number used to identify resources. The term GUID is generally used by developers working with Microsoft technologies, while UUID is used everywhere else.
Using the above link, I got the UUid:
64cf715d-f89e-4ec0-b5c5-d10ad9b53bf2
that can be used for service:
const char* UUID_serv = "64cf715d-f89e-4ec0-b5c5-d10ad9b53bf2";
Based on the above service UUid we can change it (1 byte only, for example, the "d", at the end of the first group) to create the three needed characteristics:
// UUids for accelerometer characteristics
const char* UUID_ax = "64cf715e-f89e-4ec0-b5c5-d10ad9b53bf2";
const char* UUID_ay = "64cf715f-f89e-4ec0-b5c5-d10ad9b53bf2";
const char* UUID_az = "64cf7160-f89e-4ec0-b5c5-d10ad9b53bf2";
Once we have the UUids we should instantiate all the services that interest us using the BLEService object and also the characteristics (float values):
// BLE Service
BLEService myService(UUID_serv);
// BLE Characteristics
BLEFloatCharacteristic chAX(UUID_ax, BLERead|BLENotify);
BLEFloatCharacteristic chAY(UUID_ay, BLERead|BLENotify);
BLEFloatCharacteristic chAZ(UUID_az, BLERead|BLENotify);
Details about how to create the above objects can be found in ArduinoIDE Library documentation.
The BLE module should start during setup() with the function:
BLE.begin()
This function returns true if the module is initialized correctly, after which it is possible to finish the BLE setup using the below sequence, which must be exactly this way:
set BLE name > advertised service > add characteristics > add service > set initial values > advertise
// Set BLE name
BLE.setLocalName("IMU-Acc DataLogger");
BLE.setDeviceName("XIAO-BLE-Sense");
// Set advertised Service
BLE.setAdvertisedService(myService);
// Add characteristics to the Service
myService.addCharacteristic(chAX);
myService.addCharacteristic(chAY);
myService.addCharacteristic(chAZ);
// add service to BLE
BLE.addService(myService);
// characteristics initial values
chAX.writeValue(0);
chAY.writeValue(0);
chAZ.writeValue(0);
// start advertising
BLE.advertise();
In the main() we could check if there are central devices, querying them with:
// listen for BLE centrals devices
BLEDevice central = BLE.central();
And then check if any client devices are connected by checking the value taken by:
central.connected()
once we are connected, we can send our data using the following:
chAX.writeValue(ax);
chAY.writeValue(ay);
chAZ.writeValue(az);
where ax, ay, and az are the values to be sent, in our case, the accelerometer data.
A BLE is suitable for situations where data does not change on high frequencies, such as temperature, humidity, and pressure. Also, for the IMU, I took John Bernard tutorial, which advised not to send the IMU raw values, but to average ten values every 10ms. This helps to have more stable values and only to send data every 100ms because below it, John had the impression that the reception on the mobile phone is "flooded, " causing the app to crash.
Below is the complete code:
#include <ArduinoBLE.h> // Arduino BLE library
#include "LSM6DS3.h"
#include "Wire.h"
//Create an instance of class LSM6DS3
LSM6DS3 xIMU(I2C_MODE, 0x6A); //I2C device address 0x6A
/* Online GUID / UUID Generator:
https://www.guidgenerator.com/online-guid-generator.aspx
64cf715d-f89e-4ec0-b5c5-d10ad9b53bf2
*/
// UUid for Service
const char* UUID_serv = "64cf715d-f89e-4ec0-b5c5-d10ad9b53bf2";
// UUids for accelerometer characteristics
const char* UUID_ax = "64cf715e-f89e-4ec0-b5c5-d10ad9b53bf2";
const char* UUID_ay = "64cf715f-f89e-4ec0-b5c5-d10ad9b53bf2";
const char* UUID_az = "64cf7160-f89e-4ec0-b5c5-d10ad9b53bf2";
// BLE Service
BLEService myService(UUID_serv);
// BLE Characteristics
BLEFloatCharacteristic chAX(UUID_ax, BLERead|BLENotify);
BLEFloatCharacteristic chAY(UUID_ay, BLERead|BLENotify);
BLEFloatCharacteristic chAZ(UUID_az, BLERead|BLENotify);
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println("Seeed XIAO BLE Sense IMU-Acc Data Logger");
bool err=false;
pinMode(LEDR, OUTPUT); // onboard led red
pinMode(LEDB, OUTPUT); // onboard led blue
digitalWrite(LEDR, HIGH); // led red off
digitalWrite(LEDB, HIGH); // led blue off
// init IMU
if (xIMU.begin() != 0) {
Serial.println("Device error");
err = true;
} else {
Serial.println("Device OK!");
}
// init BLE
if (!BLE.begin())
{
Serial.println("BLE: failed");
err=true;
}
Serial.println("BLE: ok");
// error: flash led forever
if (err)
{
Serial.println("Init error. System halted");
while(1)
{
digitalWrite(LEDR, LOW);
delay(500);
digitalWrite(LEDR, HIGH); // led on
delay(500);
}
}
// Set BLE name
BLE.setLocalName("IMU-Acc DataLogger");
BLE.setDeviceName("XIAO-BLE-Sense");
// Set advertised Service
BLE.setAdvertisedService(myService);
// Add characteristics to the Service
myService.addCharacteristic(chAX);
myService.addCharacteristic(chAY);
myService.addCharacteristic(chAZ);
// add service to BLE
BLE.addService(myService);
// characteristics initial values
chAX.writeValue(0);
chAY.writeValue(0);
chAZ.writeValue(0);
// start advertising
BLE.advertise();
Serial.println("Advertising started");
Serial.println("Bluetooth device active, waiting for connections...");
}
void loop()
{
static long preMillis = 0;
// listen for BLE centrals devices
BLEDevice central = BLE.central();
// central device connected?
if (central)
{
digitalWrite(LEDB, LOW); // turn on the blue led
Serial.print("Connected to central: ");
Serial.println(central.address()); // central device MAC address
// while the central is still connected to peripheral:
while (central.connected())
{
long curMillis = millis();
if (preMillis>curMillis) preMillis=0; // millis() rollover?
if (curMillis - preMillis >= 10) // check values every 10mS
{
preMillis = curMillis;
updateValues(); // call function for updating value to send to central
}
} // still here while central connected
// central disconnected:
digitalWrite(LEDB, HIGH);
Serial.print("Disconnected from central: ");
Serial.println(central.address());
} // no central
}
void updateValues()
{
uint8_t averages=10; // average on this values count (accelerometer)
// accelerometer averaged values/actual values
static float ax=0;
static float ay=0;
static float az=0;
float ax1, ay1, az1;
static uint8_t i_a=0; // accelerometer readings counter
// read accelerometer values
i_a++;
ax1 = xIMU.readFloatAccelX();
ay1 = xIMU.readFloatAccelY();
az1 = xIMU.readFloatAccelZ();
ax+=ax1;
ay+=ay1;
az+=az1;
if (i_a==averages) // send average over BLE
{
ax/=averages;
ay/=averages;
az/=averages;
Serial.println("Accelerometer: "+String(ax)+","+String(ay)+","+String(az));
chAX.writeValue(ax);
chAY.writeValue(ay);
chAZ.writeValue(az);
ax=0;
ay=0;
az=0;
i_a=0;
}
}
If everything is OK, you should see in the Serial Monitor that Device (IMU) and BLE are OK, that the advertising has started, and that the XIAO is actively waiting for connections:
You can use the LightBlue app to verify if you can connect and send data.
When connected, the accelerometer data will also be shown on the Serial Monitor:
In the last section, we start "broadcasting" or "advertising" our sensor data (in this case, the accelerometer) to the world via BLE. And now, show the steps to send data from an app created with MIT AI2 to a Google spreadsheet. The simplest way to do it is "hacking" the spreadsheet generated by a Google Form. This clever idea was shown by @TheCodingBus on this tutorial: How to send data to a google sheet with MIT app inventor.
Create a Google Form:
Start a blank Google Sheet and give it a name. Next, go to Tools
at the top menu and select Create a new form / Autocomplete
:
When the Google form appears at +
, add three Short answer
questions and name them with your sensor name (in my case, xAcc, yAcc, and zAcc):
Next on the upper-right three dots menu, select the option Get pre-filed link
:
Enter any data on the three questions (for example, 10, 20, and 30). Use the button Get link
that appears on the form bottom; click on it (1), and when the option COPY LINK
appears, use it (2):
The link is copied to the clipboard. For example, paste it on a text editor or a new tab on your spreadsheet. You will need to change it. Look for "wiewform" and replace it with "formResponse?&submit=Submit", as below:
Only for testing, copy the new link and paste it into a browser and go! If its works, you should see the data that you entered on the Google Form saved on your original spreadsheet:
Returning to our link, we should create three strings: 1) Copy everything from https:.... until the "=" signal before the first data entered in the form (in my case 10).
Save it somewhere. And 2) copy the string that goes from &entry.... until the "=" signal before the second data entered in the form (in my case 20).
And 3) repeat with the last data entered:
At this point, we should have 3 "strings" of text that, for reference, I called 1, 2, and 3. They will be used on our App.
Creating a simple test app with the MIT AI2Our app will have three Text Boxes where we will enter (manually) the dummy accelerometer data (one for each axis), a hidden WebViewr, and a button to send the data to our spreadsheet:
The code behind this app is very simple. When the button is pressed, we call the WebViwer and create an URL similar to the link that was created with the data "10, 20, and 30", but now, those data will be replaced by the data entered on the TextBox (note that we entered with the three strings (1, 2 and 3) saved on the previous section:
Entering dummy data on the app, you can see them appear automatically on the original spreadsheet:
This MIT. AI2 code (.aia) can be downloaded from my GitHub.Creating an app to bridge the data received with BLE and the Google sheet.
Again, the base for this app was the great John Bernard tutorial, which I strongly recommend a visit.
For using the BLE, the client device must use a component that is not the classic Bluetooth, and you should download it (BluetoothLE) from the site extensions.
Once the module Bluetooth extension is imported, you can start programming the app using its blocks. I will not enter many details here, but I will leave the project (.aia) on GitHub; that is the best way to understand and reproduce it with your project. Here is what the app will look like:
First, you should change the UUIds that were used on your Xiao project (below are the ones that I used):
Before we continue, let's confirm that Xiao is working and prepared to send the accelerometer data. Press reset on it and see the Serial Monitor:
Using your smartphone as AI Companion
, press the button SEARCH Devices
, and a list of Bluetooth devices will appear on the screen. Select yours (the name is defined on Setup() in your code), and press Connect
.
Immediately you see on the Serial Monito a message showing that your device is connected to a central and the accelerometer data start to be listed, which means that they have also been transmitted:
The Data will so, start to appear on the app screen due to the function updateValues as shown in this part of the code:
Looking at the above code is also noted that when data are received, another function is called sendData. This function will always send data to the spreadsheet when the Send Data
button is pressed.
Once the data is logged into our spreadsheet, the button will change color to green and be labeled Sending...
When you want to stop sending data, press the button again.
That's it! We have created a sensor datalogger with the MIT AI2 app as a comm bridge and a simple controller.
This MIT. AI2 code (.aia) can be downloaded from my GitHub.Conclusion
Using BLE, it is possible to log data generated by the Seeed XIAO BLE Sense remotely. The XIAO Sense is powerful, trustworthy, not expensive, low power, and has suitable sensors to be used on the most common embedded machine learning applications such as movement and sound.
Considering that you can connect other sensors to the MCU using an expansion board, the onboard BLE is a good wireless method to send low-frequency sampled data to a remote data repository. This collected data can, for example as.csv. sent to the Edge Impulse Studio to be trained on an Embedded Machine Learning project.
On my GitHub repository, you will find the last version of the codes used in this tutorialKnowing more
If you want to learn more about Embedded Machine Learning (TinyML), please see these references:
- "TinyML - Machine Learning for Embedding Devices" - UNIFEI
- "Professional Certificate in Tiny Machine Learning (TinyML)" – edX/Harvard
- "Introduction to Embedded Machine Learning" - Coursera/Edge Impulse
- "Computer Vision with Embedded Machine Learning" - Coursera/Edge Impulse
- "Deep Learning with Python" by François Chollet
- “TinyML” by Pete Warden, Daniel Situnayake
- "TinyML Cookbook" by Gian Marco Iodice
Also, you can take a look at the TinyML4D website. TinyML4D is an initiative to make TinyML education available to everyone globally.That's all, folks!
As always, I hope this project can help others find their way into the exciting world of AI!
link: MJRoBot.org
Greetings from the south of the world!
See you at my next project!
Thank you
Marcelo
Comments