OpenMV AE3

L’OpenMV AE3 è costruita attorno all’Alif Ensemble E3 — un SoC dual ARM Cortex‑M55 (core HP a 400 MHz + core HE a 160 MHz) con due NPU on‑chip (NPU HP a 400 MHz / 204 GOPS + NPU HE a 160 MHz / 46 GOPS). La scheda abbina le NPU al sensore global‑shutter PAG7936 da 1 MP, USB‑C high‑speed, Wi‑Fi, Bluetooth 5.1, una IMU LSM6DSM, un microfono e un telemetro time‑of‑flight VL53L8CX 8×8, il tutto su una scheda da 30 × 30 mm.

OpenMV AE3

Per il datasheet completo, le foto e le dimensioni consulta la pagina prodotto dell’OpenMV AE3.

In evidenza

  • Alif Ensemble E3 — dual ARM Cortex‑M55 con Helium SIMD a 128 bit, core HP a 400 MHz + core HE a 160 MHz (~640 / ~256 DMIPS, CoreMark 1748 / 752).

  • Doppia NPU: NPU HP a 400 MHz / 204 GOPS + NPU HE a 160 MHz / 46 GOPS per AI/ML — esegue il rilevamento oggetti YOLO insieme ad altri carichi di lavoro.

  • GPU 2D hardware per lo scaling.

  • 13,5 MB di SRAM interna più 5,5 MB di MRAM on‑chip e 32 MB di flash octal esterna (DDR 8‑bit a 100 MHz, 200 MB/s in lettura).

  • 4 KB di RAM di backup con l’RTC on‑chip.

  • Sensore PAG7936 global‑shutter a colori da 1 MP.

  • IMU integrata (accelerometro + giroscopio LSM6DSM), microfono e sensore time‑of‑flight VL53L8CX 8×8 (fino a 4 m).

  • USB‑C high‑speed (480 Mb/s) con filtraggio EMI e protezione TVS, Wi‑Fi a/b/g/n + Bluetooth 5.1 (antenna su chip o opzione U.FL).

  • 10 pin di I/O utente — P0–P3 sui connettori laterali, P4–P5 sul connettore Qwiic e P6–P9 sul connettore B2B sul retro. Ulteriori linee di debug e recovery sono anch’esse instradate sul connettore B2B.

  • Tutti i pin con uscita 3,3 V / tolleranti a 3,3 V, 25 mA per pin, abilitati agli interrupt. Gli ingressi ADC sono riferiti a 1,8 V.

  • LED RGB utente, pulsante utente, interruttore di recovery, connettore Qwiic.

  • 80 µA in deep sleep a 3,3 V (24 mA a riposo, 50–60 mA in attività).

Avvertimento

I pin di I/O dell’AE3 non sono tolleranti a 5 V. Non collegare il dispositivo direttamente a un MCU a 5 V come l’Arduino Mega — usa un level shifter per qualsiasi segnale a 5 V.

Pinout

Pinout dell'OpenMV AE3 PAG7936

Riferimento dei pin

L’AE3 espone 10 pin utente sui connettori laterali (P0–P9). Ulteriori segnali — tra cui JTAG e la linea di recovery — sono instradati su un connettore B2B (board‑to‑board) sul retro della scheda per shield e schede carrier.

Nome del pin

Riferimento

Funzione

P0

3,3 V

SPI0 MOSI / I2C2 SCL / UART4 TX / TIM0 T1 / PDM D3

P1

3,3 V

SPI0 MISO / I2C2 SDA / UART4 RX / TIM0 T0

P2

3,3 V

SPI0 SCLK / LPI2C SDA / UART5 TX / TIM1 T1

P3

3,3 V

SPI0 SS / LPI2C SCL / UART5 RX / TIM1 T0 / PDM C3

P4

3,3 V

I2C1 SCL / UART1 TX / TIM2 T1 / PDM C0 / CAN TX

P5

3,3 V

I2C1 SDA / UART1 RX / TIM2 T0 / PDM D0 / CAN RX

P6

1,8 V

I2C1 SDA / UART3 CTS / TIM9 T0 (solo B2B)

P7

1,8 V

I2C1 SCL / UART3 RTS / TIM9 T1 (solo B2B)

P8

1,8 V

I3C SDA / UART3 RX / TIM5 T0 / ADC ch S10 (solo B2B)

P9

1,8 V

I3C SCL / UART3 TX / TIM5 T1 / ADC ch S11 (solo B2B)

P10

1,8 V

GPIO / JTAG TCK (solo B2B)

P11

1,8 V

GPIO / JTAG TDO (solo B2B)

P13

1,8 V

GPIO / JTAG TMS (solo B2B)

P14

1,8 V

GPIO / JTAG TDI (solo B2B)

RESET

3,3 V

porta a GND per resettare la scheda

SW

3,3 V

pulsante utente (attivo basso)

LED_RED

3,3 V

canale rosso del LED RGB (attivo basso)

LED_GREEN

3,3 V

canale verde del LED RGB (attivo basso)

LED_BLUE

3,3 V

canale blu del LED RGB (attivo basso)

Nota

P0–P5 si trovano sui connettori laterali (riferiti a 3,3 V); P6–P9 sono esposti solo sul connettore B2B sul retro della scheda e sono riferiti a 1,8 V. Applicare 3,3 V a un pin riferito a 1,8 V danneggerà il SoC — assicurati che qualsiasi segnale collegato al connettore B2B sia a 1,8 V.

Pin di alimentazione

  • 3.3V — il rail di alimentazione principale dell’AE3. Lo stesso rail a 3,3 V è esposto sulle piazzole di saldatura del connettore GPIO, sul connettore Qwiic e sul connettore B2B sul retro della scheda.

  • 1.8V — esposto sul connettore B2B come sola uscita. Usalo per alimentare periferiche con logica a 1,8 V su una scheda carrier B2B; non pilotarlo dall’esterno della scheda.

  • GND — massa comune.

L’AE3 non ha un pin VIN né un caricabatterie LiPo. Può essere alimentata attraverso uno di tre percorsi:

  • USB‑C — il regolatore on‑board abbassa i 5 V dell’USB a 3,3 V e li inietta sul rail a 3,3 V.

  • Connettore Qwiic — fornisci un’alimentazione regolata a 3,3 V sul connettore Qwiic per alimentare la scheda da un modulo Qwiic.

  • Connettore GPIO / piazzole B2B a 3,3 V — fornisci un’alimentazione regolata a 3,3 V su una qualsiasi delle piazzole a 3,3 V del connettore di I/O o del connettore B2B.

Il regolatore USB alimenta il rail attraverso un diodo ideale, così le alimentazioni esterne a 3,3 V sul lato Qwiic / GPIO / B2B possono alimentare la scheda anche mentre l’USB è ancora collegato senza retro‑pilotare il regolatore USB.

Suggerimento

Usa lo stimatore di durata della batteria per modellare per quanto tempo l’AE3 funzionerà con una batteria per un dato ciclo di lavoro attivo / deep‑sleep.

Pin di recovery e debug

  • RESET — porta a GND per resettare la scheda. Rilasciandolo, il SoC si avvia normalmente.

C’è un interruttore di recovery sulla faccia frontale (lato camera) della scheda, nell’angolo in basso a sinistra. Quando abilitato, forza l’uscita della UART SE dell’AE3 attraverso l’USB così che OpenMV IDE possa riflashare il bootloader on‑board. La stessa modalità recovery può essere attivata da remoto portando basso il pin RECOVERY sul connettore B2B.

L’AE3 supporta sia il debug SWD che il debug JTAG completo:

  • L”header SWD a 1,8 V sul lato della scheda è per un cavo Tag-Connect ECV3-06-CTX ed espone i quattro segnali SWD (TCK / TMS / TDO / RSTN) più GND.

  • Il connettore B2B sul retro della scheda espone gli stessi pin di debug (P10 = TCK, P11 = TDO, P13 = TMS, P14 = TDI) più l”RSTN di sistema e un JTAG RSTN separato. Questi pin possono essere usati sia per SWD (TCK + TMS) sia per JTAG completo; la linea JTAG RSTN è necessaria solo in modalità JTAG completa.

Tutti i segnali di debug sono riferiti a 1,8 V — assicurati che il tuo adattatore di debug sia configurato per logica a 1,8 V prima di collegarlo.

Periferiche integrate

LED

L’AE3 ha un singolo LED RGB utente, controllabile via software tramite machine.LED

from machine import LED

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

Pulsante utente

L’AE3 ha un singolo pulsante utente (SW):

from machine import Pin

sw = Pin("SW", Pin.IN)
print(sw.value())

Per mettere la scheda in deep sleep e farla risvegliare tramite SW, basta chiamare machine.deepsleep() — non è richiesta alcuna configurazione di wakeup, il pulsante è cablato direttamente a un ingresso di wake:

import machine

machine.deepsleep()   # press SW to wake the board

Puoi anche usare SW come interruttore di accensione soft. Attiva sul fronte di salita — la linea si stabilizza alta dopo che l’utente rilascia il pulsante, così la pressione successiva è inequivocabilmente un evento di wake:

import machine
from machine import Pin

def power_off(_):
    machine.deepsleep()

Pin("SW", Pin.IN).irq(power_off, Pin.IRQ_RISING)

# ...rest of the application runs here. Press SW once to sleep,
# press it again to wake.

Sensore della camera

Il PAG7936 è pilotato attraverso il modulo csi — sensori camera

import csi

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

while True:
    img = cam.snapshot()

Il PAG7936 supporta la modalità triggered — l’integrazione dei pixel si allinea esattamente con ogni chiamata csi.CSI.snapshot invece che con il clock di frame a corsa libera, utile per sincronizzare l’acquisizione con un evento esterno o un altro sensore. Abilitala tramite csi.CSI.ioctl con csi.IOCTL_SET_TRIGGERED_MODE. Il frame rate scende a circa la metà della modalità a corsa libera perché la lettura non è più in pipeline con l’integrazione del frame successivo:

cam.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True)

NPU

Le due NPU on‑chip dell’AE3 (NPU HP a 400 MHz / 204 GOPS + NPU HE a 160 MHz / 46 GOPS) sono esposte attraverso il modulo ml — Machine Learning. I modelli memorizzati sul filesystem di sola lettura /rom si caricano direttamente dalla flash senza essere copiati in RAM, così anche detector di grandi dimensioni entrano comodamente accanto al framebuffer live. Esegui un detector YOLOv8 su ogni frame e disegna le predizioni sopra l’immagine live:

import csi
import time
import ml
from ml.postprocessing.ultralytics import YoloV8

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

# Load YOLO V8 model from ROM FS.
model = ml.Model("/rom/yolov8n_192.tflite", postprocess=YoloV8(threshold=0.4))
print(model)

# Visualization parameters.
n = len(model.labels)
model_class_colors = [
    (int(255 * i // n), int(255 * (n - i - 1) // n), 255)
    for i in range(n)
]

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

    # boxes is a list of list per class of ((x, y, w, h), score) tuples
    boxes = model.predict([img])

    # Draw bounding boxes around the detected objects
    for i, class_detections in enumerate(boxes):
        rects = [r for r, score in class_detections]
        labels = [model.labels[i] for j in range(len(rects))]
        colors = [model_class_colors[i] for j in range(len(rects))]
        ml.utils.draw_predictions(img, rects, labels, colors, format=None)

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

Core HE

L’AE3 racchiude due core Cortex‑M55 in un unico MCU: il core ad alte prestazioni (HP) che esegue l’istanza principale di MicroPython, la camera, la NPU HP, l’USB e così via; e il core ad alta efficienza (HE) che opera a potenza molto più bassa e si avvia con una piccola istanza MicroPython propria. Entrambi i core condividono un bus di messaggi Open-AMP / RPMsg, così il core HP può inviare funzioni Python al core HE, ricevere i risultati e mantenere le due metà disaccoppiate.

Il punto di ingresso più semplice è il decoratore @openamp.async_remote. Esso impacchetta una funzione Python, la invia al core HE, e il core HE la esegue come task asyncio. Dopo aver registrato i task, istanzia openamp.RemoteProc con l’indirizzo flash del firmware HE e chiama rproc.start() per avviare il secondo core. Senza callback, l’output print() della funzione decorata viene inoltrato sull’endpoint predefinito allo stdout del core HP — comodo per un «hello world»:

import time
import openamp

@openamp.async_remote
async def task1(ept):
    import asyncio
    while True:
        print("Hello from the HE core!")
        await asyncio.sleep(1)

# Boot the HE core. This runs the registered tasks.
rproc = openamp.RemoteProc(0x80320000)
rproc.start()

while True:
    print("Hello from the HP core!")
    time.sleep(1)

Per la messaggistica bidirezionale, passa una callback al decoratore. La callback viene eseguita sul core HP ogni volta che il task HE chiama ept.send()

import time
import openamp

def task_callback(src_addr, data):
    print("HP received:", data.decode())

@openamp.async_remote(task_callback)
async def task1(ept):
    import asyncio
    count = 0
    while True:
        ept.send(f"count = {count}")
        count += 1
        await asyncio.sleep(1)

rproc = openamp.RemoteProc(0x80320000)
rproc.start()

while True:
    time.sleep(1)

Il core HE ha la propria NPU HE (160 MHz, 46 GOPS), così può eseguire un secondo modello ML in parallelo a qualunque cosa stia facendo la NPU HP del core HP. Una suddivisione utile è mettere un piccolo modello di trigger / classificatore sempre attivo sul lato HE e lasciare che il core HP reagisca solo quando qualcosa di interessante viene segnalato — il riconoscimento di parole chiave dal microfono integrato è adatto perché è continuo, a bassa banda, e il core HE rimane a potenza molto più bassa rispetto all’HP. L’helper congelato ml.apps.MicroSpeech riconosce «Yes» e «No» da subito — pronuncia le parole forte e chiaro nel microfono integrato per attivare il rilevamento:

import time
import openamp

def task_callback(src_addr, data):
    print("Heard:", data.decode())

@openamp.async_remote(task_callback)
async def task1(ept):
    from ml.apps import MicroSpeech
    speech = MicroSpeech(gain_db=24)
    while True:
        label, scores = speech.listen(timeout=0, threshold=0.70)
        if label:
            ept.send(label)

rproc = openamp.RemoteProc(0x80320000)
rproc.start()

while True:
    time.sleep(1)

Per una suddivisione più ricca, esegui BlazeFace sulla NPU HP mentre il core HE gestisce il riconoscimento di parole chiave in background — il loop HP sovrappone l’ultima parola chiave udita sul frame della camera:

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

label = None
label_ticks = 0
LABEL_HOLD_MS = 2000

def task_callback(src_addr, data):
    global label, label_ticks
    label = data.decode()
    label_ticks = time.ticks_ms()

@openamp.async_remote(task_callback)
async def task1(ept):
    from ml.apps import MicroSpeech
    speech = MicroSpeech(gain_db=24)
    while True:
        l, scores = speech.listen(timeout=0, threshold=0.70)
        if l:
            ept.send(l)

# Start the HE core before initializing the camera on the HP core.
rproc = openamp.RemoteProc(0x80320000)
rproc.start()

csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
csi0.window((400, 400))

model = ml.Model("/rom/blazeface_front_128.tflite",
                 postprocess=BlazeFace(threshold=0.4))

clock = time.clock()
while True:
    clock.tick()
    img = csi0.snapshot()
    for r, score, keypoints in model.predict([img]):
        ml.utils.draw_predictions(img, [r], ("face",),
                                  ((0, 0, 255),), format=None)
        ml.utils.draw_keypoints(img, keypoints, color=(255, 0, 0))
    if label is not None:
        if time.ticks_diff(time.ticks_ms(), label_ticks) < LABEL_HOLD_MS:
            img.draw_string((4, 4), f"Heard: {label}",
                            color=(255, 0, 0), scale=2)
        else:
            label = None
    print(clock.fps(), "fps")

Il core HE è ben adatto a carichi di lavoro sempre attivi o a bassa frequenza che non vuoi mettere in competizione con la pipeline camera/NPU sul lato HP — piccole inferenze ML, DSP leggero su dati del microfono o della IMU, e lavori in background simili.

Alcuni vincoli da tenere a mente:

  • Limitati al microfono e alla IMU quando piloti periferiche dal core HE — sono ciò per cui il lato HE è progettato. Ogni periferica può essere posseduta da un solo core alla volta, quindi scegli HP o HE per essa e attieniti a quella scelta per tutta la durata dello script.

  • Ogni corpo di task @openamp.async_remote deve impacchettarsi in meno di 500 byte di bytecode mpy — mantieni la funzione piccola e fattorizza la logica più pesante in moduli di libreria separati che vengono congelati nel firmware.

  • Gli import all’interno della funzione inviata vedono solo i moduli che esistono sul filesystem del core HE. Il core HE ha la propria ROMFS /rom — separata dalla /rom del core HP — quindi i moduli e i modelli ML che vuoi disponibili su HE devono essere integrati nell’immagine ROMFS del lato HE, non in quella HP.

Microfono

Il microfono integrato viene acquisito tramite audio — Modulo Audio. Ogni buffer arriva come bytearray PCM a 16 bit con segno, il che rende banale alimentarlo in ulab/numpy per un rapido DSP. Un semplice rilevatore di volume — stampa ogni volta che il volume RMS supera una soglia:

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

IMU

L’accelerometro + giroscopio LSM6DSM integrato è esposto attraverso imu — sensore imu

import imu
import time

while True:
    print(imu.acceleration_mg())   # (x, y, z) in milli‑g
    print(imu.angular_rate_mdps()) # (x, y, z) in milli‑deg/s
    time.sleep_ms(100)

Sensore time‑of‑flight

L’AE3 monta un sensore time‑of‑flight multi‑zona VL53L8CX 8×8 che restituisce fino a 64 letture di distanza per frame, con una portata massima di ~4 m. È esposto attraverso il modulo tof — driver per sensore time-of-flight — chiama tof.init() per avviare il sensore e tof.read_depth() per acquisire un frame di profondità come lista piatta di letture in millimetri (una per zona):

import tof

tof.init()
while True:
    depth, depth_min, depth_max = tof.read_depth()
    print("min:", depth_min, "mm  max:", depth_max, "mm")

L’array di profondità può anche essere disegnato sopra un frame a colori dal sensore principale — tof.draw_depth() lo dipinge su un’immagine image.Image esistente, mentre tof.snapshot() restituisce un’immagine di profondità appena renderizzata:

import image
import tof
import csi

# Bring up the VL53L8CX time-of-flight sensor.
tof.init()

# Configure the main camera at VGA RGB565.
cam = csi.CSI()
cam.reset()
cam.pixformat(csi.RGB565)
cam.framesize(csi.VGA)

# Off-screen framebuffer used to compose the camera frame and the
# up-scaled depth heat-map side by side before pushing the result
# back to the live preview.
b = image.Image(640, 480, image.RGB565)

while True:
    # Grab a colour frame from the main camera.
    img = cam.snapshot()

    try:
        # Capture TOF data [depth map, min distance, max distance].
        # vflip / hmirror align the ToF orientation with the camera.
        depth, dmin, dmax = tof.read_depth(vflip=True, hmirror=True)

        # Zones with no return read back as 0.0 — clamp them to the
        # frame's max distance so the colour palette doesn't show
        # them as "closest".
        for i in range(0, len(depth)):
            if depth[i] == 0.0:
                depth[i] = dmax

    except RuntimeError:
        # The sensor occasionally faults on a frame; reset and skip.
        tof.reset()
        continue

    # Draw the camera frame into the left half of the framebuffer,
    # scaled to 60% so it leaves room for the depth heat-map on
    # the right.
    b.draw_image(img, x=0, y=64+8, x_scale=0.6, hint=image.BILINEAR)

    # Up-sample the 8x8 depth array 30x with bicubic smoothing and
    # blend it into the right half using the depth palette.
    # scale=(0, 400) maps 0-400 mm to the full palette range.
    tof.draw_depth(b, depth, x=320+64+16, y=64+8, alpha=255,
                   hint=image.BICUBIC, x_scale=30, y_scale=30,
                   scale=(0, 400), color_palette=image.PALETTE_DEPTH)

    # Copy the composed framebuffer back into the live preview so
    # OpenMV IDE shows both panels.
    img.set(b)

Wi‑Fi

Il CYW43439 integrato è esposto tramite network — configurazione di rete come interfaccia station. Dopo la connessione, ipconfig("addr4") restituisce la coppia (ip, netmask)

import network, time

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

Bluetooth

Lo stesso CYW43439 espone anche il Bluetooth 5.1. Usa aioble — BLE asincrono per BLE compatibile con asyncio — per esempio, annunciati come periferica e attendi che un central si connetta:

import asyncio
import aioble

async def run():
    while True:
        conn = await aioble.advertise(250_000, name="OpenMV-AE3")
        print("Connected:", conn.device)
        await conn.disconnected()

asyncio.run(run())

Riferimento dei bus

GPIO

Usa machine.Pin per leggere o pilotare uno qualsiasi dei pin serigrafati. Le uscite sono CMOS a 3,3 V e possono assorbire/erogare fino a 25 mA per pin.

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

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

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

RTS

CTS

UART1

P4

P5

UART3

P9

P8

P7

P6

UART4

P0

P1

UART5

P2

P3

from machine import UART

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

UART3 è l’unico bus con controllo di flusso hardware. Poiché P6–P9 si trovano sul connettore B2B e sono riferiti a 1,8 V, UART3 funziona solo attraverso un level shifter o una scheda carrier B2B — non collegarvi direttamente logica a 3,3 V.

I²C

Bus

SCL

SDA

I2C1

P4

P5

I2C2

P0

P1

LPI2C

P3

P2

from machine import I2C

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

Il connettore Qwiic integrato espone I2C2 a 3,3 V.

I2C1 e I2C2 possono anche essere usati in modalità target (slave) tramite machine.I2CTarget per esporre una regione di memoria a un altro controller I²C:

from machine import I2CTarget

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

Nota

La periferica LPI2C non è esposta nel firmware. Se esposta, supporterebbe solo la modalità target (slave), e I2C1 e I2C2 coprono già sia l’operazione come controller sia quella come target.

SPI

Bus

MOSI

MISO

SCK

CS

SPI0

P0

P1

P2

P3

from machine import SPI
from machine import Pin

spi = SPI(0, 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)

ADC

L’Alif Ensemble E3 espone due canali ADC a 12 bit su P8 e P9 (solo connettore B2B). Entrambi gli ingressi sono riferiti a 1,8 Vread_u16 restituisce 0–65535 nell’intervallo 0–1,8 V al pin:

from machine import ADC
import time

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

Avvertimento

Gli ingressi ADC dell’AE3 sono riferiti a 1,8 V, non a 3,3 V. Applicare un segnale grezzo a 3,3 V saturerà il convertitore e potrebbe danneggiare il pin — riduci le tensioni più alte esternamente.

PWM

Pin

Timer / canale

P0

TIM0 T1

P1

TIM0 T0

P2

TIM1 T1

P3

TIM1 T0

P4

TIM2 T1

P5

TIM2 T0

P6

TIM9 T0 (solo B2B)

P7

TIM9 T1 (solo B2B)

P8

TIM5 T0 (solo B2B)

P9

TIM5 T1 (solo B2B)

Pilotane uno qualsiasi tramite machine.PWM

from machine import Pin, PWM

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

Bus software bit‑banged

machine.SoftI2C e machine.SoftSPI funzionano su qualsiasi GPIO se ti serve un bus aggiuntivo.

Sensore termico (esterno alla scheda)

Il firmware include il driver fir — driver del sensore termico (fir == far infrared) per un termocamera AMG8833 8 × 8 cablato esternamente. Collega il modulo al bus I²C elencato di seguito, quindi leggi 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 1 — cabla il modulo a P4 (SCL) e P5 (SDA).

Timing

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 programma callback periodiche o one‑shot senza consumare uno slot di timer hardware. Passa -1 come id per usare 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 di periodo sono in millisecondi. Chiama deinit() per fermare e rilasciare lo slot.

Orologio in tempo reale

machine.RTC mantiene l’ora di sistema attraverso i reset, supportato da 4 KB di RAM di backup on‑chip che sopravvive al deep sleep:

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

L’RTC continua a funzionare anche durante il deep sleep, quindi puoi usarlo come sorgente di wakeup per machine.deepsleep().

Informazioni di boot e runtime

Finestra del bootloader USB

A ogni accensione la camera esegue un breve bootloader (qualche secondo) che permette a OpenMV IDE di aggiornare il firmware senza che l’utente debba entrare in modalità DFU. Allo scadere della finestra il bootloader passa il controllo a boot.py e poi a main.py.

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

import machine

machine.bootloader()

Filesystem e ordine di boot

Il firmware dell’AE3 monta fino a due filesystem all’avvio:

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

  • ROMFS — filesystem di sola lettura mappato in memoria su /rom usato per distribuire grandi asset di dati (es. modelli AI) che beneficiano dell’accesso zero‑copy. Montato automaticamente da MicroPython all’avvio, prima dell’esecuzione di qualsiasi codice Python utente.

Dopo il montaggio, la directory di lavoro è impostata su /flash. L’interprete esegue quindi gli script da quella directory:

  • boot.py viene eseguito a ogni soft reset (boot a freddo, Ctrl‑D dal REPL, o ogni volta che lo script in esecuzione termina).

  • main.py viene eseguito solo al boot a freddo, immediatamente dopo boot.py. I soft reset successivi rieseguono boot.py ma vanno direttamente al REPL — per rieseguire main.py devi resettare completamente la scheda.

Il main.py predefinito fornito su una scheda appena flashata si limita a far lampeggiare il canale blu del LED RGB utente come heartbeat (due brevi impulsi, breve pausa), così puoi capire che il firmware si è avviato correttamente senza alcun host collegato.

sys.path viene esteso per includere entrambi i filesystem e le loro sottodirectory lib/, così i moduli importabili possono risiedere in /flash/lib o /rom/lib.

Quando collegata via USB, /flash viene anche enumerata come unità di archiviazione di massa USB sull’host, permettendoti di modificare boot.py, main.py e qualsiasi altro file direttamente. Espelli l’unità prima di resettare la camera così che l’host svuoti le sue scritture in cache.

Nota

Poiché il sistema operativo tratta l’unità come un dispositivo a blocchi passivo, i file creati o modificati dal codice in esecuzione sull’OpenMV Cam non appariranno finché l’host non rimonta l’unità. Se sia il sistema operativo sia l’OpenMV Cam scrivono lo stesso filesystem nello stesso momento, il sistema operativo vincerà e sovrascriverà le modifiche fatte dalla camera.

Nota

Il canale rosso del LED RGB utente può accendersi brevemente mentre l’host legge o scrive sull’unità di archiviazione di massa USB — è un indicatore di attività gestito dal firmware, non un guasto.

Dimensioni di archiviazione

L’AE3 viene fornita con:

  • /flash — filesystem FAT da 8 MB, in lettura/scrittura.

  • /rom sul core HP — ROMFS mappata in memoria di sola lettura da 24 MB per script e dati che il core HP carica all’avvio.

  • /rom sul core HE — ROMFS di sola lettura da 1 MB posseduta dal core HE. I moduli e i modelli ML che vuoi disponibili ai task @openamp.async_remote devono essere integrati in questa immagine, non in quella HP.

Indicatore di hard fault

Se il LED RGB utente cicla rapidamente attraverso tutti i colori — abbastanza velocemente da sembrare un LED bianco scintillante piuttosto che tonalità distinte — il firmware ha riscontrato un hard fault irrecuperabile. Riflasha il firmware per recuperare; se il riflashaggio non aiuta, la scheda potrebbe essere danneggiata fisicamente.

Librerie software

Consulta l”indice della libreria per l’elenco completo dei moduli — inclusi quelli esclusivi della build AE3.