OpenMV AE3¶
A OpenMV AE3 é construída em torno do Alif Ensemble E3 — um SoC com dois ARM Cortex‑M55 (núcleo HP de 400 MHz + núcleo HE de 160 MHz) e duas NPUs internas (NPU HP de 400 MHz / 204 GOPS + NPU HE de 160 MHz / 46 GOPS). A placa combina as NPUs com o sensor global‑shutter PAG7936 de 1 MP, USB‑C high‑speed, Wi‑Fi, Bluetooth 5.1, uma IMU LSM6DSM, um microfone e um telêmetro time‑of‑flight VL53L8CX de 8×8, tudo em uma placa de 30 × 30 mm.
Para o datasheet completo, fotos e dimensões, consulte a página do produto OpenMV AE3.
Destaques¶
Alif Ensemble E3 — dois ARM Cortex‑M55 com SIMD Helium de 128 bits, núcleo HP de 400 MHz + núcleo HE de 160 MHz (~640 / ~256 DMIPS, CoreMark 1748 / 752).
Duas NPUs: NPU HP de 400 MHz / 204 GOPS + NPU HE de 160 MHz / 46 GOPS para IA/ML — executa detecção de objetos YOLO em paralelo com outras cargas de trabalho.
GPU 2D por hardware para escalonamento.
13,5 MB de SRAM interna mais 5,5 MB de MRAM interna e 32 MB de flash octal externa (DDR de 8 bits a 100 MHz, leitura de 200 MB/s).
4 KB de RAM de backup com o RTC interno.
Sensor global‑shutter colorido PAG7936 de 1 MP.
IMU embarcada (acelerômetro + giroscópio LSM6DSM), microfone e sensor time‑of‑flight VL53L8CX de 8×8 (até 4 m).
USB‑C high‑speed (480 Mb/s) com filtragem de EMI e proteção TVS, Wi‑Fi a/b/g/n + Bluetooth 5.1 (antena de chip ou opção U.FL).
10 pinos de I/O de usuário — P0–P3 nos headers laterais, P4–P5 no conector Qwiic e P6–P9 no header B2B na parte traseira. Linhas adicionais de depuração e recuperação também estão roteadas para o header B2B.
Todos os pinos com saída de 3,3 V / tolerantes a 3,3 V, 25 mA por pino, capazes de gerar interrupção. As entradas ADC têm referência de 1,8 V.
LED RGB de usuário, botão de usuário, chave de recuperação, conector Qwiic.
80 µA em deep sleep a 3,3 V (24 mA em repouso, 50–60 mA em atividade).
Aviso
Os pinos de I/O da AE3 não são tolerantes a 5 V. Não conecte o dispositivo diretamente a um MCU de 5 V, como o Arduino Mega — use um level shifter para qualquer sinal de 5 V.
Pinagem¶
Referência de pinos¶
A AE3 expõe 10 pinos de usuário nos headers laterais (P0–P9). Sinais adicionais — incluindo JTAG e a linha de recuperação — estão roteados para um header B2B (board‑to‑board) na parte traseira da placa, destinado a shields e carrier boards.
Nome do pino |
Referência |
Função |
|---|---|---|
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 (somente B2B) |
P7 |
1,8 V |
I2C1 SCL / UART3 RTS / TIM9 T1 (somente B2B) |
P8 |
1,8 V |
I3C SDA / UART3 RX / TIM5 T0 / ADC ch S10 (somente B2B) |
P9 |
1,8 V |
I3C SCL / UART3 TX / TIM5 T1 / ADC ch S11 (somente B2B) |
P10 |
1,8 V |
GPIO / JTAG TCK (somente B2B) |
P11 |
1,8 V |
GPIO / JTAG TDO (somente B2B) |
P13 |
1,8 V |
GPIO / JTAG TMS (somente B2B) |
P14 |
1,8 V |
GPIO / JTAG TDI (somente B2B) |
RESET |
3,3 V |
puxe para GND para resetar a placa |
SW |
3,3 V |
botão de usuário (ativo em nível baixo) |
LED_RED |
3,3 V |
canal vermelho do LED RGB (ativo em nível baixo) |
LED_GREEN |
3,3 V |
canal verde do LED RGB (ativo em nível baixo) |
LED_BLUE |
3,3 V |
canal azul do LED RGB (ativo em nível baixo) |
Nota
P0–P5 estão nos headers laterais (referência de 3,3 V); P6–P9 são expostos apenas no header B2B na parte traseira da placa e têm referência de 1,8 V. Aplicar 3,3 V em um pino com referência de 1,8 V danificará o SoC — certifique‑se de que qualquer sinal conectado ao header B2B esteja em 1,8 V.
Pinos de alimentação¶
3.3V — o barramento de alimentação principal da AE3. O mesmo barramento de 3,3 V é exposto nos pads de solda do header de GPIO, no conector Qwiic e no header B2B na parte traseira da placa.
1.8V — exposto no header B2B somente como saída. Use‑o para alimentar periféricos de lógica de 1,8 V em uma carrier board B2B; não o alimente a partir de fora da placa.
GND — terra comum.
A AE3 não possui pino VIN nem carregador de LiPo. Ela pode ser alimentada por qualquer um de três caminhos:
USB‑C — o regulador embarcado reduz os 5 V do USB para 3,3 V e injeta esse valor no barramento de 3,3 V.
Conector Qwiic — aplique uma fonte regulada de 3,3 V no header Qwiic para alimentar a placa a partir de um módulo Qwiic.
Header de GPIO / pads de 3,3 V do B2B — aplique uma fonte regulada de 3,3 V em qualquer um dos pads de 3,3 V do header de I/O ou do conector B2B.
O regulador do USB alimenta o barramento através de um diodo ideal, de modo que fontes externas de 3,3 V no lado Qwiic / GPIO / B2B podem alimentar a placa mesmo com o USB ainda conectado, sem retroalimentar o regulador do USB.
Dica
Use o estimador de vida útil da bateria para modelar por quanto tempo a AE3 funcionará com uma bateria para um determinado ciclo de trabalho ativo / deep‑sleep.
Pinos de recuperação e depuração¶
RESET — puxe para GND para resetar a placa. Soltá‑lo permite que o SoC inicie normalmente.
Há uma chave de recuperação na face frontal (lado da câmera) da placa, no canto inferior esquerdo. Quando habilitada, ela força a SE UART da AE3 a sair pelo USB para que o OpenMV IDE possa regravar o bootloader embarcado. O mesmo modo de recuperação pode ser acionado remotamente puxando o pino RECOVERY do conector B2B para nível baixo.
A AE3 suporta tanto depuração SWD quanto JTAG completo:
O header SWD de 1,8 V na lateral da placa é destinado a um cabo Tag-Connect ECV3-06-CTX e disponibiliza os quatro sinais SWD (TCK / TMS / TDO / RSTN) mais o GND.
O header B2B na parte traseira da placa expõe os mesmos pinos de depuração (P10 = TCK, P11 = TDO, P13 = TMS, P14 = TDI) mais o RSTN do sistema e um JTAG RSTN separado. Esses pinos podem ser usados tanto para SWD (TCK + TMS) quanto para JTAG completo; a linha JTAG RSTN só é necessária no modo JTAG completo.
Todos os sinais de depuração têm referência de 1,8 V — certifique‑se de que seu adaptador de depuração esteja configurado para lógica de 1,8 V antes de conectar.
Periféricos embarcados¶
LEDs¶
A AE3 tem um único LED RGB de usuário, controlável por software através de machine.LED
from machine import LED
LED("LED_RED").on()
LED("LED_GREEN").on()
LED("LED_BLUE").on()
Sensor de câmera¶
O PAG7936 é controlado através do módulo csi — sensores de câmera
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()
O PAG7936 suporta o modo triggered — a integração de pixels alinha‑se exatamente com cada chamada de csi.CSI.snapshot, em vez do clock de quadro livre, o que é útil para sincronizar a captura com um evento externo ou outro sensor. Habilite‑o através de csi.CSI.ioctl com csi.IOCTL_SET_TRIGGERED_MODE. A taxa de quadros cai para aproximadamente metade do modo de quadro livre, pois a leitura não se sobrepõe mais à integração do próximo quadro:
cam.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True)
NPU¶
As duas NPUs internas da AE3 (NPU HP de 400 MHz / 204 GOPS + NPU HE de 160 MHz / 46 GOPS) são expostas através do módulo ml — Aprendizado de Máquina. Modelos armazenados no sistema de arquivos somente leitura /rom carregam diretamente da flash sem copiar para a RAM, então mesmo detectores grandes cabem confortavelmente junto com o framebuffer ao vivo. Execute um detector YOLOv8 em cada quadro e desenhe as previsões sobre a imagem ao vivo:
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")
Núcleo HE¶
A AE3 reúne dois núcleos Cortex‑M55 em um único MCU: o núcleo de alto desempenho (HP), que executa a instância principal do MicroPython, a câmera, a NPU HP, o USB, e assim por diante; e o núcleo de alta eficiência (HE), que opera com potência muito menor e inicializa sua própria pequena instância de MicroPython. Ambos os núcleos compartilham um barramento de mensagens Open-AMP / RPMsg, de modo que o núcleo HP pode despachar funções Python para o núcleo HE, obter os resultados de volta e manter as duas metades desacopladas.
O ponto de entrada mais simples é o decorador @openamp.async_remote. Ele empacota uma função Python, a envia para o núcleo HE, e o núcleo HE a executa como uma tarefa asyncio. Após registrar as tarefas, instancie openamp.RemoteProc com o endereço de flash do firmware HE e chame rproc.start() para inicializar o segundo núcleo. Sem um callback, a saída de print() da função decorada é encaminhada pelo endpoint padrão para o stdout do núcleo HP — útil para um “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)
Para mensagens bidirecionais, passe um callback para o decorador. O callback é executado no núcleo HP sempre que a tarefa HE chama 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)
O núcleo HE tem sua própria NPU HE (160 MHz, 46 GOPS), então ele pode executar um segundo modelo de ML em paralelo com o que quer que a NPU HP do núcleo HP esteja processando. Uma divisão útil é colocar um pequeno modelo de gatilho / classificador sempre ativo no lado HE e deixar o núcleo HP reagir apenas quando algo interessante for sinalizado — a detecção de palavras‑chave do microfone embarcado é uma boa opção, pois é contínua, de baixa largura de banda, e o núcleo HE permanece com potência muito menor que o HP. O helper congelado ml.apps.MicroSpeech reconhece “Yes” e “No” de imediato — diga as palavras alta e claramente no microfone embarcado para acionar a detecção:
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)
Para uma divisão mais rica, execute o BlazeFace na NPU HP enquanto o núcleo HE cuida da detecção de palavras‑chave em segundo plano — o laço HP sobrepõe a palavra‑chave ouvida mais recentemente no quadro da câmera:
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")
O núcleo HE é bem adequado a cargas de trabalho sempre ativas ou de baixa frequência que você não quer que compitam com o pipeline da câmera/NPU no lado HP — pequenas inferências de ML, DSP leve sobre dados do microfone ou da IMU e tarefas de segundo plano semelhantes.
Algumas restrições a ter em mente:
Ao controlar periféricos a partir do núcleo HE, limite‑se ao microfone e à IMU — eles são para o que o lado HE foi projetado. Cada periférico só pode pertencer a um núcleo por vez, então escolha HP ou HE para ele e mantenha essa escolha durante toda a vida útil do script.
O corpo de cada tarefa
@openamp.async_remotedeve empacotar para menos de 500 bytes de bytecode mpy — mantenha a função pequena e fatore lógica mais pesada em módulos de biblioteca separados que sejam congelados no firmware.Imports dentro da função despachada só enxergam módulos que existem no sistema de arquivos do núcleo HE. O núcleo HE tem seu próprio ROMFS
/rom— separado do/romdo núcleo HP — então módulos e modelos de ML que você queira disponíveis no HE precisam ser incorporados à imagem ROMFS do lado HE, não à do HP.
Microfone¶
O microfone embarcado é capturado através de audio — Módulo de Áudio. Cada buffer chega como um bytearray de PCM de 16 bits com sinal, o que torna trivial alimentá‑lo em ulab/numpy para um DSP rápido. Um detector de volume simples — imprime sempre que o volume RMS cruza um limiar:
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¶
O acelerômetro + giroscópio LSM6DSM embarcado é exposto através de imu — sensor 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)
Sensor time‑of‑flight¶
A AE3 traz um sensor time‑of‑flight multizona VL53L8CX de 8×8 que retorna até 64 leituras de distância por quadro, com alcance máximo de ~4 m. Ele é exposto através do módulo tof — driver de sensor time-of-flight — chame tof.init() para iniciar o sensor e tof.read_depth() para obter um quadro de profundidade como uma lista plana de leituras em milímetros (uma por zona):
import tof
tof.init()
while True:
depth, depth_min, depth_max = tof.read_depth()
print("min:", depth_min, "mm max:", depth_max, "mm")
O array de profundidade também pode ser desenhado sobre um quadro colorido do sensor principal — tof.draw_depth() o pinta sobre uma image.Image existente, enquanto tof.snapshot() retorna uma imagem de profundidade recém‑renderizada:
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¶
O CYW43439 embarcado é exposto via network — configuração de rede como uma interface de estação. Após conectar, ipconfig("addr4") retorna o par (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¶
O mesmo CYW43439 também expõe Bluetooth 5.1. Use aioble — BLE Assíncrono para BLE amigável com asyncio — por exemplo, anuncie‑se como periférico e aguarde a conexão de um central:
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())
Referência de barramentos¶
GPIO¶
Use machine.Pin para ler ou acionar qualquer um dos pinos serigrafados. As saídas são CMOS de 3,3 V e podem drenar/fornecer até 25 mA por pino.
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())
Qualquer pino de entrada também pode disparar uma interrupção em transições de borda:
def handler(pin):
print("triggered:", pin)
Pin("P1", Pin.IN, Pin.PULL_UP).irq(
handler, Pin.IRQ_FALLING | Pin.IRQ_RISING,
)
UART¶
Barramento |
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 é o único barramento com controle de fluxo por hardware. Como P6–P9 ficam no header B2B e têm referência de 1,8 V, a UART3 só funciona através de um level shifter ou de uma carrier board B2B — não conecte lógica de 3,3 V diretamente a ela.
I²C¶
Barramento |
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")
O conector Qwiic embarcado disponibiliza a I2C2 a 3,3 V.
I2C1 e I2C2 também podem ser usados em modo target (escravo) através de machine.I2CTarget para expor uma região de memória a outro controlador I²C:
from machine import I2CTarget
buf = bytearray(32)
target = I2CTarget(1, addr=0x42, mem=buf)
Nota
O periférico LPI2C não é exposto no firmware. Se fosse exposto, ele suportaria apenas o modo target (escravo), e I2C1 e I2C2 já cobrem tanto a operação como controlador quanto como target.
SPI¶
Barramento |
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¶
O Alif Ensemble E3 expõe dois canais de ADC de 12 bits em P8 e P9 (somente no header B2B). Ambas as entradas têm referência de 1,8 V — read_u16 retorna 0–65535 ao longo de 0–1,8 V no pino:
from machine import ADC
import time
adc = ADC("P8")
while True:
voltage = adc.read_u16() * 1.8 / 65535
print(voltage)
time.sleep_ms(100)
Aviso
As entradas ADC da AE3 têm referência de 1,8 V, não de 3,3 V. Aplicar um sinal bruto de 3,3 V saturará o conversor e pode danificar o pino — reduza tensões mais altas externamente com um divisor.
PWM¶
Pino |
Timer / canal |
|---|---|
P0 |
TIM0 T1 |
P1 |
TIM0 T0 |
P2 |
TIM1 T1 |
P3 |
TIM1 T0 |
P4 |
TIM2 T1 |
P5 |
TIM2 T0 |
P6 |
TIM9 T0 (somente B2B) |
P7 |
TIM9 T1 (somente B2B) |
P8 |
TIM5 T0 (somente B2B) |
P9 |
TIM5 T1 (somente B2B) |
Acione qualquer um deles via machine.PWM
from machine import Pin, PWM
pwm = PWM(Pin("P0"), freq=1_000, duty_u16=32768)
Barramentos por bit‑banging em software¶
machine.SoftI2C e machine.SoftSPI funcionam em qualquer GPIO, caso você precise de um barramento extra.
Sensor térmico (externo)¶
O firmware inclui o driver fir — driver de sensor térmico (fir == far infrared) para um termovisor AMG8833 de 8 × 8 ligado externamente. Conecte o módulo ao barramento I²C listado abaixo e, em seguida, leia quadros com 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())
O driver fir só se comunica com o sensor pela I²C 1 — ligue o módulo a P4 (SCL) e P5 (SDA).
Temporização¶
time¶
O módulo time cobre atrasos bloqueantes, ticks monotônicos e medição de tempo decorrido:
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)
Timers virtuais¶
machine.Timer agenda callbacks periódicos ou de disparo único sem consumir um slot de timer de hardware. Passe -1 como id para usar um timer virtual (em 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"))
Os valores de período são em milissegundos. Chame deinit() para parar e liberar o slot.
Relógio de tempo real¶
machine.RTC mantém o horário de relógio de parede entre resets, respaldado por 4 KB de RAM de backup interna que sobrevive ao 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())
O RTC também continua funcionando durante o deep sleep, então você pode usá‑lo como fonte de wakeup para machine.deepsleep().
Informações de boot e de tempo de execução¶
Janela do bootloader USB¶
A cada ligamento, a câmera executa um bootloader breve (alguns segundos) que permite ao OpenMV IDE atualizar o firmware sem que o usuário precise entrar no modo DFU. Após a janela expirar, o bootloader passa o controle para boot.py e, em seguida, para main.py.
Um script em execução pode reentrar no bootloader sob demanda chamando machine.bootloader()
import machine
machine.bootloader()
Sistema de arquivos e ordem de boot¶
O firmware da AE3 monta até dois sistemas de arquivos no boot:
Flash interna — sempre montada em
/flash. Por padrão contémmain.pyeREADME.txt; criada no primeiro boot.ROMFS — sistema de arquivos somente leitura, mapeado em memória, em
/rom, usado para enviar grandes ativos de dados (por exemplo, modelos de IA) que se beneficiam de acesso sem cópia. Montado automaticamente pelo MicroPython na inicialização, antes que qualquer código Python do usuário seja executado.
Após a montagem, o diretório de trabalho é definido como /flash. O interpretador então executa scripts a partir desse diretório:
boot.pyé executado em todo soft reset (boot frio,Ctrl‑Ddo REPL ou sempre que o script em execução retorna).main.pyé executado apenas no boot frio, imediatamente apósboot.py. Soft resets subsequentes reexecutamboot.pymas caem direto no REPL — para reexecutarmain.pyé preciso resetar totalmente a placa.
O main.py padrão enviado em uma placa recém‑gravada apenas pisca o canal azul do LED RGB de usuário como um heartbeat (dois pulsos curtos, intervalo curto), para que você saiba que o firmware inicializou corretamente sem nenhum host conectado.
sys.path é estendido para incluir ambos os sistemas de arquivos e seus subdiretórios lib/, de modo que módulos importáveis podem residir em /flash/lib ou /rom/lib.
Quando conectado por USB, /flash também é enumerado como uma unidade de armazenamento em massa USB no host, permitindo editar boot.py, main.py e quaisquer outros arquivos diretamente. Ejete a unidade antes de resetar a câmera para que o host descarregue suas escritas em cache.
Nota
Como o sistema operacional trata a unidade como um dispositivo de blocos passivo, arquivos criados ou modificados por código em execução na OpenMV Cam não aparecerão até que o host remonte a unidade. Se tanto o sistema operacional quanto a OpenMV Cam escreverem no mesmo sistema de arquivos ao mesmo tempo, o sistema operacional vencerá e sobrescreverá as alterações feitas pela câmera.
Nota
O canal vermelho do LED RGB de usuário pode acender brevemente enquanto o host está lendo ou escrevendo na unidade de armazenamento em massa USB — isso é um indicador de atividade controlado pelo firmware, não uma falha.
Tamanhos de armazenamento¶
A AE3 vem com:
/flash— sistema de arquivos FAT de 8 MB, leitura/escrita./romno núcleo HP — ROMFS somente leitura mapeado em memória de 24 MB para scripts e dados que o núcleo HP carrega na inicialização./romno núcleo HE — ROMFS somente leitura de 1 MB pertencente ao núcleo HE. Módulos e modelos de ML que você queira disponíveis para as tarefas@openamp.async_remoteprecisam ser incorporados a esta imagem, não à do HP.
Indicador de hard fault¶
Se o LED RGB de usuário estiver ciclando rapidamente por todas as cores — rápido o suficiente para parecer um LED branco cintilante em vez de tons distintos — o firmware atingiu um hard fault irrecuperável. Regrave o firmware para recuperar; se a regravação não ajudar, a placa pode estar fisicamente danificada.
Bibliotecas de software¶
Consulte o índice de bibliotecas para a lista completa de módulos — incluindo quais são exclusivos do build da AE3.