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.
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 assi —
LSM9DS1sulla Rev 1,BMI270+BMM150sulla Rev 2. Il driverimuintegrato rileva entrambi all’avvio.Barometro
LPS22HB, sensore di temperatura e umiditàHTS221/HS3003, sensore di luce ambientale / colore / prossimità / gestiAPDS9960e 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,D2–D13(digitali),A0–A7(analogici).
Pinout¶
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 |
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 V — non 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
VINpossono 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 |
|
D1 |
|
D2 |
|
D3 |
|
D4 |
|
D5 |
|
D6 |
|
D7 |
|
HSYNC |
|
VSYNC |
|
PXCLK |
|
MXCLK |
|
POWER |
|
RESET |
|
SCL |
|
SDA |
|
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 |
|
|
I2C1 |
|
|
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 V — read_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 (PWM0–PWM3), 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
/flashe utilizzata come directory di lavoro. Contienemain.pyeREADME.txtper impostazione predefinita; creata al primissimo avvio.
Dopo il montaggio, l’interprete esegue quindi gli script da /flash:
boot.pyviene eseguito ad ogni soft reset.main.pyviene eseguito solo all’avvio a freddo, subito dopoboot.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.