Nicla Vision User Manual

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

Overview

This user manual will guide you through a practical journey covering the most interesting features of the Arduino Nicla Vision. With this user manual, you will learn how to set up, configure and use this Arduino board.

Hardware and Software Requirements

Hardware Requirements

Software Requirements

Product Overview

The Nicla Vision is a ready-to-use, standalone camera board for analyzing and processing images on the edge. Thanks to its 2 MP color camera, smart 6-axis motion sensor, integrated microphone, and distance sensor, it is suitable for asset tracking, object recognition, and predictive maintenance.

The Nicla Vision lets you quickly implement sensor nodes to send collected data to the Arduino® Cloud (or third-party vendor services) via its onboard Wi-Fi® and Bluetooth® module.

Board Architecture Overview

The Nicla Vision features a robust and efficient architecture that integrates a range of sensors packed into a tiny footprint. Nicla Vision combines a powerful STM32H747AII6 Dual Arm® Cortex®-M7/M4 IC processor with a 2MP color camera that supports TinyML, as well as a smart 6-axis motion sensor, integrated PDM microphone and a Time of Flight distance sensor.

Nicla Vision main components (top view)
Nicla Vision main components (top view)
Nicla Vision main components (bottom view)
Nicla Vision main components (bottom view)

Here is an overview of main components of the board, as shown in the images above:

  • Camera: the Nicla Vision features a camera based on the GC2145 Color rolling shutter image sensor. The GC2145 incorporates a 1616V x 1232H active pixel array, on-chip 10-bit ADC, and an image signal processor. The 2MP GC2145 CMOS camera module is equipped with an 80° (DFOV) stock lens, 1.75 μm pixel size and a focal length of 2.2 mm. It supports RGB output format.
  • Microcontroller: the heart of the Nicla Vision is the dual-core STM32H747 (U1), including an Arm® Cortex®-M7 running at 480 MHz and an Arm® Cortex®-M4 running at 240 MHz. The two cores communicate via a Remote Procedure Call mechanism that allows calling functions on the other processor seamlessly.
  • Onboard advanced motion sensor: the board features the LSM6DSOX, a smart IMU that includes a 3-axis accelerometer and a 3-axis gyroscope. The LSM6DSOX has a full-scale acceleration range of ±2/±4/±8/±16 g and an angular rate range of ±125/±250/±500/±1000/±2000 dps.
  • Onboard distance sensor: the VL53L1CBV0FY Time-of-Flight sensor (U4) adds accurate and low-power ranging capabilities to the Nicla Vision. The invisible near infrared VCSEL laser (including the analog driver) is encapsulated together with receiving optics in an all-in-one small module located below the camera.
  • Digital Microphone: the MP34DT05 digital MEMS microphone (U6) is omnidirectional and operates via a capacitive sensing element with a high (64 dB) signal-to-noise ratio. The sensing element, capable of detecting acoustic waves, is manufactured using a specialized silicon micromachining process dedicated to audio sensors production.
  • Wireless connectivity: the Murata® LBEE5KL1DX-883 wireless module (U9) simultaneously provides Wi-Fi® and Bluetooth® connectivity in an ultra-small package based on the Cypress CYW4343W. The IEEE802.11 b/g/n Wi-Fi® interface can operate as an access point (AP), station (STA), or dual-mode simultaneous AP/STA. It supports a maximum transfer rate of 65 Mbps. Bluetooth® interface supports Bluetooth® Classic and Bluetooth® Low Energy. An integrated antenna circuitry switch allows a single external antenna (J6) to be shared between Wi-Fi® and Bluetooth®.
  • Power management: the Nicla Vision is designed for ultra-low power operation, with efficient power management features that ensure minimal energy consumption even when using always-on motion recognition and image processing. The Nicla Vision features the PF1550 from NXP®, a highly integrated battery charge management integrated circuit (IC) designed for wearables and Internet of Things (IoT) devices.
  • Security Elements: the Nicla Vision enables IC level edge-to-cloud security capability through the NXP SE050C2 Crypto chip (U8). This provides Common Criteria EAL 6+ security certification up to OS level, as well as RSA/ECC cryptographic algorithm support and credential storage.

Board Core and Libraries

With OpenMV IDE

Before you can start programming MicroPython scripts for the Nicla Vision, you need to download and install the OpenMV IDE.

Open the OpenMV download page in your browser, download the latest version available for your operating system, and follow the instructions of the installer.

OpenMV Download Page
OpenMV Download Page

Open the OpenMV IDE and connect the Nicla Vision to your computer via the USB cable if you have not done so yet.

The OpenMV IDE after starting it
The OpenMV IDE after starting it

Click on the "connect" symbol at the bottom of the left toolbar.

Click the connect button to attach the Nicla Vision to the OpenMV IDE
Click the connect button to attach the Nicla Vision to the OpenMV IDE

If your Nicla Vision does not have the latest firmware, a pop-up will ask you to install it. Your board will enter in DFU mode and its green LED will start fading.

Select

Install the latest release firmware
. This will install the latest OpenMV firmware on the Nicla Vision. You can leave the option of erasing the internal file system unselected and click
OK
.

Install the latest version of the OpenMV firmware
Install the latest version of the OpenMV firmware

Nicla Vision's green LED will start flashing while the OpenMV firmware is being uploaded to the board. A loading bar will start showing you the flashing progress.

Wait until the green LED stops flashing and fading. You will see a message saying

DFU firmware update complete!
when the process is done.

Installing firmware on Nicla Vision board in OpenMV
Installing firmware on Nicla Vision board in OpenMV

The board will start flashing its blue LED when it is ready to be connected. After confirming the completion dialog, the Nicla Vision should already be connected to the OpenMV IDE, otherwise, click the "connect" button (plug symbol) once again (the blue blinking should stop).

When the Nicla Vision is successfully connected a green play button appears
When the Nicla Vision is successfully connected a green play button appears

While using the Nicla Vision with OpenMV, the RGB LED of the board can be used to inform the user about its current status. Some of the most important ones are the following:

🟢 Blinking Green: Your Nicla Vision onboard bootloader is running. The onboard bootloader runs for a few seconds when your Nicla Vision is powered via USB to allow OpenMV IDE to reprogram your Nicla Vision.

🔵 Blinking Blue: Your Nicla Vision is running the default main.py script onboard.

If you overwrite the main.py script on your Nicla Vision, then it will run whatever code you loaded on it instead.

If the LED is blinking blue but OpenMV IDE cannot connect to your Nicla Vision, please make sure you are connecting your Nicla Vision to your PC with a USB cable that supplies both data and power.

Blinking White: Your Nicla Vision firmware is panicking because of a hardware failure. Please check that your Nicla Vision's camera module is installed securely.

If you tap the Nicla Vision reset button once, the board resets. If you tap it twice, the board enters in Device Firmware Upgrade (DFU) mode and its green LED starts blinking and fading.

With Arduino IDE

The Arduino Mbed OS Nicla Boards core contains the libraries and examples you need to work with the board's components, such as its camera and IMU. 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 Vision
and install the latest
Arduino Mbed OS Nicla Boards
version.

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

To update the bootloader firmware of your Nicla Vision, go to File > Examples > STM32H747_System > STM32H747_manageBootloader and upload this sketch to your board.

Example sketch location for bootloader update
Example sketch location for bootloader update

After the sketch is uploaded, follow the instructions in the Serial Monitor.

Serial Monitor instructions and current bootloader info
Serial Monitor instructions and current bootloader info

Pinout

Nicla Vision pinout
Nicla Vision 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 Vision can be powered by:

  • Using a Micro USB cable (not included).
  • Using an external 5 V power supply connected to
    VIN
    pin (please, refer to the board pinout section of the user manual).
  • Using a 3.7 V 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 Vision 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.
Nicla Vision battery powered
Nicla Vision battery powered

Hello World Example

Let's program the Nicla Vision with the classic

hello world
example used in the Arduino ecosystem: the
Blink
sketch. We will use this example to verify the board's connection to the IDEs and that the Nicla Vision core and the board itself are working as expected.

With OpenMV

Copy and paste the code below into a new sketch in the OpenMV IDE.

1import time
2from machine import LED
3
4redLED = LED("LED_RED")
5greenLED = LED("LED_GREEN")
6blueLED = LED("LED_BLUE")
7
8while True:
9 redLED.on()
10 time.sleep_ms(1000)
11 redLED.off()
12 time.sleep_ms(1000)
13 greenLED.on()
14 time.sleep_ms(1000)
15 greenLED.off()
16 time.sleep_ms(1000)
17 blueLED.on()
18 time.sleep_ms(1000)
19 blueLED.off()
20 time.sleep_ms(1000)

To run the code on the Nicla Vision, click the Connect button and then the Start button.

Running the Python Script on OpenMV
Running the Python Script on OpenMV

With Arduino IDE

Copy and paste the code below into a new sketch in the Arduino IDE.

1void setup() {
2
3 pinMode(LEDR, OUTPUT);
4 pinMode(LEDG, OUTPUT);
5 pinMode(LEDB, OUTPUT);
6
7 digitalWrite(LEDR, HIGH);
8 digitalWrite(LEDG, HIGH);
9 digitalWrite(LEDB, HIGH);
10
11}
12
13void loop() {
14
15 digitalWrite(LEDR, LOW); // Nicla Vision LED's turn on with logic '0'
16 delay(1000);
17 digitalWrite(LEDR, HIGH);
18 delay(1000);
19 digitalWrite(LEDG, LOW);
20 delay(1000);
21 digitalWrite(LEDG, HIGH);
22 delay(1000);
23 digitalWrite(LEDB, LOW);
24 delay(1000);
25 digitalWrite(LEDB, HIGH);
26 delay(1000);
27
28}

To upload the code to the Nicla Vision, click the Verify button to compile the sketch and check for errors; then click the Upload button to program the board with the sketch.

Uploading a sketch to the Nicla Vision in the Arduino IDE
Uploading a sketch to the Nicla Vision in the Arduino IDE

Results

You should now repeatedly see the onboard LED turning red, green, and blue.

Hello World example running in the Nicla Vision

Pins

Analog Pins

The Nicla Vision has three analog input pins, mapped as follows:

Microcontroller PinArduino Pin Mapping
ADC1/PC_4A0
ADC2/PF_13A1
ADC3/PF_3A2

All of them can be used through the built-in functions of the Arduino programming language.

The Nicla Vision ADC reference voltage is fixed to 3.3V, this means that it will map the ADC range from 0 to 3.3 volts.

We will use the Nicla Vision analog inputs on both IDEs, OpenMV and Arduino. For the example codes shown below, we will be reading the analog input

A0
and displaying the read voltage on the Serial Monitor of both IDEs:

ADC input example wiring

With OpenMV

Using Micropython on the OpenMV IDE the Nicla boards ADC resolution is fixed to 12 bits (it's maximum).

This example code can also be found on File > Examples > Board Control > adc_read_ext_channel.py:

1import time
2from pyb import ADC
3
4adc = ADC("A0") # PC4 microcontroller port
5
6while True:
7 # The ADC has 12-bits of resolution for 4096 values.
8 print(adc.read())
9 print("ADC = %fv" % ((adc.read() * 3.3) / 4095))
10 time.sleep_ms(100)

With Arduino IDE

Nicla boards ADC can be configured to 8, 10 or 12 bits defining the argument of the following function respectively (default is 10 bits):

1analogReadResolution(12); // ADC resolution set to 12 bits (0-4095)

Example code:

1int potPin = A0; // select the input pin for the potentiometer
2
3void setup() {
4 Serial.begin(115200);
5 analogReadResolution(12);
6}
7
8void loop() {
9 // read the value from the sensor:
10 Serial.print("ADC = ");
11 Serial.print((analogRead(potPin) * 3.3) / 4095);;
12 Serial.println(" v");
13 delay(100);
14}

Digital Pins

The Nicla Vision has ten digital pins, mapped as follows:

Microcontroller PinArduino Pin Mapping
PG_12D0
PA_9D1
PA_10D2
PG_1D3
PE_12SCK
PE_13MISO
PE_14MOSI
PE_11SS
PB_8I2C_SCL
PB_9I2C_SDA

Notice that I2C and SPI pins 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 analog inputs of the Nicla Vision can be used as digital pins but they can just handle 1.8 V, a greater input may damage the board.

The digital pins of the Nicla Vision can be used as inputs or outputs through the built-in functions of the Arduino programming language.

The Nicla Vision digital I/O's are low power, so to drive output devices like LEDs, resistive loads, buzzers, etc, it is recommended to use a MOSFET driver or a buffer to guarantee the required current flow. Learn more about the Nicla I/O's considerations here.

As an application example after learning the Digital Pins basics, we are going to control an LED using a push button. See the wiring below:

Digital I/O example wiring

With OpenMV

The configuration of a digital pin is done in the upper section of the code as shown below:

1# Pin configured as an input
2pin1 = Pin("D1", Pin.IN, Pin.PULL_NONE)
3# Pin configured as an input, internal pull-up resistor enabled
4pin1 = Pin("D1", Pin.IN, Pin.PULL_UP)
5# Pin configured as an output
6pin0 = Pin("D0", Pin.OUT_PP, Pin.PULL_NONE)

The pin function can be set as:

Pin.IN
,
Pin.OUT_PP
,
Pin.OUT_OD
,
Pin.AF_PP
, or
Pin.AF_OD
. An explanation of the pin modes can be found here. The third parameter represents the pull-up/pull-down resistor. It can be set to:
Pin.PULL_NONE
,
Pin.PULL_UP
or
Pin.PULL_DOWN
.

The state of a digital pin, configured as an input, can be read as shown below:

1# Reads pin1 state, stores value in "state" variable
2state = pin1.value()

The state of a digital pin, configured as an output, can be changed as shown below:

1# Set pin0 on
2pin0.value(True)
3# Set pin0 off
4pin0.value(False)

The example code shown below uses digital pin

D0
to control an LED and reads the state of a button connected to digital pin
D1
:

1import time
2from machine import Pin
3
4# Define button and LED pin
5button = Pin("D1", Pin.IN, Pin.PULL_UP)
6led = Pin("D0", Pin.OUT_PP, Pin.PULL_NONE)
7
8while True:
9 if button.value() == 0: # if the button is pressed
10 led.value(1)
11 print("- Button is pressed. LED is on.")
12 else: # if the button is not pressed
13 led.value(0)
14 print("- Button is not pressed. LED is off.")
15 time.sleep_ms(1000) # wait for a second

With Arduino IDE

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(D1, INPUT);
3// Pin configured as an input, internal pull-up resistor enabled
4pinMode(D1, INPUT_PULLUP);
5// Pin configured as an output
6pinMode(D0, OUTPUT);

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(D1);

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(D0, HIGH);
3// Set pin off
4digitalWrite(D0, LOW);

The example code shown below uses digital pin

D0
to control an LED and reads the state of a button connected to digital pin
D1
:

1// Define button and LED pin
2int buttonPin = D1;
3int ledPin = D0;
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 // Initialize Serial communication
13 Serial.begin(115200);
14}
15
16void loop() {
17 // Read the state of the button
18 buttonState = digitalRead(buttonPin);
19 // If the button is pressed, turn on the LED and print its state to the Serial Monitor
20 if (buttonState == LOW) {
21 digitalWrite(ledPin, HIGH);
22 Serial.println("- Button is pressed. LED is on.");
23 } else {
24 // If the button is not pressed, turn off the LED and print to the Serial Monitor
25 digitalWrite(ledPin, LOW);
26 Serial.println("- Button is not pressed. LED is off.");
27 }
28 // Wait for 1000 milliseconds
29 delay(1000);
30}

PWM Pins

Most digital pins of the Nicla Vision can be used as PWM (Pulse Width Modulation) pins, including I2C and SPI interfaces.

With OpenMV

PWM outputs can be controlled with MicroPython easily by using built-in functions as shown below:

First, we need to identify the

Timer
and
Channel
used by the
PWM
output to be used. For this, search for the desired pin on the STM32H747 datasheet from page 89.

Here is a table with the details of the exposed pins on the Nicla Vision:

Microcontroller PinArduino Pin MappingTimerChannel
PA_9D1TIMER1CH2
PA_10D2TIMER1CH3
PB_8I2C_SCLTIMER4CH3
PB_9I2C_SDATIMER4CH4
PE_11SSTIMER1CH2
PE_12SCKTIMER1CH3
PE_13MISOTIMER1CH3
PE_14MOSITIMER1CH4

To use the PWM functions, you need to import the

time
,
Pin
, and
Timer
modules.

1import time
2from pyb import Pin, Timer

First you need to choose the pin you want to use PWM with.

1OUT = Pin("D1", Pin.OUT_PP, Pin.PULL_NONE)

Create a timer for the PWM, where you set the

timer number
(following the table from above) and the frequency.

1timer1 = Timer(1, freq=1000)

Then you need to start a

PWM channel
(following the table from above) with the timer object.

1channel1 = timer1.channel(2, Timer.PWM, pin=OUT, pulse_width_percentage=0)

Get or set the pulse width value on a channel. To get, pass no arguments. To set, give a value as an argument.

1channel1.pulse_width_percentage(Width) # Width (0-100)

As a complete example here is a code to generate a 50% duty cycle PWM signal at 1 Mhz.

1import time
2from pyb import Pin, Timer
3
4# D1 is connected to TIMER1_CH2
5
6OUT = Pin("D1")
7
8timer1 = Timer(1, freq=1000000)
9channel1 = timer1.channel(2, Timer.PWM, pin=OUT, pulse_width_percent=0)
10
11channel1.pulse_width_percent(50)
12
13
14while True:
15 time.sleep_ms(1000)
PWM output signal
PWM output signal

With Arduino IDE

This functionality can be used with the built-in function

analogWrite()
as shown below:

1analogWrite(pin, value);

The output resolution is 8 bits by default, so the output value should be between 0 and 255. To set a greater resolution, you can use the built-in function

analogWriteResolution
as shown below:

1analogWriteResolution(bits);

Using

analogWrite
has some limitations, for example, the PWM signal frequency is fixed at 763 Hz, and this could not be ideal for every application.

1// 12 bits PWM 50% duty cycle example code
2
3void setup() {
4 analogWriteResolution(12); // 12 bits (0-4095)
5}
6
7void loop() {
8 analogWrite(D1, 2048); // PWM output on D1
9}
PWM output signal
PWM output signal

Onboard Sensors

The Nicla Vision comes with various onboard sensors that allow you to capture and process motion data via a 6-axis IMU, distance with a Time of Flight (ToF) sensor, record sound with a PDM microphone, and capture images and videos with a camera.

The onboard sensors can be used for developing various applications, such as voice commanded projects, activity recognition, vibration detection and image classification. The onboard sensors are suitable for Machine Learning applications using our Arduino Cloud ML Tools.

IMU

The Nicla Vision features an advanced IMU, which allows the board to sense motion. The IMU on the board is the LSM6DSOXTR from ST®. 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.

Nicla Vision onboard IMU
Nicla Vision onboard IMU

With OpenMV

In this MicroPython environment, you can choose between a basic usage of the IMU by sampling raw motion data using the example code below.

1import time
2from lsm6dsox import LSM6DSOX
3from machine import Pin
4from machine import SPI
5
6lsm = LSM6DSOX(SPI(5), cs=Pin("PF6", Pin.OUT_PP, Pin.PULL_UP))
7
8while True:
9 print("Accelerometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}".format(*lsm.accel()))
10 print("Gyroscope: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}".format(*lsm.gyro()))
11 print("")
12 time.sleep_ms(100)
Accelerometer and gyroscope data output

Also, you can develop Machine Learning applications using the Nicla Vision IMU. As a practical example, we are going to test a

Vibration monitoring
model that will be able to identify three states:
no vibration
,
low vibration
and
high vibration
.

First, download the pre-trained model file from the example repository and copy it to the Nicla Vision storage drive.

UCF Machine Learning model in the drive
UCF Machine Learning model in the drive

Reset the board and run the following code on the OpenMV IDE.

1from machine import Pin
2from machine import SPI
3from lsm6dsox import LSM6DSOX
4
5INT_MODE = True # Run in interrupt mode.
6INT_FLAG = False # Set True on interrupt.
7
8
9def imu_int_handler(pin):
10 global INT_FLAG
11 INT_FLAG = True
12
13
14if INT_MODE is True:
15 int_pin = Pin("PA1", mode=Pin.IN, pull=Pin.PULL_UP)
16 int_pin.irq(handler=imu_int_handler, trigger=Pin.IRQ_RISING)
17
18# Vibration detection example
19UCF_FILE = "lsm6dsox_vibration_monitoring.ucf"
20UCF_LABELS = {0: "no vibration", 1: "low vibration", 2: "high vibration"}
21# NOTE: Selected data rate and scale must match the MLC data rate and scale.
22lsm = LSM6DSOX(
23 SPI(5),
24 cs=Pin("PF6", Pin.OUT_PP, Pin.PULL_UP),
25 gyro_odr=26,
26 accel_odr=26,
27 gyro_scale=2000,
28 accel_scale=4,
29 ucf=UCF_FILE,
30)
31
32print("MLC configured...")
33
34while True:
35 if INT_MODE:
36 if INT_FLAG:
37 INT_FLAG = False
38 print(UCF_LABELS[lsm.mlc_output()[0]])
39 else:
40 buf = lsm.mlc_output()
41 if buf is not None:
42 print(UCF_LABELS[buf[0]])

In the OpenMV IDE Serial Monitor, the inference results will be printed after a vibration event.

Model inference results
Model inference results

You can download and test many other pre-trained models available in this repository.

With Arduino IDE

First, to use this sensor with the Arduino IDE, you need to install the

Arduino_LSM6DSOX
library, which can be found in the Arduino IDE library manager. To do so in the IDE, select it from the left side menu, search for
LSM6DSOX
and install the one from Arduino.

IMU library installation
IMU library installation

The example code below shows how to get acceleration and angular velocity data from the onboard IMU and stream it to the IDE's Serial Monitor and Serial Plotter.

1#include <Arduino_LSM6DSOX.h>
2
3void setup() {
4 Serial.begin(9600);
5 while (!Serial)
6 ;
7
8 if (!IMU.begin()) {
9 Serial.println("Failed to initialize IMU!");
10
11 while (1)
12 ;
13 }
14
15 Serial.print("Accelerometer sample rate = ");
16 Serial.print(IMU.accelerationSampleRate());
17 Serial.println(" Hz");
18 Serial.println();
19 Serial.println("Acceleration in g's");
20 Serial.println("X\tY\tZ");
21
22 Serial.print("Gyroscope sample rate = ");
23 Serial.print(IMU.gyroscopeSampleRate());
24 Serial.println(" Hz");
25 Serial.println();
26 Serial.println("Gyroscope in degrees/second");
27 Serial.println("X\tY\tZ");
28
29 delay(3000); // Wait 3 seconds
30}
31
32void loop() {
33 float a_x, a_y, a_z;
34
35 if (IMU.accelerationAvailable()) {
36 IMU.readAcceleration(a_x, a_y, a_z);
37
38 Serial.print("acc_X:");
39 Serial.print(a_x);
40 Serial.print(",");
41 Serial.print("acc_Y:");
42 Serial.print(a_y);
43 Serial.print(",");
44 Serial.print("acc_Z:");
45 Serial.println(a_z);
46 }
47 float g_x, g_y, g_z;
48
49 if (IMU.gyroscopeAvailable()) {
50 IMU.readGyroscope(g_x, g_y, g_z);
51
52 Serial.print("gyro_X:");
53 Serial.print(g_x);
54 Serial.print(",");
55 Serial.print("gyro_Y:");
56 Serial.print(g_y);
57 Serial.print(",");
58 Serial.print("gyro_Z:");
59 Serial.println(g_z);
60 }
61}
Accelerometer and gyroscope output in the serial plotter
Accelerometer and gyroscope output in the serial plotter

To test a Machine Learning model on the Arduino IDE, navigate to File > Examples > MLC > NiclaVision_MLC_Motion_Intesity and it will identify three scenarios:

Stationary
,
Medium Intensity
and
High Intensity
movements.

Microphone

The onboard high-performance microphone of the Nicla Vision is the MP34DT06JTR from ST®. It 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 Vision onboard high-performance microphone
Nicla Vision onboard high-performance microphone

Using OpenMV

The OpenMV IDE includes some examples to get started using the Nicla Vision onboard microphone that can be found on File > Examples > Audio. We are going to use the one called

micro_speech.py
to test the machine-learning speech recognition capabilities of the board.

First, download the pre-trained model file from the example repository and copy it to the Nicla Vision storage drive.

Tflite Machine Learning model in the drive
Tflite Machine Learning model in the drive

Reset the board and run the following code on the OpenMV IDE.

1import audio
2import time
3import tf
4import micro_speech
5import pyb
6
7labels = ["Silence", "Unknown", "Yes", "No"]
8
9led_red = pyb.LED(1)
10led_green = pyb.LED(2)
11
12model = tf.load("/model.tflite")
13speech = micro_speech.MicroSpeech()
14audio.init(channels=1, frequency=16000, gain=24, highpass=0.9883)
15
16# Start audio streaming
17audio.start_streaming(speech.audio_callback)
18
19while True:
20 # Run micro-speech without a timeout and filter detections by label index.
21 idx = speech.listen(model, timeout=0, threshold=0.70, filter=[2, 3])
22 led = led_green if idx == 2 else led_red
23 print(labels[idx])
24 for i in range(0, 4):
25 led.on()
26 time.sleep_ms(25)
27 led.off()
28 time.sleep_ms(25)
29
30# Stop streaming
31audio.stop_streaming()

After running the code, the matches will be printed on the Serial Monitor if the board hears a

No
or a
Yes
, turning on the red and green LED respectively.

Speech recognition inference results
Speech recognition inference results

Using Arduino IDE

The Arduino IDE includes a simple example to visualize raw data from the PDM microphone. To test it, navigate to File > Examples > PDM > PDMSerialPlotter.

1#include <PDM.h>
2
3// default number of output channels
4static const char channels = 1;
5
6// default PCM output frequency
7static const int frequency = 16000;
8
9// Buffer to read samples into, each sample is 16-bits
10short sampleBuffer[512];
11
12// Number of audio samples read
13volatile int samplesRead;
14
15void setup() {
16 Serial.begin(9600);
17 while (!Serial);
18
19 // Configure the data receive callback
20 PDM.onReceive(onPDMdata);
21
22 // Optionally set the gain
23 // Defaults to 20 on the BLE Sense and 24 on the Portenta Vision Shield
24 // PDM.setGain(30);
25
26 // Initialize PDM with:
27 // - one channel (mono mode)
28 // - a 16 kHz sample rate for the Arduino Nano 33 BLE Sense
29 // - a 32 kHz or 64 kHz sample rate for the Arduino Portenta Vision Shield
30 if (!PDM.begin(channels, frequency)) {
31 Serial.println("Failed to start PDM!");
32 while (1);
33 }
34}
35
36void loop() {
37 // Wait for samples to be read
38 if (samplesRead) {
39
40 // Print samples to the serial monitor or plotter
41 for (int i = 0; i < samplesRead; i++) {
42 if(channels == 2) {
43 Serial.print("L:");
44 Serial.print(sampleBuffer[i]);
45 Serial.print(" R:");
46 i++;
47 }
48 Serial.println(sampleBuffer[i]);
49 }
50
51 // Clear the read count
52 samplesRead = 0;
53 }
54}
55
56/**
57 * Callback function to process the data from the PDM microphone.
58 * NOTE: This callback is executed as part of an ISR.
59 * Therefore using `Serial` to print messages inside this function isn't supported.
60 * */
61void onPDMdata() {
62 // Query the number of available bytes
63 int bytesAvailable = PDM.available();
64
65 // Read into the sample buffer
66 PDM.read(sampleBuffer, bytesAvailable);
67
68 // 16-bit, 2 bytes per sample
69 samplesRead = bytesAvailable / 2;
70}

Upload the example code to the Nicla Vision and open the Serial Plotter to see the sound wave output.

Audio sound waves in the serial plotter

Time of Flight (Distance) Sensor

The onboard ToF sensor of the Nicla Vision is the VL53L1CBV0FY from ST®. It adds accurate and low power ranging capabilities to the Nicla Vision. The invisible near-infrared VCSEL laser (including the analog driver) is encapsulated with receiving optics in an all-in-one small module located below the camera.

Onboard Time-of-Flight ranging sensor
Onboard Time-of-Flight ranging sensor

Here are listed the sensor's main features:

  • Up to 400 cm distance measurement
  • Up to 50 Hz ranging frequency
  • 27° field-of-view (FoV)

With OpenMV

The OpenMV IDE includes an example to start using the ToF sensor. To test it, navigate to File > Examples > Sensors > vl53l1x_tof and run it on the Nicla Vision.

1from machine import I2C
2from vl53l1x import VL53L1X
3import time
4
5tof = VL53L1X(I2C(2))
6
7while True:
8 print(f"Distance: {tof.read()}mm")
9 time.sleep_ms(50)
ToF Sensor test using OpenMV
ToF Sensor test using OpenMV

With Arduino IDE

To use the ToF sensor with the Arduino IDE, install the

VL53L1X
library authored by Pololu by searching for it on the IDE library manager and clicking on install.

Once installed, you will be able to compile and upload the example code below to your Nicla Vision.

The distance measured by the sensor will be printed on the IDE's Serial Monitor, and the built-in LED will blink proportionally to that distance.

1#include "VL53L1X.h"
2VL53L1X proximity;
3
4bool blinkState = false;
5int reading = 0;
6int timeStart = 0;
7int blinkTime = 2000;
8
9void setup() {
10 Serial.begin(115200);
11 Wire1.begin();
12 Wire1.setClock(400000); // use 400 kHz I2C
13 proximity.setBus(&Wire1);
14
15
16 pinMode(LEDB, OUTPUT);
17 digitalWrite(LEDB, blinkState);
18
19 if (!proximity.init()) {
20 Serial.println("Failed to detect and initialize sensor!");
21 while (1);
22 }
23
24 proximity.setDistanceMode(VL53L1X::Long);
25 proximity.setMeasurementTimingBudget(50000);
26 proximity.startContinuous(50);
27}
28
29void loop() {
30 reading = proximity.read();
31 Serial.print(reading);
32 Serial.println(" mm");
33
34 if (millis() - timeStart >= reading) {
35 digitalWrite(LEDB, blinkState);
36 timeStart = millis();
37
38 blinkState = !blinkState;
39 }
40}
ToF Sensor test using Arduino
ToF Sensor test using Arduino

Camera

The Nicla Vision's main feature is its onboard 2MP camera, based on the GC2145 color rolling shutter image sensor. It is perfect for Machine Learning applications such as object detection, image classification, machine/computer vision, robotics, IoT, and more.

Onboard camera sensor
Onboard camera sensor

The Nicla Vision is primarily intended to be used with the OpenMV MicroPython ecosystem. So, it's recommended to use this IDE for machine vision applications.

With OpenMV

The OpenMV IDE is designed to work specifically with machine/computer vision hardware, it is optimized for easy and fast development of image processing applications with a MicroPython framework and streaming monitors, color data graphics, and more.

The Nicla Vision uses a 2MP camera sensor, meaning its maximum resolution is 1920x1080 pixels. However, the effective resolution is 1616(H) × 1232(V).

Here we have the minimum code necessary to make the camera work streaming live video on the OpenMV IDE:

1import sensor
2import time
3
4sensor.reset() # Reset and initialize the sensor.
5sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE)
6sensor.set_framesize(sensor.QVGA) # Set frame size to QVGA (320x240)
7sensor.skip_frames(time=2000) # Wait for settings take effect.
8clock = time.clock() # Create a clock object to track the FPS.
9
10while True:
11 clock.tick() # Update the FPS clock.
12 img = sensor.snapshot() # Take a picture and return the image.
13 print(clock.fps()) # Note: OpenMV Cam runs about half as fast when connected
14 # to the IDE. The FPS should increase once disconnected.
Camera streaming demo

From the above example script, we can highlight the main functions:

  • sensor.set_pixformat(<Sensor>)
    lets you set the pixel format for the camera sensor. The Nicla Vision is compatible with these:
    sensor.GRAYSCALE
    ,
    sensor.RGB565
    ,
    sensor.BAYER
    , and
    sensor.YUV422
    .

    To define the pixel format to any of the supported ones, just add it to the

    set_pixformat
    function argument.

  • sensor.set_framesize(<Resolution>)
    lets you define the image frame size in terms of pixels. Here you can find all the different options.

  • sensor.snapshot()
    lets you take a picture and return the image so you can save it, stream it or process it.

The example code below lets you take a picture and save it on the Nicla Vision local storage as

example.jpg
.

1import sensor
2import time
3import machine
4
5sensor.reset() # Reset and initialize the sensor.
6sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE)
7sensor.set_framesize(sensor.QVGA) # Set frame size to QVGA (320x240)
8sensor.skip_frames(time=2000) # Wait for settings take effect.
9
10led = machine.LED("LED_BLUE")
11
12start = time.ticks_ms()
13while time.ticks_diff(time.ticks_ms(), start) < 3000:
14 sensor.snapshot()
15 led.toggle()
16
17led.off()
18
19img = sensor.snapshot()
20img.save("example.jpg") # or "example.bmp" (or others)
21
22raise (Exception("Please reset the camera to see the new file."))

After the snapshot is taken, reset the board by pressing the reset button and the image will be on the board storage drive.

Snapshot saved in local storage
Snapshot saved in local storage

The example code below lets you record a video and save it on the Nicla Vision local storage as

example.mjpeg
.

1import sensor
2import time
3import mjpeg
4import machine
5
6sensor.reset() # Reset and initialize the sensor.
7sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE)
8sensor.set_framesize(sensor.QVGA) # Set frame size to QVGA (320x240)
9sensor.skip_frames(time=2000) # Wait for settings take effect.
10
11led = machine.LED("LED_RED")
12
13led.on()
14m = mjpeg.Mjpeg("example.mjpeg")
15
16clock = time.clock() # Create a clock object to track the FPS.
17for i in range(200):
18 clock.tick()
19 m.add_frame(sensor.snapshot())
20 print(clock.fps())
21
22m.close(clock.fps())
23led.off()
24
25raise (Exception("Please reset the camera to see the new file."))

After the video is recorded, reset the board by pressing the reset button and the file will be on the board storage drive.

Video saved in local storage
Video saved in local storage

We recommend using VLC to play the video due to the format.

The next example lets you live stream what the camera sees through HTTP so you can watch it on your favorite browser from any device connected to the same network as the Nicla Vision.

Make sure to fill in the

SSID
and
KEY
variables with your Wi-Fi® credentials.

1import sensor
2import time
3import network
4import socket
5
6SSID = "*********" # Network SSID
7KEY = "************" # Network key
8HOST = "" # Use first available interface
9PORT = 8080 # Arbitrary non-privileged port
10
11# Init sensor
12sensor.reset()
13sensor.set_framesize(sensor.QVGA)
14sensor.set_pixformat(sensor.RGB565)
15
16# Init wlan module and connect to network
17wlan = network.WLAN(network.STA_IF)
18wlan.active(True)
19wlan.connect(SSID, KEY)
20
21while not wlan.isconnected():
22 print('Trying to connect to "{:s}"...'.format(SSID))
23 time.sleep_ms(1000)
24
25# We should have a valid IP now via DHCP
26print("WiFi Connected ", wlan.ifconfig())
27
28# Create server socket
29s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
30s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
31
32# Bind and listen
33s.bind([HOST, PORT])
34s.listen(5)
35
36# Set server socket to blocking
37s.setblocking(True)
38
39
40def start_streaming(s):
41 print("Waiting for connections..")
42 client, addr = s.accept()
43 # set client socket timeout to 5s
44 client.settimeout(5.0)
45 print("Connected to " + addr[0] + ":" + str(addr[1]))
46
47 # Read request from client
48 data = client.recv(1024)
49 # Should parse client request here
50
51 # Send multipart header
52 client.sendall(
53 "HTTP/1.1 200 OK\r\n"
54 "Server: OpenMV\r\n"
55 "Content-Type: multipart/x-mixed-replace;boundary=openmv\r\n"
56 "Cache-Control: no-cache\r\n"
57 "Pragma: no-cache\r\n\r\n"
58 )
59
60 # FPS clock
61 clock = time.clock()
62
63 # Start streaming images
64 # NOTE: Disable IDE preview to increase streaming FPS.
65 while True:
66 clock.tick() # Track elapsed milliseconds between snapshots().
67 frame = sensor.snapshot()
68 cframe = frame.compressed(quality=35)
69 header = (
70 "\r\n--openmv\r\n"
71 "Content-Type: image/jpeg\r\n"
72 "Content-Length:" + str(cframe.size()) + "\r\n\r\n"
73 )
74 client.sendall(header)
75 client.sendall(cframe)
76 print(clock.fps())
77
78
79while True:
80 try:
81 start_streaming(s)
82 except OSError as e:
83 print("socket error: ", e)
84 # sys.print_exception(e)

Once you run this script, the Nicla Vision IP address will be printed on the OpenMV serial monitor after the Wi-Fi® connection processes.

To watch the live stream, enter the device IP address followed by the

:8080
port as follows:

<Nicla Vision IP>:8080

HTTP camera live stream

To expand your knowledge using the Nicla Vision camera with MicroPython, try other built-in examples within the OpenMV IDE.

OpenMV examples for the Nicla Vision
OpenMV examples for the Nicla Vision

Machine Learning Tools

The Nicla Vision is a ready-to-use, standalone camera board, ready for analyzing and processing images on the Edge. Thanks to its 2MP color camera, smart 6-axis motion sensor, integrated microphone, and distance sensor, it is suitable for almost infinite machine-learning applications.

Creating this type of application has never been easier thanks to our Machine Learning Tool powered by Edge Impulse®, where we can easily create in a No-Code environment, Audio, Motion, Proximity and Image processing models.

The first step to start creating awesome artificial intelligence and machine learning projects is to create an Arduino Cloud account.

There you will find a dedicated integration called Machine Learning Tools.

Machine Learning Tools on Arduino Cloud
Machine Learning Tools on Arduino Cloud

Once in, create a new project and give it a name.

Creating a new project
Creating a new project

Enter your newly created project and the landing page will look like the following:

Nicla Vision project page
Nicla Vision project page

Edge Impulse® Environment Setup

Now, it is time to set up the Edge Impulse® environment on your PC. For this, follow these instructions to install the Edge Impulse CLI.

For Windows users: make sure to install Visual Studio Community andVisual Studio Build Tools.

  • Download and install the latest Arduino CLI from here. (Video Guide for Windows)

  • Download the latest Edge Impulse® firmware, and unzip the file.

  • Open the flash script for your operating system (

    flash_windows.bat
    ,
    flash_mac.command
    or
    flash_linux.sh
    ) to flash the firmware.

  • To test if the Edge Impulse CLI was installed correctly, open the Command Prompt or your favorite terminal and run:

    edge-impulse-daemon

    If everything went okay, you should be asked for your account credentials.

    Edge Impulse Daemon
    Edge Impulse Daemon
  • Enter your account username or e-mail address and your password.

  • Select the project you have created on the Arduino ML Tools, it will be listed.

  • Give your device a name and wait for it to connect to the platform.

Nicla Vision correctly connected to ML Tools
Nicla Vision correctly connected to ML Tools

Uploading Sensor Data

The first thing to start developing a machine learning project is to create a dataset for your model. This means, uploading data to your model from any of the Nicla Vision sensors.

To upload data from your Nicla Vision on the Machine Learning Tools platform, navigate to Data Acquisition.

Data Acquisition section
Data Acquisition section

In this section, you will be able to select the Nicla Vision onboard sensors individually or several interesting combinations.

This is the supported sensors list:

  • Built-in microphone
  • Inertial (IMU)
  • ADC sensor (A0)
  • Proximity sensor
  • Camera (different resolutions)

Now you know how to start with our Machine Learning Tools creating your dataset from scratch, you can get inspired by some of our ML projects listed below:

Communication

This section of the user manual covers the different communication protocols that are supported by the Nicla Vision, 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 Vision features dedicated pins for each communication protocol, making connecting and communicating with different components, peripherals, and sensors easy.

SPI

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

Microcontroller PinArduino Pin Mapping
SCLK / PE_12SCK or 9
CIPO / PE_13MISO or 10
COPI / PE_14MOSI or 8
CS / PE_11SS or 7

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

With OpenMV

Import the

SPI
submodule from the
pyb
module at the top of your sketch alongside
time
and
Pin
to use the SPI communication protocol. The SPI driver provides functions for SPI communication:

1import time
2from pyb import Pin, SPI

Before your infinite loop, configure the chip select (

CS
) pin and initialize the SPI peripheral as a
Master
:

1spi = SPI(4, SPI.MASTER, baudrate=int(480000000 / 256), polarity=0, phase=0)

To send data over SPI, use:

1spi.send(<data>) # add the data (integer or buffer) to be sent as the argument

To receive data over SPI, use:

1spi.recv(<bytes>) # add the number of bytes to be received as the argument

Here is a simple example showing how to send data over SPI.

1import time
2from pyb import Pin, SPI
3
4cs = Pin("SS", Pin.OUT_OD) # CS pin = PE11
5
6spi = SPI(4, SPI.MASTER, baudrate=int(480000000 / 256), polarity=0, phase=0)
7
8while True:
9 # Replace with the target device's address
10 address = 0x35
11 # Replace with the value to send
12 value = 0xFA
13 # Pull the CS pin LOW to select the device
14 cs.low()
15 # Send the address
16 spi.send(address)
17 # Send the value
18 spi.send(value)
19 # Pull the CS pin HIGH to unselect the device
20 cs.high()
21
22 time.sleep_ms(1000)

The example code above should output this:

SPI logic data output
SPI logic data output

With Arduino IDE

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 // Pull the CS pin HIGH to unselect the device
5 digitalWrite(SS, HIGH);
6
7 // Initialize the SPI communication
8 SPI.begin();
9}

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

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

Here is the complete sketch for a simple SPI communication:

1#include <SPI.h>
2
3void setup(){
4 // Set the chip select pin as output
5 pinMode(SS, OUTPUT);
6 // Pull the CS pin HIGH to unselect the device
7 digitalWrite(SS, HIGH);
8
9 // Initialize the SPI communication
10 SPI.begin();
11}
12
13void loop(){
14 // Replace with the target device's address
15 byte address = 0x35;
16 // Replace with the value to send
17 byte value = 0xFA;
18 // Pull the CS pin LOW to select the device
19 digitalWrite(SS, LOW);
20 // Send the address
21 SPI.transfer(address);
22 // Send the value
23 SPI.transfer(value);
24 // Pull the CS pin HIGH to unselect the device
25 digitalWrite(SS, HIGH);
26 delay(1000);
27}

The example code above should output this:

SPI logic data output
SPI logic data output

I2C

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

Microcontroller PinArduino Pin Mapping
PB_8I2C_SCL or 12
PB_9I2C_SDA or 11

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

With OpenMV

To use I2C communication with OpenMV, import

I2C
from the
pyb
module as follows.

1from pyb import I2C

Create the I2C objects and initialize them attached to a specific bus, below are some of the available commands to do it.

1i2c = I2C(1) # create on bus 1
2i2c = I2C(1, I2C.MASTER) # create and init as a master
3i2c.init(I2C.MASTER, baudrate=20000) # init as a master
4i2c.init(I2C.SLAVE, addr=0x42) # init as a slave with given address
5i2c.deinit() # turn off the peripheral

The basic methods are

send
and
recv
implemented as follows:

1# For Masters / Controllers (must include addr in send)
2i2c.send('abc', 0x42) # send 3 bytes to device on address 0x42
3
4# For Slaves / Peripherals
5i2c.send('abc') # send 3 bytes
6i2c.send(0x42) # send a single byte, given by the number
7
8data = i2c.recv(3) # receive 3 bytes

This is a simple example showing how to send data over I2C from a master to a slave.

1from pyb import I2C
2
3i2c = I2C(1, I2C.MASTER)
4
5buf = bytearray(2)
6
7buf[0] = 0x00
8buf[1] = 0xFA
9
10i2c.send(buf, 0x35)

The output data should look like the image below, where we can see the device address data frame:

I2C output data
I2C output data

To learn more about the I2C class on MicroPython, continue here.

With Arduino IDE

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 = 0x35;
3// Replace with the appropriate instruction byte
4byte instruction = 0x00;
5// Replace with the value to send
6byte value = 0xFA;
7// Begin transmission to the target device
8Wire.beginTransmission(deviceAddress);
9// Send the instruction byte
10Wire.write(instruction);
11// Send the value
12Wire.write(value);
13// End transmission
14Wire.endTransmission();

The output data should look like the image below, where we can see the device address data frame:

I2C output data
I2C output data

Here is the complete sketch for a simple I2C communication:

1#include <Wire.h>
2
3void setup(){
4 // Initialize the I2C communication
5 Wire.begin();
6}
7
8void loop(){
9 // Replace with the target device's I2C address
10 byte deviceAddress = 0x35;
11 // Replace with the appropriate instruction byte
12 byte instruction = 0x00;
13 // Replace with the value to send
14 byte value = 0xFA;
15 // Begin transmission to the target device
16 Wire.beginTransmission(deviceAddress);
17 // Send the instruction byte
18 Wire.write(instruction);
19 // Send the value
20 Wire.write(value);
21 // End transmission
22 Wire.endTransmission();
23 delay(1000);
24}

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// The number of bytes to read
4int numBytes = 2;
5// Request data from the target device
6Wire.requestFrom(deviceAddress, numBytes);
7// Read while there is data available
8while (Wire.available()) {
9 byte data = Wire.read();
10}

UART

The pins used in the Nicla Vision for the UART (external) communication protocol are the following:

Microcontroller PinArduino Pin Mapping
PA_10SERIAL1_RX
PA_9SERIAL1_TX

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

With OpenMV

To begin with UART communication, you will need to import

UART
from the
machine
module.

1from machine import UART

Then, initialize the UART object defining the bus number and baudrate.

1uart = UART(9, 115200) # bus 9 uses PA9 and PA10 as (TX and RX) respectively

To read incoming data, you can use different functions as the following.

1uart.read(10) # read 10 characters, returns a bytes object
2uart.read() # read all available characters
3uart.readline() # read a line
4uart.readinto(buf) # read and store into the given buffer

To write data, use the following function.

1uart.write('abc') # write the 3 characters

Here is the complete example that writes "Hello World!" on the external serial port of the Nicla Vision.

1import time
2from machine import UART
3
4# Init UART object.
5uart = UART(9, 115200)
6
7while True:
8 uart.write("Hello World!\r")
9 time.sleep_ms(1000)

This is the output of the example code from above.

UART "Hello World!" data frame
UART "Hello World!" data frame

With Arduino IDE

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

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

1// Start UART communication at 115200 baud
2Serial1.begin(115200); // Serial1 for the external UART pins | Serial for the internal virtual/monitor UART

Using the Arduino IDE, the minimum supported baud rate is 19200.

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 processes the data when a line-ending character is received:

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

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// Transmit the string "Hello world!" followed by a newline character
4Serial1.println("Hello world!");
UART "Hello World!" data frame
UART "Hello World!" data frame

If you want to communicate through the USB serial port, use "Serial" instead of "Serial1" for this case.

Bluetooth® Low Energy

To enable the Bluetooth® Low Energy communication on the Nicla Vision, you can use the

bluetooth
module in OpenMV or the ArduinoBLE library in the Arduino IDE.

We are going to build a Bluetooth® LE temperature monitor that using the nRF Connect app (available for Android and iOS) will let us easily connect to our Nicla Vision and monitor the temperature in real time.

With OpenMV

For this Bluetooth® LE application example, we are going to emulate the temperature sensor. Below you will find the complete sketch.

1import bluetooth
2import random
3import struct
4import time
5from ble_advertising import advertising_payload
6from machine import LED
7from micropython import const
8
9_IRQ_CENTRAL_CONNECT = const(1)
10_IRQ_CENTRAL_DISCONNECT = const(2)
11_IRQ_GATTS_INDICATE_DONE = const(20)
12
13_FLAG_READ = const(0x0002)
14_FLAG_NOTIFY = const(0x0010)
15_FLAG_INDICATE = const(0x0020)
16
17# org.bluetooth.service.environmental_sensing
18_ENV_SENSE_UUID = bluetooth.UUID(0x181A)
19# org.bluetooth.characteristic.temperature
20_TEMP_CHAR = (
21 bluetooth.UUID(0x2A6E),
22 _FLAG_READ | _FLAG_NOTIFY | _FLAG_INDICATE,
23)
24_ENV_SENSE_SERVICE = (
25 _ENV_SENSE_UUID,
26 (_TEMP_CHAR,),
27)
28
29# org.bluetooth.characteristic.gap.appearance.xml
30_ADV_APPEARANCE_GENERIC_THERMOMETER = const(768)
31
32
33class BLETemperature:
34 def __init__(self, ble, name="Py Temp Sensor"):
35 self._ble = ble
36 self._ble.active(True)
37 self._ble.irq(self._irq)
38 ((self._handle,),) = self._ble.gatts_register_services((_ENV_SENSE_SERVICE,))
39 self._connections = set()
40 self._payload = advertising_payload(
41 name=name,
42 services=[_ENV_SENSE_UUID],
43 appearance=_ADV_APPEARANCE_GENERIC_THERMOMETER,
44 )
45 self._advertise()
46 self.led = LED("LED_BLUE")
47
48 def _irq(self, event, data):
49 # Track connections so we can send notifications.
50 if event == _IRQ_CENTRAL_CONNECT:
51 conn_handle, _, _ = data
52 self._connections.add(conn_handle)
53 self.led.on()
54 elif event == _IRQ_CENTRAL_DISCONNECT:
55 conn_handle, _, _ = data
56 self._connections.remove(conn_handle)
57 # Start advertising again to allow a new connection.
58 self._advertise()
59 self.led.off()
60 elif event == _IRQ_GATTS_INDICATE_DONE:
61 conn_handle, value_handle, status = data
62
63 def set_temperature(self, temp_deg_c, notify=False, indicate=False):
64 # Data is sint16 in degrees Celsius with a resolution of 0.01 degrees Celsius.
65 # Write the local value, ready for a central to read.
66 self._ble.gatts_write(self._handle, struct.pack("<h", int(temp_deg_c * 100)))
67 if notify or indicate:
68 for conn_handle in self._connections:
69 if notify:
70 # Notify connected centrals.
71 self._ble.gatts_notify(conn_handle, self._handle)
72 if indicate:
73 # Indicate connected centrals.
74 self._ble.gatts_indicate(conn_handle, self._handle)
75
76 def _advertise(self, interval_us=500000):
77 self._ble.gap_advertise(interval_us, adv_data=self._payload)
78
79
80if __name__ == "__main__":
81 ble = bluetooth.BLE()
82 temp = BLETemperature(ble)
83
84 t = 25
85 i = 0
86
87 while True:
88 # Write every second, notify every 10 seconds.
89 i = (i + 1) % 10
90 temp.set_temperature(t, notify=i == 0, indicate=False)
91 # Random walk the temperature.
92 t += random.uniform(-0.5, 0.5)
93 time.sleep_ms(1000)

The example code shown above creates a Bluetooth® Low Energy service and characteristics according to the Bluetooth® LE standard for transmitting an emulated temperature value.

  • The code begins by importing all the necessary modules and defining the Bluetooth® Low Energy service and characteristics for an environment-sensing application.
DescriptionID
Environmental Sensing Service181A
Temperature Characteristic2A6E
  • Then sets up the Bluetooth® Low Energy service and characteristics; and begins advertising the defined Bluetooth® Low Energy service.

  • A Bluetooth® Low Energy connection is constantly verified; when a central device connects to the Nicla Vision, its built-in LED is turned on blue. The code then enters into a loop that constantly emulates a temperature reading.

    It also prints it to the Serial Monitor and transmits it to the central device over the defined Bluetooth® Low Energy characteristic.

With Arduino IDE

For this Bluetooth® LE application example, we are going to monitor the Nicla Vision IMU temperature sensor. Below you will find the complete sketch.

1#include <ArduinoBLE.h>
2#include <Arduino_LSM6DSOX.h>
3// Bluetooth® Low Energy Environmental Sensing service
4BLEService environmentService("181A");
5// Bluetooth® Low Energy Temperature Characteristic
6BLEIntCharacteristic temperatureVal("2A6E", // standard 16-bit characteristic UUID
7 BLERead | BLENotify); // remote clients will be able to get notifications if this characteristic changes
8int oldTemperature = 0; // last temperature reading from analog input
9
10long previousMillis = 0; // last time the temperature was checked, in ms
11void blePeripheralDisconnectHandler(BLEDevice central) {
12 digitalWrite(LEDR, LOW); // turn on red LED
13 digitalWrite(LEDG, HIGH);
14 digitalWrite(LEDB, HIGH);
15 Serial.println("Device disconnected.");
16}
17void blePeripheralConnectHandler(BLEDevice central) {
18 digitalWrite(LEDB, LOW); // turn on blue LED
19 digitalWrite(LEDR, HIGH);
20 digitalWrite(LEDG, HIGH);
21 Serial.println("Device connected.");
22}
23void setup() {
24 Serial.begin(9600); // initialize serial communication
25 while (!Serial)
26 ;
27
28 pinMode(LEDR, OUTPUT);
29 pinMode(LEDG, OUTPUT);
30 pinMode(LEDB, OUTPUT);
31 // turn off all the LEDs
32 digitalWrite(LEDR, HIGH);
33 digitalWrite(LEDG, HIGH);
34 digitalWrite(LEDB, HIGH);
35
36 if (!IMU.begin()) {
37 Serial.println("Failed to initialize IMU!");
38 while (1)
39 ;
40 }
41
42 // begin initialization
43 if (!BLE.begin()) {
44 Serial.println("starting BLE failed!");
45 while (1)
46 ;
47 }
48 /* Set a local name for the Bluetooth® Low Energy device
49 This name will appear in advertising packets
50 and can be used by remote devices to identify this Bluetooth® Low Energy device
51 The name can be changed but maybe be truncated based on space left in advertisement packet
52 */
53 BLE.setLocalName("Temperature Sensor");
54 BLE.setAdvertisedService(environmentService); // add the service UUID
55 environmentService.addCharacteristic(temperatureVal); // add the temperature characteristic
56 BLE.addService(environmentService); // Add the environment sensing service
57 temperatureVal.writeValue(oldTemperature); // set initial value for this characteristic
58 BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler); // handler that fires when BLE is disconnected
59 BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler); // handler that fires when BLE is disconnected
60 /* Start advertising Bluetooth® Low Energy. It will start continuously transmitting Bluetooth® Low Energy
61 advertising packets and will be visible to remote Bluetooth® Low Energy central devices
62 until it receives a new connection */
63 // start advertising
64 BLE.advertise();
65 Serial.println("Bluetooth® device active, waiting for connections...");
66}
67void loop() {
68 // wait for a Bluetooth® Low Energy central
69 BLEDevice central = BLE.central();
70 // if a central is connected to the peripheral:
71 if (central) {
72 Serial.print("Connected to central: ");
73 // print the central's BT address:
74 Serial.println(central.address());
75 // check the temperature every 200ms
76 // while the central is connected:
77 while (central.connected()) {
78 long currentMillis = millis();
79 // if 200ms have passed, check the temperature:
80 if (currentMillis - previousMillis >= 200) {
81 previousMillis = currentMillis;
82 updateTemperature();
83 }
84 }
85 Serial.print("Disconnected from central: ");
86 Serial.println(central.address());
87 }
88}
89void updateTemperature() {
90 /* Read the temperature*/
91 int temperature = 0; // this command return the battery percentage
92 if (IMU.temperatureAvailable()) {
93 IMU.readTemperature(temperature);
94 }
95
96 if (temperature != oldTemperature) { // if the battery level has changed
97 Serial.print("Temperature is: "); // print it
98 Serial.print(temperature);
99 Serial.println(" °C");
100 temperatureVal.writeValue(temperature * 100); // and update the battery level characteristic
101 oldTemperature = temperature; // save the level for next comparison
102 }
103 delay(1000);
104}

The example code shown above creates a Bluetooth® Low Energy service and characteristics according to the Bluetooth® LE standard for transmitting temperature value read by Nicla Vision IMU IC.

  • The code begins by importing all the necessary libraries and defining the Bluetooth® Low Energy service and characteristics for an environment sensing application.
DescriptionID
Environmental Sensing Service181A
Temperature Characteristic2A6E
  • In the

    setup()
    function, the code initializes the Nicla Vision board and sets up the Bluetooth® Low Energy service and characteristics; then, it begins advertising the defined Bluetooth® Low Energy service.

  • A Bluetooth® Low Energy connection is constantly verified in the

    loop()
    function; when a central device connects to the Nicla Vision, its built-in LED is turned on blue. The code then enters into a loop that constantly reads the IMU temperature sensor. It also prints it to the Serial Monitor and transmits it to the central device over the defined Bluetooth® Low Energy characteristic.

Nicla Vision temperature monitored from the nRF Connect app
Nicla Vision temperature monitored from the nRF Connect app

Wi-Fi®

The Nicla Vision onboard IEEE802.11 b/g/n Wi-Fi® interface can be operated as an access point (AP), station (STA) or dual-mode simultaneous AP/STA. It supports a maximum transfer rate of 65 Mbps.

With OpenMV

The example code below shows how to get the current time using NTP.

1import network
2import socket
3import struct
4import time
5
6SSID = "" # Network SSID
7KEY = "" # Network key
8
9TIMESTAMP = 2208988800
10
11if time.gmtime(0)[0] == 2000:
12 TIMESTAMP += 946684800
13
14# Init wlan module and connect to network
15print("Trying to connect... (This may take a while)...")
16wlan = network.WLAN(network.STA_IF)
17wlan.active(True)
18wlan.connect(SSID, KEY)
19
20while not wlan.isconnected():
21 print('Trying to connect to "{:s}"...'.format(SSID))
22 time.sleep_ms(1000)
23
24# We should have a valid IP now via DHCP
25print("WiFi Connected ", wlan.ifconfig())
26
27# Create new socket
28client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
29
30# Get addr info via DNS
31addr = socket.getaddrinfo("pool.ntp.org", 123)[0][4]
32
33# Send query
34client.sendto("\x1b" + 47 * "\0", addr)
35data, address = client.recvfrom(1024)
36
37# Print time
38t = struct.unpack(">IIIIIIIIIIII", data)[10] - TIMESTAMP
39print("Year:%d Month:%d Day:%d Time: %d:%d:%d" % (time.localtime(t)[0:6]))

Make sure to enter your Wi-Fi® credentials on the

SSID
and
KEY
variables and run the script from the OpenMV IDE.

The current time and date will be printed on the IDE serial monitor.

Requesting time with NTP server
Requesting time with NTP server

If you want to learn more about using Nicla Vision's Wi-Fi® with OpenMV, explore the built-in examples on File > Examples > WiFi.

With Arduino IDE

The example code below shows how to get the current time using NTP.

1#include <NTPClient.h> //http://librarymanager/All#NTPClient
2#include <WiFi.h>
3#include <WiFiUdp.h>
4
5const char *ssid = "";
6const char *password = "";
7
8WiFiUDP ntpUDP;
9
10NTPClient timeClient(ntpUDP);
11
12void setup() {
13 Serial.begin(115200);
14
15 WiFi.begin(ssid, password);
16
17 while (WiFi.status() != WL_CONNECTED) {
18 delay(500);
19 Serial.print(".");
20 }
21
22 timeClient.begin();
23}
24
25void loop() {
26 timeClient.update();
27
28 time_t nowEpoch = timeClient.getEpochTime();
29 struct tm *nowStruct = gmtime(&nowEpoch);
30 int year = nowStruct->tm_year + 1900;
31 int day = nowStruct->tm_mday;
32
33 Serial.print("Year: ");
34 Serial.print(year);
35 Serial.print(" Day: ");
36 Serial.print(day);
37 Serial.print(" Time: ");
38 Serial.println(timeClient.getFormattedTime());
39
40 delay(1000);
41}

Make sure to enter your Wi-Fi® credentials on the

*ssid
and
*password
variables and upload the code from the Arduino IDE.

The current time and date will be printed on the IDE serial monitor.

Requesting time with NTP server
Requesting time with NTP server

If your Nicla Vision reports an error when trying to connect to Wi-Fi® saying Failed to mount the filesystem containing the WiFi firmware, follow the next steps.

In the Arduino IDE navigate to File > Examples > STM32H747_System > WiFiFirmwareUpdater, upload this code to your Nicla Vision and wait for the update. The progress can be followed in the Serial Monitor.

ESLOV Connector

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

Nicla Vision built-in ESLOV connector
Nicla Vision 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

Arduino Cloud

Leveraging the Nicla Vision's Wi-Fi® connectivity we can develop Smart IoT projects using the Arduino Cloud.

By using the Arduino Cloud, you can, for example, monitor your Nicla's inputs and sensors, control your device's built-in LEDs remotely, and update your device's firmware OTA.

In case it is the first time you are using the Arduino Cloud:

  • You need an account. If you do not have an account, create one for free here.
  • To use the Arduino Web Editor or Arduino Cloud, the Arduino Create Agent must be running on your computer. You can install the Arduino Create Agent here.

Let's walk through a step-by-step demonstration of how to use a Nicla Vision with the Arduino Cloud.

Log in to your Arduino Cloud account; you should see the following (without any "Thing" created):

Arduino Cloud initial page
Arduino Cloud initial page

First, provision your Nicla Vision on your Arduino Cloud space. To do this, navigate to Devices and then click on the ADD button:

Arduino Cloud Devices Page
Arduino Cloud Devices Page

The Setup Device pop-up window will appear. Navigate into AUTOMATIC and select the Arduino board option:

Arduino Cloud Setup Device pop-up window
Arduino Cloud Setup Device pop-up window

After a while, your Nicla Vision should be discovered by the Arduino Cloud, as shown below:

Nicla Vision found and ready to be configured
Nicla Vision found and ready to be configured

Click the CONFIGURE button, give your device a name, and your Nicla Vision will be configured to communicate securely with the Arduino Cloud; this process can take a while.

Nicla Vision setup process
Nicla Vision setup process

Once your Nicla Vision has been configured, let's create a "Thing" to test the connection between your board and the Arduino Cloud. Navigate into Things and select the CREATE button; give your thing a name.

Arduino Cloud "Thing" setup
Arduino Cloud "Thing" setup

Navigate into Associate Device and click the Select Device button. Select your Nicla Vision and associate it with your "Thing." Then, navigate into Network and click the Configure button; enter your network credentials.

The project is ready to add variables to your "Thing"; navigate into Cloud Variables and click the ADD VARIABLE button.

Add one variable with the following characteristics:

  • Name:
    led
  • Variable type:
    boolean
  • Variable permission:
    Read & Write
  • Variable update policy:
    On change
Arduino Cloud "Thing" variable setup
Arduino Cloud "Thing" variable setup

Now, navigate into Dashboards and select the CREATE button; this will create a new dashboard and give your dashboard a name.

Arduino Cloud Dashboards page
Arduino Cloud Dashboards page

Add the following widgets to your dashboard:

  • Switch: Name the widget Switch and link it to the
    led
    variable you created before.
  • LED: Name the widget LED and link it to the
    led
    variable you created before.
  • Sticky Note: Give context to your dashboard with a descriptive title (optional).

Your dashboard should look like the following:

Arduino Cloud Dashboard setup
Arduino Cloud Dashboard setup

Go back to your Things and open the "Thing" you created. In the "Thing" setup page, navigate into Sketch, where you should see the online editor.

In the generated sketch, define

LEDR
pin as an output in the
setup()
function:

1void setup() {
2 // Initialize serial and wait for port to open:
3 Serial.begin(9600);
4 // This delay gives the chance to wait for a Serial Monitor without blocking if none is found
5 delay(1500);
6
7 // Nicla Vision's red LED macro is LEDR
8 pinMode(LEDR, OUTPUT);
9 // As they turn on with "LOW", initially turn it off.
10 digitalWrite(LEDR, HIGH);
11
12 // Defined in thingProperties.h
13 initProperties();
14
15 // Connect to Arduino Cloud
16 ArduinoCloud.begin(ArduinoIoTPreferredConnection);
17
18 /*
19 The following function allows you to obtain more information
20 related to the state of network and IoT Cloud connection and errors
21 the higher number the more granular information you’ll get.
22 The default is 0 (only errors).
23 Maximum is 4
24 */
25 setDebugMessageLevel(2);
26 ArduinoCloud.printDebugInfo();
27}

In the

onLedChange()
function, which was generated automatically by the Arduino Cloud when the variable
led
was created, you must associate the onboard red LED state with the
led
variable:

1/*
2 Since Led is READ_WRITE variable, onLedChange() is
3 executed every time a new value is received from IoT Cloud.
4*/
5void onLedChange() {
6 digitalWrite(LEDR, !led);
7}

To upload the code to the Nicla Vision from the online editor, click the green Verify button to compile the sketch and check for errors, then click the green Upload button to program the board with the sketch.

Uploading a sketch to the Nicla Vision in the Arduino Cloud
Uploading a sketch to the Nicla Vision in the Arduino Cloud

Navigate into Dashboards again, your board should connect to the Wi-Fi® network you defined before (you can follow the connection process with the online editor's integrated Serial Monitor). Your board's red LED (LEDR) should light on or off when the position of the switch changes.

Controlling the Nicla Vision LED

Support

If you encounter any issues or have questions while working with the Nicla Vision, 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 Vision. 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 Vision 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 Vision.

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 Vision.

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.