Arduino Nicla Sense Me

post hero image

Introduction

All the code written for this article is available at arduino-nicla-sense-me directory from arduino-projects GitLab repository.

I wrote many code samples with the aim of allowing a better readability and to reduce the amount of code snippets along the article. Moreover, I linked the different subdirectories where I wrote the actual code.

Feel free to make pull requests to the repository if you find something to improve or fix.

Main Features

The Arduino® Nicla Sense ME is the smallest form factor yet. It features industrial grade sensors and Bluetooth® Low Energy connectivity.

BHI260AP

AI smart sensor hub has 6-axis IMU to detect activities performed over the board:

  • 3-axis accelerometer
  • 3-axis gyroscope

It is powered by a 32-bit Synopsys DesignWare ARC™ EM4™ CPU.

BMP390

High performance digital pressure sensor: 300 to 1250 hPa with low drift.

BMM150

Low noise magnetometer with ranges of:

  • ±1300μT in x and y axes
  • ±2500μT in z axis

BME688

Environmental sensor that mainly measures pressure, humidity and temperature. The onboard smart gas sensor can also determinate the IAQ (Index Air Quality) by detecting a broad range of gases, as you’ll see in the sections below.

Tech Specs

The board is powered by Cortex-M4 nRF52832 microcontroller. It provides 64kB of SRAM and 512kB of flash memory. Moreover, a couple of MX25R1635FZUIH0, gives 2MB for data logging and 2MB storage (for BHI260AP).

The processor’s clock speed is 64MHz.

BLE connectivity is ensured by ANNA B112 Bluetooth® module, which also ensure RTC. While wired communication is given by UART, I2C and SPI protocols.

The microcontroller operates at 1.8V voltage translated to 3.3V on external pins. The board can be charged with a Li-ion/Li-Po single cell (3.7V) using the JST battery connector (3-pin 1.2 mm pitch).

Each I/O pin drains up to 4.7 mA DC current.

Lastly, the board weigh 2g with a size of 22.86 mm x 22.86 mm.

Installation

The Nicla uses the Arduino Mbed OS Nicla Boards core.

Take a look at those tutorials to properly install the library:

Pins & RGB LED

Nicla Sense ME pinout

Analog pins

There are 2 analog pins, A0 and A1, which can be controlled through analogRead() function.

PWM Pins

There are 12 PWM (Pulse Width Modulation) pins. Some of them are shared:

  • 2 with analog
  • 2 with I2C
  • 4 with SPI

Digital Pins

There are 10 digital pins: the 2 analog pins can also be used as digital pins. The configuration is usually made in setup() function:

pinMode(pin, INPUT); //configured as an input
pinMode(pin, OUTPUT); //configured as an output
pinMode(pin, INPUT_PULLUP); //uses an internal pull up resistor

bool state = digitalRead(pin); // read the state of a digital pin
digitalWrite(pin, HIGH); // write a state to a digital pin

int value = 128;
analogWrite(pin, value); // write a value from 0 to 255 to a PWM pin

RGB LED

The built-in RGB LED can be used as a feedback component for application where’s not possible to attach a Serial Monitor.

Take a look at built-in-RGB-LED directory to get code samples.

Please Note: Nicla_System header is required to use the RGB LED.

Sensors

Sensor Classes

Sensor class handles all the sensors which have a single value to be read (also event sensors, like the step detector). It provides the sensor data through the float property value.

SensorOrientation class handles sensors with the Euler format. It allows you to read the pitch, roll and heading properties.

SensorXYZ class handles sensors with the XYZ format. You can access x, y and z properties.

SensorQuaternion class handle sensors with the quaternion format. It’s used to calculate rotation vector, game rotation vector and geomagnetic rotation vector. You can access x, y, z and w properties.

SensorActivity class to handle sensors with the activity format. The activity is encoded as ID retrieved from the value property: use getActivity() to get a human-readable version of the activity.

SensorBSEC where BSEC stands for Bosch Sensortec Environmental Cluster. Access the air quality index (IAQ) level on top of other environmental parameters:

FunctionDescriptionData type
iaq()IAQ value for regular use caseunsigned 16 bit
iaq_s()IAQ value for stationary use casesunsigned 16 bit
b_voc_eq()breath VOC equivalent (ppm)float
co2_eq()CO2 equivalent (ppm) [400,]unsigned 32 bit
comp_t()compensated temperature (Celsius)float
comp_h()compensated humidityfloat
comp_g()compensated gas resistance (Ohms)unsigned 32 bit
accuracy()accuracy level: [0-3]unsigned 8 bit

Sensor IDs

Each sensor has a unique identifier, as you can see in the table below. You can also run a sketch that prints them all on the Serial Monitor: File > Examples > Arduino_BHY2 > ShowSensorList.

IDDescriptionSENSOR_ID MACROClass
1Accelerometer passthroughSENSOR_ID_ACC_PASSSensorXYZ
3Accelerometer uncalibratedSENSOR_ID_ACC_RAWSensorXYZ
4Accelerometer correctedSENSOR_ID_ACCSensorXYZ
5Accelerometer offsetSENSOR_ID_ACC_BIASSensorXYZ
6Accelerometer corrected wake upSENSOR_ID_ACC_WUSensorXYZ
7Accelerometer uncalibrated wake upSENSOR_ID_ACC_RAW_WUSensorXYZ
10Gyroscope passthroughSENSOR_ID_GYRO_PASSSensorXYZ
12Gyroscope uncalibratedSENSOR_ID_GYRO_RAWSensorXYZ
13Gyroscope correctedSENSOR_ID_GYROSensorXYZ
14Gyroscope offsetSENSOR_ID_GYRO_BIASSensorXYZ
15Gyroscope wake upSENSOR_ID_GYRO_WUSensorXYZ
16Gyroscope uncalibrated wake upSENSOR_ID_GYRO_RAW_WUSensorXYZ
19Magnetometer passthroughSENSOR_ID_MAG_PASSSensorXYZ
21Magnetometer uncalibratedSENSOR_ID_MAG_RAWSensorXYZ
22Magnetometer correctedSENSOR_ID_MAGSensorXYZ
23Magnetometer offsetSENSOR_ID_MAG_BIASSensorXYZ
24Magnetometer wake upSENSOR_ID_MAG_WUSensorXYZ
25Magnetometer uncalibrated wake upSENSOR_ID_MAG_RAW_WUSensorXYZ
28Gravity vectorSENSOR_ID_GRASensorXYZ
29Gravity vector wake upSENSOR_ID_GRA_WUSensorXYZ
31Linear accelerationSENSOR_ID_LACCSensorXYZ
32Linear acceleration wake upSENSOR_ID_LACC_WUSensorXYZ
34Rotation vectorSENSOR_ID_RVSensorQuaternion
35Rotation vector wake upSENSOR_ID_RV_WUSensorQuaternion
37Game rotation vectorSENSOR_ID_GAMERVSensorQuaternion
38Game rotation vector wake upSENSOR_ID_GAMERV_WUSensorQuaternion
40Geomagnetic rotation vectorSENSOR_ID_GEORVSensorQuaternion
41Geomagnetic rotation vector wake upSENSOR_ID_GEORV_WUSensorQuaternion
43OrientationSENSOR_ID_ORISensorOrientation
44Orientation wake upSENSOR_ID_ORI_WUSensorOrientation
48Tilt detectorSENSOR_ID_TILT_DETECTORSensor
50Step detectorSENSOR_ID_STDSensor
52Step counterSENSOR_ID_STCSensor
53Step counter wake upSENSOR_ID_STC_WUSensor
55Significant motionSENSOR_ID_SIGSensor
57Wake gestureSENSOR_ID_WAKE_GESTURESensor
59Glance gestureSENSOR_ID_GLANCE_GESTURESensor
61Pickup gestureSENSOR_ID_PICKUP_GESTURESensor
63Activity recognitionSENSOR_ID_ARSensorActivity
67Wrist tilt gestureSENSOR_ID_WRIST_TILT_GESTURESensor
69Device orientationSENSOR_ID_DEVICE_ORISensorOrientation
70Device orientation wake upSENSOR_ID_DEVICE_ORI_WUSensor
75Stationary detectSENSOR_ID_STATIONARY_DETSensor
77Motion detectSENSOR_ID_MOTION_DETSensor
91Accelerometer offset wake upSENSOR_ID_ACC_BIAS_WUSensorXYZ
92Gyroscope offset wake upSENSOR_ID_GYRO_BIAS_WUSensorXYZ
93Magnetometer offset wake upSENSOR_ID_MAG_BIAS_WUSensorXYZ
94Step detector wake upSENSOR_ID_STD_WUSensor
115BSEC dataSENSOR_ID_BSECSensorBSEC
128TemperatureSENSOR_ID_TEMPSensor
129BarometerSENSOR_ID_BAROSensor
130HumiditySENSOR_ID_HUMSensor
131GasSENSOR_ID_GASSensor
132Temperature wake upSENSOR_ID_TEMP_WUSensor
133Barometer wake upSENSOR_ID_BARO_WUSensor
134Humidity wake upSENSOR_ID_HUM_WUSensor
135Gas wake upSENSOR_ID_GAS_WUSensor
136Hardware Step counterSENSOR_ID_STC_HWSensor
137Hardware Step detectorSENSOR_ID_STD_HWSensor
138Hardware Significant motionSENSOR_ID_SIG_HWSensor
139Hardware Step counter wake upSENSOR_ID_STC_HW_WUSensor
140Hardware Step detector wake upSENSOR_ID_STD_HW_WUSensor
141Hardware Significant motion wake upSENSOR_ID_SIG_HW_WUSensor
142Any motionSENSOR_ID_ANY_MOTIONSensor
143Any motion wake upSENSOR_ID_ANY_MOTION_WUSensor

Using the table above, instantiate a sensor object and retrieve the data:

SensorXYZ mySensor(SENSOR_ID_MAG); // declare Magnetometer corrected sensor
mySensor.begin() // start pulling the sensor inside the setup()

BHY2.update(); // this should be continuously polled inside loop()
Serial.print(mySensor.x()); // print the x sensor value
Serial.print(mySensor.y()); // print the y sensor value
Serial.print(mySensor.z()); // print the z sensor value

Take a look at those sensors’ subdirectories:

Communication

You need to install both Arduino_BHY2 and Arduino_BHY2Host libraries or nicla-sense-me-fw, which include both of them. In the Arduino IDE, select Tools > Manage Libraries… and search for them.

Or you can clone the whole repository like so:

cd ~/Arduino/libraries
git clone https://github.com/arduino/nicla-sense-me-fw

On-board sensors can be read in Standalone mode using the predefined sensor classes. Sensors can also be accessed by external boards with Arduino_BHY2Host library using the following protocols:

  1. BLE
  2. UART using an ESLOV cable

Please Note: The Serial Monitor’s baud rate for the Nicla Sense ME is usually 115200.

BHY tool

Install Go language first, then build bhy tool binaries.

cd ~/Arduino/libraries/nicla-sense-me-fw/tools/bhy-controller/src/
go build

Bluetooth Low Energy

BLE Standalone

The Nicla Sense ME can communicate in standalone mode using ArduinoBLE library.

Take a look at custom_UUID.ino example sketch from arduino-projects GitLab repository.

BLE from Host board

On the Nicla, upload App.ino sketch.

On the host board, include Arduino_BHY2Host library and configure setup() function as follows:

#include "Arduino_BHY2Host.h"

void setup() {
  Serial.begin(115200);
  BHY2Host.begin(false, NICLA_VIA_BLE);
}

void loop() {
  static auto lastCheck = millis();

  // update function should be continuously polled
  BHY2Host.update();

  // check sensor values every 'delayTime' milliseconds
  if (millis() - lastCheck >= 1000) {
    lastCheck = millis();

    // add your code here
  }
}

Host’s begin() accepts two parameters: boolean value passthrough (false by default) that defines if the data should be passed through the Serial connection or not and NiclaWiring’s instance niclaConnection.

Please Note: NiclaWiring is defined as follows:

enum NiclaWiring {
  NICLA_VIA_ESLOV = 0,
  NICLA_AS_SHIELD,
  NICLA_VIA_BLE
};

From the host board, call and use the sensors as you would do from the Nicla. Examples are placed in BHY2-Host-tests directory from arduino-projects GitLab repository.

WebBLE

Make sure that Web Bluetooth® Low Energy is both supported and enabled. Take care of check the browser compatibility with WebBLE. In Google Chrome: go to chrome://flags and enable both “Experimental Web Platform features” and “Web Bluetooth”.

Chrome flags

Once App.ino is uploaded into the Nicla, use the bhy script to interact with sensors.

cd ~/Arduino/libraries/nicla-sense-me-fw/tools/bhy-controller/src

# start the webserver
./bhy webserver

When the server has started, open the landing page from localhost:8000. Click on “Open sensor page”. Then click the “Connect” button and pair your computer with the Nicla Sense ME board.

Connected WEB BLE

Once connected, the page will allow to see all sensors’ data.

Test WEB BLE

ESLOV cable

Once App.ino is uploaded into the Nicla, connect it to the host board using the ESVOL cable. On the host board, upload Passthrough.ino sketch.

You can disconnect the Nicla Sense ME from the USB cable: it’s powered through the ESLOV connection.

After you upload the sketch, a separate serial port will be exposed for the host board:

  1. debugging (for example /dev/ttyACM0)
  2. serial communication for getting the sensor data (for example /dev/ttyACM1)

Assuming the host board has /dev/ttyACM1 as second port.

# move to BHY source directory
cd ~/Arduino/libraries/nicla-sense-me-fw/tools/bhy-controller/src

# list available serial ports
./bhy list

# using an Arduino MKR WiFi 1010 as host board, I got this output
Found port: /dev/ttyACM0
   USB ID     2341:8054
   USB serial 3D2454A550534D53302E3120FF1932223D2454A550534D53302E3120FF193222
Found port: /dev/ttyACM1
   USB ID     2341:8054
   USB serial 3D2454A550534D53302E3120FF1932223D2454A550534D53302E3120FF193222

The help messages are:

./bhy
./bhy dfu
./bhy sensor
./bhy sensor read
./bhy sensor config

Configure sensor with ID equal 10 (Gyroscope passthrough) with 1Hz sample rate and 0ms of latency.

./bhy sensor config -p /dev/ttyACM1 -sensor 10 -rate 1 -latency 0

# I got this output
Connected - port: /dev/ttyACM1 - baudrate: 115200
Sending configuration: sensor 10	rate 1.000000	latency 0Sent 10 bytes
Sensor configuration correctly sent!

Continuously read sensors data when available:

./bhy sensor read -live -p /dev/ttyACM1

# I got this output
Connected - port: /dev/ttyACM1 - baudrate: 115200
Sensor id: 10   name: GYRO_PASS   values:    x : 1.000000   y : -3.000000   z : -3.000000
Sensor id: 10   name: GYRO_PASS   values:    x : 1.000000   y : 0.000000   z : 1.000000
Sensor id: 10   name: GYRO_PASS   values:    x : 5.000000   y : -7.000000   z : -6.000000
# ...

It will continue until you press Ctrl + C and interrupt the process.

Try to add another sensor, like linear acceleration, that has ID equal 31:

./bhy sensor config -p /dev/ttyACM1 -sensor 31 -rate 1 -latency 0

# I got this output
Connected - port: /dev/ttyACM1 - baudrate: 115200
Sending configuration: sensor 31	rate 1.000000	latency 0Sent 10 bytes
Sending configuration: sensor 31	rate 1.000000	latency 0Sent 10 bytes
Sensor configuration correctly sent!

If you now try to read sensors data when available, it will mix sensors output:

./bhy sensor read -live -p /dev/ttyACM1

# I got this output
Connected - port: /dev/ttyACM1 - baudrate: 115200
Sensor id: 10   name: GYRO_PASS   values:    x : 0.000000   y : 1.000000   z : 4.000000
Sensor id: 10   name: GYRO_PASS   values:    x : 1.000000   y : 1.000000   z : 3.000000
Sensor id: 31   name: LINEAR_ACC   values:    x : -2.000000   y : -2.000000   z : 0.000000
# ...

You can disable a sensor by setting its rate to 0. Let’s disable GYRO_PASS sensor, that has ID equal 10:

./bhy sensor config -p /dev/ttyACM1 -sensor 10 -rate 0 -latency 0

# I got this output
Connected - port: /dev/ttyACM1 - baudrate: 115200
Sending configuration: sensor 10	rate 0.000000	latency 0Sent 10 bytes
Sensor configuration correctly sent!

If you now try to read sensors data when available it will only show LINEAR_ACC values since it’s the only sensor still enabled.

Conclusion

This article is not yet finished: I just scratch the edge of Nicla Sense ME capabilities and I wanted to publish it as a work in progress!

Documentation

Here is the full list of links I used to write this article: