Nicla Voice User Manual

Learn about the hardware and software features of the Arduino® Nicla Voice.

Overview

This user manual will provide you with a comprehensive overview of the Arduino Nicla Voice board, covering its main hardware and software features. This user manual will also show you how to set up, configure, and use these features.

Hardware and Software Requirements

Hardware Requirements

Software Requirements

Product Overview

The Nicla Voice is an innovative and versatile development board designed by the Arduino team for sound-enabled projects and applications. This board has an onboard always-on speech recognition and sensor-fusion processor, advanced motion sensors, and wireless connectivity via Bluetooth® Low Energy. The Nicla Voice is an ideal solution for various applications, from ultra-low power predictive maintenance and gesture or voice recognition systems to contactless wireless applications.

Board Architecture Overview

The Nicla Voice features a robust and efficient architecture integrating various components to enable speech, sound, and motion projects and applications.

The Nicla Voice main components (top view)
The Nicla Voice main components (top view)
The Nicla Voice main components (bottom view)
The Nicla Voice main components (bottom view)

Here is an overview of the board's architecture's main components shown in the images above:

  • Microcontroller: at the heart of the Nicla Voice is the nRF52832, a powerful and versatile System-on-Chip (SoC) from Nordic® Semiconductor. The nRF52832 is built around a 32-bit Arm® Cortex®-M4 processor running at 64 MHz.
  • Speech and sensor-fusion processor: the board features the NDP120 Neural Decision Processor™, an ultra-low power always-on audio and sensor-fusion processor from Syntiant®, which enables several applications, including echo-cancellation, beamforming, noise suppression, speech enhancement, speaker identification, keyword spotting, acoustic event and scene classification, and multi-sensor fusion.
  • Onboard advanced motion sensors: the board features the BMI270, a high-precision Inertial Measurement Unit (IMU) from Bosch® Sensortec, which combines a 3-axis accelerometer and a 3-axis gyroscope for precise motion tracking and orientation detection. The board also features the BMM150, a compact geomagnetic sensor from Bosch® Sensortec with a 3-axis magnetometer.
  • Onboard high-performance microphone: the Nicla Voice is equipped with the IM69D130, a high-quality MEMS microphone by Infineon® Technologies. The IM69D130 offers excellent audio quality and low noise performance, ensuring accurate and distortion-free audio capturing.
  • Onboard connector for external PDM microphone: an external PDM microphone can be connected to the board via an onboard connector.
  • Wireless connectivity: the board supports Bluetooth® Low Energy connectivity, provided by the ANNA-B112 module developed by u-blox®. This compact, high-performance Bluetooth® Low Energy module allows the Nicla Voice to communicate wirelessly with other devices and systems.
  • Power management: the Nicla Voice is designed for ultra-low power operation, with efficient power management features that ensure minimal energy consumption even when using always-on speech recognition and multiple sensors. The Nicla Voice features the BQ25120 from Texas Instruments®; a highly integrated battery charge management integrated circuit (IC) designed for wearables and Internet of Things (IoT) devices.

Board Core and Libraries

The Arduino Mbed OS Nicla Boards core contains the libraries and examples you need to work with the board's components, such as its IMU, magnetometer, and onboard microphone. To install the core for Nicla boards, navigate to Tools > Board > Boards Manager or click the Boards Manager icon in the left tab of the IDE. In the Boards Manager tab, search for

nicla
and install the latest
Arduino Mbed OS Nicla Boards
version.

Installing the Arduino Mbed OS Nicla Boards core in the Arduino IDE bootloader
Installing the Arduino Mbed OS Nicla Boards core in the Arduino IDE bootloader

Pinout

Nicla Voice pinout
Nicla Voice pinout

The full pinout is available and downloadable as PDF from the link below:

Datasheet

The complete datasheet is available and downloadable as PDF from the link below:

Schematics

The complete schematics are available and downloadable as PDF from the link below:

STEP Files

The complete STEP files are available and downloadable from the link below:

First Use

Powering the Board

The Nicla voice can be powered by:

  • Using a Micro USB cable (not included).
  • Using an external 5V power supply connected to
    VIN_BQ25120
    pin (please refer to the board pinout section of the user manual).
  • Using a 3.7V Lithium Polymer (Li-Po) battery connected to the board through the onboard battery connector; the manufacturer part number of the battery connector is BM03B-ACHSS and its matching receptacle manufacturer part number is ACHR-03V-S. The recommended minimum battery capacity for the Nicla Voice is 200 mAh. A Li-Po battery with an integrated NTC thermistor is also recommended for thermal protection.
  • Using the onboard ESLOV connector, which has a dedicated 5V power line.
Different ways to power the Nicla Voice
Different ways to power the Nicla Voice

A 3.7V Li-Po battery can be also connected through the board's pins:

1 (NTC)
,
2 (VBAT)
, and
6 (GND)
. Please refer to the board's pinout to locate those pins on your Nicla Voice board.

Onboard Battery Charger


The onboard battery charger of your board is, by default, disabled. To enable it, you can use the

enableCharging()
function defined in the Nicla Voice board core:

1// Enable the onboard battery charger
2// The function parameter defines the charging current in mA (between 5 mA and 300 mA)
3nicla::enableCharging(100);

The desired charging current can be set to a value between 5 mA and 300 mA; the default value is 20 mA.

A safe default charging current value that works for most common LiPo batteries is 0.5C, which means charging at a rate equal to half the battery's capacity. For example, a 200 mAh battery could be safely charged at 100 mA (0.1 A).

To disable the onboard battery charger, you can use the

disableCharging()
function defined in the Nicla Voice board core:

1// Disable the onboard battery charger
2nicla::disableCharging();

NDP120 Processor Firmware Update

It is recommended to update the NDP120 processor firmware and the built-in speech recognition model to the latest release. Follow these three steps to complete the update process:

  1. Upload the

    Syntiant_upload_fw_ymodem
    sketch. This sketch can be found in the board's built-in examples by navigating to File -> Examples -> NDP -> Syntiant_upload_fw_ymodem. Remember to select the board in the Arduino IDE first before navigating to the examples.

  2. After uploading the sketch, format your board's external Flash memory before uploading the updated NDP120 processor firmwares files. You can do this by navigating to the Arduino IDE Serial Monitor and typing

    F
    and then Enter.

  3. Extract this .zip file, which contains the compiled uploaders for various operating systems, and the updated NDP120 processor firmware and speech recognition model, in a known location on your computer.

  4. Open a new terminal in the location where the .zip file was extracted and execute the following command:

    1syntiant-uploader send -m "Y" -w "Y" -p $portName $filename

    Replace

    portName
    and
    filename
    with the relevant information. Three different files must be uploaded to the board by executing the following three commands, for example in Windows the commands are the following:

    1./syntiant-uploader send -m "Y" -w "Y" -p COM6 mcu_fw_120_v91.synpkg
    1./syntiant-uploader send -m "Y" -w "Y" -p COM6 dsp_firmware_v91.synpkg
    1./syntiant-uploader send -m "Y" -w "Y" -p COM6 model_name.synpkg

    Ensure all executed commands return a

    filename sent successful
    message in the console, as shown in the image below.

    Uploader feedback messages
    Uploader feedback messages

After uploading the three files, your board's firmware is updated to the latest release and ready to be used.

External Memory Format


Your board NDP120 processor files (firmware and models) are stored in your board's external Flash memory. It is recommended to format your Nicla Voice external Flash memory every time you are going to update the processor firmware or when you are going to update/add models to the external Flash memory.

Follow these steps to perform the external memory format process:

  1. Upload the
    Syntiant_upload_fw_ymodem
    sketch. This sketch can be found in the board's built-in examples by navigating to File -> Examples -> NDP -> Syntiant_upload_fw_ymodem. Remember to select the board in the Arduino IDE first before navigating to the examples.
  2. After uploading the sketch, navigate to the IDE's Serial Monitor, type
    F
    , and press
    Enter
    . Your board's external memory should be formatted now, you can confirm this by typing an
    L
    and pressing
    Enter
    .

After completing this process, you can upload NDP processor's firmware and model files to your board's external memory without issues as explained before.

Built-in Speech Recognition Example

The speech recognition example can be found in the board's built-in examples by navigating to File -> Examples -> NDP -> AlexaDemo. After successfully updating the NDP120 processor firmware and the speech recognition model to the latest release, we can upload the speech recognition example to the Nicla Voice. To test the example, say "Alexa"; this should make the onboard LED of the Nicla Voice blink blue if the keyword "Alexa" is recognized. If there is no response from the board, try speaking from a closer proximity or louder. You should also see in the Serial Monitor if the word "Alexa" was detected, as shown in the image below:

AlexaDemo example feedback in the Arduino IDE Serial Monitor
AlexaDemo example feedback in the Arduino IDE Serial Monitor

Pins

The Nicla Voice pinout is shown in the image below:

Nicla Voice pinout
Nicla Voice pinout

Analog Pins

The Nicla Voice has two analog input pins, mapped as follows:

Microcontroller PinArduino Pin Mapping
ADC1
/
P0_02
A0
ADC2
/
P0_30
A1

Both pins can be used through the built-in functions of the Arduino programming language. The example code shown below reads the voltage value from a potentiometer connected to

A0
and displays it on the IDE Serial Monitor:

1// Define the potentiometer pin and variable to store its value
2int potentiometerPin = A0;
3int potentiometerValue = 0;
4
5void setup() {
6 // Initialize Serial communication
7 Serial1.begin(9600);
8}
9
10void loop() {
11 // Read the voltage value from the potentiometer
12 potentiometerValue = analogRead(potentiometerPin);
13
14 // Print the potentiometer voltage value to the Serial Monitor
15 Serial1.print("- Potentiometer voltage value: ");
16 Serial1.println(potentiometerValue);
17
18 // Wait for 1000 milliseconds
19 delay(1000);
20}

Digital Pins

The Nicla Voice has twelve digital pins, mapped as follows:

Microcontroller PinArduino Pin Mapping
P0_10
0
P0_09
1
P0_20
2
P0_23
3
P0_22
4
P0_24
5
P0_29
6
P0_27
7
P0_28
8
P0_11
9
P0_02
A0
P0_30
A1

Notice that analog pins

A0
and
A1
(
P0_02
and
P0_30
) can also be used as digital pins. Please, refer to the board pinout section of the user manual to find them on the board.

The digital pins of the Nicla Voice can be used as inputs or outputs through the built-in functions of the Arduino programming language. The configuration of a digital pin is done in the

setup()
function with the built-in function
pinMode()
as shown below:

1// Pin configured as an input
2pinMode(pin, INPUT);
3
4// Pin configured as an output
5pinMode(pin, OUTPUT);
6
7// Pin configured as an input, internal pull-up resistor enabled
8pinMode(pin, INPUT_PULLUP);

The state of a digital pin, configured as an input, can be read using the built-in function

digitalRead()
as shown below:

1// Reads pin state, stores value in state variable
2state = digitalRead(pin);

The state of a digital pin, configured as an output, can be changed using the built-in function

digitalWrite()
as shown below:

1// Set pin on
2digitalWrite(pin, HIGH);
3
4// Set pin off
5digitalWrite(pin, LOW);

The example code shown below uses digital pin

3
to control an LED and reads the state of a button connected to digital pin
2
:

1// Define button and LED pin
2int buttonPin = 2;
3int ledPin = 3;
4
5// Variable to store the button state
6int buttonState = 0;
7
8void setup() {
9 // Configure button and LED pins
10 pinMode(buttonPin, INPUT_PULLUP);
11 pinMode(ledPin, OUTPUT);
12
13 // Initialize Serial communication
14 Serial1.begin(9600);
15}
16
17void loop() {
18 // Read the state of the button
19 buttonState = digitalRead(buttonPin);
20
21 // If the button is pressed, turn on the LED and print its state to the Serial Monitor
22 if (buttonState == LOW) {
23 digitalWrite(ledPin, HIGH);
24 Serial1.println("- Button is pressed. LED is on.");
25 } else {
26 // If the button is not pressed, turn off the LED and print to the Serial Monitor
27 digitalWrite(ledPin, LOW);
28 Serial1.println("- Button is not pressed. LED is off.");
29 }
30
31 // Wait for 1000 milliseconds
32 delay(1000);
33}

PWM Pins

Most digital and analog pins of the Nicla Voice can be used as PWM (Pulse Width Modulation) pins. This functionality of the Nicla Voice pins can be used with the built-in function

analogWrite()
as shown below:

1analogWrite(pin, value);

The example code shown below uses digital pin

9
PWM functionality to control the brightness of an LED connected to it:

1// Define the LED pin, brightness, and fade amount variables
2int ledPin = 9;
3int brightness = 0;
4int fadeAmount = 5;
5
6void setup() {
7 // Configure the LED pin as an output
8 pinMode(ledPin, OUTPUT);
9}
10
11void loop() {
12 // Set the brightness of the LED
13 analogWrite(ledPin, brightness);
14
15 // Update the brightness value
16 brightness += fadeAmount;
17
18 // Reverse the fade direction when reaching the limits
19 if (brightness <= 0 || brightness >= 255) {
20 fadeAmount = -fadeAmount;
21 }
22
23 // Wait for 30 milliseconds
24 delay(30);
25}

Onboard Sensors

The Nicla Voice boards come with various onboard sensors that allow you to capture and process environmental and motion data via a high-performance microphone, a 6-axis IMU, and a 3-axis magnetometer. The onboard sensors can be used for developing various applications, such as voice recognition, gesture control, and environmental monitoring.

Microphone

The onboard high-performance microphone of the Nicla Voice is the IM69D130 from Infineon® Technologies. The IM69D130 is specifically designed for applications that require high-quality audio recording and accurate voice detection, such as voice-controlled Internet of Things (IoT) devices, smart home systems, and mobile devices.

Nicla Voice onboard high-performance microphone
Nicla Voice onboard high-performance microphone

An external PDM microphone can be connected to the board via an onboard Zero Insertion Force (ZIF) connector. The manufacturer part number of the onboard ZIF connector is FH33J-4S-0.5SH(99); a 4-pin 0.5 mm pitch FFC cable is compatible with this connector.

Nicla Voice onboard ZIF connector
Nicla Voice onboard ZIF connector

The example code shown below captures audio from the onboard microphone of the Nicla Voice, compresses the audio using the G722 codec, and streams the compressed audio data to the serial port. The example can be found in the board's built-in examples by navigating to File -> Examples -> NDP -> Record_and_stream.

Keep in mind that this example code requires the following libraries:

Make sure to install these libraries in the Arduino IDE before uploading the code to your Nicla Voice board.

1// Include necessary libraries for the Nicla Voice board, audio processing, and G722 codec support
2#include "Arduino.h"
3#include "NDP.h"
4#include "AudioTools.h"
5#include "AudioCodecs/CodecG722.h"
6
7// Create an instance of the G722Encoder class for handling audio encoding
8G722Encoder encoder;
9
10// Declare a buffer to temporarily store audio data
11uint8_t data[2048];
12
13// Define a function to turn on the green LED for a short duration as an event indicator
14void ledGreenOn() {
15 nicla::leds.begin();
16 nicla::leds.setColor(green);
17 delay(200);
18 nicla::leds.setColor(off);
19 nicla::leds.end();
20}
21
22void setup() {
23 // Start UART communication at 115200 baud
24 Serial.begin(115200);
25
26 // Initialize the Nicla Voice board, disable the LDO
27 nicla::begin();
28 nicla::disableLDO();
29
30 // Initialize the built-in RGB LED, set up as an event indicator
31 nicla::leds.begin();
32 NDP.onEvent(ledGreenOn);
33
34 // Set up the G722 encoder (1 channel, 16 kHz sample rate)
35 AudioBaseInfo bi;
36 bi.channels = 1;
37 bi.sample_rate = 16000;
38 encoder.setOptions(0);
39 encoder.begin(bi);
40
41 // Set the output stream for the encoder to the serial port
42 encoder.setOutputStream(Serial);
43
44 // Load the required firmware packages for the NDP processor, turn on the onboard microphone
45 NDP.begin("mcu_fw_120_v91.synpkg");
46 NDP.load("dsp_firmware_v91.synpkg");
47 NDP.load("alexa_334_NDP120_B0_v11_v91.synpkg");
48 NDP.turnOnMicrophone();
49
50 // Check the audio chunk size to ensure it doesn't exceed the buffer size
51 int chunk_size = NDP.getAudioChunkSize();
52 if (chunk_size >= sizeof(data)) {
53 for(;;);
54 }
55}
56
57// Continuously read audio data from the microphone, encode it using the G722 codec, send it to the serial port
58void loop() {
59 // Declare a variable to store the length of the extracted audio data
60 unsigned int len = 0;
61
62 // Extract audio data from the NDP and store it in the data buffer
63 NDP.extractData(data, &len);
64
65 // Pass the extracted audio data to the G722 encoder and send it to the serial port
66 encoder.write(data, len);
67}

Here you can find a step-by-step explanation of the code:

First, the necessary libraries are included:

  • Arduino.h
    and
    NDP.h
    for the Nicla Voice board's basic functions and microphone control;
    AudioTools.h
    and
    CodecG722.h
    for audio processing and G722 codec support.
  • An instance of the
    G722Encoder
    class is created to handle audio encoding.
  • The
    data
    buffer is declared with a size of 2048 bytes to store audio data temporarily.

In the

setup()
function:

  • The serial communication is initialized at a baud rate of 115200.
  • The Nicla Voice board is initialized, and the LDO is disabled.
  • The green LED is configured to turn on when an event occurs.
  • The
    G722Encoder
    instance is set up with the proper audio parameters (1 channel, 16 kHz sample rate).
  • The output stream for the encoder is set to the serial port.
  • The NDP Processor is set up with the necessary firmware packages and the onboard microphone is turned on.
  • The audio chunk size is checked to ensure it doesn't exceed the buffer size.

In the

loop()
function:

  • Audio data is extracted from the NDP processor and stored in the
    data
    buffer.
  • The length of the extracted audio data is stored in the
    len
    variable.
  • The extracted audio data is passed to the G722 encoder, which compresses the audio and sends it to the serial port.

To extract the audio data on a computer, you will need to set up the serial port as raw and dump the data to a file (e.g., test.g722). Then, you can open the file with a software like Audacity to play back the audio.

Machine Learning and Audio Analysis

You can use the Nicla Voice and the Machine Learning Tools of the Arduino Cloud to create your own audio analysis Machine Learning models. Check out this tutorial and start with Machine Learning with the Nicla Voice.

IMU and Magnetometer

The Nicla Voice features an advanced IMU and a magnetometer, which allows the board to sense motion, orientation, and magnetic fields. The IMU on the Nicla Voice board is the BMI270 from Bosch®. It consists of a 3-axis accelerometer and a 3-axis gyroscope. They can provide information about the board's motion, orientation, and rotation in a 3D space. The BMI270 is designed for wearables and offers low power consumption and high performance, making it suitable for various applications, such as gesture recognition, motion tracking, or stabilization.

Nicla Voice onboard IMU
Nicla Voice onboard IMU

The onboard magnetometer of the Nicla Voice can be used to determine the board's orientation relative to Earth's magnetic field, which is helpful for compass applications, navigation, or detecting the presence of nearby magnetic objects. The magnetometer on the Nicla Voice board is the BMM150, also from Bosch®. It is a 3-axis sensor that measures the strength and direction of magnetic fields surrounding the board.

Nicla Voice onboard magnetometer
Nicla Voice onboard magnetometer

Accelerometer and Gyroscope Data

The example sketch below shows how to get acceleration (m/s2) and angular velocity (in °/s) data from the onboard IMU and streams it to the Arduino IDE Serial Monitor and Serial Plotter. The sketch needs the

BMI270_Init.h
header file to be in the same directory as the sketch. You can download the example sketch and the header files here.

1/**
2 Nicla Voice accelerometer and gyroscope test sketch
3 Name: nv_acc_gyro_test.ino
4 Purpose: Sketch tests onboard accelerometer and gyroscope (BMI270)
5
6 @author Arduino PRO Content Team
7 @version 1.0 22/05/23
8*/
9
10#include "NDP.h"
11#include "BMI270_Init.h"
12
13// Named constants
14#define READ_START_ADDRESS 0x0C
15#define READ_BYTE_COUNT 16
16#define SENSOR_DATA_LENGTH 16
17
18// Accelerometer range is set to +/-2g
19// Raw accelerometer data is represented as a signed 16-bit integer
20// Raw accelerometer data can be converted to acceleration in m/s^2 units using the following scale factor:
21#define ACCEL_SCALE_FACTOR ((2.0 / 32767.0) * 9.8)
22
23// Gyroscope has a sensitivity of 16.4 LSB/dps
24#define GYRO_SCALE_FACTOR (1 / 16.4)
25
26/**
27 Turns on and off the onboard blue LED.
28
29 @param label to be printed on the Serial Monitor.
30*/
31void ledBlueOn(char* label) {
32 nicla::leds.begin();
33 nicla::leds.setColor(blue);
34 delay(200);
35 nicla::leds.setColor(off);
36 Serial.println(label);
37 nicla::leds.end();
38}
39
40/**
41 Turns on and off the onboard green LED.
42*/
43void ledGreenOn() {
44 nicla::leds.begin();
45 nicla::leds.setColor(green);
46 delay(200);
47 nicla::leds.setColor(off);
48 nicla::leds.end();
49}
50
51/**
52 Blinks onboard red LED periodically every 200 ms.
53*/
54void ledRedBlink() {
55 while (1) {
56 nicla::leds.begin();
57 nicla::leds.setColor(red);
58 delay(200);
59 nicla::leds.setColor(off);
60 delay(200);
61 nicla::leds.end();
62 }
63}
64
65// Macros for checking the sensor status.
66#define CHECK_STATUS(s) do {if (s) {Serial.print("SPI access error in line "); Serial.println(__LINE__); for(;;);}} while (0)
67
68void setup() {
69 int status;
70 uint8_t __attribute__((aligned(4))) sensor_data[SENSOR_DATA_LENGTH];
71
72 // Initiate Serial communication for debugging and monitoring.
73 Serial.begin(115200);
74
75 // Initialize Nicla Voice board's system functions.
76 // Disable the LDO regulator on the Nicla Voice board for power saving.
77 // Initialize the built-in RGB LED of the Nicla Voice board.
78 nicla::begin();
79 nicla::disableLDO();
80 nicla::leds.begin();
81
82 // Set up error and event handlers:
83 // - In case of error, the red LED will blink.
84 // - In case of match, the blue LED will turn on.
85 // - In case of any event, the green LED will turn on.
86 NDP.onError(ledRedBlink);
87 NDP.onMatch(ledBlueOn);
88 NDP.onEvent(ledGreenOn);
89
90 // NDP processor initialization with firmwares and models
91 Serial.println("- NDP processor initialization...");
92 NDP.begin("mcu_fw_120_v91.synpkg");
93 NDP.load("dsp_firmware_v91.synpkg");
94 NDP.load("ei_model.synpkg");
95 Serial.println("- NDP processor initialization done!");
96
97 // Set the BMI270 sensor in SPI mode, then read sensor data.
98 status = NDP.sensorBMI270Read(0x0, 1, sensor_data);
99 CHECK_STATUS(status);
100 status = NDP.sensorBMI270Read(0x0, 1, sensor_data);
101 CHECK_STATUS(status);
102
103 // Perform a software reset of the sensor.
104 status = NDP.sensorBMI270Write(0x7E, 0xB6);
105 CHECK_STATUS(status);
106 delay(20);
107
108 // Set the sensor back to SPI mode after the software reset.
109 status = NDP.sensorBMI270Read(0x0, 1, sensor_data);
110 CHECK_STATUS(status);
111 status = NDP.sensorBMI270Read(0x0, 1, sensor_data);
112 CHECK_STATUS(status);
113
114 // Disable power configurations.
115 status = NDP.sensorBMI270Write(0x7C, 0x00);
116 CHECK_STATUS(status);
117 delay(20);
118
119 // Prepare to load the sensor configuration.
120 status = NDP.sensorBMI270Write(0x59, 0x00);
121 CHECK_STATUS(status);
122
123 // Sensor configuration.
124 Serial.println("- BMI270 initialization starting...");
125 status = NDP.sensorBMI270Write(0x5E, sizeof(bmi270_maximum_fifo_config_file), (uint8_t*)bmi270_maximum_fifo_config_file);
126 CHECK_STATUS(status);
127 Serial.println("- BMI270 Initialization done!");
128 status = NDP.sensorBMI270Write(0x59, 0x01);
129 CHECK_STATUS(status);
130 delay(200);
131
132 // Check sensor status.
133 status = NDP.sensorBMI270Read(0x21, 1, sensor_data);
134 CHECK_STATUS(status);
135
136 // Configure the device to normal power mode with both accelerometer and gyroscope operational.
137 // Set the accelerometer and gyroscope settings such as measurement range and data rate.
138 status = NDP.sensorBMI270Write(0x7D, 0x0E); // Normal power mode
139 CHECK_STATUS(status);
140 status = NDP.sensorBMI270Write(0x40, 0xA8); // Accelerometer configuration.
141 CHECK_STATUS(status);
142 status = NDP.sensorBMI270Write(0x41, 0x00); // Set the accelerometer range to +/- 2g.
143 CHECK_STATUS(status);
144 status = NDP.sensorBMI270Write(0x42, 0xA9); // Gyroscope configuration.
145 CHECK_STATUS(status);
146 status = NDP.sensorBMI270Write(0x43, 0x00); // Set the gyroscope range to +/- 2000 dps.
147 CHECK_STATUS(status);
148}
149
150void loop() {
151 // Allocate space for raw sensor data.
152 uint8_t __attribute__((aligned(4))) sensor_data[SENSOR_DATA_LENGTH];
153
154 // Declare variables for accelerometer and gyroscope data.
155 int16_t x_acc_raw, y_acc_raw, z_acc_raw, x_gyr_raw, y_gyr_raw, z_gyr_raw;
156 float x_acc, y_acc, z_acc, x_gyr, y_gyr, z_gyr;
157
158 // Read operation status variable.
159 int status;
160
161 // Perform data read from the BMI270 sensor. The data read includes accelerometer and gyroscope data.
162 // The sensor's read function is called with 0x0C as the start address and 16 as the number of bytes to read.
163 // Collected data is placed into sensor_data array.
164 status = NDP.sensorBMI270Read(READ_START_ADDRESS, READ_BYTE_COUNT, &sensor_data[0]);
165
166 // Check the status of the read operation.
167 CHECK_STATUS(status);
168
169 // Parse the read sensor data. Data is in 16-bit format, where lower byte comes first (little endian).
170 // Data for each axis (X, Y, Z) of the accelerometer and gyroscope is extracted from the array.
171 x_acc_raw = (0x0000 | sensor_data[0] | sensor_data[1] << 8);
172 y_acc_raw = (0x0000 | sensor_data[2] | sensor_data[3] << 8);
173 z_acc_raw = (0x0000 | sensor_data[4] | sensor_data[5] << 8);
174 x_gyr_raw = (0x0000 | sensor_data[6] | sensor_data[7] << 8);
175 y_gyr_raw = (0x0000 | sensor_data[8] | sensor_data[9] << 8);
176 z_gyr_raw = (0x0000 | sensor_data[10] | sensor_data[11] << 8);
177
178 // Convert raw accelerometer data to acceleration expressed in m/s^2.
179 x_acc = x_acc_raw * ACCEL_SCALE_FACTOR;
180 y_acc = y_acc_raw * ACCEL_SCALE_FACTOR;
181 z_acc = z_acc_raw * ACCEL_SCALE_FACTOR;
182
183 // Convert raw gyroscope data to angular velocity expressed in °/s.
184 x_gyr = x_gyr_raw * GYRO_SCALE_FACTOR;
185 y_gyr = y_gyr_raw * GYRO_SCALE_FACTOR;
186 z_gyr = z_gyr_raw * GYRO_SCALE_FACTOR;
187
188 // Print accelerometer data (expressed in m/s^2).
189 Serial.print("x_acc:");
190 Serial.print(x_acc);
191 Serial.print(",");
192 Serial.print("y_acc:");
193 Serial.print(y_acc);
194 Serial.print(",");
195 Serial.print("z_acc:");
196 Serial.println(z_acc);
197
198 // Print gyroscope data (expressed in °/s).
199 Serial.print("x_gyr:");
200 Serial.print(x_gyr);
201 Serial.print(",");
202 Serial.print("y_gyr:");
203 Serial.print(y_gyr);
204 Serial.print(",");
205 Serial.print("z_gyr:");
206 Serial.println(z_gyr);
207
208 delay(1000);
209}

Here you can find a step-by-step explanation of the code:

First, the necessary libraries are included:

  • NDP.h
    and
    BMI270_Init.h
    for the Nicla Voice board's basic functions and the IMU control.
  • Macros are defined for checking the status of the IMU; these macros allow the sketch to detect and handle sensor errors.

Next, user functions

ledBlueOn()
,
ledGreenOn()
, and
ledRedBlink()
definition:

  • These functions allow the onboard LEDs to flash specific colors to indicate different states: blue for a successful match, green for an event, and red to indicate an error.

Next, in the

setup()
function:

  • The serial communication is initialized at a baud rate of 115200.
  • The Nicla Voice board is initialized, and the LDO regulator (used for putting the board into power-saving mode) is disabled to avoid communication problems with the IMU.
  • Error and event handlers are initialized.
  • NDP processor is initialized; this process includes populating the external Flash memory of the board with the NDP processor's internal microcontroller firmware (
    mcu_fw_120_v91.synpkg
    ), the NDP processor's internal DSP firmware (
    dsp_firmware_v91.synpkg
    ), and the Machine Learning model (
    ei_model.synpkg
    ).
  • The BMI270 sensor is initialized; this includes a software reset, loading the sensor configuration, and setting it into normal power mode with the accelerometer and gyroscope operational.

Finally, in the

loop()
function:

  • Memory is allocated for the sensor data; data is then read from the sensor and stored in this allocated space.
  • Raw sensor data is then parsed and extracted into raw accelerometer and gyroscope data. This data is represented as 16-bit signed integers ranging from -32 768 to 32 767.
  • Raw sensor data is converted into understandable and standard unit measurements; for the accelerometer, data is converted to meters per second squared, and for the gyroscope, data is converted to degrees per second.
  • Converted accelerometer and gyroscope data are printed on the Serial Monitor, allowing the user to observe sensor data in real-time.

After uploading the example code, you should see accelerometer and gyroscope data on the IDE's Serial Monitor as shown below:

Nicla Voice onboard IMU data on the IDE's Serial Monitor
Nicla Voice onboard IMU data on the IDE's Serial Monitor

Let's use also the Arduino IDE Serial Plotter to test the example IMU sketch; let's start visualizing only accelerometer data. To do so, comment the gyroscope data output as shown below:

1// Print accelerometer data (expressed in meters per second squared).
2 Serial.print("x_acc:");
3 Serial.print(x_acc);
4 Serial.print(",");
5 Serial.print("y_acc:");
6 Serial.print(y_acc);
7 Serial.print(",");
8 Serial.print("z_acc:");
9 Serial.println(z_acc);
10
11 /* Print gyroscope data (expressed in degrees per second).
12 Serial.print("x_gyr:");
13 Serial.print(x_gyr);
14 Serial.print(",");
15 Serial.print("y_gyr:");
16 Serial.print(y_gyr);
17 Serial.print(",");
18 Serial.print("z_gyr:");
19 Serial.println(z_gyr); */

Upload the example sketch again and open the IDE's Serial Plotter by navigating to Tools > Serial Plotter. After a while, you should see a real-time graph showing data from the board's onboard accelerometer, as shown below (move the board):

Nicla Voice onboard accelerometer data on the IDE's Serial Plotter

When the board is not moving, you should see acceleration measurements close to zero on the x and y-axis, while the z-axis will be close to 1g (approximately 9.81 m/s2). If you want to visualize gyroscope readings, uncomment the gyroscope data output and comment on the accelerometer data output; when the board is not moving, you should see gyroscope measurements on the three-axis close to zero.

Magnetometer Data

The example code below shows how to get raw magnetic field and Hall resistance data from the onboard magnetometer and stream it to the Arduino IDE Serial Monitor and Serial Plotter. You can download the example sketch here.

1/**
2 Nicla Voice magnetometer test sketch
3 Name: nv_mag_test.ino
4 Purpose: Sketch tests onboard magnetometer (BMM150)
5
6 @author Arduino PRO Content Team
7 @version 1.0 30/05/23
8*/
9
10#include "NDP.h"
11
12// Named constants
13#define READ_START_ADDRESS 0x42
14#define READ_BYTE_COUNT 8
15#define SENSOR_DATA_LENGTH 16
16
17/**
18 Turns on and off the onboard blue LED.
19
20 @param label to be printed on the Serial Monitor.
21*/
22void ledBlueOn(char* label) {
23 nicla::leds.begin();
24 nicla::leds.setColor(blue);
25 delay(200);
26 nicla::leds.setColor(off);
27 Serial.println(label);
28 nicla::leds.end();
29}
30
31/**
32 Turns on and off the onboard green LED.
33*/
34void ledGreenOn() {
35 nicla::leds.begin();
36 nicla::leds.setColor(green);
37 delay(200);
38 nicla::leds.setColor(off);
39 nicla::leds.end();
40}
41
42/**
43 Blinks onboard red LED periodically every 200 ms.
44*/
45void ledRedBlink() {
46 while (1) {
47 nicla::leds.begin();
48 nicla::leds.setColor(red);
49 delay(200);
50 nicla::leds.setColor(off);
51 delay(200);
52 nicla::leds.end();
53 }
54}
55
56// Macro definition used for checking the sensor status, print error if SPI access fails.
57#define CHECK_STATUS(s) do {\
58 if (s) {\
59 Serial.print("SPI access error in line ");\
60 Serial.println(__LINE__);\
61 for(;;);\
62 }\
63} while (0)
64
65void setup() {
66 int status;
67 uint8_t __attribute__((aligned(4))) sensor_data[SENSOR_DATA_LENGTH];
68
69 // Initiate Serial communication for debugging and monitoring.
70 Serial.begin(115200);
71
72 // Initialize Nicla Voice board's system functions.
73 // Disable the LDO regulator on the Nicla Voice board for power saving.
74 // Initialize the built-in RGB LED of the Nicla Voice board.
75 nicla::begin();
76 nicla::disableLDO();
77 nicla::leds.begin();
78
79 // Set up error and event handlers:
80 // - In case of error, the red LED will blink.
81 // - In case of match, the blue LED will turn on.
82 // - In case of any event, the green LED will turn on.
83 NDP.onError(ledRedBlink);
84 NDP.onMatch(ledBlueOn);
85 NDP.onEvent(ledGreenOn);
86
87 // NDP processor initialization with firmwares and models.
88 Serial.println("- NDP processor initialization...");
89 NDP.begin("mcu_fw_120_v91.synpkg");
90 NDP.load("dsp_firmware_v91.synpkg");
91 NDP.load("ei_model.synpkg");
92 Serial.println("- NDP processor initialization done!");
93
94 // Enable power control bit
95 status = NDP.sensorBMM150Write(0x4B, 0x01);
96 CHECK_STATUS(status);
97 delay(20);
98
99 // Read power control byte
100 status = NDP.sensorBMM150Read(0x4B, 1, sensor_data);
101 CHECK_STATUS(status);
102
103 // Set the magnetometer to active mode (normal operation), with an output data rate of 10 Hz.
104 status = NDP.sensorBMM150Write(0x4C, 0x00);
105 CHECK_STATUS(status);
106
107 // Read chip ID
108 status = NDP.sensorBMM150Read(0x40, 1, sensor_data);
109 CHECK_STATUS(status);
110}
111
112void loop() {
113 // Allocate space for raw sensor data.
114 uint8_t __attribute__((aligned(4))) sensor_data[SENSOR_DATA_LENGTH];
115
116 // Declare variables for magnetometer data.
117 int16_t x_mag_raw, y_mag_raw, z_mag_raw, hall_raw ;
118 float x_mag, y_mag, z_mag;
119
120 // Read operation status variable.
121 int status;
122
123 // Perform data read from the BMM150 sensor.
124 // The sensor's read function is called with 0x42 as the start address and 8 as the number of bytes to read.
125 // Collected data is placed into sensor_data array.
126 status = NDP.sensorBMM150Read(READ_START_ADDRESS, READ_BYTE_COUNT, sensor_data);
127
128 // Check the status of the read operation.
129 CHECK_STATUS(status);
130
131 // The sensor data is read into an array of bytes (8-bit values). Each measurement from the magnetometer consists
132 // of two bytes, hence the bit shifting and bitwise OR operations to combine these two bytes into one 16-bit value.
133 // Data for each axis (X, Y, Z) of the magnetometer is extracted from the array.
134 x_mag_raw = (0x0000 | sensor_data[0] >> 3 | sensor_data[1] << 5);
135 y_mag_raw = (0x0000 | sensor_data[2] >> 3 | sensor_data[3] << 5);
136 z_mag_raw = (0x0000 | sensor_data[4] >> 1 | sensor_data[5] << 7);
137 hall_raw = (0x0000 | sensor_data[6] >> 2 | sensor_data[7] << 6);
138
139 // Print raw magnetometer data.
140 Serial.print("x_mag_raw:");
141 Serial.print(x_mag_raw);
142 Serial.print(",");
143 Serial.print("y_mag_raw:");
144 Serial.print(y_mag_raw);
145 Serial.print(",");
146 Serial.print("z_mag_raw:");
147 Serial.print(z_mag_raw);
148 Serial.print(",");
149 Serial.print("hall_raw:");
150 Serial.println(hall_raw);
151
152 delay(1000);
153}

Here you can find a step-by-step explanation of the sketch:

First, the necessary libraries are included:

  • NDP.h
    for the Nicla Voice board's basic functions and the magnetometer control.
  • Macros are defined for checking the status of the magnetometer; these macros allow the sketch to detect and handle sensor errors.

Next, user functions

ledBlueOn()
,
ledGreenOn()
, and
ledRedBlink()
definition:

  • These functions allow the onboard LEDs to flash specific colors to indicate different states: blue for a successful match, green for an event, and red to indicate an error.

Next, in the

setup()
function:

  • The serial communication is initialized at a baud rate of 115200.
  • The Nicla Voice board is initialized, and the LDO regulator (used for putting the board into power-saving mode) is disabled to avoid communication problems with the magnetometer.
  • Error and event handlers are initialized.
  • NDP processor is initialized; this process includes populating the external Flash memory of the board with the NDP processor's internal microcontroller firmware (
    mcu_fw_120_v91.synpkg
    ), the NDP processor's internal DSP firmware (
    dsp_firmware_v91.synpkg
    ), and the Machine Learning model (
    ei_model.synpkg
    ).
  • The BMM150 sensor is initialized; this includes setting it into normal operation with an output data rate (ODR) of 10 Hz.

Finally, in the

loop()
function:

  • Memory is allocated for the sensor data; data is then read from the sensor and stored in this allocated space.
  • Raw sensor data is then extracted and parsed into raw magnetometer data. Raw sensor data is extracted from the
    sensor_data
    array, 8-bits at a time, and then combined to form a 16-bit integer for each axis (X, Y, Z) and raw Hall resistance value.
  • Raw magnetometer data is printed on the Serial Monitor, allowing the user to observe sensor data in real-time.

After uploading the example code, you should see the magnetometer data on the IDE's Serial Monitor, as shown below:

Nicla Voice onboard raw magnetometer data on the IDE's Serial Monitor
Nicla Voice onboard raw magnetometer data on the IDE's Serial Monitor

Now open the IDE's Serial Plotter by navigating to Tools > Serial Plotter. After a while, you should see a real-time graph showing raw data from the board's onboard magnetometer, as shown below (move the board):

Nicla Voice onboard raw magnetometer data on the IDE's Serial Plotter

IMU and Machine Learning

The example code below demonstrates how to use the Nicla Voice board to perform Machine Learning inference on IMU data. The code sets up event indicators using the onboard RGB LED and sends IMU data to the NDP processor for inference. The example can be found in the board's built-in examples by navigating to File > Examples > NDP > IMUDemo.

1#include "NDP.h"
2
3// Set to 'true' for the lowest power consumption mode, 'false' otherwise
4const bool lowestPower = false;
5
6// Function to turn on the blue LED and print a label to the serial monitor if not in the lowest power mode
7void ledBlueOn(char* label) {
8 nicla::leds.begin();
9 nicla::leds.setColor(blue);
10 delay(200);
11 nicla::leds.setColor(off);
12 if (!lowestPower) {
13 Serial.println(label);
14 }
15 nicla::leds.end();
16}
17
18// Function to turn on the green LED briefly
19void ledGreenOn() {
20 nicla::leds.begin();
21 nicla::leds.setColor(green);
22 delay(200);
23 nicla::leds.setColor(off);
24 nicla::leds.end();
25}
26
27// Function to make the red LED blink continuously
28void ledRedBlink() {
29 while (1) {
30 nicla::leds.begin();
31 nicla::leds.setColor(red);
32 delay(200);
33 nicla::leds.setColor(off);
34 delay(200);
35 nicla::leds.end();
36 }
37}
38
39void setup() {
40 Serial.begin(115200);
41 nicla::begin();
42 nicla::disableLDO();
43 nicla::leds.begin();
44
45 // Register event handlers for error, match, and event
46 NDP.onError(ledRedBlink);
47 NDP.onMatch(ledBlueOn);
48 NDP.onEvent(ledGreenOn);
49
50 // Load Edge Impulse model and related firmware
51 Serial.println("Loading synpackages");
52 NDP.begin("mcu_fw_120_v91.synpkg");
53 NDP.load("dsp_firmware_v91.synpkg");
54 NDP.load("ei_model_imu.synpkg");
55 Serial.println("packages loaded");
56
57 NDP.getInfo();
58 NDP.configureInferenceThreshold(1088);
59 NDP.interrupts();
60
61 // Enter the lowest power mode, if set
62 nicla::leds.end();
63 if (lowestPower) {
64 NRF_UART0->ENABLE = 0;
65 }
66}
67
68// Predefined IMU data for testing
69extern "C" const unsigned char data_opensset_bin[];
70extern "C" const unsigned char data_circ_bin[];
71extern "C" const unsigned int data_opensset_bin_len;
72extern "C" const unsigned int data_circ_bin_len;
73
74void loop() {
75 // Send openset data (no match expected)
76 Serial.println("Sending openset data... (no match expected)");
77 NDP.sendData((uint8_t*)data_opensset_bin, data_opensset_bin_len);
78 delay(1000);
79
80 // Send circular IMU data (match expected)
81 Serial.println("Sending circular IMU data.... (match expected)");
82 NDP.sendData((uint8_t*)data_circ_bin, data_circ_bin_len);
83 delay(5000);
84}

In the example code above, a Machine Learning model is loaded into the Nicla Voice board, and predefined IMU data is sent to the Machine Learning model for inferencing. Depending on the result, the board will light its built-in RGB LED with different colors:

  • If the model matches the input data with a known motion pattern, the built-in RGB LED is turned blue, and the event label is printed to the IDE's Serial Monitor.
  • If an error occurs, the built-in RGB LED will blink red continuously.
  • While an event is recognized, the built-in RGB LED is turned on green.

To learn more about your Nicla Voice board Machine Learning capabilities, check out the following tutorial and learn how to create a simple motion detection application:

Actuators

RGB LED

The Nicla Voice features a built-in I2C RGB LED that can be a visual feedback indicator for the user. The LED is connected through the boards' I2C port; therefore, specific functions must be used to operate the LED colors.

Nicla Voice built-in RGB LED
Nicla Voice built-in RGB LED

To use the RGB LED, include the

Nicla System
header:

1// Include the Nicla System header to access the built-in RGB LED functions
2#include "Nicla_System.h"

Since the functions are scoped under a class name called

nicla
, you must explicitly write it before each statement. To initialize the board's built-in RGB LED along with the Nicla system inside the void
setup()
function:

1void setup() {
2 // Initialize the Nicla system and the built-in RGB LED
3 nicla::begin();
4 nicla::leds.begin();
5}

The built-in RGB LED can be set to a desired color using the RGB color model in which the red, green, and blue primary colors of light are added together in various ways to reproduce a broad array of colors. There are predefined colors:

red
,
green
,
blue
,
yellow
,
magenta
, and
cyan
; to turn off the LED, use
off
. To set the built-in RBG LED to a predefined color, for example, green or blue:

1// Set the LED color to green, wait for 1000 milliseconds
2nicla::leds.setColor(green);
3delay(1000);
4
5// Set the LED color to blue, wait for 1000 milliseconds
6nicla::leds.setColor(blue);
7delay(1000);

To turn off the built-in RGB LED:

1// Turn off the LED
2nicla::leds.setColor(off);

You can also choose a value between 0 and 255 for each color component (red, green, or blue) to set a custom color:

1// Define custom color values for red, green, and blue components
2int red = 234;
3int green = 72;
4int blue = 122;
5
6// Set the LED to the custom color, wait for 1000 milliseconds
7nicla::leds.setColor(red, green, blue);
8delay(1000);
9
10// Turn off the LED and wait, wait for 1000 milliseconds
11nicla::leds.setColor(off);
12delay(1000);

Here you can find a complete example code to blink the built-in I2C RGB LED of the Nicla Voice:

1// Include the Nicla System header to access the built-in RGB LED functions
2#include "Nicla_System.h"
3
4void setup() {
5 // Initialize the Nicla system and the built-in RGB LED
6 nicla::begin();
7 nicla::leds.begin();
8}
9
10void loop() {
11 // Set the LED color to red, wait for 1000 milliseconds
12 nicla::leds.setColor(red);
13 delay(1000);
14
15 // Turn off the LED and wait, wait for 1000 milliseconds
16 nicla::leds.setColor(off);
17 delay(1000);
18}

Communication

This section of the user manual covers the different communication protocols that are supported by the Nicla Voice board, including the Serial Peripheral Interface (SPI), Inter-Integrated Circuit (I2C), Universal Asynchronous Receiver-Transmitter (UART), and Bluetooth® Low Energy; communication via the onboard ESLOV connector is also explained in this section. The Nicla Voice features dedicated pins for each communication protocol, making connecting and communicating with different components, peripherals, and sensors easy.

SPI

The Nicla Voice supports SPI communication, which allows data transmission between the board and other SPI-compatible devices. The pins used in the Nicla Voice for the SPI communication protocol are the following:

Microcontroller PinArduino Pin Mapping
CS
/
P0_29
SS
or
6
COPI
/
P0_27
SCK
or
8
CIPO
/
P0_28
MOSI
or
7
SCLK
/
P0_11
MISO
or
9

Please, refer to the board pinout section of the user manual to find them on the board.

Include the

SPI
library at the top of your sketch to use the SPI communication protocol. The SPI library provides functions for SPI communication:

1#include <SPI.h>

In the

setup()
function, initialize the SPI library, define and configure the chip select (
CS
) pin:

1void setup() {
2 // Set the chip select pin as output
3 pinMode(SS, OUTPUT);
4
5 // Pull the CS pin HIGH to unselect the device
6 digitalWrite(SS, HIGH);
7
8 // Initialize the SPI communication
9 SPI.begin();
10}

To transmit data to an SPI-compatible device, you can use the following commands:

1// Replace with the target device's address
2byte address = 0x00;
3
4// Replace with the value to send
5byte value = 0xFF;
6
7// Pull the CS pin LOW to select the device
8digitalWrite(SS, LOW);
9
10// Send the address
11SPI.transfer(address);
12
13// Send the value
14SPI.transfer(value);
15
16// Pull the CS pin HIGH to unselect the device
17digitalWrite(SS, HIGH);

I2C

The Nicla Voice supports I2C communication, which allows data transmission between the board and other I2C-compatible devices. The pins used in the Nicla Voice for the I2C communication protocol are the following:

Microcontroller PinArduino Pin Mapping
P0_23
SCL
or
3
P0_22
SDA
or
4

Please, refer to the board pinout section of the user manual to find them on the board. The I2C pins are also available through the onboard ESLOV connector of the Nicla Voice.

To use I2C communication, include the

Wire
library at the top of your sketch. The
Wire
library provides functions for I2C communication:

1#include <Wire.h>

In the

setup()
function, initialize the I2C library:

1// Initialize the I2C communication
2Wire.begin();

To transmit data to an I2C-compatible device, you can use the following commands:

1// Replace with the target device's I2C address
2byte deviceAddress = 0x1;
3
4// Replace with the appropriate instruction byte
5byte instruction = 0x00;
6
7// Replace with the value to send
8byte value = 0xFF;
9
10// Begin transmission to the target device
11Wire.beginTransmission(deviceAddress);
12
13// Send the instruction byte
14Wire.write(instruction);
15
16// Send the value
17Wire.write(value);
18
19// End transmission
20Wire.endTransmission();

To read data from an I2C-compatible device, you can use the

requestFrom()
function to request data from the device and the
read()
function to read the received bytes:

1// The target device's I2C address
2byte deviceAddress = 0x1;
3
4// The number of bytes to read
5int numBytes = 2;
6
7// Request data from the target device
8Wire.requestFrom(deviceAddress, numBytes);
9
10// Read while there is data available
11while (Wire.available()) {
12 byte data = Wire.read();
13}

UART

The pins used in the Nicla Voice for the UART communication protocol are the following:

Microcontroller PinArduino Pin Mapping
P0_09
TX
or
1
P0_20
RX
or
2

Please, refer to the board pinout section of the user manual to find them on the board.

To begin with UART communication, you'll need to configure it first. In the

setup()
function, set the baud rate (bits per second) for UART communication:

1// Start UART communication at 9600 baud
2Serial1.begin(9600);

To read incoming data, you can use a

while()
loop to continuously check for available data and read individual characters. The code shown above stores the incoming characters in a String variable and process the data when a line-ending character is received:

1// Variable for storing incoming data
2String incoming = "";
3
4void loop() {
5 // Check for available data and read individual characters
6 while (Serial1.available()) {
7 // Allow data buffering and read a single character
8 delay(2);
9 char c = Serial1.read();
10
11 // Check if the character is a newline (line-ending)
12 if (c == '\n') {
13 // Process the received data
14 processData(incoming);
15
16 // Clear the incoming data string for the next message
17 incoming = "";
18 } else {
19 // Add the character to the incoming data string
20 incoming += c;
21 }
22 }
23}

To transmit data to another device via UART, you can use the

write()
function:

1// Transmit the string "Hello world!
2Serial1.write("Hello world!");

You can also use the

print
and
println()
to send a string without a newline character or followed by a newline character:

1// Transmit the string "Hello world!"
2Serial1.print("Hello world!");
3
4// Transmit the string "Hello world!" followed by a newline character
5Serial1.println("Hello world!");

Bluetooth® Low Energy

To enable Bluetooth® Low Energy communication on the Nicla Voice, you can use the ArduinoBLE library.

Here is an example of how to use the ArduinoBLE library to create a voltage level monitor application:

1#include "Nicla_System.h"
2#include <ArduinoBLE.h>
3
4
5// Define the voltage service and its characteristic
6BLEService voltageService("1101");
7BLEUnsignedCharCharacteristic voltageLevelChar("2101", BLERead | BLENotify);
8
9
10const int analogPin = A0;
11
12
13/**
14 Read voltage level from an analog input of the Nicla Voice,
15 then maps the voltage reading to a percentage value ranging from 0 to 100.
16
17
18 @param none
19 @return the voltage level percentage (int).
20*/
21int readVoltageLevel() {
22 int voltage = analogRead(analogPin);
23 int voltageLevel = map(voltage, 0, 1023, 0, 100);
24 return voltageLevel;
25}
26
27
28void setup() {
29 // Initialize the Nicla system and the built-in RGB LED
30 nicla::begin();
31 nicla::leds.begin();
32
33
34 Serial.begin(9600);
35 // Wait for the serial connection to be established
36 while (!Serial)
37 ;
38
39
40 // Initialize the BLE module
41 if (!BLE.begin()) {
42 Serial.println("starting BLE failed!");
43 while (1)
44 ;
45 }
46
47
48 // Set the local name and advertised service for the BLE module
49 BLE.setLocalName("VoltageMonitor");
50 BLE.setAdvertisedService(voltageService);
51 voltageService.addCharacteristic(voltageLevelChar);
52 BLE.addService(voltageService);
53
54
55 // Start advertising the BLE service
56 BLE.advertise();
57 Serial.println("- Bluetooth device active, waiting for connections...");
58}
59
60
61void loop() {
62 // Check for incoming BLE connections
63 BLEDevice central = BLE.central();
64
65
66 // If a central device is connected
67 if (central) {
68 Serial.print("- Connected to central: ");
69 Serial.println(central.address());
70
71
72 // Turn off the LED when disconnected
73 nicla::leds.setColor(blue);
74
75
76 // While the central device is connected
77 while (central.connected()) {
78 // Read the voltage level and update the BLE characteristic with the level value
79 int voltageLevel = readVoltageLevel();
80
81
82 Serial.print("- Voltage level is: ");
83 Serial.println(voltageLevel);
84 voltageLevelChar.writeValue(voltageLevel);
85
86
87 delay(200);
88 }
89 }
90
91
92 // Turn off the LED when disconnected
93 nicla::leds.setColor(red);
94
95
96 Serial.print("- Disconnected from central: ");
97 Serial.println(central.address());
98}

The example code shown above creates a Bluetooth® Low Energy service and characteristic for transmitting a voltage value read by the analog pin A0 of the Nicla Voice to a central device Bluetooth® device like a smartphone or another microcontroller.

  • The code begins by importing all the necessary libraries and defining the Bluetooth® Low Energy service and characteristics.
  • In the
    setup()
    function, the code initializes the Nicla Voice board and sets up the Bluetooth® Low Energy service and characteristic; then, it begins advertising the defined Bluetooth® Low Energy service.
  • A Bluetooth® Low Energy connection is constantly verified in the
    loop()
    function, being the built-in LED in red while looking for a connection. When a central device connects to the Nicla Voice, its built-in LED will change its color to blue. The code then enters into a loop that constantly reads the voltage level from an analog input and maps it to a percentage value between 0 and 100. The voltage level is printed to the Serial Monitor and transmitted to the central device over the defined Bluetooth® Low Energy characteristic.

You can use the nRF Connect for Mobile app from Nordic Semiconductor to test the functionality of the example code shown below. nRF Connect is a powerful tool that allows you to scan and explore Bluetooth® Low Energy devices and communicate with them.

Bluetooth® Low Energy service and characteristic information from a Nicla Voice device
Bluetooth® Low Energy service and characteristic information from a Nicla Voice device

ESLOV Connector

The Nicla Voice board features an onboard ESLOV connector meant as an extension of the I2C communication bus. This connector simplifies connecting various sensors, actuators, and other modules to the Nicla Voice without soldering or wiring.

Nicla Voice built-in ESLOV connector
Nicla Voice built-in ESLOV connector

The ESLOV connector is a small 5-pin connector with a 1.00 mm pitch; the mechanical details of the connector can be found in the connector's datasheet.

The pin layout of the ESLOV connector is the following:

  1. VCC_IN (5V input)
  2. INT
  3. SCL
  4. SDA
  5. GND

The manufacturer part number of the ESLOV connector is SM05B-SRSS and its matching receptacle manufacturer part number is SHR-05V-S-B.

Support

If you encounter any issues or have questions while working with the Nicla Voice, we provide various support resources to help you find answers and solutions.

Help Center

Explore our Help Center, which offers a comprehensive collection of articles and guides for the Nicla Voice. The Arduino Help Center is designed to provide in-depth technical assistance and help you make the most of your device.

Forum

Join our community forum to connect with other Nicla Voice users, share your experiences, and ask questions. The forum is an excellent place to learn from others, discuss issues, and discover new ideas and projects related to the Nicla Voice.

Contact Us

Please get in touch with our support team if you need personalized assistance or have questions not covered by the help and support resources described before. We're happy to help you with any issues or inquiries about the Nicla Voice.

Suggest changes

The content on docs.arduino.cc is facilitated through a public GitHub repository. If you see anything wrong, you can edit this page here.

License

The Arduino documentation is licensed under the Creative Commons Attribution-Share Alike 4.0 license.