OpenMV AE3¶
De OpenMV AE3 is opgebouwd rond de Alif Ensemble E3 — een dual ARM Cortex‑M55 SoC (400 MHz HP‑core + 160 MHz HE‑core) met twee NPU’s op de chip (400 MHz / 204 GOPS HP NPU + 160 MHz / 46 GOPS HE NPU). Het board combineert de NPU’s met de PAG7936 1 MP global‑shutter sensor, USB‑C high‑speed, Wi‑Fi, Bluetooth 5.1, een LSM6DSM IMU, een microfoon en een 8×8 VL53L8CX time‑of‑flight afstandsmeter, alles op een board van 30 × 30 mm.
Voor de volledige datasheet, foto’s en afmetingen, zie de OpenMV AE3 productpagina.
Hoogtepunten¶
Alif Ensemble E3 — dual ARM Cortex‑M55 met Helium 128‑bit SIMD, 400 MHz HP‑core + 160 MHz HE‑core (~640 / ~256 DMIPS, CoreMark 1748 / 752).
Dubbele NPU’s: 400 MHz / 204 GOPS HP NPU + 160 MHz / 46 GOPS HE NPU voor AI/ML — draait YOLO objectdetectie naast andere workloads.
Hardware 2D GPU voor schalen.
13,5 MB intern SRAM plus 5,5 MB MRAM op de chip en 32 MB extern octal flashgeheugen (100 MHz 8‑bit DDR, 200 MB/s lezen).
4 KB backup‑RAM met de RTC op de chip.
PAG7936 1 MP kleuren global‑shutter sensor.
Ingebouwde IMU (LSM6DSM accelerometer + gyroscoop), microfoon en VL53L8CX 8×8 time‑of‑flight sensor (tot 4 m).
High‑speed USB‑C (480 Mb/s) met EMI‑filtering en TVS‑bescherming, Wi‑Fi a/b/g/n + Bluetooth 5.1 (chipantenne of U.FL‑optie).
10 gebruikers‑I/O‑pinnen — P0–P3 op de zijheaders, P4–P5 op de Qwiic‑connector en P6–P9 op de B2B‑header aan de achterkant. Aanvullende debug‑ en recovery‑lijnen worden ook naar de B2B‑header geleid.
Alle pinnen 3,3 V uitgang / 3,3 V tolerant, 25 mA per pin, geschikt voor interrupts. ADC‑ingangen zijn gerefereerd aan 1,8 V.
Gebruikers‑RGB‑LED, gebruikersknop, recovery‑schakelaar, Qwiic‑connector.
80 µA deep sleep bij 3,3 V (24 mA idle, 50–60 mA actief).
Waarschuwing
De I/O‑pinnen van de AE3 zijn niet 5 V tolerant. Sluit het apparaat niet rechtstreeks aan op een 5 V MCU zoals de Arduino Mega — gebruik een level shifter voor elk 5 V signaal.
Pinout¶
Pinreferentie¶
De AE3 stelt 10 gebruikerspinnen beschikbaar op de zijheaders (P0–P9). Aanvullende signalen — waaronder JTAG en de recovery‑lijn — worden geleid naar een B2B‑header (board‑to‑board) aan de achterkant van het board voor shields en carrier boards.
Pinnaam |
Referentie |
Functie |
|---|---|---|
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 (alleen B2B) |
P7 |
1,8 V |
I2C1 SCL / UART3 RTS / TIM9 T1 (alleen B2B) |
P8 |
1,8 V |
I3C SDA / UART3 RX / TIM5 T0 / ADC ch S10 (alleen B2B) |
P9 |
1,8 V |
I3C SCL / UART3 TX / TIM5 T1 / ADC ch S11 (alleen B2B) |
P10 |
1,8 V |
GPIO / JTAG TCK (alleen B2B) |
P11 |
1,8 V |
GPIO / JTAG TDO (alleen B2B) |
P13 |
1,8 V |
GPIO / JTAG TMS (alleen B2B) |
P14 |
1,8 V |
GPIO / JTAG TDI (alleen B2B) |
RESET |
3,3 V |
trek naar GND om het board te resetten |
SW |
3,3 V |
gebruikersknop (active low) |
LED_RED |
3,3 V |
RGB‑LED rood kanaal (active low) |
LED_GREEN |
3,3 V |
RGB‑LED groen kanaal (active low) |
LED_BLUE |
3,3 V |
RGB‑LED blauw kanaal (active low) |
Notitie
P0–P5 zitten op de zijheaders (gerefereerd aan 3,3 V); P6–P9 zijn alleen beschikbaar op de B2B‑header aan de achterkant van het board en zijn gerefereerd aan 1,8 V. Het aansturen van 3,3 V op een aan 1,8 V gerefereerde pin beschadigt de SoC — zorg ervoor dat elk signaal dat op de B2B‑header wordt aangesloten 1,8 V is.
Voedingspinnen¶
3.3V — de hoofdvoedingsrail van de AE3. Dezelfde 3,3 V rail is beschikbaar op de soldeeraansluitingen van de GPIO‑header, de Qwiic‑connector en de B2B‑header aan de achterkant van het board.
1.8V — beschikbaar op de B2B‑header als uitsluitend uitgang. Gebruik het om randapparaten met 1,8 V logica te voeden op een B2B‑carrier; stuur het niet van buiten het board aan.
GND — gemeenschappelijke massa.
De AE3 heeft geen VIN‑pin en geen LiPo‑lader. Het kan via een van drie paden worden gevoed:
USB‑C — de regulator op het board verlaagt 5 V van USB naar 3,3 V en voert dat in op de 3,3 V rail.
Qwiic‑connector — voer een gereguleerde 3,3 V voeding in op de Qwiic‑header om het board vanaf een Qwiic‑module te voeden.
GPIO‑header / B2B 3,3 V aansluitingen — voer een gereguleerde 3,3 V voeding in op een van de 3,3 V aansluitingen op de I/O‑header of de B2B‑connector.
De USB‑regulator voedt de rail via een ideale diode, zodat externe 3,3 V voedingen aan de Qwiic‑ / GPIO‑ / B2B‑zijde het board kunnen voeden terwijl USB nog is aangesloten zonder terug te voeden naar de USB‑regulator.
Tip
Gebruik de accuduurschatter om te modelleren hoe lang de AE3 op een accu draait voor een gegeven actief‑ / deep‑sleep‑duty‑cycle.
Recovery‑ en debugpinnen¶
RESET — trek naar GND om het board te resetten. Bij loslaten start de SoC normaal op.
Er is een recovery‑schakelaar aan de voorkant (camerazijde) van het board, in de linkeronderhoek. Indien ingeschakeld, forceert deze de SE UART van de AE3 over USB zodat OpenMV IDE de bootloader op het board opnieuw kan flashen. Dezelfde recovery‑modus kan op afstand worden geactiveerd door de RECOVERY‑pin op de B2B‑connector laag te trekken.
De AE3 ondersteunt zowel SWD‑ als volledig JTAG‑debuggen:
De 1,8 V SWD‑header aan de zijkant van het board is bedoeld voor een Tag-Connect ECV3-06-CTX‑kabel en splitst de vier SWD‑signalen (TCK / TMS / TDO / RSTN) plus GND uit.
De B2B‑header aan de achterkant van het board stelt dezelfde debugpinnen beschikbaar (P10 = TCK, P11 = TDO, P13 = TMS, P14 = TDI) plus de systeem‑RSTN en een aparte JTAG RSTN. Deze pinnen kunnen worden gebruikt voor zowel SWD (TCK + TMS) als volledig JTAG; de JTAG RSTN‑lijn is alleen nodig in de volledige‑JTAG‑modus.
Alle debugsignalen zijn gerefereerd aan 1,8 V — zorg ervoor dat je debug‑adapter is geconfigureerd voor 1,8 V logica voordat je verbinding maakt.
Ingebouwde randapparaten¶
LED’s¶
De AE3 heeft één enkele gebruikers‑RGB‑LED, softwarematig bestuurbaar via machine.LED
from machine import LED
LED("LED_RED").on()
LED("LED_GREEN").on()
LED("LED_BLUE").on()
Camerasensor¶
De PAG7936 wordt aangestuurd via de csi — camerasensoren‑module:
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()
De PAG7936 ondersteunt triggered mode — de pixelintegratie valt exact samen met elke csi.CSI.snapshot‑aanroep in plaats van de vrijlopende frameklok, handig om de opname te synchroniseren met een externe gebeurtenis of een andere sensor. Schakel dit in via csi.CSI.ioctl met csi.IOCTL_SET_TRIGGERED_MODE. De framerate daalt tot ruwweg de helft van de vrijlopende modus omdat de uitlezing niet langer pijplijnt met de integratie van het volgende frame:
cam.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True)
NPU¶
De twee NPU’s op de chip van de AE3 (400 MHz / 204 GOPS HP NPU + 160 MHz / 46 GOPS HE NPU) zijn beschikbaar via de ml — Machine Learning‑module. Modellen die zijn opgeslagen op het alleen‑lezen /rom‑bestandssysteem laden rechtstreeks vanuit het flashgeheugen zonder naar RAM te kopiëren, zodat zelfs grote detectors comfortabel naast de live framebuffer passen. Draai een YOLOv8‑detector op elk frame en teken de voorspellingen bovenop de live afbeelding:
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")
HE‑core¶
De AE3 bundelt twee Cortex‑M55‑cores in één MCU: de high-performance (HP) core die de hoofd‑MicroPython‑instantie, de camera, de HP NPU, USB enzovoort draait; en de high-efficiency (HE) core die op veel lager vermogen draait en in een eigen kleine MicroPython‑instantie opstart. Beide cores delen een Open-AMP / RPMsg‑berichtenbus, zodat de HP‑core Python‑functies kan doorgeven aan de HE‑core, resultaten terugkrijgt en de twee helften ontkoppeld houdt.
Het eenvoudigste beginpunt is de @openamp.async_remote‑decorator. Hij marshalt een Python‑functie, verzendt deze naar de HE‑core, en de HE‑core draait deze als een asyncio‑taak. Na het registreren van taken instantieer je openamp.RemoteProc met het flashadres van de HE‑firmware en roep je rproc.start() aan om de tweede core op te starten. Zonder callback wordt de print()‑uitvoer van de gedecoreerde functie via het standaardeindpunt doorgestuurd naar de stdout van de HP‑core — handig voor een “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)
Voor bidirectionele berichtgeving geef je een callback door aan de decorator. De callback draait op de HP‑core telkens wanneer de HE‑taak ept.send() aanroept:
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)
De HE‑core heeft zijn eigen HE NPU (160 MHz, 46 GOPS), zodat hij een tweede ML‑model parallel kan draaien naast waar de HP NPU van de HP‑core mee bezig is. Een nuttige verdeling is om een klein always-on trigger‑ / classifier‑model aan de HE‑zijde te plaatsen en de HP‑core alleen te laten reageren wanneer er iets interessants wordt gesignaleerd — keyword spotting vanaf de ingebouwde microfoon past hier goed bij omdat het continu en bandbreedte‑arm is, en de HE‑core op veel lager vermogen blijft dan HP. De ingevroren ml.apps.MicroSpeech‑helper herkent “Yes” en “No” out of the box — spreek de woorden luid en duidelijk in de ingebouwde mic om detectie te triggeren:
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)
Voor een rijkere verdeling draai je BlazeFace op de HP NPU terwijl de HE‑core keyword spotting op de achtergrond afhandelt — de HP‑lus overlayt het meest recent gehoorde keyword op het cameraframe:
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")
De HE‑core is uitstekend geschikt voor always-on of low-rate workloads die je niet wilt laten concurreren met de camera/NPU‑pijplijn aan de HP‑zijde — kleine ML‑inferentie, lichtgewicht DSP op microfoon‑ of IMU‑data en vergelijkbare achtergrondtaken.
Een paar beperkingen om in gedachten te houden:
Houd het bij de microfoon en de IMU bij het aansturen van randapparaten vanaf de HE‑core — die zijn waar de HE‑zijde voor is ontworpen. Elk randapparaat kan slechts door één core tegelijk worden bezeten, dus kies HP of HE ervoor en houd dat aan voor de levensduur van het script.
Elke
@openamp.async_remote‑taakbody moet marshallen tot minder dan 500 bytes mpy‑bytecode — houd de functie klein en factor zwaardere logica uit naar aparte bibliotheekmodules die in de firmware worden ingevroren.Imports binnen de doorgegeven functie zien alleen modules die op het bestandssysteem van de HE‑core bestaan. De HE‑core heeft een eigen
/romROMFS — gescheiden van de/romvan de HP‑core — dus modules en ML‑modellen die je beschikbaar wilt hebben op HE moeten worden ingebakken in de ROMFS‑image van de HE‑zijde, niet die van de HP.
Microfoon¶
De ingebouwde mic wordt opgenomen via audio — Audio Module. Elke buffer komt binnen als signed‑16‑bit PCM bytearray, waardoor het triviaal is om deze in te voeren in ulab/numpy voor snelle DSP. Een eenvoudige luidheidsdetector — print telkens wanneer het RMS‑volume een drempelwaarde overschrijdt:
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¶
De ingebouwde LSM6DSM accelerometer + gyroscoop is beschikbaar via imu — imu-sensor
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)
Time‑of‑flight sensor¶
De AE3 bevat een VL53L8CX 8×8 multi‑zone time‑of‑flight sensor die tot 64 afstandsmetingen per frame retourneert, met een maximumbereik van ~4 m. Hij is beschikbaar via de tof — time-of-flight-sensorstuurprogramma‑module — roep tof.init() aan om de sensor te starten en tof.read_depth() om een diepteframe te pakken als een platte lijst met metingen in millimeters (één per zone):
import tof
tof.init()
while True:
depth, depth_min, depth_max = tof.read_depth()
print("min:", depth_min, "mm max:", depth_max, "mm")
De diepte‑array kan ook over een kleurenframe van de hoofdsensor worden getekend — tof.draw_depth() schildert deze op een bestaande image.Image, terwijl tof.snapshot() een vers gerenderde diepte‑afbeelding retourneert:
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¶
De ingebouwde CYW43439 is beschikbaar via network — netwerkconfiguratie als een station‑interface. Na het verbinden retourneert ipconfig("addr4") het (ip, netmask)‑paar:
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¶
Dezelfde CYW43439 stelt ook Bluetooth 5.1 beschikbaar. Gebruik aioble — Async BLE voor asyncio‑vriendelijke BLE — adverteer bijvoorbeeld als een randapparaat en wacht tot een central verbinding maakt:
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())
Busreferentie¶
GPIO¶
Gebruik machine.Pin om een van de bedrukte pinnen te lezen of aan te sturen. Uitgangen zijn 3,3 V CMOS en kunnen tot 25 mA per pin sinken/sourcen.
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())
Elke ingangspin kan ook een interrupt afvuren bij flanktransities:
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 is de enige bus met hardwarematige flow control. Omdat P6–P9 op de B2B‑header zitten en gerefereerd zijn aan 1,8 V, werkt UART3 alleen via een level shifter of een B2B‑carrier — sluit er niet rechtstreeks 3,3 V logica op aan.
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")
De ingebouwde Qwiic‑connector splitst I2C2 uit op 3,3 V.
I2C1 en I2C2 kunnen ook in target‑modus (slave) worden gebruikt via machine.I2CTarget om een geheugengebied beschikbaar te stellen aan een andere I²C‑controller:
from machine import I2CTarget
buf = bytearray(32)
target = I2CTarget(1, addr=0x42, mem=buf)
Notitie
Het LPI2C‑randapparaat is niet beschikbaar in de firmware. Het zou alleen target‑modus (slave) ondersteunen indien beschikbaar, en I2C1 en I2C2 dekken al zowel controller‑ als targetwerking.
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¶
De Alif Ensemble E3 stelt twee 12‑bit ADC‑kanalen beschikbaar op P8 en P9 (alleen B2B‑header). Beide ingangen zijn gerefereerd aan 1,8 V — read_u16 retourneert 0–65535 over 0–1,8 V op de 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)
Waarschuwing
De ADC‑ingangen van de AE3 zijn gerefereerd aan 1,8 V, niet aan 3,3 V. Het aansturen van een ruw 3,3 V signaal verzadigt de converter en kan de pin beschadigen — deel hogere spanningen extern af.
PWM¶
Pin |
Timer / kanaal |
|---|---|
P0 |
TIM0 T1 |
P1 |
TIM0 T0 |
P2 |
TIM1 T1 |
P3 |
TIM1 T0 |
P4 |
TIM2 T1 |
P5 |
TIM2 T0 |
P6 |
TIM9 T0 (alleen B2B) |
P7 |
TIM9 T1 (alleen B2B) |
P8 |
TIM5 T0 (alleen B2B) |
P9 |
TIM5 T1 (alleen B2B) |
Stuur een ervan aan via machine.PWM
from machine import Pin, PWM
pwm = PWM(Pin("P0"), freq=1_000, duty_u16=32768)
Software bit‑banged bussen¶
machine.SoftI2C en machine.SoftSPI werken op elke GPIO als je een extra bus nodig hebt.
Thermische sensor (extern)¶
De firmware bevat de fir — thermische sensor-driver (fir == far infrared)‑driver voor een extern bedrade AMG8833 8 × 8 thermische imager. Sluit de module aan op de hieronder vermelde I²C‑bus en lees vervolgens frames met 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())
De fir‑driver praat alleen via I²C 1 met de sensor — bedraad de module naar P4 (SCL) en P5 (SDA).
Timing¶
time¶
De time‑module dekt blokkerende vertragingen, monotone ticks en het meten van verstreken tijd:
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)
Virtuele timers¶
machine.Timer plant periodieke of one‑shot callbacks zonder een hardware‑timerslot te verbruiken. Geef -1 door als id om een virtuele (software‑)timer te gebruiken:
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"))
Periodewaarden zijn in milliseconden. Roep deinit() aan om te stoppen en het slot vrij te geven.
Real‑time clock¶
machine.RTC houdt de kloktijd bij over resets heen, ondersteund door 4 KB backup‑RAM op de chip dat deep sleep overleeft:
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())
De RTC draait ook door tijdens deep sleep, zodat je deze kunt gebruiken als wakeup‑bron voor machine.deepsleep().
Boot‑ en runtime‑informatie¶
USB‑bootloadervenster¶
Bij elke inschakeling draait de camera een korte bootloader (een paar seconden) waarmee OpenMV IDE de firmware kan bijwerken zonder dat de gebruiker DFU‑modus hoeft te activeren. Nadat het venster verloopt, draagt de bootloader over aan boot.py en vervolgens main.py.
Een draaiend script kan op aanvraag de bootloader opnieuw betreden door machine.bootloader() aan te roepen:
import machine
machine.bootloader()
Bestandssysteem en bootvolgorde¶
De AE3‑firmware mount bij het opstarten maximaal twee bestandssystemen:
Intern flashgeheugen — altijd gemount op
/flash. Bevat standaardmain.pyenREADME.txt; aangemaakt bij de allereerste boot.ROMFS — alleen‑lezen, in het geheugen gemapt bestandssysteem op
/romdat wordt gebruikt om grote data‑assets (bijv. AI‑modellen) te leveren die baat hebben bij zero‑copy‑toegang. Wordt automatisch door MicroPython bij het opstarten gemount, voordat enige gebruikers‑Python draait.
Na het mounten wordt de werkmap ingesteld op /flash. De interpreter draait dan scripts vanuit die map:
boot.pywordt bij elke soft reset uitgevoerd (cold boot,Ctrl‑Dvanuit de REPL, of telkens wanneer het draaiende script terugkeert).main.pywordt alleen bij cold boot uitgevoerd, direct naboot.py. Volgende soft resets draaienboot.pyopnieuw maar vallen meteen terug op de REPL — ommain.pyopnieuw te draaien moet je het board volledig resetten.
De standaard main.py die op een vers geflasht board wordt geleverd, laat alleen het blauwe kanaal van de gebruikers‑RGB‑LED knipperen als heartbeat (twee korte pulsen, korte pauze), zodat je kunt zien dat de firmware schoon is opgestart zonder dat er een host is aangesloten.
sys.path wordt uitgebreid met beide bestandssystemen en hun lib/‑submappen, zodat importeerbare modules in /flash/lib of /rom/lib kunnen staan.
Wanneer verbonden via USB, enumereert /flash ook als een USB‑massaopslagschijf op de host, zodat je boot.py, main.py en alle andere bestanden rechtstreeks kunt bewerken. Werp de schijf uit voordat je de camera reset zodat de host zijn gecachete writes leegmaakt.
Notitie
Omdat het OS de schijf behandelt als een passief block device, verschijnen bestanden die worden aangemaakt of gewijzigd door code die op de OpenMV Cam draait pas wanneer de host de schijf opnieuw mount. Als zowel het OS als de OpenMV Cam tegelijkertijd naar hetzelfde bestandssysteem schrijven, wint het OS en overschrijft het de wijzigingen die door de camera zijn gemaakt.
Notitie
Het rode kanaal van de gebruikers‑RGB‑LED kan kort oplichten terwijl de host leest van of schrijft naar de USB‑massaopslagschijf — dit is een firmwaregestuurde activiteitsindicator, geen fout.
Opslaggroottes¶
De AE3 wordt geleverd met:
/flash— 8 MB FAT‑bestandssysteem, lezen/schrijven./romop de HP‑core — 24 MB alleen‑lezen in het geheugen gemapt ROMFS voor scripts en data die de HP‑core bij het opstarten laadt./romop de HE‑core — 1 MB alleen‑lezen ROMFS in eigendom van de HE‑core. Modules en ML‑modellen die je beschikbaar wilt hebben voor@openamp.async_remote‑taken moeten in deze image worden ingebakken, niet die van de HP.
Hard‑fault‑indicator¶
Als de gebruikers‑RGB‑LED snel door alle kleuren cyclet — snel genoeg dat het meestal lijkt op een fonkelende witte LED in plaats van afzonderlijke tinten — heeft de firmware een onherstelbare hard fault geraakt. Flash de firmware opnieuw om te herstellen; als opnieuw flashen niet helpt, is het board mogelijk fysiek beschadigd.
Softwarebibliotheken¶
Zie de bibliotheekindex voor de volledige lijst met modules — inclusief welke uniek zijn voor de AE3‑build.