Arduino Nano 33 BLE Sense

Avvertimento

Questa scheda non è più supportata. L’ultima versione del firmware OpenMV per Arduino Nano 33 BLE Sense è la 4.7.0. Non verranno rilasciati ulteriori aggiornamenti del firmware, correzioni di bug o nuove funzionalità per questo target. Le informazioni riportate di seguito sono conservate per gli utenti che utilizzano la versione 4.7.0 o precedenti.

L’Arduino Nano 33 BLE Sense è una scheda da 45 × 18 mm con form factor Arduino‑Nano costruita attorno al Nordic Semiconductor nRF52840 — un singolo ARM Cortex‑M4 con FPU funzionante a 64 MHz con 256 KB di SRAM interna e 1 MB di flash interna. Il BLE è fornito dalla radio integrata nel chip, e la scheda dispone di una IMU a 9 assi, un barometro LPS22HB, un sensore di temperatura / umidità HTS221 / HS3003, un sensore di luce ambientale / colore / prossimità / gesti APDS9960 e un microfono PDM MP34DT05. Il firmware OpenMV gestisce tutti questi componenti da MicroPython.

Arduino Nano 33 BLE Sense

Per il datasheet completo, le foto e le dimensioni, consultare la pagina del prodotto Arduino Nano 33 BLE Rev2.

Punti salienti

  • Nordic nRF52840 Cortex‑M4 con FPU a 64 MHz con 256 KB di SRAM interna e 1 MB di flash interna.

  • Bluetooth LE 5.0 tramite la radio integrata nel chip e il Nordic SoftDevice s140.

  • IMU a 9 assiLSM9DS1 sulla Rev 1, BMI270 + BMM150 sulla Rev 2. Il driver imu integrato rileva entrambi all’avvio.

  • Barometro LPS22HB, sensore di temperatura e umidità HTS221 / HS3003, sensore di luce ambientale / colore / prossimità / gesti APDS9960 e microfono PDM MP34DT05.

  • Connettore Micro USB per alimentazione, programmazione e una REPL CDC.

  • 22 pin di I/O utente sui connettori standard Nano — TX/RX, D2D13 (digitali), A0A7 (analogici).

Pinout

Pinout dell'Arduino Nano 33 BLE Sense

Riferimento dei pin

Nome pin

Riferimento

Funzione

TX

3.3 V

UART1 TX

RX

3.3 V

UART1 RX

D2

3.3 V

PWM

D3

3.3 V

PWM

D4

3.3 V

PWM

D5

3.3 V

PWM

D6

3.3 V

PWM

D7

3.3 V

PWM

D8

3.3 V

PWM

D9

3.3 V

PWM

D10

3.3 V

PWM

D11

3.3 V

PWM / SPI0 MOSI

D12

3.3 V

PWM / SPI0 MISO

D13

3.3 V

PWM / SPI0 SCK

A0

3.3 V

ADC / PWM

A1

3.3 V

ADC / PWM

A2

3.3 V

ADC / PWM

A3

3.3 V

ADC / PWM

A4 / I2C_SDA

3.3 V

ADC / PWM / I2C0 SDA

A5 / I2C_SCL

3.3 V

ADC / PWM / I2C0 SCL

A6

3.3 V

ADC / PWM

A7

3.3 V

ADC / PWM

RESET

3.3 V

premere il pulsante RESET integrato sulla scheda o collegare a GND per resettare

LED_BUILTIN

LED utente arancione su D13

LED_RED

Canale rosso del LED RGB (attivo basso)

LED_GREEN

Canale verde del LED RGB (attivo basso)

LED_BLUE

Canale blu del LED RGB (attivo basso)

Avvertimento

I pin di I/O del Nano 33 BLE Sense sono solo a 3.3 Vnon tollerano i 5 V. Applicare 5 V ad essi danneggerà l’nRF52840.

Pin di alimentazione

  • VIN — ingresso 4.5 – 21 V. Alimenta la scheda attraverso il regolatore integrato. È inoltre alimentato tramite un diodo dal rail USB a 5 V, quindi USB e VIN possono essere presenti contemporaneamente senza retroalimentarsi a vicenda.

  • +5V — non collegato per impostazione predefinita.

  • +3V3 — uscita del regolatore a 3.3 V.

  • AREF — pin di riferimento analogico. Su questa scheda non è collegato all’nRF52840 — l’ADC è sempre riferito a 3.3 V.

  • GND — massa comune.

Il Nano 33 BLE Sense può essere alimentato attraverso entrambe le vie:

  • Micro USB — fornisce 5 V al regolatore integrato.

  • Pin VIN — applicare un’alimentazione regolata di 4.5 – 21 V.

Nota

Un ponticello a saldare sul fondo della scheda, etichettato VUSB, collega +5V al rail USB a 5 V. Chiuderlo affinché il pin +5V del connettore trasporti effettivamente i 5 V.

Nota

Un ponticello a saldare normalmente chiuso sull’uscita del regolatore switching integrato a 4.5–21 V può essere tagliato per disabilitare il regolatore, in modo che la scheda possa essere alimentata direttamente da un’alimentazione esterna a 3.3 V su +3V3.

Pin di recovery e debug

  • RESET — sia un pad esposto che un pulsante RESET momentaneo sulla parte superiore della scheda, collegati alla linea di reset dell’nRF52840. Collegare a GND o premere il pulsante per resettare.

Il Nano 33 BLE Sense utilizza il doppio tocco di reset standard di Arduino per accedere al bootloader di Arduino. Premere rapidamente due volte il pulsante RESET — la scheda entra in modalità bootloader e OpenMV IDE può effettuare il flash di una nuova immagine del firmware.

I segnali SWD dell’nRF52840 sono esposti su pad placcati sul retro della scheda. Tutti i segnali di debug sono riferiti a 3.3 V.

Periferiche integrate

LED

Il Nano 33 BLE Sense dispone di un LED RGB utente — pilotato attraverso i canali serigrafati LED_RED, LED_GREEN e LED_BLUE — più un LED_BUILTIN arancione separato su D13. Tutti e quattro sono controllabili via software tramite machine.LED

from machine import LED

LED("LED_RED").on()
LED("LED_GREEN").on()
LED("LED_BLUE").on()
LED("LED_BUILTIN").on()

Un LED di alimentazione verde separato sulla scheda si accende ogni volta che il rail a +3.3 V è attivo e non è controllabile dall’utente.

Sensore camera

Il firmware OpenMV sul Nano 33 BLE Sense supporta il sensore CMOS parallelo OmniVision OV7670. La scheda non dispone di un sensore di immagine integrato — collegare un modulo OV7670 ai pin del connettore serigrafati elencati di seguito e pilotarlo tramite il modulo csi — sensori camera

import csi

cam = csi.CSI()
cam.reset()
cam.pixformat(csi.RGB565)
cam.framesize(csi.QVGA)
cam.snapshot(time=2000)       # let auto‑exposure settle

while True:
    img = cam.snapshot()

Nota

L’OV7670 utilizza 14 pin. Il firmware li collega come segue:

Segnale del sensore

Pin del Nano 33 BLE Sense

D0

D10

D1

TX

D2

RX

D3

D2

D4

D3

D5

D5

D6

D6

D7

D4

HSYNC

A1

VSYNC

D8

PXCLK

A0

MXCLK

D9

POWER

A3

RESET

A2

SCL

A5 (I²C 0)

SDA

A4 (I²C 0)

Il bus di controllo I²C dell’OV7670 è lo stesso I²C 0 esterno esposto su A5/A4. Il sensore si trova all’indirizzo a 7 bit 0x21 — i dispositivi utente su quel bus devono evitare questo indirizzo quando la camera è collegata.

IMU

La IMU a 9 assi è esposta tramite il modulo imu integrato, che rileva automaticamente se la scheda monta l’LSM9DS1 (Rev 1) o il BMI270 + BMM150 (Rev 2) e presenta una classe imu.IMU unificata. I sensori si trovano sul bus interno I²C 1 (P14 / P15):

import time
from machine import I2C, Pin
from imu import IMU

bus = I2C(1, scl=Pin("P15"), sda=Pin("P14"))
sensor = IMU(bus)

while True:
    print(sensor.accel())     # (x, y, z) in g
    print(sensor.gyro())      # (x, y, z) in deg/s
    print(sensor.magnet())    # (x, y, z) magnetometer
    time.sleep_ms(100)

Per l’accesso diretto a funzionalità come il rilevamento dei tocchi o la FIFO, importare il driver integrato corrispondente (lsm9ds1, bmi270 o bmm150) e istanziarlo sullo stesso bus.

Sensori ambientali

Il barometro (LPS22HB) e il sensore di temperatura / umidità (HTS221 sulla Rev 1, HS3003 sulla Rev 2) condividono lo stesso bus interno I²C 1 della IMU:

import time
from machine import I2C, Pin
from lps22h import LPS22H
from hts221 import HTS221

bus = I2C(1, scl=Pin("P15"), sda=Pin("P14"))
lps = LPS22H(bus)
try:
    hts = HTS221(bus)
except OSError:
    from hs3003 import HS3003
    hts = HS3003(bus)

while True:
    print("pressure:    %.2f hPa" % lps.pressure())
    print("temperature: %.2f C"   % lps.temperature())
    print("humidity:    %.2f %%"  % hts.humidity())
    time.sleep_ms(500)

Luce / colore / prossimità / gesti

L’APDS9960 Broadcom (APDS9960) si trova sullo stesso bus interno I²C 1 e fornisce rilevamento di luce ambientale, colore RGB, prossimità e gesti:

import time
from machine import I2C, Pin
from apds9960 import uAPDS9960 as APDS9960

bus = I2C(1, scl=Pin("P15"), sda=Pin("P14"))
apds = APDS9960(bus)
apds.enableLightSensor()

while True:
    print("ambient light:", apds.readAmbientLight())
    time.sleep_ms(250)

Microfono

Il microfono PDM MP34DT05 integrato viene acquisito tramite audio — Modulo Audio. Ogni buffer arriva come PCM a 16 bit con segno in un bytearray, pronto per essere inviato a ulab/numpy per l’elaborazione DSP:

import audio
from ulab import numpy as np

def loudness(pcmbuf):
    samples = np.array(np.frombuffer(pcmbuf, dtype=np.int16), dtype=np.float)
    rms = np.sqrt(np.mean(samples ** 2))
    if rms > 10000:
        print("Loud!", int(rms))

audio.init(channels=1, frequency=16000, gain_db=24)
audio.start_streaming(loudness)

while True:
    pass

Bluetooth

La radio Bluetooth LE 5.0 dell’nRF52840 funziona sul Nordic SoftDevice s140 ed è esposta tramite il modulo legacy ubluepy — le moderne API bluetooth / aioble — BLE asincrono non sono abilitate in questa build. Sono disponibili sia il ruolo peripheral (server GATT, advertise) che il ruolo central (observer / scanner GAP + connessione).

Effettua l’advertise come peripheral con un singolo servizio Environmental Sensing e una caratteristica di temperatura notificabile — la callback event_handler viene attivata alla connessione, disconnessione e scritture CCCD:

from ubluepy import Service, Characteristic, UUID, Peripheral, constants
from machine import LED

def event_handler(event_id, handle, data):
    if event_id == constants.EVT_GAP_CONNECTED:
        LED("LED_GREEN").on()
    elif event_id == constants.EVT_GAP_DISCONNECTED:
        LED("LED_GREEN").off()
        periph.advertise(device_name="Nano 33", services=[svc])

svc = Service(UUID("181A"))                          # Environmental Sensing
char = Characteristic(UUID("2A6E"),                  # Temperature
                      props=Characteristic.PROP_NOTIFY | Characteristic.PROP_READ,
                      attrs=Characteristic.ATTR_CCCD)
svc.addCharacteristic(char)

periph = Peripheral()
periph.addService(svc)
periph.setConnectionHandler(event_handler)
periph.advertise(device_name="Nano 33", services=[svc])

Effettua la scansione dei dispositivi in advertising nelle vicinanze nel ruolo central:

from ubluepy import Scanner

for entry in Scanner().scan(1_000):                  # 1 second window
    print(entry.addr(), entry.rssi(), "dBm")

Consultare il riferimento di ubluepy per l’API completa — UUID, Service, Characteristic, Peripheral, Scanner, ScanEntry e il namespace delle constants.

Riferimento dei bus

GPIO

Utilizzare machine.Pin per leggere o pilotare uno qualsiasi dei pin serigrafati. Le uscite sono CMOS a 3.3 V — 15 mA per pin, 25 mA totali su tutti i GPIO.

from machine import Pin

out = Pin("D2", Pin.OUT)
out.on()
out.off()
out.value(1)

inp = Pin("D3", Pin.IN, Pin.PULL_UP)
print(inp.value())

Qualsiasi pin di ingresso può anche generare un interrupt sulle transizioni di fronte:

def handler(pin):
    print("triggered:", pin)

Pin("D3", Pin.IN, Pin.PULL_UP).irq(
    handler, Pin.IRQ_FALLING | Pin.IRQ_RISING,
)

UART

Bus

TX

RX

UART1

TX

RX

Utilizzare i nomi serigrafati TX/RX con machine.UART

from machine import UART

uart = UART(1, baudrate=115200)
uart.write("hello")
uart.read(5)

I²C

Bus

SDA

SCL

I2C0

I2C_SDA / A4

I2C_SCL / A5

I2C1

P14

P15

Entrambi i bus richiedono che i loro pin siano passati esplicitamente a machine.I2C

from machine import I2C, Pin

bus0 = I2C(0, scl=Pin("I2C_SCL"), sda=Pin("I2C_SDA"), freq=400_000)
bus0.scan()

bus1 = I2C(1, scl=Pin("P15"), sda=Pin("P14"), freq=400_000)
bus1.scan()

Nota

Il bus 1 è il bus interno dei sensori su P14/P15 (non sui connettori utente) — serve la IMU, il barometro, il sensore ambientale e l’APDS9960. I driver dei sensori integrati lo utilizzano direttamente; il codice utente può anche scansionarlo, ma gli indirizzi sono già occupati dai sensori integrati sulla scheda.

SPI

Bus

MOSI

MISO

SCK

CS

SPI0

D11

D12

D13

D10

La linea CS non è pilotata dalla periferica SPI — configurare D10 come uscita e commutarlo manualmente attorno al trasferimento:

from machine import SPI, Pin

spi = SPI(0, baudrate=10_000_000)
cs = Pin("D10", Pin.OUT, value=1)   # CS is not driven by the SPI peripheral

cs.value(0)
spi.write(b"hello")
cs.value(1)

Nota

D13 funge anche da LED_BUILTIN arancione — pilotare l’SPI su questo bus farà lampeggiare il LED in sincronia con il clock del bus.

ADC

L’nRF52840 dispone di otto canali ADC a 12 bit (SAADC) esposti su A0–A7, tutti riferiti a 3.3 Vread_u16 restituisce 0–65535 su un intervallo di 0–3.3 V al pin. Il pin AREF della scheda non è collegato, quindi il riferimento è sempre 3.3 V:

from machine import ADC
import time

adc = ADC("A0")
while True:
    voltage = adc.read_u16() * 3.3 / 65535
    print(voltage)
    time.sleep_ms(100)

PWM

L’nRF52840 espone quattro periferiche PWM (PWM0PWM3), ciascuna delle quali pilota quattro canali, per un totale di 16 slot PWM hardware. A differenza delle porte a funzione fissa, le periferiche vengono instradate attraverso la matrice GPIOTE — qualsiasi GPIO può essere un’uscita PWM, quindi non esiste una mappatura pin‑slice. Il costo di questa flessibilità sono due vincoli integrati nel silicio:

  • Tutti e quattro i canali all’interno di un modulo condividono un unico periodo/frequenza.

  • Ogni canale ha il proprio duty cycle e la propria polarità.

Concettualmente i 16 slot si presentano così:

Modulo

Ch 0

Ch 1

Ch 2

Ch 3

PWM0

duty

duty

duty

duty

PWM1

duty

duty

duty

duty

PWM2

duty

duty

duty

duty

PWM3

duty

duty

duty

duty

Ogni riga funziona a una frequenza; le quattro celle in una riga pilotano ciascuna un pin scelto indipendentemente con il proprio duty cycle. Righe diverse possono funzionare a frequenze completamente diverse.

Pilota qualsiasi pin serigrafato (o i LED integrati sulla scheda) tramite machine.PWM

from machine import Pin, PWM

pwm = PWM(Pin("D3"), freq=1_000, duty_u16=32768)

Avvertimento

L’auto‑allocazione consuma un intero modulo per chiamata. Quando si crea un PWM senza gli argomenti device=/channel=, il driver acquisisce il primo modulo libero e collega il pin solo al suo canale 0. I restanti tre canali di quel modulo rimangono inattivi e sono raggiungibili solo tramite device=/channel= espliciti. Questo limita le chiamate PWM(Pin(...)) non assistite a quattro prima che il driver sollevi ValueError: all PWM devices in use — anche se tecnicamente dodici slot sono ancora liberi.

Per utilizzare più di quattro PWM, o per condividere deliberatamente una frequenza tra più pin, passare device (0–3) e channel (0–3):

# Two PWMs on the same module → forced to share frequency,
# but each gets its own duty cycle.
pwm_a = PWM(Pin("D3"), device=0, channel=0,
            freq=1_000, duty_u16=32768)
pwm_b = PWM(Pin("D5"), device=0, channel=1,
            freq=1_000, duty_u16=16384)

# A third PWM on a separate module, free to pick any frequency.
pwm_c = PWM(Pin("D6"), device=1, channel=0,
            freq=20_000, duty_u16=49152)

Il duty cycle accetta duty (0–100%), duty_u16 (0–65535) o duty_ns. Aggiungere invert=1 per invertire la polarità dell’uscita (utile per il LED RGB attivo basso).

Nota

Poiché la frequenza è una proprietà a livello di modulo, chiamare pwm.freq(new_freq) su qualsiasi canale di un modulo riesegue nrfx_pwm_init per l’intero modulo e modifica la frequenza vista da ogni altro canale che lo condivide.

Nota

Le frequenze consentite spaziano grosso modo da 4 Hz a 5.3 MHz, derivate dal clock base di 16 MHz con prescaler 1/2/4/8/16/32/64/128 e un contatore di periodo a 15 bit. Il driver sceglie automaticamente il divisore più vicino — freq() riporta il valore richiesto, non quello esatto effettivamente ottenibile.

Bus bit‑banged via software

machine.SoftI2C e machine.SoftSPI funzionano su qualsiasi GPIO se è necessario un bus aggiuntivo.

Sensore termico (esterno alla scheda)

Il firmware include il driver fir — driver del sensore termico (fir == far infrared) per termocamere collegate esternamente:

  • MLX90621 — array IR 16 × 4

  • MLX90640 — array IR 32 × 24

  • MLX90641 — array IR 16 × 12

  • AMG8833 — array IR 8 × 8

Collegare il modulo al bus I²C della scheda e leggere i frame con fir.init() + fir.snapshot()

import time
import image
import fir

fir.init()                          # auto‑detects the sensor
clock = time.clock()

while True:
    clock.tick()
    try:
        img = fir.snapshot(x_scale=5, y_scale=5,
                           color_palette=image.PALETTE_IRONBOW,
                           hint=image.BICUBIC,
                           copy_to_fb=True)
    except OSError:
        continue
    print(clock.fps())

Il driver fir comunica con il sensore solo tramite I²C 0 — collegare il modulo ai pad I2C_SCL / I2C_SDA (A5 / A4).

Temporizzazione

time

Il modulo time copre i ritardi bloccanti, i tick monotoni e la misurazione del tempo trascorso:

import time

time.sleep(1)              # seconds
time.sleep_ms(500)
time.sleep_us(10)

start = time.ticks_ms()
# ...do work...
elapsed = time.ticks_diff(time.ticks_ms(), start)

Timer virtuali

machine.Timer pianifica callback periodiche o one‑shot senza consumare uno slot di timer hardware. Passare -1 come id per utilizzare un timer virtuale (software):

from machine import Timer

one_shot = Timer(-1)
one_shot.init(period=5_000, mode=Timer.ONE_SHOT,
              callback=lambda t: print("once"))

periodic = Timer(-1)
periodic.init(period=2_000, mode=Timer.PERIODIC,
              callback=lambda t: print("tick"))

I valori del periodo sono in millisecondi. Chiamare deinit() per fermare e rilasciare lo slot.

Orologio in tempo reale

machine.RTC mantiene l’ora di sistema attraverso i reset. L’RTC dell’nRF52840 è collegato all’oscillatore on‑chip e non sopravvive a una perdita totale di alimentazione — impostare l’ora ad ogni avvio a freddo se è importante per la propria applicazione:

from machine import RTC

rtc = RTC()
rtc.datetime((2026, 4, 30, 4, 12, 0, 0, 0))   # Y, M, D, weekday, h, m, s, subsec
print(rtc.datetime())

Watchdog

machine.WDT resetta la scheda se l’applicazione si blocca. Una volta avviato non può essere fermato o riconfigurato — alimentarlo periodicamente all’interno del ciclo principale:

from machine import WDT

wdt = WDT(timeout=5_000)   # 5 second window
while True:
    # ...do work...
    wdt.feed()

Informazioni di avvio e runtime

Aggiornamento del firmware

Il Nano 33 BLE Sense utilizza il doppio tocco di reset standard di Arduino per accedere al bootloader di Arduino. Premere rapidamente due volte il pulsante RESET — la scheda entra in modalità bootloader e OpenMV IDE può effettuare il flash di una nuova immagine del firmware.

Uno script in esecuzione può rientrare nel bootloader su richiesta chiamando machine.bootloader()

import machine

machine.bootloader()

Filesystem e ordine di avvio

Il firmware del Nano 33 BLE Sense monta un singolo filesystem all’avvio:

  • Flash interna — sempre montata su /flash e utilizzata come directory di lavoro. Contiene main.py e README.txt per impostazione predefinita; creata al primissimo avvio.

Dopo il montaggio, l’interprete esegue quindi gli script da /flash:

  • boot.py viene eseguito ad ogni soft reset.

  • main.py viene eseguito solo all’avvio a freddo, subito dopo boot.py.

Il main.py predefinito fornito su una scheda appena flashata fa semplicemente lampeggiare il canale blu del LED RGB utente come heartbeat (due impulsi brevi, breve pausa), in modo da poter capire che il firmware si è avviato correttamente senza alcun host collegato.

Su questa scheda /flash non è esposto come unità di archiviazione di massa USB.

Dimensioni di archiviazione

Il Nano 33 BLE Sense è fornito con:

  • /flash — filesystem FAT da 64 KB, in lettura/scrittura.

La build del Nano 33 BLE Sense non include un ROMFS; distribuire i moduli Python direttamente su /flash.

Librerie software

Consultare l”indice della libreria per l’elenco completo dei moduli — inclusi quelli esclusivi della build del Nano 33 BLE Sense.