OpenMV Pure Thermal

La OpenMV Pure Thermal es una placa de imagen térmica de sistema completo construida en torno al STMicroelectronics STM32H743 (Cortex‑M7 @ 480 MHz) con 64 MB de SDRAM externa, 32 MB de memoria flash QSPI, un códec JPEG por hardware, una pantalla táctil capacitiva IPS de 4,3» y 800×480, una salida HDMI, un zócalo térmico FLIR® Lepton® y una cámara de luz visible OV5640 de 5MP. También incorpora Wi‑Fi, un zócalo microSD, un telémetro láser, un zumbador y un iluminador blanco de alta potencia.

OpenMV Pure Thermal

Para consultar la hoja de datos completa, fotos y dimensiones, consulta la página del producto OpenMV Pure Thermal.

Aspectos destacados

  • STMicroelectronics STM32H743XI Cortex‑M7 a 480 MHz.

  • Codificador/decodificador JPEG por hardware.

  • 64 MB de SDRAM externa (~400 MB/s) más 1 MB de SRAM interna.

  • 2 MB de memoria flash interna + 32 MB de memoria flash QSPI externa (~50 MB/s de lectura).

  • Sensor de luz visible OV5640 de 5MP con obturador rodante.

  • Zócalo FLIR® Lepton® — acepta cualquier módulo Lepton 1/2/2.5/3/3.5, radiométrico o no radiométrico, con temperatura por píxel en grados Celsius.

  • Pantalla táctil capacitiva IPS de 4,3» y 800×480 (color de 24 bits @ 60 Hz) con soporte de gestos de hasta 5 puntos.

  • Salida HDMI a través del serializador DVI TFP410 — hasta 1280×720 @ 60 Hz.

  • Wi‑Fi a través del WINC1500; MJPEG sobre RTSP soportado de fábrica.

  • USB‑C de velocidad completa (12 Mb/s, limitado a 900 mA de corriente) — aparece como VCP + almacenamiento masivo USB ante el host, y también gestiona la carga.

  • Zócalo microSD — SD de hasta 2 GB, SDHC de hasta 32 GB, SDXC de hasta 2 TB.

  • Telémetro láser VL53L1CX (hasta ~4 m).

  • Zumbador con volumen / frecuencia controlados por software.

  • LED iluminador blanco de alta potencia además del LED de estado RGB de usuario.

  • Conector de batería LiPo con carga por USB a 500 mA.

  • 10 pines de E/S, tolerantes a 5 V con salida de 3,3 V, 25 mA por pin (120 mA en total), con capacidad de interrupción. P6 no es tolerante a 5 V cuando se usa en modo ADC o DAC.

  • Conector SWD ARM de 10 pines para depuración con ST‑LINK / J‑Link.

  • Conector Qwiic para periféricos I²C.

Nota

La placa tiene una ranura en su borde inferior izquierdo para una tuerca de trípode opcional de ¼»–20. No viene instalada de fábrica — suelda una en la ranura si quieres montar la placa en un trípode de cámara estándar.

Distribución de pines

Distribución de pines de la OpenMV Pure Thermal

Referencia de pines

Nombre del pin

Función

P0

UART1 RX / SPI2 MOSI

P1

UART1 TX / SPI2 MISO

P2

SPI2 SCK / FDCAN2 TX

P3

SPI2 NSS (CS) / FDCAN2 RX

P4

I2C2 SCL / UART3 TX / TIM2 CH3

P5

I2C2 SDA / UART3 RX / TIM2 CH4

P6

ADC / DAC / TIM2 CH1

P7

I2C4 SCL / TIM4 CH1

P8

I2C4 SDA / TIM4 CH2

P9

E/S digital

RESET

lleva a GND para reiniciar la placa

SYN

pad de sincronización de fotograma — no conectado

VIN

pad VIN del shield — no conectado

BOOT0

lleva a 3,3 V al encender para DFU / gestor de arranque (bootloader) ROM

BUZZER

zumbador piezoeléctrico de a bordo (controlado por TIM2/PWM)

LED_RED

canal rojo del LED de estado RGB (activo en bajo)

LED_GREEN

canal verde del LED de estado RGB (activo en bajo)

LED_BLUE

canal azul del LED de estado RGB (activo en bajo)

LED_WHITE

LED iluminador blanco de alta potencia

Nota

Los pads SYN y VIN del shield/cabezal no tienen conexión eléctrica en la Pure Thermal — están ahí solo por compatibilidad con el cabezal. Alimenta la placa a través de USB‑C o del conector de batería LiPo de a bordo en su lugar (consulta Pines de alimentación más abajo). Ten en cuenta también que el pad VIN está serigrafiado como VBAT en la placa (un error de etiquetado) — la posición es la del pin VIN estándar del cabezal OpenMV y no está conectado de ninguna manera.

Pines de alimentación

  • 3.3V — riel regulado de 3,3 V. Hasta 250 mA disponibles para shields.

  • GND — tierra común.

La Pure Thermal se alimenta a través de USB‑C o del conector de batería LiPo de a bordo. El puerto USB‑C está limitado a 900 mA de corriente total y también gestiona la carga del LiPo a 500 mA, por lo que se admite conectar una batería junto con el USB.

El botón de encendido de a bordo activa y desactiva los rieles del sistema y funciona tanto si la placa se alimenta por USB como por el LiPo. Mantén pulsado el botón durante un par de segundos para cambiar de estado — una pulsación rápida se ignora para evitar un apagado accidental.

La selección de fuente sigue dos reglas sencillas:

  • La batería solo alimenta la placa cuando su voltaje está por encima de 3 V. Por debajo de ese umbral, el PMIC de a bordo desconecta la batería para protegerla de una sobredescarga.

  • Cuando hay USB presente, el USB alimenta la placa y cualquier LiPo conectado se carga en segundo plano.

El conector LiPo también cuenta con protección contra polaridad inversa, por lo que conectar la batería al revés no dañará la placa.

Nota

La placa también enruta el voltaje de la batería y una señal de detección de corriente de la batería de vuelta a los canales ADC del MCU, pero todavía no se ha añadido soporte de firmware para ninguna de las dos.

Pines de recuperación y depuración

  • RESET — lleva a GND para reiniciar la placa. La Pure Thermal también tiene un botón RESET dedicado en la placa que hace lo mismo.

  • BOOT0 — lleva a 3,3 V mientras alimentas la placa para entrar en el gestor de arranque (bootloader) ROM del STM32 (modo DFU). OpenMV IDE usa este modo para reflashear el bootloader de a bordo. Un botón BOOT0 dedicado en la placa hace lo mismo — mantenlo pulsado mientras aplicas la alimentación.

La placa expone un cabezal de depuración SWD (RST / SWCLK / SWDIO / SWO) junto al cabezal GPIO, compatible con adaptadores ST‑LINK y SEGGER J‑Link. También se incluye un conector SWD ARM de 10 pines independiente — lleva las mismas señales SWD (sin JTAG completo) pero en el formato estándar de 10 pines a 0,05».

Nota

El pin de traza SWO se comparte con el reloj SPI del FLIR® Lepton® de a bordo. SWO no se puede usar al mismo tiempo que el Lepton — elige uno u otro.

Un tercer conector PURE Modules Debug está montado en la placa. Saca un puñado de señales orientadas a la depuración (SWCLK, SWDIO, RST, SPI2_MISO, SPI2_MOSI, VBUS, 3,3 V, GND y dos pines GPIO) para conectar módulos complementarios. Los dos pines GPIO de este conector son controlados por un bus I²C emulado por software (bit‑banged) interno en lugar de un periférico de hardware.

Los tres conectores de depuración (el cabezal SWD en línea, el conector SWD ARM de 10 pines y el conector PURE Modules Debug) están referenciados a 3,3 V — asegúrate de que tu adaptador de depuración esté configurado para lógica de 3,3 V antes de conectar.

Periféricos de a bordo

LEDs

La Pure Thermal tiene tres LEDs en la placa:

  • LED RGB de usuario — controlable por software, expuesto como LED_RED, LED_GREEN y LED_BLUE:

    from machine import LED
    
    LED("LED_RED").on()
    LED("LED_GREEN").on()
    LED("LED_BLUE").on()
    
  • Iluminador blanco — controlado a través de LED_WHITE. LED_WHITE está cableado activo en alto en hardware, mientras que el firmware trata cualquier otro LED de a bordo como activo en bajo, así que usa low() / high() en lugar de on() / off() (que invertirían el sentido):

    from machine import LED
    
    light = LED("LED_WHITE")
    light.low()    # turn the white LED ON
    light.high()   # turn the white LED OFF
    
  • LED de carga — controlado directamente por el hardware de gestión de energía de a bordo, sin control por software. Funciona tanto si los rieles del sistema están encendidos como apagados (es decir, con el botón de encendido en cualquier posición).

    Color

    Significado

    Azul

    cargando — consulta la fe de erratas: puede que no se apague cuando la carga se complete

    Verde

    carga completa — consulta la fe de erratas: puede que no se active de forma fiable

    Rojo

    batería baja (≤ 3,2 V, solo cuando no se está cargando activamente)

Zumbador

El zumbador piezoeléctrico de a bordo está cableado a un canal de temporizador — contrólalo con machine.PWM para tonos con frecuencia (tono) y ciclo de trabajo (volumen) controlados por software:

import time
from machine import Pin, PWM

beep = PWM(Pin("BUZZER"), freq=2_000, duty_u16=32768)   # ~50% duty
time.sleep_ms(500)                                      # sound for 0.5 s
beep.deinit()

Sensor de cámara

El OV5640 es la CSI principal de la Pure Thermal — pasa cid=csi.OV5640 para direccionarlo explícitamente:

import csi

cam = csi.CSI(cid=csi.OV5640)
cam.reset(hard=True)
cam.pixformat(csi.RGB565)
cam.framesize(csi.WVGA)
cam.snapshot(time=2000)       # let auto‑exposure settle

while True:
    img = cam.snapshot()

El OV5640 tiene un compresor JPEG de a bordo. Configura csi.CSI.pixformat a csi.JPEG y el sensor entrega fotogramas comprimidos directamente a la cámara a través del bus de cámara, lo que hace prácticas las capturas de alta resolución: csi.HD (1280×720), csi.FHD (1920×1080) y los 5MP completos csi.WQXGA2 (2592×1944) se transmiten como JPEG. Ajusta la compresión con csi.CSI.quality (0‑100, mayor = fotogramas más grandes, más detalle):

cam.pixformat(csi.JPEG)
cam.framesize(csi.WQXGA2)
cam.quality(90)

El OV5640 tiene una lente de enfoque automático con actuador de bobina móvil. Activa una única pasada de enfoque automático mediante csi.CSI.ioctl con csi.IOCTL_TRIGGER_AUTO_FOCUS — el sensor barre el motor de enfoque una vez y se fija en lo que tenga delante:

cam.ioctl(csi.IOCTL_TRIGGER_AUTO_FOCUS)

Vuelve a emitir el ioctl cada vez que la escena cambie — el enfoque automático es de una sola toma, no continuo.

Nota

La salida STROBE del OV5640 (usada para flash sincronizado / iluminación IR) está cableada al MCU en la Pure Thermal, pero todavía no se ha añadido soporte de firmware para ella.

Sensor de cámara térmica

El zócalo FLIR® Lepton® aparece como una segunda CSI en la misma API csi — sensores de cámara. Pasa cid=csi.LEPTON para direccionarlo, y omite el reinicio por hardware:

import csi

lepton = csi.CSI(cid=csi.LEPTON)
lepton.reset(hard=False)
lepton.pixformat(csi.GRAYSCALE)
lepton.framesize(csi.QVGA)

while True:
    img = lepton.snapshot()

Nota

La salida VSYNC del Lepton (un pulso por fotograma térmico) está cableada al MCU en la Pure Thermal, pero todavía no se ha añadido soporte de firmware para ella.

Ambas CSIs pueden funcionar en paralelo. El ejemplo siguiente extrae un fotograma de color del OV5640 y un fotograma térmico del Lepton, y luego superpone el Lepton sobre el fotograma de color usando una paleta Ironbow y una máscara alfa que se desvanece a transparente con baja intensidad:

import csi
import image
import math

alpha_pal = image.Image(256, 1, image.GRAYSCALE)
for i in range(256):
    alpha_pal[i] = int(math.pow((i / 255), 2) * 255)

csi0 = csi.CSI()
csi0.reset(hard=True)
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.WVGA)

csi1 = csi.CSI(cid=csi.LEPTON)
csi1.reset(hard=False)
csi1.pixformat(csi.GRAYSCALE)
csi1.framesize(csi.QVGA)

img1 = image.Image(csi1.width(), csi1.height(), csi1.pixformat())

while True:
    img0 = csi0.snapshot()
    csi1.snapshot(blocking=False, image=img1)
    img0.draw_image(
        img1, 0, 0,
        color_palette=image.PALETTE_IRONBOW,
        alpha_palette=alpha_pal,
        hint=image.BILINEAR,
    )

Aprendizaje automático

ml — Aprendizaje automático ejecuta modelos TFLite cuantizados en el Cortex‑M7 con núcleos CMSIS‑NN — lo bastante rápido para detectores compactos a unos pocos fotogramas por segundo. Los modelos en el sistema de archivos de solo lectura /rom se cargan directamente desde la memoria flash sin copiar a la RAM. Aquí tienes un detector BlazeFace de 128×128 que superpone la cara detectada y sus seis puntos de referencia en cada fotograma de la cámara de luz visible:

import csi
import time
import ml
from ml.postprocessing.mediapipe import BlazeFace

# Initialize the sensor.
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
csi0.window((400, 400))

# Load built-in face detection model
model = ml.Model("/rom/blazeface_front_128.tflite", postprocess=BlazeFace(threshold=0.4))
print(model)

clock = time.clock()
while True:
    clock.tick()
    img = csi0.snapshot()

    # faces is a list of ((x, y, w, h), score, keypoints) tuples
    for r, score, keypoints in model.predict([img]):
        ml.utils.draw_predictions(img, [r], ("face",), ((0, 0, 255),), format=None)

        # keypoints is a ndarray of shape (6, 2)
        # 0 - right eye (x, y)
        # 1 - left eye (x, y)
        # 2 - nose (x, y)
        # 3 - mouth (x, y)
        # 4 - right ear (x, y)
        # 5 - left ear (x, y)
        ml.utils.draw_keypoints(img, keypoints, color=(255, 0, 0))

    print(clock.fps(), "fps")

Telémetro láser

El telémetro de tiempo de vuelo ST VL53L1CX de a bordo está cableado al bus I²C 2. Usa el controlador congelado vl53l1x — Controlador del sensor de distancia ToF VL53L1X para obtener lecturas de distancia de hasta ~4 m:

import time
from machine import I2C
import vl53l1x

bus = I2C(2)
tof = vl53l1x.VL53L1X(bus)

while True:
    print("Distance:", tof.read(), "mm")
    time.sleep_ms(100)

Salida LCD

La LCD de a bordo de 4,3» es de 800 × 480 (WVGA) y se controla a través de la interfaz de pantalla RGB del módulo display — controlador de pantalla — pasa framesize=display.FWVGA para coincidir con su resolución nativa:

import display

lcd = display.RGBDisplay(framesize=display.FWVGA, refresh=60)
lcd.backlight(True)           # turn the LCD backlight on
lcd.write(img)

La retroiluminación está cableada a un GPIO, así que backlight() acepta True / False (o cualquier valor de 0–100, donde 0 es apagado y cualquier valor distinto de cero es encendido):

lcd.backlight(False)           # turn the backlight off
lcd.backlight(True)            # back on

Pantalla táctil

El controlador táctil capacitivo es el FT5X06; las posiciones multitáctiles y los eventos de gestos se exponen a través de ft5x06 — Controlador de pantalla táctil. Registra una función de retorno (callback) para reaccionar a los toques y leer los puntos activos dentro de ella:

import ft5x06

touch = ft5x06.FT5X06()

def on_touch(n):
    for i in range(n):
        x = touch.get_point_x(i)
        y = touch.get_point_y(i)
        print("touch", i, "at", x, y)

    gesture = touch.get_gesture()
    if gesture != ft5x06.GESTURE_NONE:
        print("gesture:", gesture)

touch.touch_callback(on_touch)

Salida HDMI

El firmware también reparte el framebuffer de la LCD hacia el serializador HDMI tfp410 — Controlador DVI/HDMI de a bordo, de modo que un monitor externo refleja lo que hay en la LCD. Instancia tfp410.TFP410 para habilitar la salida HDMI:

import tfp410

hdmi = tfp410.TFP410()

Si solo quieres salida HDMI y no te importa la LCD de a bordo, apaga la retroiluminación y aumenta la resolución del framebuffer por encima de WVGA. El TFP410 admite hasta 1280×720 @ 60 Hz, así que por ejemplo:

lcd = display.RGBDisplay(framesize=display.HD, refresh=60)
lcd.backlight(False)         # the on‑board LCD can't render HD
hdmi = tfp410.TFP410()

El panel de a bordo está fijado en 800×480, por lo que cualquier valor por encima de WVGA solo tiene sentido en el monitor HDMI externo.

Para saber cuándo se ha conectado o desconectado un monitor HDMI, registra una función de retorno (callback) de conexión en caliente en el TFP410. La función de retorno se dispara con True al conectar y False al desconectar:

def on_hotplug(connected):
    print("HDMI", "connected" if connected else "disconnected")

hdmi.hotplug_callback(on_hotplug)

También puedes consultar el estado de la conexión en cualquier momento con isconnected() (solo cuando no hay ninguna función de retorno (callback) registrada).

El puerto HDMI también lleva los canales DDC (datos de pantalla) y CEC (control de electrónica de consumo), expuestos a través de la clase clase DisplayData – Datos de pantalla. Úsala para leer el bloque EDID del monitor conectado (de modo que puedas adaptarte a su resolución / frecuencia de actualización nativa) o para enviar/recibir tramas CEC para controlar otros dispositivos HDMI en el mismo cable:

from display import DisplayData

dd = DisplayData(cec=True, ddc=True)

edid = dd.display_id()        # EDID bytes from the monitor
print(len(edid), "byte EDID")

# Send a CEC "image view on" command (opcode 0x04) from address 1 to address 0
dd.send_frame(0, 1, b"\x04")

# ...or wait for an incoming CEC frame addressed to us (logical address 1)
src, data = dd.receive_frame(1, timeout=5_000)
print("CEC from", src, ":", data)

Wi‑Fi

El Wi‑Fi funciona sobre un módulo Microchip WINC1500, expuesto a través de la interfaz class WINC – controlador del escudo WiFi:

import network, time

wlan = network.WINC()
wlan.connect("ssid", "password")
while not wlan.isconnected():
    time.sleep(1)
print("Wi‑Fi IP:", wlan.ipconfig("addr4")[0])

Nota

Debido a la escasez de componentes, algunas unidades Pure Thermal se enviaron sin el módulo WINC1500 montado. Si network.WINC lanza un error o nunca se conecta, comprueba si a la placa le falta el módulo Wi‑Fi — el resto de la cámara funciona exactamente igual sin él.

tarjeta microSD

Cuando se inserta una tarjeta, se monta automáticamente en /sdcard y se puede usar a través del sistema de archivos habitual:

import os

for entry in os.listdir("/sdcard"):
    print(entry)

Referencia de buses

GPIO

Usa machine.Pin para leer o controlar cualquiera de los pines serigrafiados. Las salidas son CMOS de 3,3 V, tolerantes a 5 V en el lado de entrada, y pueden absorber/suministrar hasta 25 mA por pin (120 mA en total en todo el cabezal).

from machine import Pin

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

inp = Pin("P1", 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("P1", Pin.IN, Pin.PULL_UP).irq(
    handler, Pin.IRQ_FALLING | Pin.IRQ_RISING,
)

UART

Bus

TX

RX

UART1

P1

P0

UART3

P4

P5

from machine import UART

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

I²C

Bus

SCL

SDA

I2C2

P4

P5

I2C4

P7

P8

from machine import I2C

i2c = I2C(2, freq=400_000)
i2c.scan()
i2c.writeto(0x76, b"hi")

El mismo hardware también se puede usar en modo objetivo (esclavo) a través de machine.I2CTarget para exponer una región de memoria a otro controlador I²C:

from machine import I2CTarget

buf = bytearray(32)
target = I2CTarget(2, addr=0x42, mem=buf)

El conector Qwiic de a bordo saca uno de estos buses I²C para módulos plug‑and‑play. La línea Qwiic se eleva de nivel a 5 V mediante transistores de drenaje abierto, por lo que el bus está limitado únicamente a modo estándar (100 kHz) y modo rápido (400 kHz) — no intentes ejecutar fast‑mode‑plus o velocidades superiores a través del cabezal Qwiic.

El conector Qwiic emite 5 V para alimentar los módulos conectados; no se puede usar para alimentar la propia Pure Thermal — alimenta la placa a través de USB‑C o del conector de batería LiPo en su lugar.

SPI

Bus

MOSI

MISO

SCK

CS

SPI2

P0

P1

P2

P3

from machine import SPI
from machine import Pin

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

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

CAN (FDCAN)

Bus

TX

RX

FDCAN2

P2

P3

from machine import CAN

can = CAN(2, 500_000)
can.set_filters(None)
can.send(0x123, b"\xDE\xAD\xBE\xEF")
print(can.recv())

ADC y DAC

P6 es el único pin analógico de usuario. Se puede usar como entrada ADC de 12 bits o como salida DAC.

  • ADC — escala completa a 3,3 V en el pin:

    from machine import ADC
    import time
    
    adc = ADC("P6")
    while True:
        voltage = adc.read_u16() * 3.3 / 65535
        print(voltage)
        time.sleep_ms(100)
    
  • DAC — a través de pyb.DAC. El valor de 8 bits cubre 0–3,3 V:

    from pyb import DAC
    
    dac = DAC("P6")
    voltage = 1.65
    dac.write(int(voltage / 3.3 * 255))
    

En modo ADC o DAC, P6 es tolerante solo a 3,3 V — no le apliques 5 V.

PWM

Pin

Temporizador / canal

P4

TIM2 CH3

P5

TIM2 CH4

P6

TIM2 CH1

P7

TIM4 CH1

P8

TIM4 CH2

Nota

TIM1 está reservado por el firmware para generar el reloj de píxel del sensor de cámara, por lo que los canales de TIM1 que están físicamente en P0/P1/P2 no se pueden usar para PWM de usuario sin romper la cámara.

TIM4 se comparte con pyb.Servo — instanciar un servo reconfigura todo el temporizador para operar a 50 Hz, así que no mezcles machine.PWM en P7/P8 con pyb.Servo en el mismo script.

Controla cualquiera de ellos a través de machine.PWM:

from machine import Pin, PWM

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

Buses emulados por software (bit‑banged)

machine.SoftI2C y machine.SoftSPI funcionan en cualquier GPIO si necesitas un bus adicional.

Sensor térmico (externo)

Además del FLIR Lepton de a bordo, el firmware también incluye el controlador fir — controlador de sensor térmico (fir == infrarrojo lejano) para imágenes térmicas I²C cableadas 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

Cablea 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 2 — cablea el módulo a P4 (SCL) y P5 (SDA).

Temporización

time

import time

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

start = time.ticks_ms()
elapsed = time.ticks_diff(time.ticks_ms(), start)

Temporizadores virtuales

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 de tiempo real

from machine import RTC

rtc = RTC()
rtc.datetime((2026, 4, 30, 4, 12, 0, 0, 0))
print(rtc.datetime())

Si hay una batería LiPo conectada, el RTC mantiene la hora incluso mientras los rieles del sistema están apagados (apagados mediante el botón de encendido de a bordo). Con solo el USB conectado, pulsar el botón de encendido también corta la alimentación al RTC — por lo que la hora del reloj no sobrevivirá a un ciclo de apagado sin una batería conectada.

Watchdog

from machine import WDT

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

Información de arranque y de ejecución

Ventana del gestor de arranque (bootloader) USB

En cada encendido, la cámara ejecuta un breve gestor de arranque (bootloader) (unos pocos segundos) que permite a OpenMV IDE actualizar el firmware sin que el usuario tenga que entrar en modo DFU. Una vez que expira la ventana, el bootloader cede el control a boot.py y luego a main.py.

Un script en ejecución puede volver a entrar en el gestor de arranque (bootloader) a demanda llamando a machine.bootloader().

Sistema de archivos y orden de arranque

El firmware de la Pure Thermal monta hasta tres sistemas de archivos en el arranque:

  • Memoria flash interna — siempre montada en /flash. Contiene main.py y README.txt por defecto; se crea en el primer arranque.

  • tarjeta microSD — si se inserta una tarjeta, se monta en /sdcard.

  • ROMFS — sistema de archivos de solo lectura mapeado en memoria en /rom, usado para distribuir grandes recursos de datos (p. ej. modelos de IA) que se benefician del acceso sin copia. Montado automáticamente por MicroPython al inicio, antes de que se ejecute cualquier código Python de usuario.

Tras el montaje, el directorio de trabajo se establece en /sdcard cuando la tarjeta está presente, de lo contrario en /flash. El intérprete ejecuta entonces los scripts desde ese directorio:

  • boot.py se ejecuta en cada reinicio suave.

  • main.py se ejecuta solo en el arranque en frío, inmediatamente después de boot.py.

Colocar un boot.py o main.py en la tarjeta SD anula la copia en la memoria flash sin tocarla.

Cuando está conectada por USB, el sistema de archivos de arranque (/sdcard si hay una tarjeta presente, de lo contrario /flash) también se enumera como una unidad de almacenamiento masivo USB en el host. Expulsa la unidad antes de reiniciar la cámara para que el host vacíe sus escrituras en caché.

Nota

Los archivos creados o modificados por el código que se ejecuta en la OpenMV Cam no aparecerán en el host hasta que la unidad se vuelva a montar. Usa la tarjeta SD para cualquier dato que el script escriba de vuelta, y vuelve a montar antes de leer esos archivos desde el host.

Tamaños de almacenamiento

La Pure Thermal viene con:

  • /flash — sistema de archivos FAT de 24 MB, lectura/escritura.

  • /rom — ROMFS de solo lectura mapeado en memoria de 8 MB, usado para distribuir scripts y modelos de ML que se benefician del acceso por mmap sin copia.

  • /sdcard — el tamaño completo de cualquier tarjeta microSD que se inserte (cuando está presente), lectura/escritura.

Indicador de fallo grave (hard fault)

Si el LED RGB de usuario está recorriendo rápidamente todos los colores — lo bastante rápido como para que tienda a parecer un LED blanco parpadeante en lugar de tonos distintos — el firmware ha sufrido un fallo grave irrecuperable. Reflashea el firmware para recuperarlo.

Fe de erratas del hardware

Un puñado de peculiaridades a nivel de placa están documentadas en la fe de erratas del hardware de la Pure Thermal. Aspectos clave a tener en cuenta:

  • Interferencia del conector de batería — hay componentes en la PCB situados justo debajo del conector de batería LiPo, y la cuña sobresaliente del enchufe del cable de la batería puede engancharse en ellos al retirar el cable, arrancando a veces piezas de la placa. Recorta la cuña del enchufe del cable con alicates de corte enrasado antes del primer uso.

  • El RTC se detiene mientras la placa está apagada — la capacitancia de carga del cristal de 32 kHz (Y2) es demasiado alta. Quitar C96 y C97 (el par que flanquea el cristal junto al STM32) permite que el RTC siga funcionando con la alimentación de respaldo. La mayoría de las placas se envían con estos condensadores ya retirados; si tu RTC pierde la hora al desconectarlo, comprueba esas posiciones. Consulta los issues de GitHub #1536 y #1600 para ver el hilo completo.

  • El LED indicador de carga se queda azul — el cargador puede terminar su ciclo de carga en cualquier punto entre 4,15 V y 4,19 V sin cambiar el indicador de azul (cargando) a verde (cargado). La batería sigue completamente cargada en este caso; confía en una medición de voltaje, no en el LED.

  • La serigrafía etiqueta erróneamente VIN como VBAT — el pad en la posición VIN estándar del cabezal OpenMV está serigrafiado como VBAT en la Pure Thermal. La etiqueta es incorrecta, pero no importa en la práctica porque el pad no tiene conexión eléctrica de ninguna manera.

Bibliotecas de software

Consulta el índice de la biblioteca para ver la lista completa de módulos — incluidos los que son exclusivos de la build de la Pure Thermal.