Arduino Nano 33 BLE Sense¶
Advertencia
Esta placa ya no recibe soporte. La última versión del firmware de OpenMV para la Arduino Nano 33 BLE Sense es la 4.7.0. No se publicarán más actualizaciones de firmware, correcciones de errores ni nuevas funciones para este objetivo. La información que figura a continuación se conserva para los usuarios que utilicen la 4.7.0 o versiones anteriores.
La Arduino Nano 33 BLE Sense es una placa de 45 × 18 mm con factor de forma Arduino-Nano construida en torno al nRF52840 de Nordic Semiconductor: un único ARM Cortex-M4 con FPU que funciona a 64 MHz con 256 KB de SRAM interna y 1 MB de memoria flash interna. El BLE proviene de la radio integrada en el chip, y la placa incorpora una IMU de 9 ejes, un barómetro LPS22HB, un sensor de temperatura/humedad HTS221 / HS3003, un sensor de luz ambiental / color / proximidad / gestos APDS9960 y un micrófono PDM MP34DT05. El firmware de OpenMV controla todos ellos desde MicroPython.
Para la hoja de datos completa, fotos y dimensiones, consulta la página del producto Arduino Nano 33 BLE Rev2.
Aspectos destacados¶
Nordic nRF52840 Cortex-M4 con FPU a 64 MHz con 256 KB de SRAM interna y 1 MB de memoria flash interna.
Bluetooth LE 5.0 a través de la radio integrada en el chip y el Nordic SoftDevice s140.
IMU de 9 ejes —
LSM9DS1en la Rev 1,BMI270+BMM150en la Rev 2. El controladorimucongelado detecta ambos en el arranque.Barómetro
LPS22HB, sensor de temperatura y humedadHTS221/HS3003, sensor de luz ambiental / color / proximidad / gestosAPDS9960y micrófono PDM MP34DT05.Conector Micro USB para alimentación, programación y un REPL por CDC.
22 pines de E/S de usuario en los conectores estándar de la Nano:
TX/RX,D2–D13(digitales),A0–A7(analógicos).
Distribución de pines¶
Referencia de pines¶
Nombre del pin |
Referencia |
Función |
|---|---|---|
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 |
pulsa el botón RESET de la placa o conecta a GND para reiniciar |
LED_BUILTIN |
— |
LED de usuario naranja en |
LED_RED |
— |
Canal rojo del LED RGB (activo a nivel bajo) |
LED_GREEN |
— |
Canal verde del LED RGB (activo a nivel bajo) |
LED_BLUE |
— |
Canal azul del LED RGB (activo a nivel bajo) |
Advertencia
Los pines de E/S de la Nano 33 BLE Sense son solo de 3,3 V: no toleran 5 V. Aplicar 5 V a ellos dañará el nRF52840.
Pines de alimentación¶
VIN — entrada de 4,5 – 21 V. Alimenta la placa a través del regulador integrado. También recibe alimentación mediante un diodo desde la línea de 5 V del USB, por lo que el USB y
VINpueden estar presentes al mismo tiempo sin realimentarse entre sí.+5V — sin conectar de forma predeterminada.
+3V3 — salida del regulador de 3,3 V.
AREF — pin de referencia analógica. No está cableado al nRF52840 en esta placa: el ADC siempre se referencia a 3,3 V.
GND — tierra común.
La Nano 33 BLE Sense puede alimentarse por cualquiera de estas vías:
Micro USB — suministra 5 V al regulador integrado.
Pin VIN — aplica una fuente regulada de 4,5 – 21 V.
Nota
Un puente de soldadura en la parte inferior de la placa etiquetado VUSB conecta +5V con la línea de 5 V del USB. Ciérralo para que el pin del conector +5V realmente lleve 5 V.
Nota
Un puente de soldadura normalmente cerrado en la salida del regulador conmutado integrado de 4,5–21 V puede cortarse para deshabilitar el regulador, de modo que la placa pueda alimentarse directamente desde una fuente externa de 3,3 V en +3V3.
Pines de recuperación y depuración¶
RESET — tanto un pad expuesto como un botón RESET momentáneo en la parte superior de la placa, conectados a la línea de reinicio del nRF52840. Conecta a GND o pulsa el botón para reiniciar.
La Nano 33 BLE Sense utiliza el doble pulsado de reset estándar de Arduino para entrar en el bootloader de Arduino. Pulsa rápidamente el botón RESET dos veces: la placa entra en modo bootloader y OpenMV IDE puede grabar una nueva imagen de firmware.
Las señales SWD del nRF52840 están expuestas en pads chapados en la parte posterior de la placa. Todas las señales de depuración están referenciadas a 3,3 V.
Periféricos integrados¶
LEDs¶
La Nano 33 BLE Sense tiene un LED RGB de usuario —controlado a través de los canales serigrafiados LED_RED, LED_GREEN y LED_BLUE— más un LED_BUILTIN naranja independiente en D13. Los cuatro se pueden controlar por software mediante machine.LED:
from machine import LED
LED("LED_RED").on()
LED("LED_GREEN").on()
LED("LED_BLUE").on()
LED("LED_BUILTIN").on()
Un LED verde de alimentación independiente en la placa se enciende siempre que la línea de +3,3 V está activa y no es controlable por el usuario.
Sensor de cámara¶
El firmware de OpenMV en la Nano 33 BLE Sense admite el sensor CMOS paralelo OmniVision OV7670. La placa no tiene sensor de imagen integrado: conecta un módulo OV7670 a los pines del conector serigrafiados que se enumeran a continuación y contrólalo mediante el módulo csi — sensores de cámara:
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
El OV7670 ocupa 14 pines. El firmware los conecta de la siguiente manera:
Señal del sensor |
Pin de la Nano 33 BLE Sense |
|---|---|
D0 |
|
D1 |
|
D2 |
|
D3 |
|
D4 |
|
D5 |
|
D6 |
|
D7 |
|
HSYNC |
|
VSYNC |
|
PXCLK |
|
MXCLK |
|
POWER |
|
RESET |
|
SCL |
|
SDA |
|
El bus de control I²C del OV7670 es el mismo I²C 0 externo expuesto en A5/A4. El sensor se encuentra en la dirección de 7 bits 0x21: los dispositivos de usuario en ese bus deben evitar esta dirección cuando la cámara está conectada.
IMU¶
La IMU de 9 ejes se expone a través del módulo imu congelado, que detecta automáticamente si la placa tiene el LSM9DS1 (Rev 1) o el BMI270 + BMM150 (Rev 2) y presenta una clase imu.IMU unificada. Los sensores están en el 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)
Para acceder directamente a funciones como la detección de toques o la FIFO, importa el controlador congelado correspondiente (lsm9ds1, bmi270 o bmm150) e instáncialo en el mismo bus.
Sensores ambientales¶
El barómetro (LPS22HB) y el sensor de temperatura/humedad (HTS221 en la Rev 1, HS3003 en la Rev 2) comparten el mismo bus interno I²C 1 que la 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)
Luz / color / proximidad / gestos¶
El APDS9960 de Broadcom está en el mismo bus interno I²C 1 y proporciona detección de luz ambiental, color RGB, proximidad y gestos:
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)
Micrófono¶
El micrófono PDM MP34DT05 integrado se captura mediante audio — Módulo de audio. Cada búfer llega como PCM de 16 bits con signo en un bytearray, listo para introducirlo en ulab/numpy para procesamiento de señales:
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 del nRF52840 funciona sobre el Nordic SoftDevice s140 y se expone a través del módulo heredado ubluepy; las APIs modernas bluetooth / aioble — BLE asíncrono no están habilitadas en esta compilación. Están disponibles tanto el rol periférico (servidor GATT, anunciar) como el rol central (observador / escáner GAP + conexión).
Anúnciate como periférico con un único servicio Environmental Sensing y una característica de temperatura notificable: la función de retorno (callback) event_handler se activa en la conexión, la desconexión y las escrituras del 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])
Escanea dispositivos cercanos que anuncian en el rol central:
from ubluepy import Scanner
for entry in Scanner().scan(1_000): # 1 second window
print(entry.addr(), entry.rssi(), "dBm")
Consulta la referencia de ubluepy para la API completa: UUID, Service, Characteristic, Peripheral, Scanner, ScanEntry y el espacio de nombres constants.
Referencia de buses¶
GPIO¶
Usa machine.Pin para leer o controlar cualquiera de los pines serigrafiados. Las salidas son CMOS de 3,3 V: 15 mA por pin, 25 mA en total entre todos los 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())
Cualquier pin de entrada también puede disparar una interrupción en las transiciones de flanco:
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 |
Usa los nombres serigrafiados 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 |
|
|
Ambos buses necesitan que sus pines se pasen explícitamente 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
El bus 1 es el bus interno de sensores en P14/P15 (no está en los conectores de usuario): da servicio a la IMU, el barómetro, el sensor ambiental y el APDS9960. Los controladores de sensores congelados lo usan directamente; el código de usuario también puede escanearlo, pero las direcciones ya están ocupadas por los sensores integrados.
SPI¶
Bus |
MOSI |
MISO |
SCK |
CS |
|---|---|---|---|---|
SPI0 |
D11 |
D12 |
D13 |
D10 |
La línea CS no la controla el periférico SPI: configura D10 como salida y conmútala manualmente alrededor de la transferencia:
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 también funciona como el LED_BUILTIN naranja: controlar SPI en este bus hará parpadear el LED al ritmo del reloj del bus.
ADC¶
El nRF52840 tiene ocho canales ADC de 12 bits (SAADC) expuestos en A0–A7, todos referenciados a 3,3 V: read_u16 devuelve 0–65535 a lo largo de 0–3,3 V en el pin. El pin AREF de la placa no está cableado, por lo que la referencia siempre es 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¶
El nRF52840 expone cuatro periféricos PWM (PWM0–PWM3), cada uno controlando cuatro canales, para un total de 16 ranuras de PWM por hardware. A diferencia de los puertos de función fija, los periféricos se enrutan a través de la matriz GPIOTE: cualquier GPIO puede ser una salida PWM, por lo que no hay una correspondencia fija entre pin y segmento. El precio de esa flexibilidad son dos restricciones grabadas en el silicio:
Los cuatro canales dentro de un módulo comparten un único período/frecuencia.
Cada canal tiene su propio ciclo de trabajo y polaridad.
Conceptualmente, las 16 ranuras se ven así:
Módulo |
Canal 0 |
Canal 1 |
Canal 2 |
Canal 3 |
|---|---|---|---|---|
PWM0 |
ciclo de trabajo |
ciclo de trabajo |
ciclo de trabajo |
ciclo de trabajo |
PWM1 |
ciclo de trabajo |
ciclo de trabajo |
ciclo de trabajo |
ciclo de trabajo |
PWM2 |
ciclo de trabajo |
ciclo de trabajo |
ciclo de trabajo |
ciclo de trabajo |
PWM3 |
ciclo de trabajo |
ciclo de trabajo |
ciclo de trabajo |
ciclo de trabajo |
Cada fila funciona a una frecuencia; las cuatro celdas de una fila controlan cada una un pin elegido independientemente con su propio ciclo de trabajo. Filas distintas pueden funcionar a frecuencias completamente diferentes.
Controla cualquier pin serigrafiado (o los LEDs de la placa) mediante machine.PWM:
from machine import Pin, PWM
pwm = PWM(Pin("D3"), freq=1_000, duty_u16=32768)
Advertencia
La asignación automática consume un módulo entero por llamada. Cuando creas un PWM sin los argumentos device=/channel=, el controlador toma el primer módulo libre y vincula tu pin únicamente a su canal 0. Los tres canales restantes de ese módulo quedan inactivos y solo se pueden alcanzar mediante device=/channel= explícitos. Eso limita las llamadas PWM(Pin(...)) sin ayuda a cuatro antes de que el controlador genere ValueError: all PWM devices in use, aunque técnicamente sigan libres doce ranuras.
Para usar más de cuatro PWM, o para compartir deliberadamente una frecuencia entre varios pines, pasa device (0–3) y 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)
El ciclo de trabajo acepta duty (0–100 %), duty_u16 (0–65535) o duty_ns. Añade invert=1 para invertir la polaridad de la salida (útil para el LED RGB activo a nivel bajo).
Nota
Como la frecuencia es una propiedad por módulo, llamar a pwm.freq(new_freq) en cualquier canal de un módulo vuelve a ejecutar nrfx_pwm_init para todo el módulo y cambia la frecuencia que ven todos los demás canales que lo comparten.
Nota
Las frecuencias permitidas abarcan aproximadamente de 4 Hz a 5,3 MHz, derivadas del reloj base de 16 MHz con preescaladores 1/2/4/8/16/32/64/128 y un contador de período de 15 bits. El controlador elige el divisor más cercano automáticamente: freq() informa del valor solicitado, no del valor exacto alcanzable.
Buses emulados por software (bit-banged)¶
machine.SoftI2C y machine.SoftSPI funcionan en cualquier GPIO si necesitas un bus adicional.
Sensor térmico (externo)¶
El firmware incluye el controlador fir — controlador de sensor térmico (fir == infrarrojo lejano) para sensores de imagen térmica cableados externamente:
MLX90621 — matriz IR de 16 × 4
MLX90640 — matriz IR de 32 × 24
MLX90641 — matriz IR de 16 × 12
AMG8833 — matriz IR de 8 × 8
Conecta el módulo al bus I²C de la placa y lee fotogramas 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())
El controlador fir solo se comunica con el sensor a través de I²C 0: conecta el módulo a los pads I2C_SCL / I2C_SDA (A5 / A4).
Temporización¶
time¶
El módulo time cubre retardos bloqueantes, ticks monótonos y la medición del tiempo transcurrido:
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)
Temporizadores virtuales¶
machine.Timer programa funciones de retorno periódicas o de un solo disparo sin consumir una ranura de temporizador por hardware. Pasa -1 como id para usar un temporizador virtual (por 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"))
Los valores de período están en milisegundos. Llama a deinit() para detener y liberar la ranura.
Reloj en tiempo real¶
machine.RTC mantiene la hora del reloj a lo largo de los reinicios. El RTC del nRF52840 está vinculado al oscilador del chip y no sobrevive a una pérdida total de alimentación: ajusta la hora en cada arranque en frío si es importante para tu aplicación:
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 reinicia la placa si la aplicación se cuelga. Una vez iniciado, no se puede detener ni reconfigurar: aliméntalo periódicamente dentro de tu bucle principal:
from machine import WDT
wdt = WDT(timeout=5_000) # 5 second window
while True:
# ...do work...
wdt.feed()
Información de arranque y de ejecución¶
Actualización del firmware¶
La Nano 33 BLE Sense utiliza el doble pulsado de reset estándar de Arduino para entrar en el bootloader de Arduino. Pulsa rápidamente el botón RESET dos veces: la placa entra en modo bootloader y OpenMV IDE puede grabar una nueva imagen de firmware.
Un script en ejecución puede volver a entrar en el bootloader bajo demanda llamando a machine.bootloader():
import machine
machine.bootloader()
Sistema de archivos y orden de arranque¶
El firmware de la Nano 33 BLE Sense monta un único sistema de archivos en el arranque:
Memoria flash interna — siempre montada en
/flashy usada como directorio de trabajo. Contienemain.pyyREADME.txtde forma predeterminada; se crea en el primer arranque.
Tras el montaje, el intérprete ejecuta entonces los scripts desde /flash:
boot.pyse ejecuta en cada reinicio en caliente.main.pyse ejecuta solo en el arranque en frío, inmediatamente después deboot.py.
El main.py predeterminado que se incluye en una placa recién grabada simplemente hace parpadear el canal azul del LED RGB de usuario como latido (dos pulsos cortos, breve pausa), de modo que puedes saber que el firmware arrancó correctamente sin ningún host conectado.
/flash no se expone como una unidad de almacenamiento masivo USB en esta placa.
Tamaños de almacenamiento¶
La Nano 33 BLE Sense se entrega con:
/flash— sistema de archivos FAT de 64 KB, lectura/escritura.
La compilación de la Nano 33 BLE Sense no incluye un ROMFS; distribuye los módulos de Python directamente en /flash.
Bibliotecas de software¶
Consulta el índice de la biblioteca para ver la lista completa de módulos, incluidos los que son exclusivos de la compilación de la Nano 33 BLE Sense.