XIAO-Big Power, Small Board
XIAO-Big Power, Small Board
XIAO-Big Power, Small Board
Preface................................................................................................................................................................4
Acknowledgments..........................................................................................................................................4
Introduction......................................................................................................................................................4
About this Book............................................................................................................................................... 5
3
Preface
From the expansive boards of the past, Arduino has come a long way and entered the Seeed
Studio XIAO series: thumb-sized yet power-packed, opening a vast horizon for innovation. “XIAO:
Big Power, Small Board” dives deep into these capabilities, guiding readers from the basics of
Arduino to intricate miniaturized projects. Whether readers want to illuminate an LED or delve
into Embedded Machine Learning (TinyML) with XIAO boards and Edge Impulse Studio, this
book covers them. Need for prior knowledge? No worries! This book takes a hands-on, project-
based approach, ensuring readers grasp the concepts while implementing them. By the end,
they will be adept with XIAO and inspired by many user-created projects showcasing the
endless possibilities this small board offers.
Acknowledgments
We want to express our sincere gratitude to Jiamou Yang, Yanming Wen, Mengdu Li, Chunchun
Tian, Haixu Liu, Tianrui Wang, and Jianjing Huang for their invaluable technical support and
manuscript revisions. This book would not have been possible without their contributions.
We extend our deepest gratitude to the entire TinyML4D Academic Network, comprised of
distinguished professors, researchers, and professionals. Notable contributions f rom Marco
Zennaro, Brian Plancher, José Alberto Ferreira, Jesus Lopez, Diego Mendez, Shawn Hymel, Dan
Situnayake, Pete Warden, and Laurence Moroney have been instrumental in advancing our
understanding of Embedded Machine Learning (TinyML).
Special commendation is reserved for Professor Vijay Janapa Reddi of Harvard University. His
steadfast belief in the transformative potential of open-source communities, coupled with
his invaluable guidance and teachings, has served as a beacon for our efforts from the very
beginning.
Acknowledging these individuals, we pay tribute to the collective wisdom and dedication that
have enriched this field and our work.
Illustrative images on the e-book and chapter’s covers generated by OpenAI’s DALL-E via
ChatGPT
Introduction
From the expansive boards of the past, Arduino has come a long way and entered the Seeed
Studio XIAO series: thumb-sized yet power-packed, opening a vast horizon for innovation. “XIAO:
Big Power, Small Board” dives deep into these capabilities, guiding readers from the basics of
Arduino to intricate miniaturized projects. Whether readers want to illuminate an LED or delve
into Embedded Machine Learning (TinyML) with XIAO boards and Edge Impulse Studio, this
book covers them. Need for prior knowledge? No worries! This book takes a hands-on, project-
based approach, ensuring readers grasp the concepts while implementing them. By the end,
they will be adept with XIAO and inspired by many user-created projects showcasing the
endless possibilities this small board offers.
5
Software dependencies
Arduino IDE:
Major updates or changes to the Arduino IDE might affect content related to Arduino
development and programming in the book.
MQTT Libraries/Protocols:
Any changes related to MQTT libraries or the protocol itself could influence the content of
telemetry and commands.
ESP32 Libraries:
Updates to libraries used by the XIAO ESP32C3 and ESP32S3 board may impact associated
projects or examples.
Book outline
Chapter 1: Introduction to Hardware and Programming
In this chapter, readers start with basic programming on XIAO using Arduino IDE. Through
simple example programs, they will learn to control LED lights, buttons, buzzers, and other
electronic components, mastering core programming concepts like digital I/O, analog I/O, tone
generation, and mapping values. By manually typing out code examples line-by-line, they will
develop strong coding habits and grasp programming syntax.
In this chapter, readers will learn the basics of designing prototypes with XIAO through
beginner-f riendly projects. They will start f rom an idea and quickly create a verif ication
prototype, focusing more on the practical application of code rather than line-by-line analysis.
By leveraging Arduino libraries, community resources, and example programs, they will learn
how to find and adapt code snippets to achieve desired effects efficiently. Furthermore, they will
explore how to design the physical appearance of prototypes by creatively combining electronic
hardware with everyday items. The key outcomes are grasping a project-based approach and
developing skills to build simple interactive prototypes.
Copyright Statement
This book, "XIAO: Big Power, Small Board," is published under the GNU General Public License
version 3.0 (GPL-3.0), ensuring it remains free and open for all to use, modify, and distribute. The
GPL-3.0 License is a widely respected and used free software license, guaranteeing users the
freedom to run, study, share, and modify the software. The authors, Lei Feng and Marcelo Rovai,
along with all contributors, embrace the principles of open knowledge and education, ensuring
that this valuable resource remains accessible to those who seek to
advance their skills in Arduino, TinyML, and beyond. By choosing this
license, we commit to fostering a community-driven approach to
learning and innovation. For more details on the rights and limitations
under this license, please refer to the official GPL-3.0 documentation.
7
Chapter 1:
Introduction to Hardware and
Programming
In this unit, we will enter the world of electronics and programming and explore how to
control hardware through code. Starting with the example program, Blink, we will learn
how to light up an LED, turn the light on and off through a button, control the sound of
a passive buzzer, and so on. In each task, we will master commonly used programming
languages, such as digital input/output, analog input/output, tone and map functions,
etc., and learn the primary usage of libraries. The programs in this unit are relatively
simple. During the learning process, write the program code for each task by hand,
develop good habits, and avoid program upload failures due to errors in symbols or
unfamiliar rules.
Due to space limitations, all parts of this course’s program code and hardware connection
are based on Seeed Studio XIAO SAMD21. Most of the code in the book can be applied to all
products in the Seeed Studio XIAO series. If there are exceptions, they will be additionally
marked or explained for applicable hardware. If not marked, they apply to multiple
products.
We must add the Seeed Studio XIAO series products to the Arduino IDE to start our learning
journey.
• For Windows users, first, open your Arduino IDE, click “File→Preferences” in the top menu bar,
as shown in the figure, and copy the following URL into “Additional Boards Manager URLs.”
• For Mac users, first, open your Arduino IDE, click “Arduino IDE→Preferences” in the top menu
bar, as shown in the figure, and copy the following URL into “Additional Boards Manager
URLs.”
If you f requently use multiple different models of XIAO at the same time, you can click on
the icon on the right side of the address bar and add all three addresses above to the board
manager, as shown in the figure below.
Next, click on "Tools → Board → Board Manager", enter the keyword Seeeduino XIAO in the
search bar, find Seeed SAMD Boards in the appeared entries, and click INSTALL.
- Attention -
• Enter “RP2040” in the search bar to find the installation package for Seeed XIAO RP2040.
• Enter “XIAO nrf52840” to find two installation packages: Seeed nRF52 Boards (for low-
power projects) and Seeed nRF52 mbed-enabled Boards (for higher-power TinyML
projects).
• Enter “ESP32” to find the installation package for ESP32 by Espressif Systems.
• If your development board is XIAO nRF52840, please select Seeed XIAO nrf52840.
• If your development board is XIAO nRF52840 Sense, please select Seeed XIAO nrf52840
Sense.
• If your development board is XIAO RP2040, please select Seeed XIAO RP2040.
• If your development board is XIAO ESP32C3, please select XIAO_ESP32C3.
• If your development board is XIAO ESP32S3, please select XIAO_ESP32S3.
- Attention -
XIAO ESP32C3 may not be adequately recognized in Arduino IDE 2, and you need to specify
the development board and port manually.
Now you can see that the development board and port are in the correct state.
Sometimes when the program upload fails, the Seeed Studio XIAO port may disappear, and
we need to perform a reset operation. The reset method will be different for different models
of XIAO.
Reset of Seeed Studio XIAO SAMD21
• Connect XIAO SAMD21 to your computer.
• Open “Blink” in the Arduino IDE sample program
and click upload.
• While uploading, short circuit the RST pin in the
figure once with tweezers or a short wire.
• The reset is completed when the orange LED
flashes and lights up.
As shown in the figure below.
Reset of Seeed Studio XIAO PR2040
• Connect Seeed Studio XIAO RP2040 to your
computer.
• Press the reset button marked with “R” once, the
position is shown in the figure below.
If this does not work, hold down the Boot button
marked with “B”, connect the board to your computer
while holding down the BOOT button, and then
release it to enter the bootloader mode.
Reset of Seeed Studio XIAO nRF52840 and Sense version
• Connect Seeed Studio XIAO nRF52840 or Sense
version to your computer.
• Press the reset button marked with “RST” once, the
position is shown in the figure below.
If this does not work, you can quickly click it twice to
enter the bootloader mode.
Reset of Seeed Studio XIAO ESP32C3
• Connect Seeed Studio XIAO ESP32C3 to your
computer.
• Press the reset button marked with “R” once, the
position is shown in the figure below.
If this does not work, hold down the Boot button
marked with “B”, connect the board to your computer
while holding down the BOOT button, and then
release it to enter the bootloader mode.
Reset of Seeed Studio XIAO ESP32S3
• Connect Seeed Studio XIAO ESP32S3 to your
computer.
setup()
This function is called when the program begins. Use it to initialize variables, pin modes, start
using libraries, etc. setup() runs only once each time the Arduino board is powered on or reset.
loop()
After the program in setup() is executed, the program in loop() begins to execute. The
program in loop() runs repeatedly.
- Knowledge Window -
• The contents after “/* */” and “//” are comments to help you understand and manage code,
the comments will not affect the normal operation of the program;
• When writing programs, we need to use “{}” to wrap a set of codes;
• After each line of code, use “;” as an end symbol to tell the Arduino editor that this line of
code instruction is over.
In Arduino, you can use functions to set the status and function of pins. Here are the basic steps
to set pins through functions:
1. First, determine the pin number of the pin you want to control.
2. In the Arduino code, use the pinMode() function to set the function of the pin, such as
input or output. For example, to set the pin to output mode, you can use the following
code:
3. Once you have set the pin to output mode, you can use the digitalWrite() function
to set the status of the pin, such as setting it to high or low level. For example, to set the
pin to high level, you can use the following code:
4. If you set the pin to input mode, you can use the digitalRead() function to read the
status of the pin, such as detecting whether it is high or low level. For example, to read
the status of the pin and save it to a variable, you can use the following code:
By using functions like pinMode(), digitalWrite(), and digitalRead(), you can easily set and
control the status and function of pins in Arduino.
After opening the example program, you can see the following code, which implements the
effect of LED flashing. You can see that the code has orange and green color prompts, which
proves that your input is correct. Pay attention to the difference between uppercase and
lowercase.
Turns an LED on for one second, then off for one second, repeated
Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO
it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to
the correct LED pin independent of which board is used.
If you want to know what pin the on-board LED is connected to on your Arduino
model, check the Technical Specs of your board at:
https://www.arduino.cc/en/Main/Products
https://www.arduino.cc/en/Tutorial/BuiltInExamples/Blink
*/
// the setup function runs once when you press reset or power the b
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
Code Analysis
pinMode(LED_BUILTIN, OUTPUT);
The f irst thing the code does is to initialize LED_BUILTIN as an output pin in the setup()
function. Most Arduino series boards default the onboard LED to digital pin 13. The constant
LED_BUILTIN connects the onboard LED to pin 13.
digitalWrite(LED_BUILTIN, HIGH);
In the loop() function, we set the LED_BUILTIN pin to the "on" state, outputting 5V or 3.3V
voltage to this pin, which can be represented by HIGH. However, note that all I/O pins on XIAO are
3.3V. Do not input a voltage exceeding 3.3V, or it may damage the CPU.
digitalWrite(LED_BUILTIN, LOW);
What comes on must turn off. This statement sets the LED_BUILTIN pin to the "off" state,
outputting 0V voltage to this pin, which can be represented by LOW.
Choose the serial port of the development board from the “Tools” bar. For Windows users, it is
generally COM3 or a larger number. Select it as shown in the figure below.
If several ports are displayed for selection, unplug the data cable, reopen the “Tools” bar, and
the port that disappears is the XIAO port. Reconnect the circuit board and then select this
serial port. After selecting the board and the serial port, you can see the controller model and
corresponding serial port that have been set up in the lower right corner of the IDE interface.
Next, we can upload the program. Before uploading, we can click the verify-button.png(verify
button) to verify whether the program is correct. If “Compilation Completed” is displayed, the
program is correct.
Click the upload-button.png (upload button), the debug window will display “Compiling
Project→Upload”. When “Upload Completed” is displayed, you can see the effect of the program
running on XIAO, as shown in the upload successful prompt window displayed on a Mac
computer.
When you start writing code, you will often forget the rules of uppercase and lowercase,
punctuation, and make mistakes. Therefore, try to write code by yourself instead of copying
and pasting. After the example program is successfully uploaded, try to create a new Sketch
and start manually inputting the code.
- Attention -
void setup() {
// Initialize the digital pin 'led' as output
pinMode(led, OUTPUT);
}
void loop() {
digitalWrite(led, HIGH); // Turn the LED on
delay(1000); // Wait for a second
Code Analysis
int led = D10;
Seeed XIAO ESP32C3 does not have an onboard LED, so we did not preset an LED corresponding
pin in the Arduino core. Just now, we connected the LED to the D10 pin, so we need to declare it
in the program.
pinMode(led, OUTPUT);
We defined led as D10, and this step is to initialize led(D10) as an output pin.
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(500);
}
In the previous section, we learned how to control an LED light to blink using only the Seeed
Studio XIAO and the onboard LED light. However, there was no interaction with the external
environment, such as controlling the LED light through light or sound. In this section, we will
introduce a simple sensor - the button switch, to form an automatic control system of sensor-
controller-actuator. Before starting the task, we need to learn some basic knowledge, like what
variables are and the common program structures, so that we can better understand and run
the program.
In most cases, the XIAO expansion board is suitable for all Seeed Studio XIAO series products.
When we need to use the XIAO expansion board, we need to connect the XIAO development
board to the corresponding position on the expansion board, as shown in the figure below.
Connect the pin headers on the XIAO main board to the position circled in yellow on the
expansion board. Be sure to align it before pressing down to avoid damaging the pins. After that,
we can start working on projects in combination with the expansion board.
- Attention -
Please f irst plug the Seeed Studio XIAO into the two female headers on the expansion
board, and then plug in the Type-C power supply, otherwise it will damage the Seeed Studio
XIAO and the expansion board.
Sequential Structure
As the name suggests, the program in a sequential structure is executed
in the order of the statements. It is the most basic and simple program
structure. As shown in the figure below, the program will first execute
the operation in the S1 box, then the operation in the S2 box, and so on.
Selection Structure
In a program,sometimes we need to make judgments based on the situation to decide the next
step. For instance, the program might need to judge the light value in the current environment.
If the light value is high, indicating a bright environment, there’s no need to light up the light.
If the light value is low, indicating a dim
environment, then it’s necessary to turn on
the light. In such cases, we use a selection
structure.
As shown in the following f igures, the
selection structure will judge whether the
condition is fulfilled. If “True”, it executes S1; if
“False”, it executes S2; or if “True”, it executes
S1, if “False”, it exits the selection structure.
The if Statement
The if statement is the most common selection structure, which executes the following
statement when the given expression is true. The if statement has three structural forms as
shown in the following example. Simple branch structure: Execute when the condition is
fulfilled.
if (expression) {
statement;
}
Dual branch structure: Execute statement1 when the condition is fulfilled, otherwise execute
statement2.
if (expression) {
statement1;
}
else {
statement2;
}
if (expression1) {
statement1;
}
else if (expression2) {
statement2;
}
else if (expression3) {
statement3;
}
switch……case Statement
When dealing with multiple selection branches, using an “if……else” structure to write a program
can be quite lengthy. In this case, it’s much more convenient to use a switch statement. The
switch structure compares the expression in parentheses with the constants after case. If they
match, it executes the corresponding statement and exits the structure via a break statement.
If none match, it runs the statement after default. It's important to note that the expression in
parentheses after switch must be of integer or character type.
switch (expression) {
case constant_expression1:
statement1;
break;
case constant_expression2:
statement2;
break;
……
default:
statementn;
break;
}
break Statement
The break statement can only be used in a switch multi-branch selection structure and loop
structures. It is used to terminate the current program structure, allowing the program to jump
to subsequent statements for execution.
Loop Structure
A loop structure is used when a part of the program needs to be executed repeatedly, based on
given judgment conditions to determine whether to continue executing a certain operation or
exit the loop. There are three common types of loop statements:
while Loop
The while loop is a type of “when” loop that executes the statements
in the loop body when a certain condition is met.
while (expression) {
statement;
}
do {
statement;
} while (expression);
for Loop
This includes three expressions: Expression1 for initialization,
Expression2 for judgment, and Expression3 for increment.
1.2.2 Task 1: Control the LED on the XIAO using the button on
the XIAO expansion board
Analysis
The effect we want to achieve is that when the button is pressed, the LED lights up; when the
button is released, the LED goes off. The program is written in three steps:
• Define pins and create variables.
• Initialize and set pin status.
• Read the button status, implement condition judgment. If the button is pressed, the light is
on, otherwise, the light is off.
- Variable -
In a program, a value that can change is called a variable. For example, defining an integer
variable i as int i;. We can assign a value to the variable at the same time as we define
it, such as int i =0;. Furthermore, depending on the data type, different statements are
used to define variables, such as defining a floating point number, float x = 1.9;, and so
on. For more details, refer to the Arduino data types and constants documentation https://
www.arduino.cc/reference/en/#variables.
const int buttonPin = 1; // The on-board button switch on the XIAO expansion board
is D1, which we define as pin 1
// If you are using XIAO RP2040, please change 1 to D1
int buttonState = 0; // buttonState is a variable to store the button status
Step 2: Set pin status. Set the LED pin to output status and the button pin to input pull-up
status. Use INPUT_PULLUP to enable internal pull-up resistors. When the button is not pressed, it
returns 1 or HIGH (high level). When the button is pressed, it returns 0 or LOW (low level).
void setup() {
pinMode(LED_BUILTIN, OUTPUT);// Set the LED pin to output status
pinMode(buttonPin, INPUT_PULLUP);// Set the button pin to input status
}
Step 3: Continuously read the button status. If the button is pressed, the light is on, otherwise,
the light is off. Because the on-board LED of the XIAO is negative logic, when the button is
pressed and returns 0, the LED is on; when it returns 1, the LED is off.
void loop() {
// Read the button status and store it in the buttonState variable
buttonState = digitalRead(buttonPin);
// Check whether the button is pressed, if the button is pressed
if (buttonState == HIGH) {
// Turn on the LED:
digitalWrite(LED_BUILTIN, HIGH);
}
else {
// Turn off the LED:
digitalWrite(LED_BUILTIN, LOW);
}
}
- Attention -
/*
* Button controlling external LED of XIA
Apologies for the confusion. It seems that there was an issue with quoting text from
the document. Let’s continue:
#### Task 2: Use the button on the XIAO expansion board to control the external LED
on the XIAO ESP32C3
For the Seeed XIAO ESP32C3, it doesn’t have an on-board LED for users to use. To ex-
ecute the Blink program, you first need to connect an LED to the board’s `D10` pin as
shown.
> ⚠️ Note: Make sure to add a resistor (about 150Ω) in series with the LED to limit
the current flowing through the LED and prevent overcurrent from burning out the LED.
void setup() {
// Initialize the LED pin as an output:
pinMode(led, OUTPUT);
// Initialize the button pin as an input:
pinMode(buttonPin, INPUT_PULLUP);
}
Expanded Exercise
Flow Chart
Before writing the program, you can first draw a flow chart of the program to help organize your
thoughts. The common flow chart symbols are as follows:
Everyone knows that “SOS” is an internationally recognized emergency signal, a form of Morse
code. Today, we will transform Seeed Studio’s XIAO into a Morse code transmitter. We will try to
make the onboard buzzer of the expansion board send signals automatically. In addition, we will
learn how to control the buzzer manually with a button.
noTone() Function
This function is used to stop the sound of the buzzer controlled by the tone() function. If there
is no sound generated, the function is invalid.
Syntax: noTone(pin);
Parameters:
pin: The pin to stop the sound.
Common Operators
In previous studies, we have used some operators. Next, we will learn about common types of
operators and their usage methods.
https://en.wikipedia.org/wiki/
Samuel_Morse
If it's expressed in sound, it sounds like this.
https://files.seeedstudio.com/wiki/XIAO_Big_Power-Board-ebook-photo/chapter_1-3/sos.wav
tone(pinBuzzer, 200);
delay(100);
noTone(pinBuzzer);
delay(100);
int pinBuzzer = 3; // Define the buzzer on pin 3, if you're using XIAO RP2040/XIAO
ESP32, change 3 to A3
void setup() {
pinMode(pinBuzzer, OUTPUT); // Set the buzzer pin to output state
}
void loop() {
// Emit three short signals:
for(int i = 0; i < 3; i++){
tone(pinBuzzer, 200);
delay(100);
noTone(pinBuzzer);
delay(100);
}
delay(200);
Analysis
The program is also written in three steps:
• Define the buzzer and button pins.
• Initialize by setting the state of the buzzer and button pins.
• Determine whether the button is pressed; if pressed, emit a sound.
const int buttonPin = 1; // The button is on pin 1, if you are using XIAO RP2040/
XIAO ESP32, please change 1 to D1
int pinBuzzer = 3;// The buzzer is on pin 3, if you are using XIAO RP2040/XIAO
ESP32, please change 3 to A3
void setup() {
// Set the buzzer pin as output:
pinMode(pinBuzzer, OUTPUT);
// Set the button pin as input:
Step 3: Check the button state, if the button is pressed, the buzzer sounds. Here, the tone()
function is used to control the passive buzzer to make a sound.
void loop() {
// buttonState is the button variable, read the button state and store it in the
variable:
int buttonState = digitalRead(buttonPin);
- Attention -
There are two identical buttons on the expansion board, one is the RESET button, which is
closer to the Type-C interface, and the other is the user-defined button, which is closer to the
lithium battery interface. When testing, press the one closer to the lithium battery interface.
/*
* Button-SOS
*/
const int buttonPin = 1; // The button is on pin 1, if you are using XIAO RP2040/
XIAO ESP32, please change 1 to D1!
int pinBuzzer = 3; // The buzzer is on pin 3, if you are using XIAO RP2040/XIAO
ESP32, please change 3 to A3!
void setup() {
// Set the buzzer pin as output:
pinMode(pinBuzzer, OUTPUT);
// Set the button pin as input:
pinMode(buttonPin, INPUT_PULLUP);
}
void loop() {
// buttonState is the button variable, read the button state and store it in the
variable:
int buttonState = digitalRead(buttonPin);
When we write a few lines of code to control the board to light up the LED, or to use a button
switch to control the buzzer, we can intuitively see the working state of these external hardware.
If it achieves our expected results, it is very fortunate. What if it doesn’t? The program compiles
without error, where is the mistake? It would be nice if they could speak up. In this section, we
will learn how to communicate with the computer through the serial monitor and check the
running status and information of the program and hardware.
Analog I/O
In the Arduino series of development boards, the pins with “A” in front of the pin number are
analog input pins. We can read the analog value on these pins to achieve the effect we want.
Analog Signal
In life, analog signals are everywhere, such as the change in sound, light, temperature, etc., the
frequency, amplitude, etc. of the signal can change continuously with time.
So how do we read the analog value of the
pin through the development board? The
analog input pin has an ADC (analog-to-digital
converter), which can convert the external
input analog signal into a digital signal that
the development board can recognize, thereby
achieving the function of reading in analog
values, i.e., it can convert a 0-5V voltage signal
into an integer value of 0-1023.
• analogWrite();
Corresponding to analog input is analog output. We use the analogWrite() function to achieve
this function. It should be noted that when using this function, it is only through a special way
to output different voltages to achieve the effect of approximate analog values. This method is
called PWM pulse width modulation, so we are writing PWM square waves to the specified pin,
not the true analog value.
Syntax: analogWrite(pin, value);
Parameters:
pin: The pin to output PWM, allowed data type: int.
value: Duty cycle, between 0-255, allowed data type: int.
Pulse width modulation (PWM) is a way to achieve analog results through digital output.
Simply put, you can control the charging current by adjusting the period of PWM and the
duty cycle of PWM. As shown in the figure, the voltage is switched back and forth between
0V (low level) and 5V (high level). A switchback
is a period. In this period, if the time of high
voltage is 25% and the time of low voltage
is 75%, the duty cycle is 25%, and the output
voltage is 5V.
When we write a few lines of code to control
the lighting of LEDs on the development
board, or use button switches to control the
buzzer, we can directly observe the working
status of these external hardware. If it
achieves our expected results, we are lucky.
But what if it doesn’t? The program compiles
without errors, so where is the problem? It
would be nice if they could talk. In this section,
we will learn how to communicate with the
computer, monitor the running status and
information of the program and hardware
through the serial monitor.
Serial Communication
When we want to communicate with other devices using XIAO, the most common method
is serial communication. All Arduino series development boards have this functionality. As we
• **Serial.begin();**
This function is used to open the serial port and set the data transmission rate.
Syntax: Serial.begin(speed);
Parameters:
Serial: Serial port object.
speed: Baud rate, commonly set to values like 9600, 115200, etc.
• **Serial.println();**
Syntax: Serial.println(val);
Parameters:
Serial: Serial port object.
val: The value to be printed, which can be of any data type.
For example, to print “hello world!!!” to the Serial Monitor, we need to initialize the serial port in
the setup() function and output “hello world!!!” through the serial port in the loop() function:
void setup() {
Serial.begin(9600); // Initialize the serial port and set the data transmission
rate to 9600
}
void loop() {
Serial.println("hello world!!!"); // Output "hello world!!!" through the serial
port
}
Returning to the question at the beginning of this section: when we have written the code and
verified it to be correct, but the effect of running the code exceeds expectations or the hardware
doesn’t respond at all, where is the problem? At this time, we can use the Serial Monitor to
observe the data sent or received by the hardware to make a judgment. For instance, we can
control the on-off state of an LED with a button, and we can use the Serial Monitor to check the
returned value when the button is pressed to determine whether the button is working properly.
Next, we will learn how to use the Serial Monitor to make the hardware “speak”.
const int buttonPin = 1; // Define the button switch as pin 1. If you are using XIAO
RP2040/XIAO ESP32, please change 1 to D1
int buttonState = 0; // Define buttonState as a variable to store the button status
Step 2: Initialize the serial port, set the baud rate of the serial port, and set the button switch pin
status.
void setup() {
pinMode(buttonPin, INPUT_PULLUP); // Set the button pin as input
Serial.begin(9600); // Initialize the serial port
}
Step 3: Read the button status and send it to the serial port
void loop() {
buttonState = digitalRead(buttonPin); // Read the button status and store it in
the buttonState variable
Serial.println(buttonState); // Send the button status data to the serial port
delay(500);
}
When we press the button on the XIAO expansion board, the serial monitor shows 0, and when
we release the button, the serial monitor shows 1.
Step 2: Initialize the serial port, set the baud rate of the serial port, and set the status of the knob
potentiometer pin.
void setup()
{
Serial.begin(9600);//Initialize the serial port
pinMode(ROTARY_ANGLE_SENSOR, INPUT);//Set the rotary potentiometer pin to input
state
}
Step 3: Read and calculate the rotational angle value of the rotary potentiometer and send
it to the serial port. Here, we f irst need to set the data type of the voltage variable, set the
analog value variable of the rotary potentiometer pin, and then calculate the real-time voltage.
After calculating the real-time voltage, calculate the rotational angle value of the rotary
potentiometer.
void loop()
{
float voltage; //Variable voltage is of floating-point type
int sensorValue = analogRead(ROTARY_ANGLE_SENSOR); //Read the analog value at
the rotary potentiometer pin
voltage = (float)sensorValue*ADC_REF/1023; //Calculate real-time voltage
float degrees = (voltage*FULL_ANGLE)/GROVE_VCC; //Calculate the rotation angle
of the knob
Serial.println("The angle between the mark and the starting position:"); //
Print characters at the serial port
Serial.println(degrees); //Print the rotation angle value of the rotary po-
tentiometer at the serial port
delay(100);
}
#define is a pre-processing command used for macro definitions. In Arduino, we can use
#define to name constants. During the compilation of the program, all occurrences of the
"macro name" will be replaced with the string in the macro definition, such as #define
ledPin 5. During compilation, 5 will replace all uses of ledPin. Syntax: #define constant
name constant value. The “#” symbol is mandatory, and there is no need to use the “;”
symbol at the end of the sentence.
/*
* Use the serial monitor to view the knob potentiometer
*/
#define ROTARY_ANGLE_SENSOR A0//Define the rotary potentiometer interface A0
#define ADC_REF 3 //ADC reference voltage 3V
void setup()
{
Serial.begin(9600);//Initialize the serial port
pinMode(ROTARY_ANGLE_SENSOR, INPUT);//Set the rotary potentiometer pin as an in-
put
}
void loop()
{
float voltage;//Variable voltage is of floating-point type
int sensorValue = analogRead(ROTARY_ANGLE_SENSOR);//Read the analog value at the
rotary potentiometer pin
voltage = (float)sensorValue*ADC_REF/1023;//Calculate real-time voltage
float degrees = (voltage*FULL_ANGLE)/GROVE_VCC;//Calculate the rotation angle of
the knob
Serial.println("The angle between the mark and the starting position:");//Print
characters at the serial port
Serial.println(degrees);//Print the rotation angle value of the rotary potenti-
ometer at the serial port
delay(100);
}
In the Arduino IDE, click on the verification button to verify the program. If it verif ies
correctly, click the upload button to upload the program to the hardware. When the
debugging area shows “Done uploading.”, you can proceed. Open the serial monitor and rotate
the knob potentiometer to observe the data changes displayed in the serial monitor. These
changes represent the angle value of the knob.Extended Exercise
The serial plotter draws the data obtained from the serial port into an XY axis curve chart, where
the X-axis represents the change in time and the Y-axis represents the data obtained from the
serial port. Through the chart, you can more intuitively see the change in data. Please give it a try.
In the last section, we learned how to use the serial monitor and observed the differences
between digital input and analog input through it. In this section, we will further explore the use
of analog values by combining them with a rotary potentiometer!
void setup() {}
void loop() {
int val = analogRead(0); // read the value from analog pin A0
val = map(val, 0, 1023, 0, 255); // map val to the range 0-255
analogWrite(9, val); // output the analog value to pin 9
}
Analysis:
When using a knob potentiometer to control the LED, we need to use the map() function,
because the analog value directly output by the knob potentiometer is 0-1023, this value is
not the angle value of the knob rotation, we need to calculate the angle value of the knob
potentiometer rotation first, then map this value to the brightness range of the LED 0-255 with
the map() function. The steps to write the program are as follows:
• Define the knob potentiometer, LED pin.
• Initialize the serial port, set the status of the knob potentiometer and LED pin.
• Read and calculate the rotation angle value of the knob potentiometer, and send it to the
serial port.
• Map the angle value of the knob potentiometer to the LED brightness value and store it in
the brightness variable, and the LED outputs this variable value.
Step 2: Initialize the serial port, set the status of the knob potentiometer and LED pin.
void setup()
{
Serial.begin(9600); //Initialize serial communication
pinMode(ROTARY_ANGLE_SENSOR, INPUT); //Set the rotary potentiometer pin to input
pinMode(LEDPIN,OUTPUT); //Set the LED pin to output
}
Step 3: Read and calculate the rotation angle value of the knob potentiometer, and send it to
the serial port.
void loop()
{
float voltage; //Variable voltage of type float
int sensor_value = analogRead(ROTARY_ANGLE_SENSOR); //Read the analog value at
the rotary potentiometer pin
voltage = (float)sensor_value*ADC_REF/1023; //Calculate the real-time voltage
float degrees = (voltage*FULL_ANGLE)/GROVE_VCC; //Calculate the angle of rotation
of the knob
Serial.println(“The angle between the mark and the starting position:”); //Print
character on serial monitor
Serial.println(degrees); //Print the rotation angle value of the rotary potenti-
Step 4: Map the angle value of the knob potentiometer to the LED brightness value and store it
in the brightness variable, and the LED outputs this variable value.
//After Step 3
int brightness; //Define brightness variable
brightness = map(degrees, 0, FULL_ANGLE, 0, 255); //Map the rotation angle value
of the rotary potentiometer to the brightness value of the LED and store it in the
brightness variable
analogWrite(LEDPIN,brightness); //Output the variable value to the LED
delay(500);
}
void setup()
{
Serial.begin(9600); //Initialize serial communication
pinMode(ROTARY_ANGLE_SENSOR, INPUT); //Set the rotary potentiometer pin to input
pinMode(LEDPIN,OUTPUT); //Set the LED pin to output
}
void loop()
{
float voltage; //Variable voltage of type float
int sensor_value = analogRead(ROTARY_ANGLE_SENSOR); //Read the analog value at
the rotary potentiometer pin
voltage = (float)sensor_value*ADC_REF/1023; //Calculate the real-time voltage
float degrees = (voltage*FULL_ANGLE)/GROVE_VCC; //Calculate the angle of rotation
of the knob
Serial.println(“The angle between the mark and the starting position:”); //Print
character on serial monitor
Serial.println(degrees); //Print the rotation angle value of the rotary potenti-
ometer on the serial monitor
delay(100);
- Attention -
Be sure to connect a resistor (about 150Ω) in series with the LED to limit the current passing
through the LED and prevent it from being damaged by overcurrent.
void setup()
{
Serial.begin(9600); // Initialize serial communication
pinMode(ROTARY_ANGLE_SENSOR, INPUT); // Set the rotary potentiometer pin to in-
put mode
pinMode(LEDPIN, OUTPUT); // Set the LED light pin to output mode
}
void loop()
{
float voltage; // Define voltage variable as float
int sensor_value = analogRead(ROTARY_ANGLE_SENSOR); // Read the analog value on
the rotary potentiometer pin
voltage = (float)sensor_value*ADC_REF/1023; // Calculate real-time voltage
float degrees = (voltage*FULL_ANGLE)/GROVE_VCC; // Calculate the angle of rota-
tion of the knob
Serial.println("The angle between the mark and the starting position:"); //
Print string to serial port
Serial.println(degrees); // Print the rotation angle value of the rotary poten-
tiometer to the serial port
delay(100);
Program Writing
Step 1: Declare the servo library, def ine the servo rotation angle variable, def ine the rotary
potentiometer pin and voltage.
Step 2: Initialize the serial port, set the status of the rotary potentiometer and servo pins.
void setup() {
Serial.begin(9600);// Initialize the serial port
pinMode(ROTARY_ANGLE_SENSOR, INPUT);// Set the rotary potentiometer pin as input
myservo.attach(5); // The myservo signal is transmitted through pin 5, if you
are using XIAO RP2040/XIAO ESP32, please modify 5 to D5
}
Step 3: Read and calculate the rotation angle value of the rotary potentiometer, send it to the
serial port, and drive the servo to rotate according to the angle value change.
void loop() {
float voltage;// Set voltage as a floating point
int sensor_value = analogRead(ROTARY_ANGLE_SENSOR);// Read the analog value at
the rotary potentiometer pin
voltage = (float)sensor_value * ADC_REF / 1023;// Real-time voltage is the read
analog value multiplied by the reference voltage divided by 1023
float degrees = (voltage * FULL_ANGLE) / GROVE_VCC;// The rotation angle of the
knob is the real-time voltage multiplied by the maximum rotation angle of the rotary
potentiometer divided by the voltage value of the GROVE module interface
Serial.println(“The angle between the mark and the starting position:”);// Print
characters on the serial port
Serial.println(degrees);// Print the rotation angle value of the rotary potenti-
ometer on the serial port
delay(50);
myservo.write(degrees); // Write the rotation angle value of the rotary potenti-
ometer into the servo
}
void setup() {
Serial.begin(9600);// Initialize the serial port
pinMode(ROTARY_ANGLE_SENSOR, INPUT);// Set the rotary potentiometer pin as input
myservo.attach(5); // The myservo signal is transmitted through pin 5, if you
are using XIAO RP2040/XIAO ESP32, please modify 5 to D5
}
void loop() {
float voltage;// Set voltage as a floating point
int sensor_value = analogRead(ROTARY_ANGLE_SENSOR);// Read the analog value at
the rotary potentiometer pin
voltage = (float)sensor_value * ADC_REF / 1023;// Real-time voltage is the read
analog value multiplied by the reference voltage divided by 1023
float degrees = (voltage * FULL_ANGLE) / GROVE_VCC;// The rotation angle of the
knob is the real-time voltage multiplied by the maximum rotation angle of the rotary
potentiometer divided by the voltage value of the GROVE module interface
Serial.println(“The angle between the mark and the starting position:”);// Print
characters on the serial port
Serial.println(degrees);// Print the rotation angle value of the rotary potenti-
ometer on the serial port
delay(50);
myservo.write(degrees); // Write the rotation angle value of the rotary potenti-
ometer into the servo
}
Upload Program
After writing the program, f irst connect the knob potentiometer and the servo to the XIAO
expansion board as shown in the figure below. Then, connect the XIAO main control board to
the computer with a data cable.
After the connection, click (the verify button) in the Arduino IDE to verify the program.
If the verif ication is error-f ree, click (the upload button) to upload the program to the
hardware. When the debugging area shows “Done uploading.”, you can open the serial monitor,
rotate the knob potentiometer, and observe the changes in angle value and the movement of
the servo. What have you found?
In our daily life, we see displays everywhere - televisions, computers, phones, car displays, LCD
billboards in shopping malls… Without a variety of screens, our lives would lose much of its fun.
Of course, these screens, besides leisure and entertainment, are also indispensable tools for daily
life. Common displays include LCD displays, OLED displays, etc. They all have their own strengths
and weaknesses as display devices and can be applied in different fields and scenarios. The XIAO
expansion board integrates an OLED display. In this lesson, we will learn how to use OLED to
display text, patterns, and images.
OLED Display
OLED, also known as Organic Light Emitting Diode, has advantages such as self-luminous, low
power consumption, fast response speed, high resolution, light weight, etc. Its application field is
very wide. The XIAO expansion board integrates a 0.96 inch 128x64 pixel OLED display, which can
be used directly without wiring. During project production, we can display time, temperature
and humidity, and other sensor return values through the OLED display, and we can also directly
display letters, numbers, graphics, and even patterns, achieving visual interactive effects.
If the library is installed correctly, you can see the prompt information for successful library
installation in the output window.
U8g2
Includes all graphic procedures (line/box/circle drawing); Supports various fonts, (almost) no
restrictions on font height; Some memory in the microcontroller is needed to display.
U8x8
Only supports text (character) output; Only allows each character to use a fixed-size font (8x8
pixels); Writes directly to the display, no buffer is needed in the microcontroller. Simply put, when
we want the OLED display to display various fonts, graphics, patterns, and present visual content
more flexibly, we can use the U8g2 library; when we want to display characters more directly,
with no font requirements, just to display sensor values, time, etc., we can use the U8x8 library,
which is more efficient. We can find many example programs in "File → Examples → U8g2", and
familiarize ourselves with the use of the library through the example programs.
Next, we will display characters and draw circles using two libraries respectively.
Before starting to write a program for the OLED of the XIAO expansion board, make sure
the Arduino IDE has loaded the U8g2_Arduino library f ile. The loading method can be
referred to the description in the “How to Download and Install Arduino Library” section of
this lesson.
Analysis
If you just want to display “Hello World!” on the OLED, you can directly write characters with the
U8x8 library. The steps are as follows:
• Declare the library file, set the constructor, and the constructor defines the display type,
controller, RAM buffer size, and communication protocol.
• Initialize the display.
• Set the display font, set the print starting position, and output “Hello World!”.
#include <Arduino.h>
#include <U8x8lib.h>//Use U8x8 library file
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);
//Set the constructor, define the display type, controller, RAM buffer size, and com-
munication protocol, generally determine according to the used display model
Step 2: Initialize the display. After declaring the library file in the previous step, you can use the
functions in the library to set the OLED display.
void setup(void) {
u8x8.begin();//Initialize u8x8 library
u8x8.setFlipMode(1);//Flip the display 180 degrees, generally numbers 0 and 1
}
Step 3: Set the display font (there are various fonts to choose from in the u8x8 library, we can
refer to https://github.com/olikraus/u8g2/wiki/fntlist8x8 to choose), set the print starting position,
and output “Hello World!”.
void loop(void) {
u8x8.setFont(u8x8_font_chroma48medium8_r);//Define u8x8 font
u8x8.setCursor(0, 0);//Set the position of the drawing cursor
u8x8.print("Hello World!");//Draw content on OLED: Hello World !
}
#include <Arduino.h>
#include <U8x8lib.h>//Use U8x8 library file
void setup(void) {
u8x8.begin();//Initialize u8x8 library
u8x8.setFlipMode(1);//Flip the display 180 degrees, generally numbers 0 and 1
}
void loop(void) {
u8x8.setFont(u8x8_font_chroma48medium8_r);//Define u8x8 font
u8x8.setCursor(0, 0);//Set the position of the drawing cursor
u8x8.print("Hello World!");//Draw content on OLED: Hello World !
}
Program Upload
After the program is written, we connect the XIAO main control board to the computer interface
using a data cable, as shown in the image below:
Click “Upload” to transfer the program to the main control board. Once the upload is complete,
check if the OLED display shows “Hello World!”.
Analysis
To draw a circle on the OLED display, we need to use the U8g2 library. Programming involves
four steps:
• Declare the U8g2 library file, determine whether to use SPI or I2C protocol, and set up the
constructor to connect to the OLED display.
• The draw() function uses the u8g2.drawCircle function to draw a circle on the OLED.
• Initialize the U8g2 library.
• In the loop() function, call related functions to draw images on the OLED.
Program Writing
Step 1: Declare the U8g2 library file, determine whether to use SPI or I2C protocol, and set up the
constructor to connect to the OLED display.
#include<Arduino.h>
#include<U8g2lib.h>//Use U8g2 library
Step 2: The draw() function uses the u8g2.drawCircle function to draw a circle on the OLED.
The u8g2.drawCircle(x0,y0,rad,opt) function parameters are as follows:
• x0,y0: The position of the center of the circle.
• rad: Defines the size of the circle, with the diameter of the circle being 2*rad+1.
• opt: Choose a part or all of the circle.
void draw(void) {
u8g2.drawCircle(20, 25, 10, U8G2_DRAW_ALL);// Draw a full circle with a diameter
of 21 at coordinates (20, 25)
}
void setup(void) {
u8g2.begin();// Initialize the library
}
Step 4: In the loop() function, call related functions to draw images on the OLED. Use the
f irstPage and nextPage functions to cycle through image content. They need to be used
together, as shown in the program below:
void loop(void) {
// Cycle through image display
u8g2.firstPage();
do {
draw();// Use draw function
} while( u8g2.nextPage() );
delay(1000);
}
In the first unit, we’ve entered the realm of electronic hardware and programming, learning
how to control electronic hardware through code to achieve desired effects, such as controlling
an LED in various ways, making a buzzer sound, displaying text on an OLED screen, and more.
Mastering this knowledge will help us turn the ideas in our minds into reality. In this section, we
will learn about the process from an idea to a prototype and then to a product. Only when you
have mastered this knowledge can you step into the world of product prototype design. If you
have managed to stick with this course up to this point, there’s no doubt that you are a “maker”
at heart. The idea of “wanting to make something on your own” keeps swirling around in your
mind. This section will provide you with some advice on how to become a maker and guidance
on how to create electronic product prototype designs.
Keep it playful
Play opens us to creative ideas and new experiences. While
we play, we engage our bodies and our mind, and we often
engage with others. While we play, learning feels natural and
we can take risks to do things we didn’t know we can do.
Be Curious
Ask questions – who, what, why, and how. How are things
around you made? Who makes them and where are they
made?
Make something
You might design something that solves a problem — it could be a problem for you or a problem
for others. You might build something that’s interactive such as a play toy, or a toy car or plane.
Paper airplane launchers are popular, as are rockets.
Author Introduction:
Functional
Hardware Product Function Introduction
Requirements
The chosen hardware modules have a great structural design, which can be directly used to
build the distance alarm’s form factor, saving time in making a shell. Thus, the production
method is quite simple: all that’s needed is to connect each piece of hardware to the appropriate
interface, arrange their respective positions, and then bond them together with hot melt
adhesive. This quickly completes the hardware connection and form factor building of a one-
meter distance alarm. The completed hardware product is as follows:
After completing the prototype, it was time for testing. First, I needed to test whether the
prototype implemented the basic functionality, i.e., whether it would sound and light an alarm
when a person was detected within a one-meter range. Then, I had to use it in an actual scenario
to see if the user experience was good enough. If it could meet the product’s requirements
and definition satisfactorily, the product prototype could be deemed successful, and the next
#include <Grove_LED_Bar.h>
#include “Seeed_vl53l0x.h”
void setup() {
bar.begin();
pinMode(Buzzer, OUTPUT);
digitalWrite(Buzzer, LOW); // turn the Buzzer on (HIGH is the voltage level)
// Turn off all LEDs
bar.setBits(0x0);
VL53L0X.VL53L0X_long_distance_ranging_init();
if (VL53L0X_ERROR_NONE != Status) {
SERIAL.println(“Starting VL53L0X measurement failed!”);
VL53L0X.print_pal_error(Status);
while (1);
}
void loop() {
memset(&RangingMeasurementData, 0, sizeof(VL53L0X_RangingMeasurementData_t));
Status = VL53L0X.PerformSingleRangingMeasurement(&RangingMeasurementData);
if (VL53L0X_ERROR_NONE == Status) {
if (RangingMeasurementData.RangeMilliMeter >= 2000) {
SERIAL.println(“Out of range!!”);
digitalWrite(Buzzer, LOW); // turn the Buzzer off (LOW is the voltage
level)
}
else if (RangingMeasurementData.RangeMilliMeter <= 1000) {
digitalWrite(Buzzer, HIGH); // turn the Buzzer on (HIGH is the voltage
level)
// Turn on all LEDs
bar.setBits(0b111111111111111111111111);
SERIAL.print(“Distance:”);
SERIAL.print(RangingMeasurementData.RangeMilliMeter);
SERIAL.println(“ mm”);
}
else {
digitalWrite(Buzzer, LOW); // turn the Buzzer off (LOW is the voltage
level)
SERIAL.print(“Distance:”);
SERIAL.print(RangingMeasurementData.RangeMilliMeter);
SERIAL.println(“ mm”);
}
}
else {
SERIAL.print(“Measurement failed!! Status code =”);
SERIAL.println(Status);
digitalWrite(Buzzer, LOW); // turn the Buzzer off (LOW is the voltage lev-
el)
delay(250);
Thermometers and hygrometers are ubiquitous in daily life, providing real-time measurement
of temperature and humidity in our environment. We frequently use them to measure body
temperature when we feel feverish or unwell. The invention of these devices has brought
tremendous convenience to our lives. Despite their size, these devices hold a great deal
of science. In this section, we will create a smart hygrometer and thermometer using a
temperature and humidity sensor. Do you know what a temperature and humidity sensor is and
what it can do?
Temperature
Temperature is closely tied to our daily lives; it informs what clothes we wear before stepping
out, and ensures the food or drink we consume is not too hot or too cold. When you step outside
your home, you can sense the cold or heat, but to quantify exactly how cold or hot it is, we use
“temperature”.
Temperature is a physical quantity that indicates the degree of coldness or hotness of an
object. The high and low temperature of an object is a macroscopic phenomenon that reflects
the intensity of thermal motion of the molecules that make up the object at the microscopic
level. Hence, the temperature is a manifestation of the intensity of thermal motion of a large
number of molecules that constitute an object. The faster the molecular motion, the higher
the temperature, and the hotter the object; the slower the molecular motion, the lower the
temperature, and the colder the object.
To measure temperature accurately, we need to establish a temperature unit standard and
design corresponding temperature measurement tools.
Thermometer
A thermometer is a tool for measuring temperature. Since temperature is not a physical quantity
that can be seen directly, the measurement of temperature requires the assistance of physical
phenomena directly related to temperature. For instance, in ancient China, there is a record of
‘lustrous pure blue flame,’ which is measured by observing the color of the flame.
Another example is the inf rared thermometer, as shown in the image to the right, which
measures temperature through the
radiation differences of objects at
different temperatures. Humans, like
other organisms, also radiate infrared
energy around them. This energy
typically has a wavelength of 9-13μm
and falls within the near-infrared band
of 0.76-100μm. Since light within this
wavelength range is not absorbed
by air, the surface temperature of
the human body can be accurately
measured by simply measuring the
infrared energy radiated by the human
b o d y. T h e h u m a n b o d y i n f r a r e d
temperature sensor is designed and
manufactured based on this principle.
Humidity
Humidity is a physical quantity that indicates the degree of
dryness in the atmosphere. Under a certain temperature, the
less water vapor a certain volume of air contains, the drier
the air; the more water vapor, the more humid the air. The
dryness or wetness of air is called “humidity.” Weather forecasts
typically report humidity values in terms of relative humidity,
which is a percentage obtained by comparing the actual
amount of water vapor in the air to the maximum amount of
water vapor the air can hold at the same temperature.
- Attention -
If the DHTtester example is not found in the menu after installing the library files, it can be
viewed by closing and reopening the Arduino IDE.
After opening the example program, we can see a program like the one shown below. This
program reads the temperature and relative humidity in the environment and displays real-time
data in the serial monitor. Part of the example program’s code needs to be modified.
#include “DHT.h”
/*Notice: The DHT10 and DHT20 is different from other DHT* sensor ,it uses i2c in-
terface rather than one wire*/
/*So it doesn’t require a pin.*/
#define DHTPIN 2 // what pin we’re connected to(DHT10 and DHT20 don’t need define
it)
DHT dht(DHTPIN, DHTTYPE); // DHT11 DHT21 DHT22
//DHT dht(DHTTYPE); // DHT10 DHT20 don’t need to define Pin
#if defined(ARDUINO_ARCH_AVR)
#define debug Serial
void setup() {
debug.begin(115200);
debug.println(“DHTxx test!”);
Wire.begin();
dht.begin();
}
void loop() {
float temp_hum_val[2] = {0};
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds ‘old’ (its a very slow sensor)
if (!dht.readTempAndHumidity(temp_hum_val)) {
debug.print(“Humidity: “);
debug.print(temp_hum_val[0]);
debug.print(“ %\t”);
debug.print(“Temperature: “);
debug.print(temp_hum_val[1]);
debug.println(“ *C”);
} else {
debug.println(“Failed to get temprature and humidity value.”);
}
delay(1500);
#include "DHT.h"
#define DHTTYPE DHT20 // DHT 20
DHT dht(DHTTYPE);
#if defined(ARDUINO_ARCH_AVR)
#define debug Serial
void setup() {
debug.begin(115200);
debug.println("DHTxx test!");
Wire.begin();
dht.begin();
}
void loop() {
float temp_hum_val[2] = {0};
if (!dht.readTempAndHumidity(temp_hum_val)) {
debug.print("Humidity: ");
debug.print(temp_hum_val[0]);
debug.print(" %\t");
debug.print("Temperature: ");
debug.print(temp_hum_val[1]);
debug.println(" *C");
} else {
debug.println("Failed to get temprature and humidity value.");
}
delay(1500);
}
#include “DHT.h”
#define DHTTYPE DHT11 // DHT 11
#define DHTPIN 0
DHT dht(DHTPIN, DHTTYPE);
#if defined(ARDUINO_ARCH_AVR)
#define debug Serial
void setup() {
debug.begin(115200);
debug.println(“DHTxx test!”);
void loop() {
float temp_hum_val[2] = {0};
if (!dht.readTempAndHumidity(temp_hum_val)) {
debug.print(“Humidity: “);
debug.print(temp_hum_val[0]);
debug.print(“ %\t”);
debug.print(“Temperature: “);
debug.print(temp_hum_val[1]);
debug.println(“ *C”);
} else {
debug.println(“Failed to get temprature and humidity value.”);
}
delay(1500);
}
Program Writing
Referencing the example program above, one of the effects we want to achieve is to display
the temperature and humidity values on the OLED display of the XIAO expansion board. The
code for reading the temperature and humidity sensor detection values can be reused by just
#include “DHT.h”
#include “DHT.h” //Use DHT library
#include <Arduino.h>
#include <U8x8lib.h> //Use u8x8 library
#define DHTTYPE DHT20
DHT dht(DHTTYPE); //DHT20 does not need to define pins
void setup() {
Wire.begin(); //Initialize wire library, and join I2C network
dht.begin(); //DHT starts working
u8x8.begin(); //u8x8 starts working
u8x8.setPowerSave(0); //Turn off power saving mode, 1 is on, and nothing can be
seen on the screen after power saving mode is on
u8x8.setFlipMode(1);
}
Step 3: Define temperature and humidity variables to store readings, read temperature and
humidity values and display them on the OLED screen. Pay attention to the coordinate positions
of temperature and humidity display.
void loop() {
float temp, humi; //Set the variables temp and humi to floating point type, rep-
resenting temperature and humidity respectively
temp = dht.readTemperature(); //Read temperature value and store it in temp
humi = dht.readHumidity(); //Read humidity value and store it in humi
u8x8.setFont(u8x8_font_chroma48medium8_r); //Set display font
u8x8.setCursor(0, 33); //Set the position of the drawing cursor (0,33)
Boolean Operators
&&: Logical AND, represents “and”,if (expression1 && expression2), only when all
expressions in the parentheses are true will it execute the statements in if {}.
||: Logical OR, represents “or”,if (expression1 || expression2), if either of the
expressions are satisfied, the entire expression is true, and the statements in if {} are
executed.
!: Logical NOT, represents “not”, if (!expression1), only when the value of expression1 in
the parentheses is false will it execute the statements in if {}.
Usage example:
When the temperature exceeds 30 or the humidity falls below 40, satisfying either condition
will make the buzzer sound an alarm.
The added part of the program mainly sets the buzzer and makes decisions based on
temperature and humidity, controlling the buzzer to make a sound.
void setup() {
pinMode(buzzerPin , OUTPUT); // Sets the buzzer pin as output
}
void loop() {
float temp, humi;
temp = dht.readTemperature();
humi = dht.readHumidity();
if (temp > 30 || humi < 40) { // When the temperature exceeds 30 or the humidi-
ty falls below 40, satisfying either condition will make the buzzer sound an alarm.
tone(buzzerPin, 200, 200);
}
Add the above code to the corresponding location of the Task 1 program to realize all functions.
The complete program is shown below:
void setup() {
pinMode(buzzerPin , OUTPUT); // Set buzzer pin to output mode
Wire.begin(); // Initialize Wire library and join to I2C network
dht.begin(); // DHT begins operation
u8x8.begin(); // u8x8 begins operation
u8x8.setPowerSave(0); // Disable power save mode, 1 is enable. After enabling
power save mode, nothing will be seen on the screen
u8x8.setFlipMode(1);
}
void loop() {
float temp, humi; // Set variables temp and humi to floating point type, repre-
senting temperature and humidity respectively
temp = dht.readTemperature(); // Read temperature value and store it in temp
humi = dht.readHumidity(); // Read humidity value and store it in humi
if (temp > 30 || humi < 40) { // When the temperature is above 30 or the humid-
ity is below 40, if either condition is met, the buzzer will sound an alarm
tone(buzzerPin, 200, 200);
}
After connection, click the “Verify” button to check the program. If the verification is successful,
click the “Upload” button to upload the program to the hardware. When the debugging area
shows “Upload Successful”, it is complete. To verify whether the alarm function runs smoothly,
tightly grip the temperature and humidity sensor with your hand, observe the value change on
the OLED display, and listen for the buzzer alarm when the temperature exceeds 30℃ .
Task 2-2: Use Grove DHT11 sensor to display temperature and humidity on
the XIAO extension board’s OLED and add an alarm function.
For the Grove DHT11 sensor with a blue casing, the program is shown below:
void setup() {
pinMode(buzzerPin , OUTPUT);//Set buzzer pin to output mode
Wire.begin();//Initialize wire library and join to I2C network
dht.begin();//DHT begins operation
u8x8.begin();//u8x8 begins operation
u8x8.setPowerSave(0); //Disable power save mode, 1 is enable. After enabling pow-
er save mode, nothing will be seen on the screen
u8x8.setFlipMode(1);
}
void loop() {
float temp, humi;//Set variables temp and humi to floating point type, representing
temperature and humidity respectively
Product Functions Real-time display of temperature and humidity values, and emits an
alarm when temperature and humidity values exceed the comfortable
range.
Product Appearance (For example, made into a pendant to hang on the backpack that is
carried around, stick on the tissue storage box in the bedroom, etc.)
Case reference
Are you thinking about gifting a special birthday present to your friend? Instead of buying one,
you can create it with the modules we have at hand. In this section, we are going to create
a surprise gift box for a good f riend. What kind of surprise will appear when the gift box is
opened? What kind of modules do we need to complete such a surprise gift box? Start today’s
class with these questions.
Photoresistive Type
Firstly, the photoresistive type, its module will integrate a
photoresistor, as shown below. The photoresistor is extremely
sensitive to light, any light visible to our eyes can cause its
reaction. High-intensity light will cause the resistance value to
decrease, and low-intensity light will cause the resistance value
to increase. By adjusting the resistance value in the circuit
through the light intensity, it can control other devices, such as
controlling the LED light on and off.
Photodiode Type
Ph otodiodes, also kn own as ph otoelec tric sensors or
photodetectors, when a beam of light hits the diode, the electrons
in the tube will quickly scatter to form electron holes, thereby
causing current to flow. The stronger the light, the stronger
the current. Since the current generated by the photodiode is
proportional to the intensity of light, it is very beneficial for light
detection that requires a rapid change in light response. The light
sensor we are going to use in this lesson is of this type.
Talking about the uses of light sensors, we can build a light-controlled switch through a light
sensor, such as controlling the light on and off through a light sensor, turning off the light
during the day, and turning on the light at night. The main purpose of the light control device
is to save energy, improve efficiency through intelligent automation, the most common in life
is probably the light control light, light control desk lamp, light control street lamp, highway
tunnel lighting, etc., bringing convenience to our life and also contributing to environmental
protection and energy conservation.
2.3.2 Task 1: Light up RGB LED Strip To get started with RGB
LED strips, start by installing and understanding its library.
Add the Adafruit_NeoPixel Library
Before starting to program the RGB LED strip with the Arduino IDE, you need to add the
necessary library f iles. Enter the library f ile address https://github.com/adaf ruit/Adaf ruit_
NeoPixel in the browser address bar, enter the GitHub page, click Code→Download ZIP to
download the resource package Adafruit_NeoPixel-master.zip to your local machine.
#include <Adafruit_NeoPixel.h>
void setup() {
// These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
// Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
// END of Trinket-specific code.
void loop() {
pixels.clear(); // Set all pixel colors to ‘off’
This program allows the strip to light up 30 beads (green light) in sequence. This is a simple light
strip example, and we need to modify some parameters:
#define PIN 0, you need to modify the pin connected to the light strip according to the actual
situation. It is connected to the A0 interface of the XIAO expansion board, so it is PIN 0.
#define NUMPIXELS 30, defines the number of LEDs in the light strip. Since the light strip has
different models and the number of integrated beads is different, we use a light strip with 30
beads, so it is NUMPIXELS 30.
After modifying the parameters, you can remove the English comments for a clearer view of the
code. It occupies a large amount of space.
#define PIN 0 // The light strip is connected to pin 0. If you are using XIAO RP2040,
please change 0 to A0
#define NUMPIXELS 30 // The number of LED lights on the light strip
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); // Create a new
light strip object, define data mode
#define DELAYVAL 500 // The interval time for each light to light up
void setup() {
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
pixels.begin(); // The light strip is ready to output data
}
void loop() {
pixels.clear(); // All beads on the light strip are turned off
for(int i=0; i<NUMPIXELS; i++) {
pixels.setPixelColor(i, pixels.Color(0, 150, 0)); // Light up the beads in
sequence, the color is green
pixels.show(); // Display the light strip
delay(DELAYVAL);
}
}
The light strip can change color, flicker, and present various lighting effects such as breathing.
We can refer to the sample program in the library: File → Example → Adaf ruit NeoPixel →
buttoncycler. This sample program switches different lighting effects on the light strip through
buttons. We can find the code for various lighting effects in it, such as flickering, rainbow lights,
chasing, etc.
Program Writing
The program writing idea is as follows:
• Declare the files to be called, create a new light strip object, define the sensor pin and the
number of LEDs on the light strip.
• Initialize the light strip and set the light sensor pin mode.
• Read the light value. If the light value is greater than 100, the light strip will present a rainbow
and breathing light effect. Otherwise, the light strip will turn off.
The program is completed in two tasks:
#define PIXEL_PIN 0 // The light strip is connected to pin A0. If you are using XIAO
RP2040, please change 0 to A0
#define PIXEL_COUNT 30 // The number of LED lights on the light strip
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
// Declare a new light strip object and define the data mode
void setup() {
strip.begin(); // Initialize the light strip, the light strip is ready to output
data
}
Step 3: The light strip presents a rainbow and breathing light effect. This part uses the for()
function to present the breathing effect. For example, for( i = 0; i<5; i ++ ){} means that
the initial value of i is 0, when i is less than 5, the statement in the loop body {} is run, each
time the loop is run, i is incremented by 1. This loop will run 5 times.
void loop() {
strip.clear();// Turn off all the lights on the light strip
rainbow(10);// The light strip displays a rainbow light effect. The number in
Use a data cable to connect XIAO to the computer, click the “Upload” button, and upload the
program to the hardware. When the debugging area shows “Upload successful”, you can
observe the light effect of the light strip.
We add the entered statement to the corresponding position of Task 1’s program. See the
complete program:
- Attention -
If you are using XIAO BLE, please connect the light sensor to the I2C interface of the XIAO
expansion board.
If you are using XIAO RP2040, due to the limited number of pins exposed, you need to
connect the SIG pin of the light sensor and the A3 pin of XIAO RP2040 with Dupont wires on
your own.
Next, connect XIAO to your computer with a data cable,
click the “Upload” button in the Arduino IDE to upload
the program to the hardware. When the debugging area
shows “Upload successful”, you can cover the light sensor
with your hand, then release the light sensor, and observe
the changes in the light strip. Note that because it takes
a certain amount of time for the light strip to display light
effects, the light strip will not turn off immediately when
you cover the light sensor.
Case reference
Product Name Surprise Gift Box
Product
Appearance
When we use smartphones or tablets, we notice that the screen display automatically flips
depending on whether the device is vertical or horizontal. In racing or flying games, phones
and tablets can be used as steering wheels, with turning accomplished by tilting the device.
Increasingly popular drones, for the most part, can now fly more and more steadily by
detecting and controlling the attitude of the aircraft. All of these feats are thanks to the triaxial
accelerometer. In this section, we will learn to use programming to retrieve data from a triaxial
accelerometer and use this data for display and control.
Applications of Accelerometers
Accelerometers can help robots understand their environment. Are they climbing a hill? Or
going downhill, or have they fallen? For balance cars or drones, accelerometers can help them
maintain balance. In addition to everyday areas like smartphones and health wristbands,
accelerometers have also found wide application in other fields.
Compensation for GPS Navigation System Blind Spots: GPS systems determine an object’s
location by receiving signals f rom three satellites distributed at 120 degrees. In special
circumstances and terrains, like tunnels, dense buildings, jungle areas, GPS signals may weaken
2.4.2 Task1: Reading Values from the XYZ Axes of the Three-
Axis Accelerometer
The key to using a three-axis accelerometer for project creation is learning how to read the
values of the X, Y, Z axes of the accelerometer.
void setup()
{
Serial.begin(115200);
while (!Serial)
{
};
LIS.begin(WIRE); //IIC init dafault :0x18
//LIS.begin(WIRE, 0x19); //IIC init
LIS.openTemp(); //If ADC3 is used, the temperature detection needs to be turned
off.
// LIS.closeTemp();//default
delay(100);
// LIS.setFullScaleRange(LIS3DHTR_RANGE_2G);
// LIS.setFullScaleRange(LIS3DHTR_RANGE_4G);
// LIS.setFullScaleRange(LIS3DHTR_RANGE_8G);
// LIS.setFullScaleRange(LIS3DHTR_RANGE_16G);
//temperature
Serial.print(“temp:”);
Serial.println(LIS.getTemperature());
delay(500);
}
The sample program can read the values of the X, Y, Z axes of the three-axis accelerometer
and output through the serial monitor. The sample program provides us with different setting
choices using the “//” comment method, but you need to manually select the required parts, as
follows:
LIS.begin(WIRE): initializes the default values, you can choose between 0×18 and 0×19, we need
to choose LIS.begin(WIRE,0×19);.
LIS.setOutputDataRate(LIS3DHTR_DATARATE_50HZ): The accelerometer’s output rate has
multiple choices, choose 50Hz. The three-axis accelerometer can also monitor the ambient
temperature, we temporarily do not need to delete the related code, the complete program is as
follows:
void setup()
{
Serial.begin(9600);
while (!Serial) { }; // If you can’t open the serial monitor, the code will stop
100 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
here
LIS.begin(WIRE, 0x19); // Initialize I2C with default value
delay(100);
LIS.setOutputDataRate(LIS3DHTR_DATARATE_50HZ); // Set the accelerometer’s output
rate to 50Hz.
}
void loop()
{
if (!LIS) {
Serial.println(“LIS3DHTR didn’t connect.”);
while (1);
return;
}
// Read the values of the X, Y, Z axes from the sensor, and display them on the
serial monitor
Serial.print(“x:”); Serial.print(LIS.getAccelerationX()); Serial.print(“ “);
Serial.print(“y:”); Serial.print(LIS.getAccelerationY()); Serial.print(“ “);
Serial.print(“z:”); Serial.println(LIS.getAccelerationZ());
delay(500);
}
Program Writing
To control the RGB LED strip to change the light effects via the three-axis accelerometer, follow
these steps:
• Declare the library files that need to be invoked, define the strip pin and LED quantity.
• Initialize the three-axis accelerometer and the strip.
• Set the light effect of the strip to red, green and blue flashing, set the conditional judgment,
and control the change by different value intervals on the X, Y, Z axis of the three-axis
accelerometer.
Task: Control RGB LED Strip to Change Light Effects via Three-
Axis Accelerometer
Step 1: Declare the library files that need to be invoked, define the strip pin and the number of
LEDs.
102 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
#include <avr/power.h>
#endif
// Below are to initialize the module using software I2C or hardware I2C
#ifdef SOFTWAREWIRE
#include <SoftwareWire.h>
SoftwareWire myWire(3, 2);
LIS3DHTR<SoftwareWire> LIS;
#define WIRE myWire
#else
#include <Wire.h>
LIS3DHTR<TwoWire> LIS;
#define WIRE Wire
#endif
#define PIXEL_PIN 0 // Define the pin of the strip, if you use XIAO RP2040/XIAO ESP32,
please modify 0 to A0
#define PIXEL_COUNT 30 // Define the number of LEDs in the strip as 30
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800); // Declare
the strip object, set the data type
Step 2: Initialize the three-axis accelerometer and the strip. Here, you need to initialize the
accelerometer and set the rate to 50HZ.
void setup() {
Serial.begin(9600); // Initialize the serial monitor
while (!Serial) {}; // If the serial monitor isn't opened, the code will stop
here, so please open the serial monitor
LIS.begin(WIRE, 0x19); // Initialize I2C
delay(100);
LIS.setOutputDataRate(LIS3DHTR_DATARATE_50HZ); // Set the accelerometer's output
rate to 50Hz
strip.begin(); // Start the strip
strip.show(); // Display the strip
}
Step 3: Set the light effects to flash in red, green, and blue, respectively. Conditionals are used
to change the color of the light strip according to the varying readings on the X, Y, and Z axes of
the 3-axis accelerometer. These readings can be viewed via the serial monitor. By observing the
change in values when the accelerometer is moved along the X, Y, and Z axes, we can determine
the appropriate settings for the light strip. Since the readings may sometimes be negative, we
take the absolute value of the readings. The abs() function can be used to get the absolute
value, for example, abs(LIS.getAccelerationX()) would give the absolute value of the reading
on the X-axis.
void loop() {
if (!LIS) { // Check if the 3-axis accelerometer is connected properly
Serial.println(“LIS3DHTR didn’t connect.”);
while (1);
return;
}
// Read the values of the X, Y, and Z axes from the sensor and display them on
the serial monitor
Serial.print(“x:”); Serial.print(LIS.getAccelerationX()); Serial.print(“ “);
Serial.print(“y:”); Serial.print(LIS.getAccelerationY()); Serial.print(“ “);
Serial.print(“z:”); Serial.println(LIS.getAccelerationZ());
delay(500);
}
// Set theaterChase for flashing light effects
void theaterChase(uint32_t color, int wait) {
for(int a=0; a<10; a++) {
for(int b=0; b<3; b++) {
strip.clear();
for(int c=b; c<strip.numPixels(); c += 3) {
strip.setPixelColor(c, color);
}
strip.show();
delay(wait);
}
}
}
#include “LIS3DHTR.h”// Declare the library file for the 3-axis accelerometer
#include <Adafruit_NeoPixel.h>// Declare the library file for the light strip
#ifdef __AVR__
#include <avr/power.h>
#endif
// The following is to initialize the module using software I2C or hardware I2C
#ifdef SOFTWAREWIRE
#include <SoftwareWire.h>
SoftwareWire myWire(3, 2);
LIS3DHTR<SoftwareWire> LIS;
#define WIRE myWire
#else
#include <Wire.h>
LIS3DHTR<TwoWire> LIS;
#define WIRE Wire
#endif
#define PIXEL_PIN 0 // Define the pin of the light strip, if you are using XIAO
RP2040/XIAO ESP32, please change 0 to A0
#define PIXEL_COUNT 30 // Define the number of LEDs on the light strip as 30
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800); // Declare
the light strip object and set the data type
void setup() {
104 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Serial.begin(9600); // Initialize the serial monitor
while (!Serial) {};// If you do not open the serial monitor, the code will stop
here, so please open the serial monitor
LIS.begin(WIRE, 0x19); // IIC initialization
delay(100);
LIS.setOutputDataRate(LIS3DHTR_DATARATE_50HZ); // Set the output rate of the ac-
celerometer to 50Hz
strip.begin(); // The light strip starts working
strip.show(); // The light strip displays
}
void loop() {
if (!LIS) { // Check if the 3-axis accelerometer is connected correctly
Serial.println(“LIS3DHTR didn’t connect.”);
while (1);
return;
}
// Read the values of the X, Y, and Z axes from the sensor and display them on
the serial monitor
Serial.print(“x:”); Serial.print(LIS.getAccelerationX()); Serial.print(“ “);
Serial.print(“y:”); Serial.print(LIS.getAccelerationY()); Serial.print(“ “);
Serial.print(“z:”); Serial.println(LIS.getAccelerationZ());
delay(500);
}
// Set theaterChase for flashing light effects
void theaterChase(uint32_t color, int wait) {
for(int a=0; a<10; a++) {
for(int b=0; b<3; b++) {
strip.clear();
for(int c=b; c<strip.numPixels(); c += 3) {
strip.setPixelColor(c, color);
}
strip.show();
delay(wait);
}
}
}
Use a data cable to connect XIAO to your computer, click the “Upload” button in Arduino IDE,
and upload the program to the hardware. Once the debugging area shows “Upload Successful”,
you can open the serial monitor and try swinging the three-axis accelerometer left, right, up,
and down to feel the light effect changes of the light strip.
106 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Chapter 3:
Intermediate Project Practice -
Complex Projects
In this unit, we will delve into more intricate and comprehensive projects, striving
towards mature works in terms of program implementation and design of appearance
structure. These include miniaturized smart homes, wearable electronic devices,
interactive electronic instruments, Wi-Fi connectivity, and applications enabled by
XIAO ESP32C3 or telemetry and command via the MQTT protocol. We will provide the
laser-cut design blueprints for the first three cases for your reference. Of course, you’re
not limited to these examples; you could use other, more accessible materials, such as
corrugated cardboard or cardstock, for crafting. Feel free to unleash your creativity and
design the work you wish to present!
In everyday life, privacy and security are matters of great concern to everyone. In recent
years, the doors of residential areas have become increasingly smart, only accessible through
electronic keys or passwords, preventing outsiders from gaining entry. In public areas or parking
lot entrances, having a smart remote control to operate doors could facilitate the work of
security personnel. A simple smart remote control door can be implemented using an infrared
transmitter and an infrared receiver, which sends and receives infrared signals to open and
close the door. In this section, we will build such a smart remote control door.
108 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
to add the necessary library f iles. Enter the library f ile address https://github.com/Arduino-
IRremote/Arduino-IRremote in your browser address bar, go to the GitHub page, and click
Code→Download ZIP to download the resource package Arduino-IRremote-master.zip to your
local machine, as shown in the image below:
#include <Arduino.h>
#include <IRremote.h>
void setup() {
Serial.begin(115200);
Serial.println(F(“Enabling IRin”));
void loop() {
if (IrReceiver.decode()) // Decode successfully, receive a set of infrared sig-
nals
{
Serial.println(IrReceiver.decodedIRData.command, HEX); // Output infrared de-
coding result (hexadecimal)
Serial.println(IrReceiver.decodedIRData.command); // Output infrared decoding
result (octal)
IrReceiver.resume(); // Receive the next set of values
}
}
- Attention -
110 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
- Attention -
Different remote controls may give different values.3.1.3 Project Creation: Smart Remote
Door
Program Writing
To control the rotation of the servo with an infrared remote control, you need to follow these
steps:
• Declare the IRremote library and Serve library to be called, and define variables.
• Initialize the library files, initialize the servo.
• Read the infrared decoding result and control the rotation of the servo according to the
instructions to the left and right.
#include <IRremote.h>
#include <Servo.h>
void setup()
{
Serial.begin(9600);
Serial.println(“Enabling IRin”);
irrecv.enableIRIn();
myservo.attach(5); // Connect the servo on pin 5 to myservo. If you are using
XIAO RP2040/XIAO ESP32, please change 5 to D5
}
- Attention -
In the example, the infrared signal value of the right key 16761405, and the infrared signal
value of the left key 16712445, need to be replaced by the values obtained from the “Read
Remote Control Key Code” example using the remote control in your hand. Otherwise, there
will be no response after pressing the key.
void loop() {
if (irrecv.decode(&results)) { // If decoding is successful, a set of infrared
signals is received
if (results.value == 16761405) { // If the received signal is 16761405 (right
key)
for (pos; pos <= 89; pos += 1) { // Then the servo is incremented from
0° to 90° in sequence
myservo.write(pos); // Write the rotation angle value
to the servo pin
delay(40);
// The following is to interrupt the above instruction and exit the
loop
if (irrecv.decode(&results)) {
irrecv.resume();
if (results.value == 16712445)
break;
}
}
}
}
delay(100);
}
112 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Complete program details:
#include <IRremote.h>
#include <Servo.h>
void setup()
{
Serial.begin(9600);
Serial.println(“Enabling IRin”);
irrecv.enableIRIn();
myservo.attach(5); // Connect the servo on pin 5 to myservo. If you are using
XIAO RP2040/XIAO ESP32, please change 5 to D5
}
// Note: Left 16712445 Right 16761405, please replace with the key values read from
your own remote control
void loop() {
if (irrecv.decode(&results)) { // If decoding is successful, a set of infrared
signals is received
if (results.value == 16761405) { // If the received signal is 16761405 (right
key)
for (pos; pos <= 89; pos += 1) { // Then the servo is incremented from
0° to 90° in sequence
myservo.write(pos); // Write the rotation angle value
to the servo pin
delay(40);
// The following is to interrupt the above instruction and exit the
loop
if (irrecv.decode(&results)) {
irrecv.resume();
if (results.value == 16712445)
break;
}
}
}
}
delay(100);
}
- Attention -
If you are using XIAO RP2040, please connect the inf rared receiving module to the A0
interface.
Connect XIAO to the computer with a data cable, click the “Upload” button, upload the program
to the hardware, and when the debug area shows “Upload Successful”, open the serial monitor,
aim the remote control at the infrared receiver, press the “Left” key and the “Right” key, observe
the rotation of the servo, and check the encoding information output by the serial monitor.
114 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Download files for use with a laser cutter
https://github.com/mouseart/XIAO-Mastering-Arduino-and-TinyML/blob/main/dxf/XIAO_ADR.dxf.
The watch is a common item in life. Even though various electronic devices now have timing
functions, and mobile phones can replace watches as timing tools, watches are still a popular
item. They are not only timing tools, but also have fashion matching functions. Although
watches are delicate and small, they involve complex craftsmanship. Now with XIAO and its
expansion board, we can easily make them.
Position of the RTC clock chip on the XIAO expansion Grove RTC Module
board
116 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Add the previously downloaded resource package PCF8563-Arduino-Library-master.zip in
Sketch→Include Library→Add .ZIP Library in the menu bar until you see the library loading
success prompt.
void setup() {
Serial.begin(9600);
pcf.init();//Initialize the clock
pcf.stopClock();//Stop the clock
//Set the current date and time. After setting, it will start timing from this
moment
pcf.setYear(23);//Year
pcf.setMonth(05);//Month
pcf.setDay(29);//Day
pcf.setHour(16);//Hour
pcf.setMinut(10);//Minute
pcf.setSecond(0);//Second
118 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Programming
The program consists of the following steps:
• Declare the necessary libraries and define variables.
• Initialize the libraries, and set the current time.
• Read temperature and humidity variables, get the current time, and display the temperature,
humidity, and date/time on the OLED screen.
- Attention -
Before starting to program for the OLED of the XIAO expansion board, make sure that the
U8g2_Arduino library has been loaded into the Arduino IDE. The loading method can be
referred to the instructions in the “How to Download and Install Arduino Libraries” section of
Section 1.1.
Before starting to program for the Grove temperature and humidity sensor, make sure that
the Arduino IDE has loaded the Grove_Temperature_And_Humidity_Sensor library. The
loading method can be referred to the instructions in the “Adding the Grove_Temperature_
And_Humidity_Sensor Library” section of Section 2.2.
#include <Arduino.h>
#include <U8x8lib.h> //use u8x8 library
#include <PCF8563.h> //RTC library
PCF8563 pcf; //define variable pcf
#include <Wire.h>
#include “DHT.h” //DHT library
#define DHTTYPE DHT20 //The type of the temperature and humidity sensor is DHT20
DHT dht(DHTTYPE);
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE); //OLED’s con-
structor, set data type, connect OLED display
void setup() {
Serial.begin(9600);
u8x8.begin(); //u8x8 starts working
u8x8.setFlipMode(1);
Wire.begin();
pcf.init(); //Initialize the clock
pcf.stopClock(); //Stop the clock
//Set the current time and date:
pcf.setYear(23);
pcf.setMonth(05);
pcf.setDay(29);
pcf.setHour(18);
pcf.setMinut(53);
pcf.setSecond(0);
pcf.startClock(); //The clock starts timing
}
void loop() {
float temp, humi; //Define temperature and humidity variables
temp = dht.readTemperature(); //Read the temperature value
humi = dht.readHumidity(); //Read the humidity value
Time nowTime = pcf.getTime(); //Get the time
u8x8.setFont(u8x8_font_chroma48medium8_r); //u8x8 font
//Display the current date, time, temperature, and humidity at different coordi-
nates on the OLED screen.
u8x8.setCursor(0, 0);
u8x8.print(nowTime.day);
u8x8.print(“/”);
u8x8.print(nowTime.month);
u8x8.print(“/”);
u8x8.print(“20”);
u8x8.print(nowTime.year);
u8x8.setCursor(0, 1);
u8x8.print(nowTime.hour);
u8x8.print(“:”);
u8x8.print(nowTime.minute);
u8x8.print(“:”);
u8x8.println(nowTime.second);
delay(1000);
u8x8.setCursor(0, 2);
u8x8.print(“Temp:”);
u8x8.print(temp);
u8x8.print(“C”);
u8x8.setCursor(0,3);
u8x8.print(“Humidity:”);
u8x8.print(humi);
u8x8.print(“%”);
u8x8.refreshDisplay();
delay(200);
}
#include <Arduino.h>
#include <U8x8lib.h> //use u8x8 library
#include <PCF8563.h> //RTC library
PCF8563 pcf; //define variable pcf
#include <Wire.h>
#include “DHT.h” //DHT library
#define DHTTYPE DHT20 //The type of the temperature and humidity sensor is DHT20
DHT dht(DHTTYPE);
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE); //OLED’s con-
structor, set data type, connect OLED display
void setup() {
Serial.begin(9600);
u8x8.begin(); //u8x8 starts working
u8x8.setFlipMode(1);
Wire.begin();
pcf.init(); //Initialize the clock
120 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
pcf.stopClock(); //Stop the clock
//Set the current time and date:
pcf.setYear(23);
pcf.setMonth(05);
pcf.setDay(29);
pcf.setHour(18);
pcf.setMinut(53);
pcf.setSecond(0);
pcf.startClock(); //The clock starts timing
}
void loop() {
float temp, humi; //Define temperature and humidity variables
temp = dht.readTemperature(); //Read the temperature value
humi = dht.readHumidity(); //Read the humidity value
Time nowTime = pcf.getTime(); //Get the time
u8x8.setFont(u8x8_font_chroma48medium8_r); //u8x8 font
//Display the current date, time, temperature, and humidity at different coordi-
nates on the OLED screen.
u8x8.setCursor(0, 0);
u8x8.print(nowTime.day);
u8x8.print(“/”);
u8x8.print(nowTime.month);
u8x8.print(“/”);
u8x8.print(“20”);
u8x8.print(nowTime.year);
u8x8.setCursor(0, 1);
u8x8.print(nowTime.hour);
u8x8.print(“:”);
u8x8.print(nowTime.minute);
u8x8.print(“:”);
u8x8.println(nowTime.second);
delay(1000);
u8x8.setCursor(0, 2);
u8x8.print(“Temp:”);
u8x8.print(temp);
u8x8.print(“C”);
u8x8.setCursor(0,3);
u8x8.print(“Humidity:”);
u8x8.print(humi);
u8x8.print(“%”);
u8x8.refreshDisplay();
delay(200);
}
#include <Arduino.h>
#include <U8x8lib.h>
#include <PCF8563.h>
PCF8563 pcf;
#include <Wire.h>
#include “DHT.h”
#define DHTPIN 0
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);
//U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ SCL, /* data=*/ SDA, /* re-
set=*/ U8X8_PIN_NONE); // OLEDs without Reset of the Display
void setup() {
Serial.begin(115200);
u8x8.begin();
u8x8.setFlipMode(1);
Wire.begin();
pcf.init(); //initialize the clock
pcf.stopClock(); //stop the clock
pcf.setYear(23); //set year
pcf.setMonth(05); //set month
pcf.setDay(29); //set date
pcf.setHour(18); //set hour
pcf.setMinut(53); //set minute
pcf.setSecond(0); //set second
pcf.startClock(); //start the clock
}
122 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
void loop() {
float temp, humi;
temp = dht.readTemperature();
humi = dht.readHumidity();
Time nowTime = pcf.getTime(); //get current time
u8x8.setFont(u8x8_font_chroma48medium8_r); // choose a suitable font
u8x8.setCursor(0, 0);
u8x8.print(nowTime.day);
u8x8.print(“/”);
u8x8.print(nowTime.month);
u8x8.print(“/”);
u8x8.print(“20”);
u8x8.print(nowTime.year);
u8x8.setCursor(0, 1);
u8x8.print(nowTime.hour);
u8x8.print(“:”);
u8x8.print(nowTime.minute);
u8x8.print(“:”);
u8x8.println(nowTime.second);
delay(1000);
u8x8.setCursor(0, 2);
u8x8.print(“Temp:”);
u8x8.print(temp);
u8x8.print(“C”);
u8x8.setCursor(0,3);
u8x8.print(“Humidity:”);
u8x8.print(humi);
u8x8.print(“%”);
u8x8.refreshDisplay();
delay(200);
}
124 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
3.3
Air Piano
Normally, when we play a musical instrument, we have to pluck strings or press keys to produce
musical notes. However, with electronic modules, playing music can become more exciting.
For instance, you can simulate piano playing with push-button switches and even integrate
light effects for interactive music. But if you use push-button switches as piano keys, you
need to integrate many modules into the circuit. Is there a simpler and more unique idea?
A combination of ultrasonic distance sensor and passive buzzer can do just that - detecting
different distances with ultrasonics to trigger different notes, just like playing a piano in the air.
- Attention -
The Grove Ultrasonic Distance Sensor module is not included in the Seeed Studio XIAO
Starter Kit!
126 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Open the modified sample file through the following path, https://github.com/mouseart/XIAO-
Mastering-Arduino-and-TinyML/tree/main/code/L13_UltrasonicDisplayOnTerm_XIAO_en.
128 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
the corresponding distances. As shown in the figure below: According to the width of the palm,
one musical note corresponds to a unit of 2cm, and the performance starts from 4cm. “Do, Re,
Mi, Fa, Sol, La, Xi, Do” respectively correspond to 4cm, 6cm, 8cm, 10cm, 12cm, 14cm, 16cm, 18cm…
and so on.
Step 2: Initialize the baud rate and set the buzzer pin status.
void setup()
{
Serial.begin(9600);
pinMode(buzzerPin,OUTPUT);
}
Step 3: Read the distance (cm) measured by the ultrasonic distance sensor and make a
condition judgment to set different distances to emit different notes. Since the setting of the air
harp is that different distances trigger different notes, and this distance is a long integer value,
so we need to use the long() function to define the value returned by the ultrasonic wave. For
example, (long)RangeInCentimeters== 4, that is, the distance value returned by the ultrasonic
wave is 4. Corresponding to the buzzer emitting different notes, use the tone() function, for
example, tone(3,NOTE_C5,100), that is, the buzzer on pin 3, emits NOTE_C5 (Do) note, lasts for
100 milliseconds.
void loop()
{
// Read the distance value detected by the ultrasonic distance sensor, in centi-
meters, and print it on the serial monitor
long RangeInCentimeters;
RangeInCentimeters = ultrasonic.MeasureInCentimeters();
Serial.print(RangeInCentimeters);
Serial.println(“ cm”);
delay(250);
// Using an if statement for conditional judgment, when the distance is 4, 6, 8,
10, 12, 14, 16, 18, it corresponds to C5, D5, E5, F5, G5, A5, B5, C6
if (((long)RangeInCentimeters== 4)) { //Do
130 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
tone(3,NOTE_C5,100);
}
if (((long) RangeInCentimeters== 6)) { //Re
tone(3,NOTE_D5,100);
}
if (((long) RangeInCentimeters== 8)) { //Mi
tone(3,NOTE_E5,100);
}
if (((long) RangeInCentimeters== 10)) { //Fa
tone(3,NOTE_F5,100);
}
if (((long) RangeInCentimeters== 12)) { //Sol
tone(3,NOTE_G5,100);
}
if (((long) RangeInCentimeters== 14)) { //La
tone(3,NOTE_A5,100);
}
if (((long) RangeInCentimeters== 16)) { //Xi
tone(3,NOTE_B5,100);
}
if (((long) RangeInCentimeters== 18)) { //Do
tone(3,NOTE_C6,100);
}
}
132 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
3.4
Implementing Wi-Fi Connection and
Applications with XIAO ESP32C3
- Learn more -
We often use the ICMP protocol in the network, such as the Ping command (available in both
Linux and Windows) we often use to check whether the network is available. This Ping process is
actually the working process of the ICMP protocol. ping can test the connection speed between
two devices and accurately report the time it takes for a packet to reach its destination and
return to the sender’s device. Although ping does not provide data about routing or hops, it
is still a useful metric for measuring latency between two devices. Below we will learn how to
implement ping requests on XIAO ESP32C3.
Before starting this attempt, we need to learn how to connect XIAO ESP32C3 with your Wi-Fi.
- Attention -
Be careful when attempting to use the XIAO ESP32C3 development board as a hotspot
(access point). Overheating issues may occur and lead to burns.
134 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Software Setup: Add the ESP32 Board Package to the Arduino IDE
Step 1: Open the Arduino IDE preferences to add the Board Manager URL.
• For Windows users, first open your Arduino IDE, click on “File→Preferences” in the top menu
bar, and copy the following URL into “Additional Board Manager URLs”.
• For Mac users, first open your Arduino IDE, click on “Arduino IDE→Preferences” in the top
menu bar, and copy the following URL into “Additional Board Manager URLs”.
For Seeed Studio XIAO ESP32C3, copy the link below: https://raw.githubusercontent.com/
espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json to the Board Manager URL bar
and confirm, as shown in the figure below.
Step 2: In the Arduino IDE menu, click “Tools→Board→Board Manager”, type “esp32” into the
search bar, find the latest version of ESP32 Arduino in the resulting entries, and click “Install”.
When the installation starts, you will see an output pop-up. Once the installation is complete,
the “Installed” option will appear.
Step 3: Select the Board.
Navigate to “Tools > Board > ESP32 Arduino” and select “XIAO_ESP32C3”. The list will be a bit
long, and you will need to scroll down to find it, as shown in the figure below.
136 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Scanning Nearby Wi-Fi Networks (STA Mode)
In this example, we will use the XIAO ESP32C3 to scan for available Wi-Fi networks in the area. The
development board in this example will be configured in STA mode.
Step 1: Copy and paste the code below into the Arduino IDE.
#include “WiFi.h”
void setup()
{
Serial.begin(115200);
// Set WiFi to station mode and disconnect from an AP if it was previously con-
nected
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(100);
Serial.println(“Setup done”);
}
void loop()
{
Serial.println(“scan start”);
#include <WiFi.h>
void setup()
{
Serial.begin(115200);
delay(10);
Serial.println();
Serial.println();
Serial.print(“Connecting to “);
Serial.println(ssid);
WiFi.begin(ssid, password);
Serial.println(“”);
Serial.println(“WiFi connected”);
138 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Serial.println(“IP address: “);
Serial.println(WiFi.localIP());
}
void loop()
{
}
Learn more
You can read the Wiki documentation for more about using the XIAO ESP32C3.
Step 2: Copy and paste the code below into the Arduino IDE. This code sets the test website to
www.seeedstudio.com. Remember to replace your-ssid in the code with your Wi-Fi network
name and your-password in the code with your Wi-Fi password.
////////////////////////////////////////////////////////////////////////////////
// IDE:
// Arduino 2.0.3
// Platform:
140 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
// esp32 2.0.4 - https://github.com/espressif/arduino-esp32
// Board:
// XIAO_ESP32C3
// Libraries:
// ESP32Ping 1.6 - https://github.com/marian-craciunescu/ESP32Ping
////////////////////////////////////////////////////////////////////////////////
// Includes
#include <WiFi.h>
#include <ESP32Ping.h>
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.println();
Serial.println();
Serial.println(“WIFI: Start.”);
WiFi.mode(WIFI_STA);
if (WIFI_SSID[0] != ‘\0’)
{
WiFi.begin(WIFI_SSID, WIFI_PASSPHRASE);
}
else
{
WiFi.begin();
}
}
void loop()
{
static int count = 0;
Serial.print(count);
Serial.print(‘\t’);
Serial.print(wifiStatus ? 1 : 0);
Serial.print(‘\t’);
Serial.print(wifiRssi);
Serial.print(‘\t’);
Serial.print(pingResult ? 1 : 0);
Serial.print(‘\t’);
Serial.println(pingTime);
count++;
142 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Introduction to HTTPS Protocol
HTTPS stands for HyperText Transfer Protocol Secure. It’s a protocol for secure communication
over a computer network. HTTPS communicates via HTTP but uses SSL/TLS to encrypt packets.
The main purpose of HTTPS is to authenticate the website server’s identity and protect the
privacy and integrity of the exchanged data.
1 GET Requests specified page information and returns the entity body.
The data sent f rom the client to the server replaces the content of a
4 PUT
specified document.
Reserved in HTTP/1.1 for proxy servers that can switch the connection to a
6 CONNECT
pipe mode.
Echoes the request received by the server, mainly used for testing or
8 TRACE
diagnosis.
We’ve already learned how to connect to a Wi-Fi network using XIAO ESP32C3. Now, let’s try
some more complex operations based on the network. The following sections will introduce how
to use XIAO ESP32C3 to send HTTP GET and HTTP POST requests.
app = FastAPI()
items = {}
class Sensor_Item(BaseModel):
name: str
value: float
@app.on_event(“startup”)
async def startup_event():
items[“sensor”] = {“name”: “Sensor”,”Value”:0}
@app.get(“/items/{item_id}”)
async def read_items(item_id: str):
return items[item_id],datetime.datetime.now()
@app.post(“/sensor/”)
async def update_sensor(si: Sensor_Item):
items[“sensor”][“Value”] = si.value
return si
@app.get(“/”)
def read_root():
return {“Hello”: “World”}
This code snippet, implemented using the Python FastAPI framework, can return the latest
information of the Sensor stored on the backend server when we use a get request on http://
domain/items/sensor. When we use post to send data to http://domain/sensor/, it can
modify and record the latest Sensor value. The operation steps are as follows:
Step 1: Create a python file named main.py locally, copy and paste the code above into main.py.
Then, on your PC, open the terminal and execute the following commands to install FastAPI.
Step 2: Execute the following command to start the backend service and local monitoring.
144 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
- Attention -
When running the command above, make sure the terminal is currently in the directory
where main:app resides. If there is a prompt during running:
This means the current address is already occupied and there is an address conflict. You
can specify a specific port as shown in the command below.
If the [Errno 48] error still appears, you can modify the port number after port.
The prompt information after the command is successfully run is as follows
#include “WiFi.h”
#include <HTTPClient.h>
void setup()
{
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println(“Connecting”);
Serial.println(“Setup done”);
}
void loop()
{
if ((millis() - lastTime) > timerDelay) {
//Check WiFi connection status
if(WiFi.status()== WL_CONNECTED){
HTTPClient http;
http.begin(serverPath.c_str());
if (httpResponseCode>0) {
Serial.print(“HTTP Response code: “);
Serial.println(httpResponseCode);
String payload = http.getString();
Serial.println(payload);
}
else {
Serial.print(“Error code: “);
Serial.println(httpResponseCode);
}
http.end();
}
else {
Serial.println(“WiFi Disconnected”);
}
lastTime = millis();
}
}
- Attention -
We need to change the serverName in the Arduino code to the IP address of the host
running the backend service. The XIAO ESP32C3 needs to be on the same local area
network as it. If the local area network IP of the backend server (in this example, your PC) is
192.168.1.2, then the GET request interface is http://192.168.1.2/items/sensor, and
other interfaces are similar. If you specified a port when running the backend service and
146 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
local monitoring, the GET request interface would be http://192.168.1.2:1234/items/
sensor.
Step 2: Upload the code to XIAO ESP32C3 in the Arduino IDE. After the upload is successful,
open the serial monitor to check the result returned by our backend server after the GET is
issued, as shown in the figure below.
The prompt HTTP Response code: 200 means the request has been successful, and our XIAO
ESP32C3 has successfully gotten data from the server.
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println(“Connecting”);
while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(“.”);
}
Serial.println(“”);
Serial.print(“Connected to WiFi network with IP Address: “);
Serial.println(WiFi.localIP());
void loop() {
//Send an HTTP POST request every 10 minutes
if ((millis() - lastTime) > timerDelay) {
//Check WiFi connection status
if(WiFi.status()== WL_CONNECTED){
WiFiClient client;
HTTPClient http;
http.begin(client, serverName);
http.addHeader(“Content-Type”, “application/json”);
int httpResponseCode = http.POST(“{\”name\”:\”sensor\”,\”value\”:\”123\”}”);
// Free resources
http.end();
}
else {
Serial.println(“WiFi Disconnected”);
}
lastTime = millis();
}
}
148 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Step 2: Upload the code to XIAO ESP32C3 using the Arduino IDE. After a successful upload, open
the Serial Monitor to examine the result returned by our backend server in response to the GET
request. The image below illustrates the process.
The message **HTTP Response code: 200** signifies a successful request. On your local PC,
open a browser and navigate to **http://192.168.1.2/items/sensor** (please replace the
IP address according to your actual PC’s IP address, and if a port has been set, append a colon
followed by the designated port number after the IP address). You should now see the most
recent data sent by the XIAO ESP32C3. Since XIAO sends data every 5 seconds, you can always
view the most recent data received by the backend server by refreshing the current page (the
timestamp of the data will change).
We have now successfully sent data from XIAO ESP32C3 to the local backend server.
In the previous section, we learned how to send HTTP GET or POST requests from the XIAO
ESP32C3 to a local machine on a local area network via Wi-Fi. In this section, we’ll step through:
communication protocols, Message Queuing Telemetry Transport (MQTT), telemetry (data
gathered from sensors and sent to the cloud), and commands (messages sent from the cloud to
a device instructing it to do something).
Data collected from sensors and sent to the cloud is called telemetry.
IoT devices can also receive information from the cloud. This information typically consists of
commands—instructions to perform internal actions (such as rebooting or updating firmware)
or to actuate (e.g., turning on a light).
Communication Protocols
There are many popular communication
protocols that IoT devices use to communicate
with the internet. The most popular are based
around the publishing/subscribing of messages
through some agent: IoT devices connect to the
agent, publish telemetry data and subscribe to
commands. Cloud services also connect to the
agent, subscribe to all telemetry information, and
publish commands to specific devices or groups
of devices, as shown in the figure below.
MQTT is the most popular communication protocol for IoT devices and will be covered in this
section. Other protocols include AMQP and HTTP/HTTPS, which we introduced in the previous
section.
150 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
MQTT is short for Message Queuing Telemetry Transport. It is a messaging protocol based
on the publish/subscribe paradigm under the ISO standard: ISO/IEC PRF 20922. It can be
seen as a “bridge for data delivery”. It operates on top of the TCP/IP protocol stack and is a
publish/subscribe type messaging protocol designed for remote devices with poor hardware
performance and poor network conditions. It is a lightweight, open standard messaging
transport protocol that can send messages between devices. Originally designed in 1999 for
monitoring oil pipelines, it was published as an open standard by IBM 15 years later.
The biggest advantage of MQTT is that it provides a real-time and reliable messaging service for
connecting remote devices with minimal code and limited bandwidth. As a low-overhead, low-
bandwidth consumption instant communication protocol, it is widely used in IoT, small devices,
mobile applications, and so on.
MQTT has one broker and multiple clients. All clients connect to the broker, which then routes
messages to the relevant clients. Messages are routed using named topics, not sent directly to
a single client. Clients can publish to a topic, and any client subscribed to that topic will receive
the message.
Do some research. If you have a large
number of IoT devices, how can you
ensure that your MQTT broker can
handle all messages?
HiveMQ
HiveMQ is a cloud-based MQTT platform, offering scalable, secure, and reliable IoT
communication services. HiveMQ can help enterprises and developers quickly build and
manage IoT applications, supporting millions of devices and messages.
152 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
3.5.2 Task 1: Connect the XIAO ESP32C3 to the MQTT Broker
The first step to adding internet control to your smart temperature and humidity meter is to
connect the XIAO ESP32C3 to an MQTT broker.
In this part of the section, you’ll connect your smart temperature and humidity meter from
Section 2.2 to the internet, enabling it to provide telemetry and be remotely controlled. Later
in this section, your device will send a telemetry message via MQTT to a public MQTT broker,
which will be received by some server code you’ll write. This code will check the temperature and
humidity values, and send a command message to the device, telling it to turn a buzzer on or off.
One real-world use of this setup would be in a large
indoor space with many temperature and humidity
sensors, such as a farm. Before deciding to turn on
air conditioning, data can be gathered from multiple
temperature and humidity sensors. If only one sensor
reading exceeds the threshold, but other sensor
readings are normal, this can prevent the entire air
conditioning system from being turned on.
Can you think of other situations where an evaluation of data from multiple sensors is required
before issuing a command?
Remember, this test broker is public and unsecure, and anyone can listen in on what you’re
publishing, so it should not be used for any data that needs to be kept confidential.
Follow the related steps below to connect your device to the MQTT broker we introduced earlier:
public.cloud.shiftr.io.
After the example program is opened, you can see the program as shown below. Then change
the ssid in the code to your Wi-Fi network name, and change the pass in the code to the
corresponding Wi-Fi password for your Wi-Fi network.
#include <WiFi.h>
#include <MQTT.h>
154 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
const char ssid[] = “ssid”;
const char pass[] = “pass”;
WiFiClient net;
MQTTClient client;
void connect() {
Serial.print(“checking wifi...”);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(“.”);
delay(1000);
}
Serial.print(“\nconnecting...”);
while (!client.connect(“arduino”, “public”, “public”)) {
Serial.print(“.”);
delay(1000);
}
Serial.println(“\nconnected!”);
client.subscribe(“/hello”);
// client.unsubscribe(“/hello”);
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. “Computer.local” on OSX) are not supported
// by Arduino. You need to set the IP address directly.
client.begin(“public.cloud.shiftr.io”, net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
delay(10); // <- fixes some issues with WiFi stability
if (!client.connected()) {
connect();
}
You can see the messages you sent by accessing public.cloud.shiftr.io in your browser. However,
because this is a public broker, your device will quickly get lost in the crowd.
Keep in mind, this test broker is public and insecure. Anyone can listen to what you’re
publishing, so it should not be used for anything requiring confidentiality.
156 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
3.5.3 Deep Dive into MQTT
Topics can have a hierarchy, and clients can use wildcards to subscribe to different levels of
different hierarchies. For example: you can send temperature telemetry to the /telemetry/
temperature topic, humidity data to the /telemetry/humidity topic, and then subscribe to
the /telemetry/* topic in your cloud application to receive both temperature and humidity
telemetry. When messages are sent, a Quality of Service (QoS) can be specif ied which
determines the guarantee of message delivery.
• At most once: The message is sent only once, and no additional steps are taken by the client
and the broker to confirm delivery (Fire and Forget).
• At least once: The message is retried by the sender until it receives an acknowledgment
(Acknowledged delivery).
• Exactly once: A two-level handshake is performed by the sender and receiver to ensure that
only one copy of the message is received (Assured delivery).
In what scenarios might you need to deliver messages on a Fire and Forget basis?
Although MQTT (Message Queuing Telemetry Transport) has “Message Queuing” in its name
(the first two letters of MQTT), it does not actually support message queues. This means that if
a client disconnects and then reconnects, it will not receive messages that were sent while it
was disconnected, except for those messages that it had already begun processing using the
QoS process. A retain flag can be set on a message. If this flag is set, the MQTT broker will store
the last message sent on a topic with this flag, and will send it to any clients who subsequently
subscribe to that topic. This way, clients always receive the latest message.
MQTT also supports a keep-alive feature to check if the connection is still online during long
intervals between messages.
MQTT connections can be public, or encrypted and protected using usernames, passwords, or
certificates.
MQTT communicates over TCP/IP, the same underlying network protocol as HTTP, but on a
different port. You can also communicate with web applications running in a browser over
MQTT on websockets, or in situations where firewalls or other network rules block standard
MQTT connections.
3.5.4 Telemetry
The word “telemetry” comes f rom Greek roots meaning “remote measurement”. Telemetry
refers to the act of collecting data from sensors and sending it to the cloud.
Then, the cloud service can use this telemetry data to decide what commands to send to control
cooling or heating.
Load the following program into the Arduino IDE to test sending telemetry data f rom your
device to the MQTT broker. Note that in this example, we’re trying a different MQTT broker than
in Task 1: broker.hivemq.com, and we’ve set XIAO_ESP32C3_Telemetry/ as the subscription
name.
////////////////////////////////////////////////////////////////////////////////
// IDE:
// Arduino 2.0.0
// Platform:
// esp32 2.0.5 - https://github.com/espressif/arduino-esp32
// Board:
// XIAO_ESP32C3
// Libraries:
// MQTT 2.5.0 - https://github.com/knolleary/pubsubclient
// ArduinoJson 6.19.4 - https://github.com/bblanchon/ArduinoJson
////////////////////////////////////////////////////////////////////////////////
158 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
// Includes Serial.print(“Attempting MQTT con-
nection...”);
#include <WiFi.h> // Attempt to connect
#include <PubSubClient.h> if (client.connect(“XIAO_ESP32”)) {
#include <Wire.h> Serial.println(“connected”);
#include “DHT.h” // Subscribe
#define DHTTYPE DHT20 client.subscribe(“XIAO_ESP32/
DHT dht(DHTTYPE); LEDOUTPUT”);
} else {
const char* ssid = “ssid”; Serial.print(“failed, rc=”);
const char* password = “pass”; Serial.print(client.state());
Serial.println(“ try again in 5
const char* mqtt_server = “broker. seconds”);
hivemq.com”; // Wait 5 seconds before retry-
ing
WiFiClient espClient; delay(5000);
PubSubClient client(espClient); }
long lastMsg = 0; }
char msg[50]; }
int value = 0;
void loop() {
float temperature = 0;
float humidity = 0; if (!client.connected()) {
reconnect();
void setup() { }
Serial.begin(115200); client.loop();
setup_wifi();
client.setServer(mqtt_server, 1883); long now = millis();
Wire.begin(); float temp_hum_val[2] = {0};
dht.begin(); if (now - lastMsg > 5000) {
} lastMsg = now;
How can you see the sensor data from another platform? There are many ways, such as MQTT
X. After downloading and installing the software suitable for your PC system, the interface is as
shown in the image below.
160 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Clicking the + New Connection button will bring you to the connection creation window, as
shown in the image below. Fill in XIAO-DHT20 in the Name box as the connection name. The
Host is broker.hivemq.com that we set in the program, no other settings are needed, click
Connect in the upper right corner.
Now, we can see the telemetry data sent from XIAO ESP32C3, as shown in the image below.
In this situation, you could consider first using an edge device to handle the telemetry data
to reduce dependence on the internet.
Losing connection
Internet connections can be unreliable, and it’s common to lose signal. In this case, what should
an IoT device do? Should it lose data, or should it store data until the connection is restored?
Again, the answer is — it depends on the device being monitored.
For a thermostat, data is likely lost once a new temperature measurement has been made. If the
current temperature is 19°C, the heating system doesn’t care that the temperature 20 minutes
ago was 20.5°C; it’s the current temperature that dictates whether the heat should be turned on
or off.
For some machines, you may want to retain this data, especially if it’s being used to look for
trends. Some machine learning models can identify anomalies in data streams by looking at a
defined time period (e.g., the last hour). This is often used for predictive maintenance, looking
for signs that something might be about to fail so you can repair
or replace it before disaster strikes. You may want every point of
telemetry f rom a machine sent so it can be used for anomaly
detection, so once an IoT device can reconnect, it will send all the
telemetry data generated during the internet outage.
IoT device designers should also consider whether an IoT device
can operate during an internet outage or if it loses signal due to
location. If a smart thermostat is unable to send telemetry data
to the cloud due to an internet outage, it should be able to make
some limited decisions to control heating.
162 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
For MQTT handling connection interruptions, if necessary, the device and server code will need
to be responsible for ensuring message delivery, for example, requiring all sent messages to be
replied to by an additional message on the reply topic, and if not, to manually queue them for
later resending.
3.5.6 Commands
Commands are messages sent by the cloud to
a device instructing it to do something. Most
often, this involves providing some output via
an actuator, but it could be an instruction to
the device itself, such as to reboot, or to collect
additional telemetry data and send it as a
response to the command.
A thermostat could receive a command from
the cloud to turn on the heat. Based on the
telemetry data f rom all sensors, if the cloud
service has decided that the heat should
be turned on, then it sends the appropriate
command.
////////////////////////////////////////////////////////////////////////////////
// IDE:
// Arduino 2.0.0
// Platform:
// esp32 2.0.5 - https://github.com/espressif/arduino-esp32
// Board:
// XIAO_ESP32C3
// Libraries:
// MQTT 2.5.0 - https://github.com/knolleary/pubsubclient
// ArduinoJson 6.19.4 - https://github.com/bblanchon/ArduinoJson
// https://github.com/Seeed-Studio/Seeed_Arduino_MultiGas
////////////////////////////////////////////////////////////////////////////////
// Includes
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
void reconnect() {
void setup_wifi() { // Loop until we’re reconnected
delay(10); while (!client.connected()) {
// We start by connecting to a WiFi Serial.print(“Attempting MQTT con-
network nection...”);
Serial.println(); // Attempt to connect
Serial.print(“Connecting to “); if (client.connect(“XIAO_ESP32”)) {
Serial.println(ssid); Serial.println(“connected”);
// Subscribe
164 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Then modify the ssid in the code to your Wi-Fi network name, and modify the pass in the code
to the Wi-Fi password corresponding to your Wi-Fi network name.
The logic of the program execution is explained as follows:
client.setServer(mqtt_server, 1883);
client.subscribe(“XIAO_ESP32/Recieve”);
client.setCallback(callback);
During the setup stage, the connection between XIAO and the MQTT server is initialized, and
the topic subscription settings and callback functions are set. Here we subscribe to the topic
XIAO_ESP32/Recieve as an example. When we send a message to this topic f rom the host
computer, the corresponding callback function callback will be executed:
Here it will first print out the received message, then extract the character at position 0. When
the character at position 0, which is the first character, is 0, it satisfies the condition for the if
statement to perform an action. Here we connect the XIAO ESP32C3 and the expansion board
together. When the condition is met, the buzzer on the expansion board will change its level
briefly and beep for 2 seconds, while sending the prompt message RUN to the serial port.
In the process of development and testing by readers, you can also try to integrate the receive
and send functions
o f M Q T T, a n d s e n d
messages to specif ic
topics in the callback
function, so that the
sender can ensure
that XIAO has received
the message.
On the host computer,
we use MQTT X to test.
Open MQTT X, the
interface is as shown in
the following figure.
Now we can publish messages to the specified topic, which is the topic XIAO_ESP32/Recieve
we subscribed to on XIAO. Then we enter 00 in the input box of XIAO_ESP32/Recieve at the
lower right corner of the interface, and then click the send button in the lower right
corner.
At this time, in the serial monitor on the PC side, you can see the prompt message received from
XIAO, as shown in the following figure, and prompt RUN, the buzzer will sound for 2 seconds,
indicating that the message has been received.
166 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Now, we have successfully driven the buzzer on the expansion board connected to the Wi-Fi
connected XIAO ESP32C3 through the instruction sent by the PC side.
The action of the buzzer can be replaced with the control of any peripheral to achieve the
desired function.
Lost connection
If a cloud service needs to send a command to an offline IoT device, what should it do? Again,
the answer depends on the situation. If the latest command overwrites the previous one, the
previous command may be ignored. If the cloud service sends a command to turn on the
heating, and then sends another command to turn off the heating, then the turn-on command
can be ignored and does not need to be resent.
If the commands need to be processed in order, such as first moving the robot arm up and then
closing the gripper, then they need to be sent in order once the connection is restored.
How can device or server code ensure that commands are always sent and processed in
order through MQTT if needed?
XIAO nRF52840, XIAO nRF52840 Sense, XIAO ESP32C3 all support Bluetooth function, you
can refer to the related Wiki documents to learn how to use the Bluetooth function.
• Bluetooth Usage on Seeed Studio XIAO ESP32C3
• Bluetooth Usage (Seeed nRF52 Boards Library)
• Bluetooth Usage (Seeed nrf52 mbed-enabled Boards Library)
Among the XIAO series products, the Seeed Studio XIAO nRF52840 Sense has Bluetooth
5.0 wireless connectivity, low power consumption, and onboard 6-axis IMU and PDM
microphone sensors. Besides, the XIAO ESP32S3 Sense further integrates PSRAM, a
camera, a digital microphone, and SD card support.
Those characteristics make those devices powerful tools for TinyML (Tiny Machine
Learning) projects.
168 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
4.1
Understanding TinyML and Edge Impulse
Studio
This section will explain embedded machine learning, the differences between TinyML
and other artif icial intelligence, and some essential applications. This section will help you
understand what TinyML is and why we need it. Edge Impulse Studio is one of the tools that
allows developers to create next-generation intelligent device solutions through embedded
machine learning. This section will introduce you to this tool and help you understand the basic
steps to build an embedded machine-learning model.
Embedded Systems
An embedded system is a computer used only to solve a few particular problems and is
challenging to change. The term “embedded” means that it is built into the system. It is a
permanent part of the more extensive system. It usually doesn’t look like a computer; in most
cases, it doesn’t have a keyboard, monitor, or mouse. But like any computer, it has a processor,
software, input, and output.
Embedded systems are computers controlling
various physical and electronic devices,
and now they are almost everywhere. From
the Bluetooth headphones you use, home
audio-visual equipment, game consoles, air
conditioners, sweeping robots, rice cookers,
and washing machines to the control units
o f e l e c t r i c v e h i c l e s to c o m m u n i c a t i o n
equipment, factor y equipment, medical
equipment, off ice places, and even military
equipment, almost any electrically driven
device has the presence of embedded systems.
Embedded software is the software running
on them, and the following figures show some
scenes where you can see embedded systems.
Embedded systems can be large or small, as small as the microcontroller controlling the digital
watch and as large as the embedded computer in the autonomous car. Unlike general-purpose
computers such as laptops or smartphones, embedded systems usually perform a specif ic
specialized task.
Look around you, what devices might have embedded systems in them?
The size and complexity of embedded systems vary, but they all contain three basic components:
• Hardware: These systems use microprocessors and microcontrollers as their hardware.
Microprocessors are similar to microcontrollers because they are both related to CPUs (central
processing units), and CPUs are combined with other essential computer parts (such as
storage chips and digital signal processors (DSPs)). Microcontrollers integrate all these parts
into a single chip.
• Firmware and Software: The complexity of the system software differs from industrial-grade
microcontrollers and embedded IoT systems, on the other hand, they usually run relatively
basic software, using very little memory.
• Real-Time Operating System (RTOS): These systems, especially the more minor, often do
not include these operation systems. By supervising software during program execution and
establishing standards, RTOS determines how the system operates.
Embedded systems are often also constrained by their deployment environment. For example,
many embedded systems need to run on battery power. Hence, their design needs to consider
energy efficiency metrics - perhaps memory is limited, or the clock speed is extremely slow.
The challenge for engineers programming for embedded systems is often implementing
functional requirements within these limited hardware and environmental resource constraints.
You must consider hardware resource constraints when learning to build your TinyML project
model for XIAO later.
170 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
At this stage, computational tasks were
centralized on the core machine. These large
computers evolved into “minicomputers,”
typically consisting of a central host and
multiple terminals connected to the host.
Multiple users could issue computational
instructions through the terminals, but most
of the computation still occurred on the
central host. As time passed, the terminals
became more complex and took over more
and more functions of the central computer.
It wasn’t until the advent of personal
computers that computation truly expanded
to the “edge.” The rapid development of
personal computers also led to the decline
of those massive machines, and the scale of
human computation quickly tilted towards the
“edge.”
The emergence and development of the
Internet led to a large concentration of
servers to provide a variety of data storage
and computational services, search engines,
streaming video, online games, social
networks, etc. The highly centralized cloud
computing era had arrived, and many internet
service providers owned massive data center The processor manual cover for the minicomputer
pdp11/70, showing the host and connected terminals.
rooms.
The 70s Wang computer, once a world leader in Google’s data center located on the outskirts of Pryor,
market share. (Personal computer Wang 2200 PCS II. Oklahoma, USA
It is located in the Belgrade Museum of Technology.)
In theory, all our computing services can be completed in the cloud. But these cloud-based
services are useless in many areas without internet connections or when the internet goes down.
The computers we use for work and entertainment are just some devices connected to these
cloud services. As of 2021, there were as many as 12.2 billion connected devices worldwide, and
we call this network of devices IoT (Internet of Things). It includes everything you can and can’t
think of, such as mobile phones, smart speakers, connected security cameras, cars, containers,
pet trackers, industrial sensors, etc.
172 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
machine to recognize these motion patterns. The traditional way involves manually analyzing
and checking the data, identifying specif ic logical rules for different movements through
mathematical analysis, and then writing a recognition program to perform the desired action
based on these rules. Sounds complicated, doesn’t it?
Fortunately, we now have machine learning methods. Training and testing these data will yield
an algorithm, and the device only needs to run this algorithm to automatically complete our
desired “inference” process and deliver results. From the current state of machine learning
development, this method is exceptionally proficient in handling complex data scenarios. We
will learn more about this process in subsequent sections.
Edge AI
As the term suggests, Edge AI combines edge devices and artif icial intelligence. The
development of Edge AI stems f rom the pursuit of lower system power consumption and
higher eff iciency. For example, popular smartwatches or f itness bands often have built-in
accelerometers that generate hundreds of readings per second—a large volume of data—
and continuous data reading is required to recognize movement states. If the recognition of
movements is performed in the cloud, the smartwatch would need to consume a lot of energy
to send data to the cloud, and there would usually be a delay in receiving the result. This makes
the entire computational process uneconomical—high energy consumption and latency. This
latency can also prevent us from effectively using data for real-time feedback.
Edge AI solves this problem by recognizing movements on the smartwatch or band itself. This
allows for quick results without relying on the cloud. If necessary data needs to be uploaded
to the cloud, there’s no need to send a large amount of sensor data; instead, just the essential
motion recognition results are sent, signif icantly reducing communication volume and
consuming less electric power.
174 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
• Model: A mathematical formula trying to generalize information from a given dataset.
• Training: The process of automatically updating the parameters from data within a model.
This model “learns” to make conclusions and generalize about the data.
• Inference: Providing new, unseen data to a trained model to make predictions, decisions, or
classifications.
Thus, under normal circumstances, we would rely on powerful server clusters to train new
models, constructing datasets from raw data collected on-site (images, sensor data, etc.) and
using this dataset to train our machine learning models.
- Attention -
In some cases, we can train on the device side. However, this is generally unfeasible due to
the memory and processing limitations of such edge devices.
Environmental Protection
• Smart grid monitoring to detect faults in
power lines early
• Wildlife tracking and behavior research
• Forest fire detection and early warning
Agriculture
Benjamin Cabé used TinyML technology to create
• Precision weeding, fertilization, pesticide an artificial nose thatdistinguishes between various
distinct smells.
application, or irrigation
Smart Buildings
• Monitoring of intrusions and recognition of abnormal states
• Air conditioning systems that adapt based on the number of people in a room
Human-Machine Interaction
• Voice activation word detection
• Gesture and device motion recognition for auxiliary control
Industry
• Automatic safety helmet detection
• Machine, equipment, and facility condition monitoring
• Production line defect detection
• Position and motion state detection
The computational power typically required to perform machine learning inference at the edge
is often more significant than simply polling sensors and transmitting raw data. However, locally,
achieving such computations requires less power than sending raw data to a remote server.
The following table provides some suggestions on the type of hardware needed to perform
machine learning inference at the edge, depending on the required application.
[Source: https://docs.edgeimpulse.com/docs/concepts/what-is-edge-machine-learning]
Sensors
Audio
Images
Videos
nRF52840
nRF52840
XIAO & ESP32S3 ESP32S3 Sense ESP32S3 Sense —
Sense
Sense
176 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Embedded hardware is also rapidly evolving, and it’s expected that the contents of this
table will change soon.
4.2.2 Introduction
As you learned in the previous section, microcontrollers (MCUs) are very cheap electronic
components, usually with just a few kilobytes of RAM, designed to use tiny amounts of energy.
They can be found in almost any consumer, medical, automotive, and industrial device. Over
40 billion microcontrollers will be sold this year, and there are probably hundreds of billions in
service nowadays. However, these devices get little attention because they’re often only used
to replace the functionality of older electro-mechanical systems in cars, washing machines, or
remote controls. More recently, with the Internet of Things (IoT) era, a significant part of those
MCUs is generating “quintillions” of data that, in its majority, is not used due to the high cost and
complexity (bandwidth and latency) of data transmission.
On the other hand, in recent decades, we have seen a lot of development in Machine Learning
models trained with vast amounts of data in very powerful and power-hungry mainframes.
And what is happening today is that due to those developments, it is now possible to take noisy
signals like images, audio, or accelerometers and extract meaning from them by using Machine
Learning algorithms such as Neural Networks.
And what is more important is that we can run these algorithms on microcontrollers and
sensors themselves using very little power, interpreting much more of those sensor data that we
are currently ignoring. This is TinyML, a new technology that enables machine intelligence right
next to the physical world.
178 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
TinyML can have many exciting applications for the benefit of society at large.
This section will explore TinyML, running on a robust and tiny device, the Seed XIAO nRF52840
Sense (also called XIAO BLE Sense).
Now, you can access this device from your Arduino IDE by selecting the development board and
serial port, as shown in the figure below.
180 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Your development board is now ready to run code on it. Let’s start with Blink - lighting up the
LED. Note that the board does not have a regular LED like most Arduino boards. Instead, you will
find an RGB LED that can be activated with “reverse logic” (you should apply LOW to activate
each of the three separate LEDs). Test your RGB LED with the following code:
void setup() {
// initialize serial.
Serial.begin(115200);
while (!Serial);
Serial.println(“Serial Started”);
// Pins for the built-in RGB LEDs on the Arduino Nano 33 BLE Sense
pinMode(LEDR, OUTPUT);
pinMode(LEDG, OUTPUT);
pinMode(LEDB, OUTPUT);
// Note: The RGB LEDs are ON when the pin is LOW and off when HIGH.
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, HIGH);
void loop() {
digitalWrite(LEDR, LOW);
Serial.println(“LED RED ON”);
delay(1000);
digitalWrite(LEDR, HIGH);
Serial.println(“LED RED OFF”);
delay(1000);
digitalWrite(LEDG, LOW);
Serial.println(“LED GREEN ON”);
delay(1000);
digitalWrite(LEDG, HIGH);
Serial.println(“LED GREEN OFF”);
delay(1000);
digitalWrite(LEDB, LOW);
Serial.println(“LED BLUE ON”);
delay(1000);
digitalWrite(LEDB, HIGH);
Serial.println(“LED BLUE OFF”);
delay(1000);
}
#include <PDM.h>
void setup() {
Serial.begin(9600);
while (!Serial);
void loop() {
// wait for samples to be read
if (samplesRead) {
182 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
}
void onPDMdata() {
// query the number of bytes available
int bytesAvailable = PDM.available();
The above code will continuously capture data to its buffer, displaying it in the Serial Monitor
and Plotter:
Also, note that the RGB LED will be set up depending on the intensity of sound.
The Micrphone will not be used on this project in particular, but it is good to have it tested if
it is your first time using the XIAO nRF52840 Sense.
Run the test code based on Harvard University’s tinymlx - Sensor Test
Now, run the following test code based on Harvard University’s tinymlx - Sensor Test.
#include “LSM6DS3.h”
#include “Wire.h”
char c;
int sign = 0;
void setup() {
Serial.begin(115200);
while (!Serial);
Serial.println(“Welcome to the IMU test for the built-in IMU on the XIAO BLE
Sense\n”);
Serial.println(“Available commands:”);
Serial.println(“a - display accelerometer readings in g’s in x, y, and z direc-
tions”);
Serial.println(“g - display gyroscope readings in deg/s in x, y, and z direc-
tions”);
Serial.println(“t - display temperature readings in oC and oF”);
}
184 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
void loop() {
// Read incoming commands from serial monitor
if (Serial.available()) {
c = Serial.read();
Serial.println(c);
}
if(c == ‘a’)sign=1;
else if(c == ‘g’)sign=2;
else if(c == ‘t’)sign=3;
float x, y, z;
if (sign ==1) { // testing accelerometer
//Accelerometer
x = xIMU.readFloatAccelX();
y = xIMU.readFloatAccelY();
z = xIMU.readFloatAccelZ();
Serial.print(“\nAccelerometer:\n”);
Serial.print(“Ax:”);
Serial.print(x);
Serial.print(‘ ‘);
Serial.print(“Ay:”);
Serial.print(y);
Serial.print(‘ ‘);
Serial.print(“Az:”);
Serial.println(z);
}
else if (sign ==2) { // testing gyroscope
//Gyroscope
Serial.print(“\nGyroscope:\n”);
x = xIMU.readFloatGyroX();
y = xIMU.readFloatGyroY();
z = xIMU.readFloatGyroZ();
Serial.print(“wx:”);
Serial.print(x);
Serial.print(‘ ‘);
Serial.print(“wy:”);
Serial.print(y);
Serial.print(‘ ‘);
Serial.print(“wz:”);
Serial.println(z);
}
else if (sign ==3) { // testing thermometer
//Thermometer
Serial.print(“\nThermometer:\n”);
Serial.print(“ Degrees oC = “);
Serial.println(xIMU.readTempC(), 0);
Serial.print(“ Degrees oF = “);
Serial.println(xIMU.readTempF(), 0);
delay(1000);
}
}
186 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
4.2.4 The TinyML Motion Classification Model
For our project, 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).
In this video, you can learn alternative ways to send data to the Edge Impulse Studio.
In this project, we should first connect our device to the Edge Impulse Studio for data collection,
which will also be used for data pre-processing, model training, testing, and deployment.
#include “LSM6DS3.h”
#include “Wire.h”
188 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
#define FREQUENCY_HZ 50
#define INTERVAL_MS (1000 / (FREQUENCY_HZ + 1))
void setup() {
Serial.begin(115200);
while (!Serial);
void loop() {
float x, y, z;
x = xIMU.readFloatAccelX();
y = xIMU.readFloatAccelY();
z = xIMU.readFloatAccelZ();
Serial.print(x * CONVERT_G_TO_MS2);
Serial.print(‘\t’);
Serial.print(y * CONVERT_G_TO_MS2);
Serial.print(‘\t’);
Serial.println(z * CONVERT_G_TO_MS2);
}
}
$ edge-impulse-data-forwarder --clean
Next, enter your EI credentials, and choose your project, variable, and device names:
Data Collection
As discussed before, we should capture data from all four Transportation Classes:
• lift (up-down)
• terrestrial (left-right)
• maritime (zig-zag, etc.)
• idle
You can capture, for example, around 2 minutes (twelve samples of 10 seconds) for each of
the four classes (a total of 8 minutes of data). Using the three dots menu after each one of
the samples, select 2 of them, reserving them for the Test set. Alternatively, you can use the
automatic Train/Test Split tool on the Danger Zone of Dashboard tab.
190 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Once you have captured your dataset, you can explore it in more detail using the Data
Explorer, a visual tool to f ind outliers or mislabeled data (helping to correct them). The
data explorer first tries to extract meaningful features from your data (by applying signal
processing and neural network embeddings) and then uses a dimensionality reduction
algorithm such as PCA or t-SNE to map these features to a 2D space. This gives you a one-
look overview of your complete dataset.
Data Pre-Processing
Data pre-processing is extracting features from the dataset captured with the accelerometer,
which involves processing and analyzing the raw data. Accelerometers measure the
acceleration of an object along one or more axes (typically three, denoted as X, Y, and Z). These
measurements can be used to understand various aspects of the object’s motion, such as
movement patterns and vibrations.
Raw accelerometer data can be noisy and contain errors or irrelevant information. Preprocessing
steps, such as filtering and normalization, can clean and standardize the data, making it more
suitable for feature extraction. In our case, we should divide the data into smaller segments or
windows. This can help focus on specific events or activities within the dataset, making feature
extraction more manageable and meaningful. The window size and overlap (window increase)
choice depend on the application and the frequency of the events of interest. As a thumb rule,
we should try to capture a couple of “cycles of data”.
With a sampling rate (SR) of 50Hz and a window size of 2 seconds, we will get 100 samples
per axis, or 300 in total (3 axis x 2 seconds x 50 samples). We will slide this window every
200ms, creating a larger dataset where each instance has 300 raw features.
You can learn more about how each feature is calculated by downloading the notebook
Edge Impulse - Spectral Features Block Analysis TinyML under the hood: Spectral Analysis
or opening it directly on Google CoLab.
Those 63 features will be the Input Tensor of a Neural Network Classifier.
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:
192 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Impulse Design
A complete Impulse comprises three primary building blocks: the input block - which obtains
the raw data, the processing block - which extracts features, and the learning block - which
classifies the data. The following image shows the interface when the three building blocks still
need to be added, and our machine-learning pipeline will be implemented by adding these
three blocks.
Impulse obtains raw data through the input block, uses the processing block to extract features,
and then uses the learning block to classify new data. In our continuous action recognition, the
added blocks include:
As shown in the f igure below, set the Window Size to 2000 ms (2 seconds), the Window
Increase to 80 milliseconds, and the Frequency to 51 Hz based on the calculations we made in
the data preprocessing section on the Time Series Data block that appears.
The effect after adding the processing block is shown in the figure below.
194 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
3. Adding the learning block: Classification
Click the “Add Learning Block” button and select Classification in the pop-up window as shown
below to match our motion analysis task type.
The interface of Impulse design after addition is shown in the f igure below, and now the
machine learning pipeline has been built.
In addition, we can also use a second model - K-means, which can be used for anomaly
detection. If we imagine that we can treat our known classes as clusters, then any sample that
does not fit into it might be an anomaly (for example, a container falling into the sea when the
ship is at sea).
Click the “Add Learning Block” button again and select Anomaly Detection (K-means) in the
pop-up window below.
196 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
The final Impulse design is as shown in the figure below, click the Save Impulse button on the
far right.
Generating features
At this point in our project, we have def ined the pre-processing method and the model
designed. Now, it is time to have the job done. First, let’s take the raw data (time-series type) and
convert it to tabular data. Go to the Spectral Features tab, select Save Parameters, and at the
top menu, select Generate Features option and Generate Features button:
With the visualization, it is possible to verify that the classes present an excellent separation,
which indicates that the classifier should work well.
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:
As hyperparameters, we will use a Learning Rate of 0.005 and 20% of data for validation for 30
epochs.
198 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
After training, we can see that the accuracy is 100%.
200 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
At this point, we have completed the basic machine learning training process.
Testing
Using the 20% of data set aside during the data collection phase, we can verify the model’s
performance with unknown data. As shown in the image below, click on the Model Testing
section on the left side of the Edge Impulse interface. Next to the [Classify All] button, there
is an icon with three dots, click on it to open the Set Confidence Thresholds popup window.
Here, you can set confidence thresholds for the results of the two learning blocks. We should
define an acceptable threshold for results considered as anomalies. If a result is not 100% (which
is often the case) but is within the threshold range, it is still usable.
Live Classification
Once the model is obtained, you should use the opportunity to test the Live Classification when
your device is still connected to the Edge Impulse Studio. As shown in the image below, click on
the Live Classification section on the left side of the Edge Impulse interface, then click the
[Start Sampling] button.
At this time, you can, for example, shake the XIAO, the process is the same as the sampling;
wait a few seconds, and the classification results will be given. As shown in the image below, I
shook the XIAO vigorously, and the model unhesitatingly inferred that the entire process was
anomalous.
202 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Try now with the same movements
used during data capture. The
result should match the class used
for training.
- Attention -
Here, you will capture real data with your device and upload it to the Edge Impulse Studio,
where the trained model will be used for inference (though the model is not in your device).
Deployment
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, select Quantized (Int8) and Build.
On your Arduino IDE, go to Sketch tab and select the option Add .ZIP Library.
204 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Inference
Now, it is time for a real test . We will make
inferences wholly disconnected f rom the Studio.
Let’s change one of the code examples created
when you deploy the Arduino Library.
In your Arduino IDE, go to File/Examples tab and
look for your project, and on examples, select nano_
ble_sense_accelerometer:
Of course, the Arduino Nano BLE 33 differs f rom
your board, the XIAO, 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_BLE_Sense_-_Motion_Classification_inferencing.h>
#include <Arduino_LSM9DS1.h>
Change the “includes” portion with the code related to the XIAO nRF52840 Sense IMU:
/* Includes -------------------------------------------------------------- */
#include <XIAO_BLE_Sense_-_Motion_Classification_inferencing.h>
#include “LSM6DS3.h”
#include “Wire.h”
On the setup function, initiate the IMU using the name that you stated before:
if (xIMU.begin() != 0) {
ei_printf(“Failed to initialize IMU!\r\n”);
}
else {
ei_printf(“IMU initialized\r\n”);
}
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:
buffer[ix] = xIMU.readFloatAccelX();
buffer[ix + 1] = xIMU.readFloatAccelY();
buffer[ix + 2] = xIMU.readFloatAccelZ();
206 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Post-processing
Now that we know the model is working since it detects the movements, we suggest that you
modify the code to see the result with the XIAO completely offline (disconnected from the PC
and powered by a battery, a power bank, or an independent 5V power supply).
The idea is that if one specific movement is detected, a particular LED could be lit. For example,
if terrestrial is detected, the Green LED will light; if maritime, the Red LED will light, if it is a lift,
the Blue LED will light; and if no movement is detected (idle), the LEDs will be OFF. You can also
add a condition when an anomaly is detected, in this case, for example, a white color can be
used (all e LEDs light simultaneously).
Conclusion
The Seeed Studio XIAO nRF52840 Sense is a giant tiny device! It is powerful, trustworthy, not
expensive, low power, and has suitable sensors to be used on the most common embedded
machine learning applications. Even though Edge Impulse does not off icially support XIAO
nRF52840 Sense, we also realized that it could be easily connected with the Studio.
On the GitHub repository, you will find the last version of the codes: Seeed-XIAO-BLE-Sense.
The applications for motion classification and anomaly detection are extensive, and the XIAO is
well-suited for scenarios where low power consumption and edge processing are advantageous.
Its small form factor and efficiency in processing make it an ideal choice for deploying portable
and remote applications where real-time processing is crucial and connectivity may be limited.
Case Applications
Before we finish, consider that Movement Classification and Object Detection can be utilized in
many applications across various domains. Here are some of the potential applications:
Consumer Electronics
• Gesture Control: Interpreting specific motions to control devices, such as turning on lights
with a hand wave.
• Gaming: Enhancing gaming experiences with motion-controlled inputs.
Agriculture
• Equipment Monitoring: Tracking the performance and usage of agricultural machinery.
• Animal Behavior Analysis: Monitoring livestock movements to detect behaviors indicating
health issues or stress.
Environmental Monitoring
• Seismic Activity: Detecting irregular motion patterns that precede earthquakes or other
geologically relevant events.
• Oceanography: Studying wave patterns or marine movements for research and safety
purposes.
208 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
4.3
Sound Classification (KWS)
4.3.2 Introduction
In the last section, Anomaly Detection & Motion Classification, we explored Embedded Machine
Learning, or simply TinyML, running on the Seeed XIAO nRF52840 Sense. Besides installing and
testing the device, we explored motion classification using actual data signals from its onboard
accelerometer. This new project will use the same XIAO nRF52840 Sense to classify sound,
explicitly working as “Key Word Spotting” (KWS). A KWS is a typical TinyML application and an
essential part of a voice assistant.
To start, it is essential to realize that Voice Assistants on the market, like Google Home or
Amazon Echo-Dot, only react to humans when they are “waked up” by particular keywords such
as ” Hey Google” on the first one and “Alexa” on the second.
210 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
The Machine Learning Workflow
The main component of the KWS application is its model. So, we must train such a model with
our specific keywords:
Dataset
The critical component of Machine Learning Workflow is the dataset. Once we have decided on
specific keywords (UNIFEI and IESTI), all datasets should be created from zero. When working
with accelerometers, creating a dataset with data captured by the same type of sensor was
essential. In the case of sound, it is different because of what we will classify as audio data.
The critical difference between sound and audio is the type of energy. Sound is mechanical
perturbation (longitudinal sound waves) that propagate through a medium, causing
variations of pressure in it. Audio is an electrical (analog or digital) signal representing
sound.
The sound waves should be converted to audio data when we speak a keyword. The conversion
should be done by sampling the signal generated by the microphone in 16KHz with a 16-bit depth.
So, any device that can generate audio data with this basic specification (16Khz/16bits) will work
fine. As a device, we can use the proper XIAO nRF52840 Sense, a computer, or even your mobile
phone.
We will not explore this option here, but you can easily follow the EI documentation and
tutorial.
This expansion board enables the building of prototypes and projects easily and quickly, using
its rich peripherals such as OLED Display, SD Card interface, RTC, passive buzzer, RESET/User
button, 5V servo connector, and multiple data interfaces.
This project will focus on classifying keywords, and the MicroSD card available on the device will
be very important in helping us with data capture.
212 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
And install the downloaded library: Seeed_Arduino_Mic-master.zip on your Arduino IDE:
Sketch -> Include Library -> Add .ZIP Library...
Next, navigate to File > Examples > Seeed Arduino Mic > mic_Saved_OnSDcard to open
the sketch: mic_Saved_OnSDcard.
Each time you press the reset button, a 5 seconds audio sample is recorded and saved on the
SD card. I changed the original file to add LEDs to help during the recording process as below:
• During the time that LED Red is ON is possible to record ==> RECORD
• During the file writing process, LED Red is OFF ==> WAIT
• When finished writing, LED Green is ON ==> Press Reset Button once and wait for LED Red
ON again, and proceed with a new sample recording
I realized that sometimes at the beginning and the end of each sample, a “spike” was recorded,
so I cut the initial 300ms from each 5s sample. The spike verified at the end always happened
after the recording process and should be eliminated on Edge Impulse Studio before training.
Also, I increased the microphone gain to 30 dB.
The complete file (Xiao_mic_Saved_OnSDcard.ino) can be found on the Git Hub (3_KWS): Seeed-
XIAO-BLE-Sense.
During the recording process, the.wav file names are shown on Serial Monitor:
214 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Note that any smartphone app can be used for audio recording or even your computer, for
example using Audacity.
Once the project is created, go to the Data Acquisition section and select the Upload
Existing Data tool. Choose the f iles to be uploaded, for example, I started uploading the
samples recorded with the XIAO nRF52840 Sense:
Click on three dots after the sample name and select Split sample. Once inside de tool, split
the data into 1-second records (try to avoid start and end portions):
216 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
This procedure should be repeated for all samples. After that, upload other class samples (IESTI
and SILENCE) captured with the XIAO and your PC or smartphone.
- Attention -
For longer audio files (minutes), first, split into 10-second segments and after that, use the
tool again to get the final 1-second splits.
In the end, the dataset has around 70 1-second samples for each class:
We can optionally check all datasets using the tab Data Explorer. The data points seem apart,
which means that the classification model should work:
218 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
First, we will take the data points with a 1-second window, augmenting the data, sliding that
window each 500ms. Note that the option zero-point pad is set. It is important to fill with zeros
samples smaller than 1 second in some cases, I reduced the 1000 ms window on the split tool to
avoid noises and spikes.
Each 1-second audio sample should be pre-processed and converted to an image (for example,
13 x 50 x 1). We will use Audio (MFCC), which extracts features from audio signals using Mel
Frequency Cepstral Coefficients, which are well suited for the human voice, which is our case here.
Next, we select the Classification block to build our model from scratch using a Convolution
Neural Network (CNN).
Pre-Processing (MFCC)
The next step is to create the images to be trained in the next phase:
220 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Going under the hood
To understand better how the raw sound is preprocessed, look at the Feature Engineering for
Audio Classification chapter. You can play with the MFCC features generation by downloading
this notebook from GitHub or Opening it In Colab.
As hyper-parameters, we will have a Learning Rate of [0.005] and a model trained by [100]
epochs. We will also include a data augmentation method based on SpecAugment. We trained
the 1D and the 2D models with the same hyperparameters. The 1D architecture had a better
overall result (91.1% accuracy) when compared with 88% of the 2D, so we will use the 1D.
Using 1D convolutions is more eff icient because it requires fewer parameters than 2D
convolutions, making them more suitable for resource-constrained environments.
Testing
Testing the model with the data put apart before training (Test Data), we got an accuracy of 75%.
Based on the small amount of data used, it is OK, but I strongly suggest increasing the number
of samples.
222 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Collecting more data, the Test accuracy moved up around 5%, going from 75% to around 81%:
Now, we can proceed with the project, but before deployment on our device, it is possible to
perform Live Classification using a Smart Phone, confirming that the model is working with live
and real data:
On your Arduino IDE, go to the Sketch tab and select the option Add .ZIP Library.
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_ble33_sense_microphone_continuous:
224 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Even though the XIAO is not the same as the Arduino, both have the same MPU and PDM
microphone, so the code works as it is. Upload the sketch to XIAO and open the Serial Monitor.
Start talking about one or another Keyword and confirm that the model is working correctly:
Postprocessing
Now that we know that the model is working by detecting our two keywords, let’s modify the
code so we can see the result with the XIAO nRF52840 Sense completely offline (disconnected
from the PC and powered by a battery).
The idea is that whenever the keyword UNIFEI is detected, the LED Red will be ON; if it is IESTI,
LED Green will be ON, and if it is SILENCE (No Keyword), both LEDs will be OFF.
#include <Arduino.h>
#include <U8x8lib.h>
#include <Wire.h>
void setup(void) {
u8x8.begin();
u8x8.setFlipMode(0); // set number from 1 to 3, the screen word should rotate
180
}
void loop(void) {
u8x8.setFont(u8x8_font_chroma48medium8_r);
u8x8.setCursor(0, 0);
u8x8.print(“Hello World!”);
}
And you should see the “Hello World” displayed on the SSD:
Now, let’s create some functions that, depending on the values of pred_index and pred_value,
will trigger the proper LED and display the class and probability. The code below will simulate
some inference results and present them on display and LEDs:
/* Includes ---------------------------------------------------------------- */
#include <Arduino.h>
#include <U8x8lib.h>
#include <Wire.h>
#define NUMBER_CLASSES 3
/** OLED */
U8X8_SSD1306_128X64_NONAME_HW_I2C oled(PIN_WIRE_SCL, PIN_WIRE_SDA, U8X8_PIN_NONE);
Int pred_index = 0;
float pred_value = 0;
String lbl = “ “;
226 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
void setup() { digitalWrite(LEDG, LOW);
pinMode(LEDR, OUTPUT); lbl = “IESTI “ ;
pinMode(LEDG, OUTPUT); break;
pinMode(LEDB, OUTPUT);
case 1:
digitalWrite(LEDR, HIGH); turn_off_leds();
digitalWrite(LEDG, HIGH); lbl = “SILENCE”;
digitalWrite(LEDB, HIGH); break;
oled.begin(); case 2:
oled.setFlipMode(2); turn_off_leds();
oled.setFont(u8x8_font_chroma48me- digitalWrite(LEDR, LOW);
dium8_r); lbl = “UNIFEI “;
oled.setCursor(0, 0); break;
oled.print(“ XIAO Sense KWS”); }
} oled.setCursor(0, 2);
oled.print(“ “);
/** oled.setCursor(2, 4);
* @brief turn_off_leds function - oled.print(“Label:”);
turn-off all RGB LEDs oled.print(lbl);
*/ oled.setCursor(2, 6);
void turn_off_leds(){ oled.print(“Prob.:”);
digitalWrite(LEDR, HIGH); oled.print(pred_value);
digitalWrite(LEDG, HIGH); }
digitalWrite(LEDB, HIGH);
} void loop() {
for (int i = 0; i < NUMBER_CLASS-
/** ES; i++) {
* @brief Show Inference Results pred_index = i;
on OLED Display pred_value = 0.8;
*/ display_oled(pred_index, pred_
void display_oled(int pred_index, float value);
pred_value){ delay(2000);
switch (pred_index){ }
case 0: }
turn_off_leds();
Running the above code, you should get the below result:
ei_printf(“: \n”);
...
#if EI_CLASSIFIER_HAS_ANOMALY == 1
And replacing the original function to print inference results on the Serial Monitor:
Here you can see how the final project is: https://youtu.be/1ex88hSqqyI
Conclusion
The Seeed XIAO nRF52840 Sense is really a giant tiny device! However, it 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.
Even though Edge Impulse does not off icially support XIAO nRF52840 Sense (yet!), we also
realized that it could use Studio for training and deployment.
On the GitHub repository, you will find the last version of the codes in the 3_KWS folder:
Seeed-XIAO-BLE-Sense
Before we finish, consider that Sound Classification is more than just voice. For example, you can
develop TinyML projects around sound in several areas as:
• Security (Broken Glass detection)
• Industry (Anomaly Detection)
• Medical (Snore, Toss, Pulmonary diseases)
• Nature (Beehive control, insect sound)
228 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
4.4
Image Classification
• Arduino IDE
4.4.2 Introduction
More and more, we are facing an artificial intelligence (AI) revolution where, as stated by Gartner,
Edge AI has a very high impact potential, and it is for now!
In the “bull-eye” of emerging technologies,
radar is the Edge Computer Vision, and when
we talk about Machine Learning (ML) applied
to vision, the f irst thing that comes to mind
is Image Classif ication, a kind of ML “Hello
World”!
Seeed Studio released a new affordable
development board, the XIAO ESP32S3 Sense,
which integrates a camera sensor, digital
microphone, and SD card support. Combining
embedded ML computing power and
photography capability,
this development board
is a great tool to start
with TinyML (intelligent
voice and vision AI).
For more details, please refer to the Seeed Studio WiKi page:
https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
230 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
4.4.3 Installing the XIAO ESP32S3 Sense on Arduino IDE
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
Next, open boards manager. Go to Tools > Board > Boards Manager… and enter with esp32.
Select and install the most updated and stable package (avoid alpha versions) :
- Attention -
Alpha versions (for example, 3.x-alpha) do not work correctly with the XIAO and Edge
Impulse. Use the last stable version (for example, 2.0.11) instead.
On Tools, select the Board (XIAO ESP32S3):
#define LED_BUILT_IN 21
void setup() {
pinMode(LED_BUILT_IN, OUTPUT); // Set the pin as output
}
Note that the pins work with inverted logic: LOW to Turn on
and HIGH to turn off.
As commented in the introduction, the expansion board, or the “sense” part of the device, has a
1600x1200 OV2640 camera, an SD card slot, and a digital microphone.
232 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
When producing sound, you can verify it on the Serial Plotter.
Save recorded sound (.wav audio files) to a microSD card.
Now, the onboard SD Card reader can save .wav audio files. For that, we need to habilitate the
XIAO PSRAM.
ESP32-S3 has only a few hundred kilobytes of internal RAM on the MCU chip. It can be
insufficient for some purposes so that ESP32-S3 can use up to 16 MB of external PSRAM
(Psuedostatic RAM) connected with the SPI flash chip. The external memory is incorporated
in the memory map and, with certain restrictions, is usable in the same way as internal
data RAM.
234 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
The sound quality is excellent!
The explanation of how the code works is beyond the scope of this tutorial, but you can find
an excellent description on the wiki page.
You can monitor how your server is working with the Serial Monitor.
236 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Take the IP address and enter
it on your browser:
You will see a page with links that can turn the built-in LED of your XIAO ON and OFF.
This program can be used for an image dataset capture with an Image Classif ication
project.
Inspect the code; it will be easier to understand how the camera works. This code was developed
based on the great Rui Santos Tutorial ESP32-CAM Take Photo and Display in Web Server, which
I invite all of you to visit.
238 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
If the code is executed correctly, you should see the address on the Serial Monitor:
Copy the address on your browser and wait for the page to be uploaded. Select the camera
resolution (for example, QVGA) and select [START STREAM]. Wait for a few seconds/minutes,
depending on your connection. You can save an image on your computer download area using
the [Save] button.
The whole idea of our project will be to train a model and proceed with inference on the XIAO
ESP32S3 Sense. For training, we should find some data (in fact, tons of data!).
But first of all, we need a goal! What do we want to classify?
With TinyML, a set of techniques associated with machine learning inference on embedded
devices, we should limit the classif ication to three or four categories due to limitations
(mainly memory). We will differentiate apples from bananas and potatoes (you can try other
categories).
So, let’s find a specific dataset that includes images from those categories. Kaggle is a good
start:
https://www.kaggle.com/kritikseth/fruit-and-vegetable-image-recognition
This dataset contains images of the following food items:
• Fruits - banana, apple, pear, grapes, orange, kiwi, watermelon, pomegranate, pineapple,
mango.
• Vegetables - cucumber, carrot, capsicum, onion, potato, lemon, tomato, radish, beetroot,
cabbage, lettuce, spinach, soybean, cauliflower, bell pepper, chili pepper, turnip, corn,
sweetcorn, sweet potato, paprika, jalepeño, ginger, garlic, peas, eggplant.
Each category is split into the train (100 images), test (10 images), and validation (10 images).
• Download the dataset from the Kaggle website to your computer.
Optionally, you can add some fresh photos of bananas, apples, and potatoes from your
home kitchen, using, for example, the codes discussed in the last section.
240 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
4.4.10 Training the model with Edge Impulse Studio
We will use the Edge Impulse Studio to train our model. As you know, Edge Impulse is a leading
development platform for machine learning on edge devices.
Enter your account credentials (or create a free account) at Edge Impulse. Next, create a new
project:
Data Acquisition
Next, on the UPLOAD DATA section, upload from your computer the files from chosen categories:
It would be best if you now had your training dataset split into three classes of data:
It would be best if you now had your training dataset split into three classes of data:
Impulse Design
An impulse takes raw data (in this case, images), extracts features (resize pictures), and
then uses a learning block to classify new data.
Classifying images is the most common use of deep learning, but much data should be used
to accomplish this task. We have around 90 images for each category. Is this number enough?
Not at all! We will need thousands of images to “teach or model” to differentiate an apple from a
banana. But, we can solve this issue by re-training a previously trained model with thousands of
images. We call this technique “Transfer Learning” (TL).
242 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
With TL, we can fine-tune a pre-trained image classification model on our data, performing well
even with relatively small image datasets (our case).
So, starting f rom the raw images, we will resize them (96x96) pixels and feed them to our
Transfer Learning block:
Do not forget to [Save parameters].” This will generate the features to be used in training.
Exposure to these variations during training can help prevent your model from taking shortcuts
by “memorizing” superficial clues in your training data, meaning it may better reflect the deep
244 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
underlying patterns in your dataset.
The final layer of our model will have 16 neurons with a 10% dropout for overfitting prevention.
Here is the Training output:
The result could be better. The model reached around 77% accuracy, but the amount of RAM
expected to be used during the inference is relatively tiny (about 60 KBytes), which is very good.
Deployment
The trained model will be deployed as a .zip Arduino library:
Under the Examples tab on Arduino IDE, you should find a sketch code under your project name.
246 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Open the Static Buffer example:
You can see that the first line of code is exactly the calling of a library with all the necessary stuff
for running inference on your device.
#include <XIAO-ESP32S3-CAM-Fruits-vs-Veggies_inferencing.h>
Of course, this is a generic code (a “template”) that only gets one sample of raw data (stored on
the variable: features = {} and runs the classifier, doing the inference. The result is shown on the
Serial Monitor.
We should get the sample (image) f rom the camera and pre-process it (resizing to 96x96,
converting to grayscale, and flatting it). This will be the input tensor of our model. The output
tensor will be a vector with three values (labels), showing the probabilities of each one of the
classes.
Returning to your project (Tab Image), copy one of the Raw Data Sample:
Edge Impulse included the library ESP NN in its SDK, which contains optimized NN (Neural
Network) functions for various Espressif chips, including the ESP32S3 (running at Arduino IDE).
When running the inference, you should get the highest score for “banana.”
248 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Great news! Our device handles an inference, discovering that the input image is a banana. Also,
note that the inference time was around 317ms, resulting in a maximum of 3 fps if you tried to
classify images from a video. It is a better result than the ESP32 CAM (525ms of latency).
Now, we should incorporate the camera and classify images in real time.
Go to the Arduino IDE Examples and download from your project the sketch esp32_camera:
You should change lines 32 to 75, which define the camera model and pins, using the data
related to our model. Copy and paste the below lines, replacing the lines 32-75:
Other tests:
250 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Even with a bigger model, the accuracy could be better, and the amount of memory necessary
to run the model increases five times, with latency increasing seven times.
Note that the performance here is estimated with a smaller device, the ESP-EYE. The actual
inference with the ESP32S3 should be better.
Doing an inference with MobilinetV2 96x96 0.35, having as input RGB images, the latency was
219ms, which is great for such a bigger model.
Note that the estimated latency for an Arduino Portenta (ou Nicla), running with a clock of
480MHz is 45ms.
Deploying the model, I got an inference of only 135ms, remembering that the XIAO runs with
half of the clock used by the Portenta/Nicla (240MHz):
252 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
4.4.13 Running inference on the SenseCraft-Web-Toolkit
One signif icant limitation of viewing inference on Arduino IDE is that we can not see what
the camera focuses on. A good alternative is the SenseCraft-Web-Toolkit, a visual model
deployment tool provided by SSCMA(Seeed SenseCraft Model Assistant). This tool allows you
to deploy models to various platforms easily through simple operations. The tool offers a user-
friendly interface and does not require any coding.
Follow the following steps to start the SenseCraft-Web-Toolkit:
1. Open the SenseCraft-Web-Toolkit website.
2. Connect the XIAO to your computer:
• Having the XIAO connected, select it as below:
You can try several Computer Vision models previously uploaded by Seeed Studio. Try them
and have fun!
5. On SenseCraft-Web-Toolkit, use the blue button at the bottom of the page: [Upload Custom
AI Model]. A window will pop up. Enter the Model file that you downloaded to your computer
from Edge Impulse Studio, choose a Model Name, and enter with labels (ID: Object):
Note that you should use the labels trained on EI Studio, entering them in alphabetic order
(in our case: apple, banana, potato).
After a few seconds (or minutes), the model will be uploaded to your device, and the camera
image will appear in real-time on the Preview Sector:
254 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
The Classification result will be at the top of the image. You can also select the Confidence of
your inference cursor Confidence.
Clicking on the top button (Device Log), you can open a Serial Monitor to follow the inference,
the same that we have done with the Arduino IDE:
4.4.14 Conclusion
The XIAO ESP32S3 Sense is very flexible, inexpensive, and easy to program. The project proves
the potential of TinyML. Memory is not an issue; the device can handle many post-processing
tasks, including communication.
You will find the last version of the codes on the GitHub repository: XIAO-ESP32S3-Sense.
256 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
4.5
Object Detection
• Arduino IDE
4.5.2 Introduction
In the last section regarding Computer Vision (CV) and the XIAO ESP32S3, Image Classification,
we learned how to set up and classify images with this remarkable development board.
Continuing our CV journey, we will explore Object Detection on microcontrollers.
The model used in the previous example is the MobileNet, trained with a large dataset, the
ImageNet, running on a Raspberry Pi.
To solve this issue, we need another type of model, where not only multiple categories (or
labels) can be found but also where the objects are located on a given image.
As we can imagine, such models are much more complicated
and bigger, for example, the MobileNetV2 SSD FPN-Lite
320x320, trained with the COCO dataset. This pre-trained
object detection model is designed to locate up to 10 objects
within an image, outputting a bounding box for each object
detected. The below image is the result of such a model
running on a Raspberry Pi:
Those models used for object detection (such as the MobileNet SSD or YOLO) usually have
several MB in size, which is OK for use with Raspberry Pi but unsuitable for use with embedded
devices, where the RAM usually is lower than 1M Bytes or at least a few MB as in the case of the
XIAO ESP32S3.
To understand more about FOMO, you can go into the official FOMO announcement by
Edge Impulse, where Louis Moreau and Mat Kelcey explain in detail how it works.
258 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
We are interested in which object is in the image, its location (centroid), and how many we can
find on it. The object’s size is not detected with FOMO, as with MobileNet SSD or YOLO, where
the Bounding Box is one of the model outputs.
We will develop the project using the XIAO ESP32S3 for image capture and model inference.
The ML project will be developed using the Edge Impulse Studio. But before starting the object
detection project in the Studio, let’s create a raw dataset (not labeled) with images that contain
the objects to be detected.
Data Collection
You can use the XIAO, your phone, or other devices for the image capture. Here, we will use the
XIAO with a code in the ESP32 library.
- Attention -
Alpha versions (for example, 3.x-alpha) do not work correctly with the XIAO and Edge
Impulse. Use the last stable version (for example, 2.0.11) instead.
You also should comment on all cameras’ models, except the XIAO model pins:
#define CAMERA_MODEL_XIAO_ESP32S3 // Has PSRAM
And on Tools, enable the PSRAM. Enter your wifi credentials and upload the code to the device:
If the code is executed correctly, you should see the address on the Serial Monitor:
Edge impulse suggests that the objects should be of similar size and not overlapping for better
performance. This is OK in an industrial facility, where the camera should be fixed, keeping the
same distance from the objects to be detected. Despite that, we will also try using mixed sizes
and positions to see the result.
We do not need to create separate folders for our images because each contains multiple
labels.
We suggest around 50 images mixing the objects and varying the number of each appearing
on the scene. Try to capture different angles, backgrounds, and light conditions.
The stored images use a QVGA frame size 320x240 and RGB565 (color pixel format).
After capturing your dataset, [Stop Stream] and move your images to a folder.
260 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Here, you can clone the project developed for this hands-on: XIAO-ESP32S3-Sense-Object_
Detection
On your Project Dashboard, go down and on Project info and select Bounding boxes (object
detection) and Espressif ESP-EYE (most similar to our board) as your Target Device:
All the not-labeled images (47) were uploaded but must be labeled appropriately before being
used as a project dataset. The Studio has a tool for that purpose, which you can find in the link
Labeling queue (47).
There are two ways you can use to perform AI-assisted labeling on the Edge Impulse Studio (free
version):
• Using yolov5
• Tracking objects between frames
262 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Edge Impulse launched an auto-labeling feature for Enterprise customers, easing labeling
tasks in object detection projects.
Ordinary objects can quickly be identified and labeled using an existing library of pre-trained
object detection models from YOLOv5 (trained with the COCO dataset). But since, in our case,
the objects are not part of COCO datasets, we should select the option of tracking objects. With
this option, once you draw bounding boxes and label the images in one frame, the objects
will be tracked automatically from frame to frame, partially labeling the new ones (not all are
correctly labeled).
You can use the EI uploader to import your data if you already have a labeled dataset
containing bounding boxes.
Continue with this process until the queue is empty. At the end, all images should have the
objects labeled as those samples below:
Next, review the labeled samples on the Data acquisition tab. If one of the labels is wrong, you
can edit it using the three dots menu after the sample name:
264 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
4.5.5 The Impulse Design
In this phase, you should define how to:
• Pre-processing consists of resizing the individual images from 320 x 240 to 96 x 96 and
squashing them (squared form, without cropping). Afterward, the images are converted
from RGB to Grayscale.
• Design a Model, in this case, “Object Detection.”
The feature explorer shows that all samples evidence a good separation after the feature
generation.
Some samples seem to be in the wrong space, but clicking on them confirms the correct
labeling.
266 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
4.5.6 Model Design, Training, and Test
We will use FOMO, an object detection model based on MobileNetV2 (alpha 0.35) designed to
coarsely segment an image into a grid of background vs objects of interest (here, boxes and
wheels).
FOMO is an innovative machine learning model for object detection, which can use up to 30
times less energy and memory than traditional models like Mobilenet SSD and YOLOv5. FOMO
can operate on microcontrollers with less than 200 KB of RAM. The main reason this is possible
is that while other models calculate the object’s size by drawing a square around it (bounding
box), FOMO ignores the size of the image, providing only the information about where the
object is located in the image through its centroid coordinates.
Note that FOMO automatically added a 3rd label background to the two previously defined
(box and wheel).
In object detection tasks, accuracy is generally not the primary evaluation metric. Object
detection involves classifying objects and providing bounding boxes around them, making
it a more complex problem than simple classification. The issue is that we do not have the
bounding box, only the centroids. In short, using accuracy as a metric could be misleading
and may not provide a complete understanding of how well the model is performing.
Because of that, we will use the F1 score.
268 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Once connected, you can use the smartphone to capture actual images to be tested by the
trained model on Edge Impulse Studio.
One thing to be noted is that the model can produce false positives and negatives. This can be
minimized by defining a proper Confidence Threshold (use the Three dots menu for the setup).
Try with 0.8 or more.
Under the Examples tab on Arduino IDE, you should f ind a sketch code (esp32 > esp32_
camera) under your project name.
270 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
You should change lines 32 to 75, which define the camera model and pins, using the data
related to our model. Copy and paste the below lines, replacing the lines 32-75:
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 10
#define SIOD_GPIO_NUM 40
#define SIOC_GPIO_NUM 39
#define Y9_GPIO_NUM 48
#define Y8_GPIO_NUM 11
#define Y7_GPIO_NUM 12
#define Y6_GPIO_NUM 14
#define Y5_GPIO_NUM 16
#define Y4_GPIO_NUM 18
#define Y3_GPIO_NUM 17
#define Y2_GPIO_NUM 15
#define VSYNC_GPIO_NUM 38
#define HREF_GPIO_NUM 47
#define PCLK_GPIO_NUM 13
Fruits
Bugs
272 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Note that the model latency is 143ms, and the frame rate per second is around 7 fps (similar to
what we got with the Image Classification project). This happens because FOMO is cleverly built
over a CNN model, not with an object detection model like the SSD MobileNet. For example,
when running a MobileNetV2 SSD FPN-Lite 320x320 model on a Raspberry Pi 4, the latency is
around five times higher (around 1.5 fps).
5. On SenseCraft-Web-Toolkit, use the blue button at the bottom of the page: [Upload
Custom AI Model]. A window will pop up. Enter the Model file that you downloaded to
your computer from Edge Impulse Studio, choose a Model Name, and enter with labels (ID:
Object):
274 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Note that you should use the labels trained on EI Studio, entering them in alphabetic order
(in our case: background, bug, fruit).
After a few seconds (or minutes), the model will be uploaded to your device, and the camera
image will appear in real-time on the Preview Sector:
The detected objects will be marked (the centroid). You can select the Conf idence of your
inference cursor Conf idence. and IoU, which is used to assess the accuracy of predicted
bounding boxes compared to truth bounding boxes
Clicking on the top button (Device Log), you can open a Serial Monitor to follow the inference, as
we did with the Arduino IDE.
4.5.9 Conclusion
FOMO is a significant leap in the image processing space, as Louis Moreau and Mat Kelcey put it
during its launch in 2022:
FOMO is a ground-breaking algorithm that brings real-time object detection, tracking, and
counting to microcontrollers for the first time.
Multiple possibilities exist for exploring object detection (and, more precisely, counting them) on
embedded devices.
276 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
4.6
To learn more
This section contains links to courses, books, and projects to learn more about Machine Learning
and TinyML applications.
Online Courses
Harvard School of Engineering and Applied Sciences - CS249r: Tiny Machine Learning
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
UNIFEI-IESTI01 TinyML: “Machine Learning for Embedding Devices”
Books
“Python for Data Analysis by Wes McKinney”
“Deep Learning with Python” by François Chollet - GitHub Notebooks
“TinyML” by Pete Warden, Daniel Situnayake
“TinyML Cookbook” by Gian Marco Iodice
“Technical Strategy for AI Engineers, In the Era of Deep Learning” by Andrew Ng
“AI at the Edge” book by Daniel Situnayake, Jenny Plunkett
“MACHINE LEARNING SYSTEMS for TinyML” Collaborative effort
Projects Repositories
Edge Impulse Expert Network
MRovai XIAO ESP32S3 Movement/Sound/Image
Since its launch, the Seeed Studio XIAO series has been widely acclaimed for its
compact size, powerful performance, and versatile product range. The maker
community has produced a large number of projects created with XIAO. Due to space
constraints, we have selected some outstanding projects made with XIAO by our
makers. These projects fully demonstrate the powerful functions and wide applications
of XIAO. Let us follow the makers’ steps, stimulate creativity, and explore the endless
possibilities of XIAO. We hope you can draw inspiration from these projects, use your
imagination, and explore new territories with XIAO.
278 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
5.1
Creative and useful XIAO projects
After going through the sections in this book, you might have many novel ideas that you can’t
wait to implement. But before you rush into it, let’s take a look at what interesting stuff others
have done with XIAO. For this, we have collected some user-made project cases using XIAO
primarily from the globally renowned innovators’ communities like hackster.io and instructables,
to help you see more possibilities of XIAO.
280 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
5.1.3 Bicycle Computer on Spresense
https://www.hackster.io/jens6151/bicycle-computer-on-spresense-b0e332
Author: Jens
The goal of this project is to build a bike computer using the Sony Spresense main board, LTE
expansion board, XIAO, and other peripherals. The main features include:
1. Capture a low-resolution video stream and display it on a monitor. Option to take high-
resolution photos and store them on an SD card.
2. Capture mono audio, using the OPUS codec and OGG container format for high
compression, to be sent or recorded to SD card via an LTE-M connection.
3. Track location via GNSS, combining the location with weather data and points of interest (POI)
data received from cloud services via an LTE connection.
4. Connect bike sensors (currently heart rate) via Bluetooth Low Energy, display data on the
monitor, and record.
5. Remote access to the camera, real-
time audio stream, and various data
(including location) via MQTT.
6. Theft detection and notif ication via
GNSS geofencing, accelerometer, and
monitoring for nearby smartphones.
This project by Jens demonstrates the
astonishing complexity of a hardcore
prototype project, as can be seen from the
schematic on the right.
282 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
This project repurposes an old hoverboard into a remote-controlled robot equipped with
a hydrogen sensor for early detection of hydrogen leaks. It uses Bluetooth to connect the
Seedstudio Xiao Ble Sense, MQ-8 gas sensor, and other devices, and uses Edge Impulse Studio
to train a machine learning model. The robot also uses the Blues Wireless Notecard NBGL
cellular connection technology to upload data to the cloud. With Remo.TV, it can be remotely
operated to drive the robot and view real-time camera feeds through a browser.
5.1.8 Pet Activity Tracker using XIAO BLE Sense & Edge
Impulse
https://www.hackster.io/mithun-das/pet-activity-tracker-using-xiao-ble-
sense-edge-impulse-858d73
Author: Mithun Das
284 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
This project is a wearable device that tracks pet activities using XIAO BLE Sense and Edge
Impulse, aimed at helping our pets stay active. The XIAO BLE Sense is a mini controller equipped
with a powerful Nordic nRF52840 MCU, built-in Bluetooth 5.0 module, and designed around a
32-bit ARM® Cortex™-M4 CPU. It features a 6-axis IMU that can be used to predict activities such
as rest, walking, and running.
With the accompanying smartphone app,
users can connect to the device via Bluetooth
and obtain minute-by-minute prediction data.
The data is stored in the smartphone’s local
storage and presented graphically to provide
meaningful insights.
The project collects data via the EI Blue mobile
app, creates machine learning models using
Edge Impulse Studio, and builds an iOS app
using Google Flutter. The whole system can
monitor the pet’s activity status in real-time
and view the data through the mobile app.
286 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
4. Use a perforated board to place components
and solder. First, place resistors and the
female pins of XIAO, then solder the ECG
sensor. Finally, cut the perforated board to
the required size.
6. When connecting the LED, use a perforated board to connect all cathodes and place a
ground connector. After all connections are completed, it is necessary to check whether VCC
is isolated from the ground and perform a test.
7. Not everything can go as expected. During the connection check, the fixture exerted too
much force, causing the perforated board to break. It had to be redone.
8. Finally, it’s time to connect the battery and isolate all circuits to avoid short circuits. Usually,
heat-shrink tubing would be used here, but if there is no suitable size, hot glue can also work.
288 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
5.1.12 DIY eurorack modular synth Raspberry Pi VCO with
Seeed XIAO
https://www.hackster.io/hagiwo/diy-eurorack-modular-synth-rasberry-pi-vco-with-seeed-xiao-
133ac0
Author: HAGIWO/ ハギヲ
A maker from Japan, HAGIWO / ハ ギ ヲ , used
the Seeed XIAO RP2040 development board
to create a Voltage-Controlled Oscillator (VCO)
module for a Eurorack modular synthesizer.
T h i s b o a rd h a s a R a s p b e r r y Pi R P 2 0 4 0
microcontroller, 4 AD converters, and is easier
to use than the Raspberry Pi Pico. The VCO
module has three modes: Wavefold, FM, and
AM, with eight built-in waveforms, costing only
about 1100 yen.
290 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
5.1.16 HackerBox 0077: Veritas
https://www.instructables.com/HackerBox-0077-Veritas/
Author: HackerBoxes
This project teaches you how to make a simple lie detector. It involves configuring the Seeeduino
XIAO microcontroller module, modifying the OLED module to achieve dual display operation
with a single microcontroller, assembling a Galvanic Skin Response (GSR) sensor based on an
operational amplifier, and integrating a heart rate sensor. XIAO acts as the core controller in the
project, realizing data collection, processing, and display.
292 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
Magnetic Attachment: After analyzing pain points, it was decided to use magnets to attach the
product to places where interaction and operation are more easily realized.
1st Prize:
TOTEM | a tiny splitkeyboard with splay
(2x)19 key ergo split: 3-key thumb cluster, pinky splay, low profile. Useful repo and classy, unique
case. Nicely documented and open source. And it’s a usable keyboard, which could be used as a
daily driver. Other than that, Marc took a great effort to present his design aesthetically
https://www.hackster.io/geist/totem-a-tiny-splitkeyboard-with-splay-cb2e43
Author: Marc Rühl
2nd Prize:
Beyblock20 | a magnetic, modular
MacroPad
https://github.com/ChrisChrisLoLo/beyblock20
Author: Christian Lo
294 XIAO: Big Power, Small Board - Mastering Arduino and TinyML
3rd Prize:
KLEIN | a wireless ergonomical
keyboard
https://www.hackster.io/nosnk/
klein-a-wireless-ergonomical-
keyboard-b4cd9a
Author: Shashank
Marcelo Rovai is a recognized figure in engineering and technology education, holding the
title of Professor Honoris Causa from the Federal University of Itajubá, Brazil. His educational
background includes an Engineering degree from UNIFEI and an advanced specialization from
the Polytechnic School of São Paulo University. Further enhancing his expertise, he earned an
MBA from IBMEC (INSPER) and a Master’s in Data Science from the Universidad del Desarrollo
in Chile.
With a career spanning several high-profile technology companies such as AVIBRAS Airspace,
ATT, NCR, and IGT, where he served as Vice President for Latin America, he brings a wealth of
industry experience to his academic endeavors. He is a prolific writer on electronics-related
topics and shares his knowledge through open platforms like Hackster.io.
296 XIAO: Big Power, Small Board - Mastering Arduino and TinyML