OpenMV Pure Thermal

OpenMV Pure Thermal — это полнофункциональная плата тепловизионной съёмки, построенная на базе STMicroelectronics STM32H743 (Cortex‑M7 @ 480 МГц) с 64 МБ внешней SDRAM, 32 МБ QSPI‑флеш‑памяти, аппаратным кодеком JPEG, 4,3» ёмкостным сенсорным IPS‑экраном 800×480, выходом HDMI, разъёмом для тепловизионного модуля FLIR® Lepton® и 5‑мегапиксельной камерой видимого света OV5640. Кроме того, она включает Wi‑Fi, разъём microSD, лазерный дальномер, зуммер и мощный белый осветитель.

OpenMV Pure Thermal

Полный технический паспорт, фотографии и размеры см. на странице продукта OpenMV Pure Thermal.

Основные характеристики

  • STMicroelectronics STM32H743XI Cortex‑M7 на частоте 480 МГц.

  • Аппаратный кодировщик/декодировщик JPEG.

  • 64 МБ внешней SDRAM (~400 МБ/с) плюс 1 МБ встроенной SRAM.

  • 2 МБ встроенной флеш‑памяти + 32 МБ внешней QSPI‑флеш‑памяти (чтение ~50 МБ/с).

  • OV5640 — 5‑мегапиксельный датчик видимого света с построчным затвором (rolling shutter).

  • Разъём FLIR® Lepton® — принимает любой модуль Lepton 1/2/2.5/3/3.5, радиометрический или нерадиометрический, с попиксельной температурой в градусах Цельсия.

  • 4,3» ёмкостный сенсорный IPS‑экран 800×480 (24‑битный цвет @ 60 Гц) с поддержкой жестов до 5 точек касания.

  • Выход HDMI через DVI‑сериализатор TFP410 — до 1280×720 @ 60 Гц.

  • Wi‑Fi через WINC1500; MJPEG поверх RTSP поддерживается «из коробки».

  • Полноскоростной USB‑C (12 Мбит/с, ограничение тока 900 мА) — определяется хостом как VCP + USB‑накопитель, а также обеспечивает зарядку.

  • Разъём microSD — SD до 2 ГБ, SDHC до 32 ГБ, SDXC до 2 ТБ.

  • Лазерный дальномер VL53L1CX (до ~4 м).

  • Зуммер с программно управляемой громкостью / частотой.

  • Мощный белый светодиодный осветитель в дополнение к пользовательскому RGB‑светодиоду состояния.

  • Разъём для LiPo‑аккумулятора с зарядкой по USB током 500 мА.

  • 10 выводов ввода/вывода, толерантных к 5 В, с выходом 3,3 В, 25 мА на вывод (всего 120 мА), с поддержкой прерываний. P6 не толерантен к 5 В при использовании в режиме ADC или DAC.

  • 10‑контактный разъём ARM SWD для отладки через ST‑LINK / J‑Link.

  • Разъём Qwiic для периферии I²C.

Примечание

На нижнем левом краю платы есть гнездо для опциональной гайки штатива ¼»–20. С завода она не устанавливается — впаяйте её в гнездо, если хотите крепить плату на стандартный фотоштатив.

Распиновка

Распиновка OpenMV Pure Thermal

Справочник по выводам

Имя вывода

Функция

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

цифровой ввод/вывод

RESET

замкните на GND для сброса платы

SYN

контактная площадка кадровой синхронизации — не подключена

VIN

контактная площадка VIN шилда — не подключена

BOOT0

подайте 3,3 В при включении питания для DFU / ROM‑загрузчика

BUZZER

встроенный пьезоэлектрический зуммер (управляется TIM2/PWM)

LED_RED

красный канал RGB‑светодиода состояния (активный низкий уровень)

LED_GREEN

зелёный канал RGB‑светодиода состояния (активный низкий уровень)

LED_BLUE

синий канал RGB‑светодиода состояния (активный низкий уровень)

LED_WHITE

мощный белый светодиод осветителя

Примечание

Контактные площадки SYN и VIN на шилде/разъёме на Pure Thermal не имеют электрического подключения — они присутствуют только для совместимости с разъёмом. Питайте плату через USB‑C или встроенный разъём LiPo‑аккумулятора (см. Выводы питания ниже). Обратите также внимание, что контактная площадка VIN на плате подписана как VBAT (ошибка маркировки) — её положение соответствует стандартному выводу VIN разъёма OpenMV, и она в любом случае не подключена.

Выводы питания

  • 3.3V — стабилизированная шина 3,3 В. Для шилдов доступно до 250 мА.

  • GND — общая земля.

Pure Thermal питается через USB‑C или встроенный разъём LiPo‑аккумулятора. Порт USB‑C ограничен по току до 900 мА суммарно и также обеспечивает зарядку LiPo током 500 мА, поэтому подключение аккумулятора одновременно с USB поддерживается.

Встроенная кнопка питания включает и выключает системные шины и работает независимо от того, питается ли плата от USB или от LiPo. Удерживайте кнопку пару секунд для переключения состояния — короткое нажатие игнорируется, чтобы предотвратить случайное выключение.

Выбор источника подчиняется двум простым правилам:

  • Аккумулятор питает плату только когда его напряжение выше 3 В. Ниже этого порога встроенный PMIC отключает аккумулятор, чтобы защитить его от чрезмерного разряда.

  • Когда присутствует USB, плату питает USB, а любой подключённый LiPo заряжается в фоновом режиме.

Разъём LiPo также оснащён защитой от обратного напряжения, поэтому подключение аккумулятора наоборот не повредит плату.

Примечание

Плата также направляет сигналы напряжения аккумулятора и датчика тока аккумулятора обратно на каналы ADC микроконтроллера, но программная поддержка ни того, ни другого пока не добавлена.

Выводы восстановления и отладки

  • RESET — замкните на GND для сброса платы. У Pure Thermal также есть отдельная кнопка RESET на плате, которая делает то же самое.

  • BOOT0 — подайте 3,3 В во время включения платы для входа в ROM‑загрузчик STM32 (режим DFU). OpenMV IDE использует этот режим для перепрошивки встроенного загрузчика. Отдельная кнопка BOOT0 на плате делает то же самое — удерживайте её при подаче питания.

Плата предоставляет отладочный разъём SWD (RST / SWCLK / SWDIO / SWO) рядом с разъёмом GPIO, совместимый с адаптерами ST‑LINK и SEGGER J‑Link. Также установлен отдельный 10‑контактный разъём ARM SWD — он несёт те же сигналы SWD (без полного JTAG), но в стандартном 10‑контактном форм‑факторе 0,05».

Примечание

Трассировочный вывод SWO разделяется с тактовым сигналом SPI встроенного FLIR® Lepton®. SWO нельзя использовать одновременно с Lepton — выберите что‑то одно.

На плате установлен третий разъём PURE Modules Debug. Он выводит несколько отладочных сигналов (SWCLK, SWDIO, RST, SPI2_MISO, SPI2_MOSI, VBUS, 3,3 В, GND и два вывода GPIO) для подключения сопутствующих модулей. Два вывода GPIO на этом разъёме управляются внутренней программно эмулируемой (bit‑banged) шиной I²C, а не аппаратным периферийным устройством.

Все три отладочных разъёма (встроенный разъём SWD, 10‑контактный разъём ARM SWD и разъём PURE Modules Debug) рассчитаны на 3,3 В — убедитесь, что ваш отладочный адаптер настроен на логику 3,3 В, прежде чем подключать его.

Встроенная периферия

Светодиоды

У Pure Thermal на плате три светодиода:

  • Пользовательский RGB‑светодиод — управляемый программно, представлен как LED_RED, LED_GREEN и LED_BLUE:

    from machine import LED
    
    LED("LED_RED").on()
    LED("LED_GREEN").on()
    LED("LED_BLUE").on()
    
  • Белый осветитель — управляется через LED_WHITE. LED_WHITE аппаратно подключён с активным высоким уровнем, тогда как прошивка рассматривает все остальные встроенные светодиоды как активные низкие, поэтому используйте low() / high() вместо on() / off() (которые инвертировали бы логику):

    from machine import LED
    
    light = LED("LED_WHITE")
    light.low()    # turn the white LED ON
    light.high()   # turn the white LED OFF
    
  • Светодиод зарядки — управляется напрямую встроенной аппаратурой управления питанием, без программного контроля. Он работает независимо от того, включены системные шины или нет (то есть при любом положении кнопки питания).

    Цвет

    Значение

    Синий

    зарядка — см. список ошибок: может не гаснуть по завершении зарядки

    Зелёный

    зарядка завершена — см. список ошибок: может срабатывать ненадёжно

    Красный

    низкий заряд аккумулятора (≤ 3,2 В, только когда зарядка не активна)

Зуммер

Встроенный пьезоэлектрический зуммер подключён к каналу таймера — управляйте им через machine.PWM для воспроизведения тонов с программно управляемой частотой (высотой) и коэффициентом заполнения (громкостью):

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

Датчик камеры

OV5640 — это основной CSI на Pure Thermal; передайте cid=csi.OV5640, чтобы обратиться к нему явно:

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

OV5640 имеет встроенный JPEG‑компрессор. Установите csi.CSI.pixformat в csi.JPEG, и датчик будет передавать сжатые кадры прямо в камеру по шине камеры, что делает съёмку с высоким разрешением практичной: csi.HD (1280×720), csi.FHD (1920×1080) и полные 5 Мп csi.WQXGA2 (2592×1944) — все передаются в виде JPEG. Настройте сжатие через csi.CSI.quality (0–100, выше = крупнее кадры, больше деталей):

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

OV5640 оснащён автофокусным объективом с актуатором на звуковой катушке. Запустите однократный проход автофокуса через csi.CSI.ioctl с csi.IOCTL_TRIGGER_AUTO_FOCUS — датчик один раз проводит мотором фокуса и фиксируется на том, что находится перед ним:

cam.ioctl(csi.IOCTL_TRIGGER_AUTO_FOCUS)

Повторно вызывайте ioctl каждый раз при изменении сцены — автофокус однократный, а не непрерывный.

Примечание

Выход STROBE датчика OV5640 (используемый для синхронизированной вспышки / ИК‑подсветки) на Pure Thermal подключён к микроконтроллеру, но программная поддержка для него пока не добавлена.

Датчик тепловизионной камеры

Разъём FLIR® Lepton® представлен как второй CSI через тот же API csi — датчики камеры. Передайте cid=csi.LEPTON, чтобы обратиться к нему, и пропустите аппаратный сброс:

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

Примечание

Выход VSYNC датчика Lepton (один импульс на тепловой кадр) на Pure Thermal подключён к микроконтроллеру, но программная поддержка для него пока не добавлена.

Оба CSI могут работать параллельно. В примере ниже извлекается цветной кадр с OV5640 и тепловой кадр с Lepton, после чего Lepton накладывается поверх цветного кадра с использованием палитры Ironbow и альфа‑маски, которая становится прозрачной при низкой интенсивности:

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,
    )

Машинное обучение

ml — Машинное обучение запускает квантованные модели TFLite на Cortex‑M7 с ядрами CMSIS‑NN — достаточно быстро для компактных детекторов с несколькими кадрами в секунду. Модели в файловой системе только для чтения /rom загружаются прямо из флеш‑памяти без копирования в RAM. Вот детектор BlazeFace 128×128, накладывающий обнаруженное лицо и его шесть ключевых точек на каждый кадр с камеры видимого света:

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")

Лазерный дальномер

Встроенный дальномер ST VL53L1CX, работающий по принципу времени пролёта (time‑of‑flight), подключён к шине I²C 2. Используйте встроенный (frozen) драйвер vl53l1x — Драйвер датчика расстояния ToF VL53L1X для получения показаний расстояния до ~4 м:

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)

Вывод на LCD

Встроенный 4,3» LCD имеет разрешение 800 × 480 (WVGA) и управляется через RGB‑интерфейс дисплея модуля display — драйвер дисплея — передайте framesize=display.FWVGA, чтобы соответствовать его собственному разрешению:

import display

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

Подсветка подключена к GPIO, поэтому backlight() принимает True / False (или любое значение 0–100, где 0 — выключено, а любое ненулевое — включено):

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

Сенсорный экран

Контроллер ёмкостного касания — FT5X06; позиции мультитач и события жестов доступны через ft5x06 — драйвер сенсорного экрана. Зарегистрируйте функцию обратного вызова, чтобы реагировать на касания и считывать активные точки внутри неё:

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)

Вывод HDMI

Прошивка также дублирует буфер кадра LCD на встроенный HDMI‑сериализатор tfp410 — Контроллер DVI/HDMI, поэтому внешний монитор зеркалирует то, что отображается на LCD. Создайте экземпляр tfp410.TFP410, чтобы включить выход HDMI:

import tfp410

hdmi = tfp410.TFP410()

Если вам нужен только выход HDMI и встроенный LCD не важен, выключите подсветку и увеличьте разрешение буфера кадра выше WVGA. TFP410 поддерживает до 1280×720 @ 60 Гц, например:

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

Встроенная панель фиксирована на 800×480, поэтому всё, что выше WVGA, имеет смысл только на внешнем HDMI‑мониторе.

Чтобы узнать, когда HDMI‑монитор был подключён или отключён, зарегистрируйте функцию обратного вызова горячего подключения на TFP410. Функция вызывается со значением True при подключении и False при отключении:

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

hdmi.hotplug_callback(on_hotplug)

Вы также можете в любой момент опросить состояние подключения с помощью isconnected() (только когда не зарегистрирована функция обратного вызова).

Порт HDMI также несёт каналы DDC (данные дисплея) и CEC (управление бытовой электроникой), доступные через класс class DisplayData – данные дисплея. Используйте его для чтения блока EDID подключённого монитора (чтобы адаптироваться к его собственному разрешению / частоте обновления) или для отправки/приёма кадров CEC для управления другими HDMI‑устройствами на той же линии:

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

Wi‑Fi работает через модуль Microchip WINC1500, доступный через интерфейс class WINC – драйвер 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])

Примечание

Из‑за дефицита компонентов некоторые экземпляры Pure Thermal поставлялись без установленного модуля WINC1500. Если network.WINC вызывает ошибку или никогда не подключается, проверьте плату на отсутствие модуля Wi‑Fi — остальная часть камеры работает точно так же и без него.

Карта microSD

При вставке карта автоматически монтируется в /sdcard и доступна через обычную файловую систему:

import os

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

Справочник по шинам

GPIO

Используйте machine.Pin для чтения или управления любым из выводов с шелкографией. Выходы — 3,3 В CMOS, толерантны к 5 В на входе, и могут принимать/отдавать до 25 мА на вывод (всего 120 мА на весь разъём).

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

Любой входной вывод также может вызывать прерывание при переходах фронта:

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

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

UART

Шина

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

Шина

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")

То же оборудование можно также использовать в режиме целевого устройства (slave) через machine.I2CTarget, чтобы предоставить область памяти другому контроллеру I²C:

from machine import I2CTarget

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

Встроенный разъём Qwiic выводит одну из этих шин I²C для модулей plug‑and‑play. Линия Qwiic преобразуется по уровню до 5 В через транзисторы с открытым стоком, поэтому шина ограничена только стандартным режимом (100 кГц) и быстрым режимом (400 кГц) — не пытайтесь использовать fast‑mode‑plus или более высокие скорости через разъём Qwiic.

Разъём Qwiic выводит 5 В для питания подключённых модулей; его нельзя использовать для питания самого Pure Thermal — питайте плату через USB‑C или разъём LiPo‑аккумулятора.

SPI

Шина

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)

Шина

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 и DAC

P6 — единственный пользовательский аналоговый вывод. Его можно использовать как 12‑битный вход ADC или выход DAC.

  • ADC — полная шкала при 3,3 В на выводе:

    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 — через pyb.DAC. 8‑битное значение охватывает 0–3,3 В:

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

В режиме ADC или DAC P6 толерантен только к 3,3 В — не подавайте на него 5 В.

PWM

Вывод

Таймер / канал

P4

TIM2 CH3

P5

TIM2 CH4

P6

TIM2 CH1

P7

TIM4 CH1

P8

TIM4 CH2

Примечание

TIM1 зарезервирован прошивкой для генерации пиксельного тактового сигнала датчика камеры, поэтому каналы TIM1, физически выведенные на P0/P1/P2, нельзя использовать для пользовательского PWM, не нарушив работу камеры.

TIM4 разделяется с pyb.Servo — создание сервопривода переконфигурирует весь таймер на работу с частотой 50 Гц, поэтому не смешивайте machine.PWM на P7/P8 с pyb.Servo в одном скрипте.

Управляйте любым из них через machine.PWM:

from machine import Pin, PWM

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

Программно эмулируемые (bit‑banged) шины

machine.SoftI2C и machine.SoftSPI работают на любом GPIO, если вам нужна дополнительная шина.

Тепловизионный датчик (внешний)

В дополнение к встроенному FLIR Lepton, прошивка также включает драйвер fir — драйвер теплового датчика (fir == far infrared, дальний инфракрасный) для внешне подключённых тепловизоров с интерфейсом I²C:

  • MLX90621 — ИК‑матрица 16 × 4

  • MLX90640 — ИК‑матрица 32 × 24

  • MLX90641 — ИК‑матрица 16 × 12

  • AMG8833 — ИК‑матрица 8 × 8

Подключите модуль к шине I²C платы и считывайте кадры с помощью 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())

Драйвер fir общается с датчиком только по I²C 2 — подключите модуль к P4 (SCL) и P5 (SDA).

Тайминг

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)

Виртуальные таймеры

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"))

Значения периода указаны в миллисекундах. Вызовите deinit(), чтобы остановить таймер и освободить слот.

Часы реального времени

from machine import RTC

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

Если подключён LiPo‑аккумулятор, RTC продолжают отсчёт времени даже при выключенных системных шинах (выключение через встроенную кнопку питания). При подключённом только USB нажатие кнопки питания отключает питание и от RTC — поэтому без подключённого аккумулятора астрономическое время не сохранится при перезапуске питания.

Сторожевой таймер (watchdog)

from machine import WDT

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

Информация о загрузке и времени выполнения

Окно USB‑загрузчика

При каждом включении камера запускает короткий загрузчик (несколько секунд), который позволяет OpenMV IDE обновить прошивку без необходимости входить в режим DFU. По истечении окна загрузчик передаёт управление boot.py, а затем main.py.

Работающий скрипт может повторно войти в загрузчик по запросу, вызвав machine.bootloader().

Файловая система и порядок загрузки

Прошивка Pure Thermal при загрузке монтирует до трёх файловых систем:

  • Встроенная флеш‑память — всегда монтируется в /flash. По умолчанию содержит main.py и README.txt; создаётся при самой первой загрузке.

  • Карта microSD — если карта вставлена, она монтируется в /sdcard.

  • ROMFS — файловая система только для чтения с отображением в память по адресу /rom, используемая для размещения крупных ресурсов данных (например, моделей ИИ), которым выгоден доступ без копирования. Монтируется автоматически MicroPython при запуске, до выполнения любого пользовательского кода Python.

После монтирования рабочий каталог устанавливается в /sdcard, если карта присутствует, иначе в /flash. Затем интерпретатор запускает скрипты из этого каталога:

  • boot.py выполняется при каждом программном сбросе.

  • main.py выполняется только при холодной загрузке, сразу после boot.py.

Размещение boot.py или main.py на SD‑карте переопределяет копию во флеш‑памяти, не затрагивая её.

При подключении по USB загрузочная файловая система (/sdcard, если карта присутствует, иначе /flash) также определяется на хосте как USB‑накопитель. Извлеките накопитель перед сбросом камеры, чтобы хост сбросил свои кэшированные записи.

Примечание

Файлы, созданные или изменённые кодом, выполняющимся на OpenMV Cam, не появятся на хосте, пока накопитель не будет смонтирован заново. Используйте SD‑карту для любых данных, которые записывает скрипт, и перемонтируйте её перед чтением этих файлов с хоста.

Объёмы хранилища

Pure Thermal поставляется со следующим:

  • /flash — файловая система FAT объёмом 24 МБ, для чтения/записи.

  • /rom — отображённая в память ROMFS только для чтения объёмом 8 МБ, используемая для размещения скриптов и ML‑моделей, которым выгоден доступ через mmap без копирования.

  • /sdcard — полный объём вставленной карты microSD (если присутствует), для чтения/записи.

Индикатор аппаратного сбоя (hard fault)

Если пользовательский RGB‑светодиод быстро перебирает все цвета — настолько быстро, что выглядит как мерцающий белый светодиод, а не отдельные оттенки — прошивка столкнулась с невосстановимым аппаратным сбоем (hard fault). Для восстановления перепрошейте прошивку.

Аппаратные ошибки (errata)

Несколько особенностей уровня платы описаны в списке аппаратных ошибок Pure Thermal. Ключевые моменты, о которых следует знать:

  • Помехи со стороны разъёма аккумулятора — компоненты на печатной плате расположены прямо под разъёмом LiPo‑аккумулятора, и выступающий клин на штекере кабеля аккумулятора может зацепиться за них при отсоединении кабеля, иногда срывая детали с платы. Срежьте клин со штекера кабеля торцевыми кусачками перед первым использованием.

  • RTC останавливаются при выключенной плате — ёмкость нагрузки на кварцевом резонаторе 32 кГц (Y2) слишком велика. Удаление C96 и C97 (пары по обе стороны от резонатора рядом с STM32) позволяет RTC продолжать работу от резервного питания. На большинстве плат эти конденсаторы уже удалены при поставке; если ваши RTC теряют время при отключении, проверьте эти позиции. Полное обсуждение см. в задачах GitHub #1536 и #1600.

  • Индикаторный светодиод зарядки остаётся синим — зарядное устройство может завершить цикл зарядки где‑то между 4,15 В и 4,19 В, не переключая индикатор с синего (зарядка) на зелёный (заряжен). Аккумулятор в этом случае всё равно полностью заряжен; доверяйте измерению напряжения, а не светодиоду.

  • Шелкография ошибочно подписывает VIN как VBAT — контактная площадка в стандартной позиции VIN разъёма OpenMV на Pure Thermal подписана шелкографией как VBAT. Подпись неверна, но на практике это не имеет значения, поскольку площадка в любом случае не имеет электрического подключения.

Программные библиотеки

Полный список модулей — включая те, что уникальны для сборки Pure Thermal — см. в индексе библиотек.