OpenMV Pure Thermal

OpenMV Pure Thermal คือบอร์ดถ่ายภาพความร้อนแบบระบบสมบูรณ์ที่สร้างบน STMicroelectronics STM32H743 (Cortex‑M7 @ 480 MHz) พร้อม SDRAM ภายนอก 64 MB, แฟลช QSPI 32 MB, ตัวแปลงสัญญาณ JPEG แบบฮาร์ดแวร์, จอสัมผัสแบบ capacitive IPS 4.3" ขนาด 800×480, ช่องออก HDMI, ซ็อกเก็ต FLIR® Lepton® สำหรับกล้องความร้อน และกล้องแสงปกติ OV5640 ขนาด 5MP นอกจากนี้ยังมี Wi‑Fi, ช่องเสียบ microSD, เครื่องวัดระยะด้วยเลเซอร์, บัซเซอร์ และหลอด LED สีขาวกำลังสูง

OpenMV Pure Thermal

สำหรับข้อมูลแผ่นข้อมูล รูปถ่าย และขนาด โปรดดูที่ หน้าผลิตภัณฑ์ OpenMV Pure Thermal

คุณสมบัติเด่น

  • STMicroelectronics STM32H743XI Cortex‑M7 ที่ 480 MHz

  • ตัวแปลงสัญญาณ JPEG encoder/decoder แบบฮาร์ดแวร์

  • SDRAM ภายนอก 64 MB (~400 MB/s) บวก SRAM ภายใน 1 MB

  • แฟลชภายใน 2 MB + แฟลช QSPI ภายนอก 32 MB (~50 MB/s อ่าน)

  • เซนเซอร์แสงปกติแบบ rolling‑shutter OV5640 ขนาด 5MP

  • ซ็อกเก็ต FLIR® Lepton® — รองรับ Lepton รุ่น 1/2/2.5/3/3.5 ทั้งแบบ radiometric และ non‑radiometric พร้อมอุณหภูมิต่อพิกเซลเป็นองศาเซลเซียส

  • จอสัมผัสแบบ capacitive IPS 4.3" ขนาด 800×480 (สี 24 บิต @ 60 Hz) รองรับท่าทางสัมผัสได้สูงสุด 5 จุด

  • ช่องออก HDMI ผ่าน TFP410 DVI serializer — สูงสุด 1280×720 @ 60 Hz

  • Wi‑Fi ผ่าน WINC1500; รองรับ MJPEG over RTSP แบบพร้อมใช้งานทันที

  • USB‑C ความเร็วเต็ม (12 Mb/s, จำกัดกระแส 900 mA) — ปรากฏเป็น VCP + USB mass storage ต่อโฮสต์ รวมถึงรองรับการชาร์จด้วย

  • ช่องเสียบ microSD — SD สูงสุด 2 GB, SDHC สูงสุด 32 GB, SDXC สูงสุด 2 TB

  • เครื่องวัดระยะเลเซอร์ VL53L1CX (สูงสุด ~4 ม.)

  • บัซเซอร์ ที่ควบคุมระดับเสียง/ความถี่ด้วยซอฟต์แวร์

  • หลอด LED สีขาวกำลังสูง นอกเหนือจาก LED แสดงสถานะ RGB ของผู้ใช้

  • ขั้วต่อแบตเตอรี่ LiPo พร้อมการชาร์จผ่าน USB ที่ 500 mA

  • พิน I/O จำนวน 10 พิน, รองรับ 5 V ทนได้ มีแรงดันเอาต์พุต 3.3 V, 25 mA ต่อพิน (รวม 120 mA), รองรับอินเทอร์รัปต์ P6 ไม่ รองรับ 5 V เมื่อใช้ในโหมด ADC หรือ DAC

  • ARM 10‑pin SWD สำหรับการดีบัก ST‑LINK / J‑Link

  • ขั้วต่อ Qwiic สำหรับอุปกรณ์ต่อพ่วง I²C

Note

บอร์ดมีช่องที่ขอบล่างซ้ายสำหรับใส่ น็อตขาตั้งกล้อง ¼"–20 แบบเสริม ไม่ได้ติดมาจากโรงงาน — บัดกรีเข้าในช่องถ้าต้องการติดบอร์ดบนขาตั้งกล้องมาตรฐาน

การกำหนดพิน

OpenMV Pure Thermal pinout

ข้อมูลอ้างอิงพิน

ชื่อพิน

ฟังก์ชัน

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

digital I/O

RESET

ดึงไปที่ GND เพื่อรีเซ็ตบอร์ด

SYN

แพด frame‑sync — ไม่ได้ต่อเชื่อม

VIN

แพด shield VIN — ไม่ได้ต่อเชื่อม

BOOT0

ดึงไปที่ 3.3 V ขณะเปิดเครื่องเพื่อเข้า DFU / ROM bootloader

BUZZER

บัซเซอร์ piezo บนบอร์ด (ขับด้วย TIM2/PWM)

LED_RED

ช่องแดงของ LED แสดงสถานะ RGB (active low)

LED_GREEN

ช่องเขียวของ LED แสดงสถานะ RGB (active low)

LED_BLUE

ช่องน้ำเงินของ LED แสดงสถานะ RGB (active low)

LED_WHITE

หลอด LED สีขาวกำลังสูง

Note

แพด SYN และ VIN บน shield/header ไม่มีการเชื่อมต่อทางไฟฟ้า บน Pure Thermal — มีไว้เพื่อความเข้ากันได้ของ header เท่านั้น จ่ายไฟให้บอร์ดผ่าน USB‑C หรือขั้วต่อแบตเตอรี่ LiPo บนบอร์ดแทน (ดู พินจ่ายไฟ ด้านล่าง) นอกจากนี้ แพด VIN มีการพิมพ์บนบอร์ดว่า VBAT (เป็นข้อผิดพลาดในการระบุชื่อ) — ตำแหน่งเป็นพิน VIN ของ header OpenMV มาตรฐานและไม่ได้ต่อเชื่อมไม่ว่าทิศทางใด

พินจ่ายไฟ

  • 3.3V — ราง 3.3 V ที่ผ่านการปรับแรงดัน มีให้สูงสุด 250 mA สำหรับ shield

  • GND — กราวด์ร่วม

Pure Thermal รับไฟผ่าน USB‑C หรือ ขั้วต่อแบตเตอรี่ LiPo บนบอร์ด พอร์ต USB‑C จำกัดกระแสรวมไว้ที่ 900 mA และรองรับการชาร์จ LiPo ที่ 500 mA ดังนั้นสามารถต่อแบตเตอรี่พร้อมกับ USB ได้

ปุ่มเปิด/ปิด บนบอร์ดสลับเปิด/ปิดรางไฟฟ้าของระบบ และทำงานได้ไม่ว่าจะจ่ายไฟจาก USB หรือ LiPo กดปุ่มค้างไว้สักสองสามวินาที เพื่อสลับสถานะ — การกดเบาจะถูกละเว้นเพื่อป้องกันการปิดเครื่องโดยไม่ตั้งใจ

การเลือกแหล่งจ่ายไฟทำตามกฎง่าย ๆ สองข้อ:

  • แบตเตอรี่จะจ่ายไฟให้บอร์ดก็ต่อเมื่อแรงดันสูงกว่า 3 V ต่ำกว่าค่าขีดแบ่งนั้น PMIC บนบอร์ดจะตัดแบตเตอรี่ออกเพื่อป้องกันการคายประจุมากเกินไป

  • เมื่อมี USB ต่ออยู่ USB จะจ่ายไฟให้บอร์ด และ LiPo ที่ต่ออยู่จะชาร์จในเบื้องหลัง

ขั้วต่อ LiPo ยังมี การป้องกันแรงดันย้อนกลับ ดังนั้นการเสียบแบตเตอรี่กลับด้านจะไม่ทำให้บอร์ดเสียหาย

Note

บอร์ดยังส่ง แรงดันแบตเตอรี่ และ สัญญาณวัดกระแสแบตเตอรี่ กลับไปยังช่อง ADC ของ MCU แต่ยังไม่มีการเพิ่มการรองรับเฟิร์มแวร์สำหรับทั้งสองอย่าง

พินสำหรับการกู้คืนและดีบัก

  • RESET — ดึงไปที่ GND เพื่อรีเซ็ตบอร์ด Pure Thermal ยังมี ปุ่ม RESET บนบอร์ดที่ทำงานเหมือนกัน

  • BOOT0 — ดึงไปที่ 3.3 V ขณะเปิดเครื่องเพื่อเข้าสู่ ROM bootloader ของ STM32 (โหมด DFU) OpenMV IDE ใช้โหมดนี้ในการเขียน bootloader บนบอร์ดใหม่ ปุ่ม BOOT0 บนบอร์ดทำงานเหมือนกัน — กดค้างขณะเปิดเครื่อง

บอร์ดมีหัวต่อดีบัก SWD (RST / SWCLK / SWDIO / SWO) อยู่ถัดจากหัวต่อ GPIO เข้ากันได้กับอะแดปเตอร์ ST‑LINK และ SEGGER J‑Link นอกจากนี้ยังมีขั้วต่อ ARM 10‑pin SWD แบบแยกติดตั้งอยู่ด้วย — มีสัญญาณ SWD เหมือนกัน (ไม่มี JTAG เต็มรูปแบบ) แต่ในรูปแบบ 0.05" 10‑pin มาตรฐาน

Note

พิน trace SWO ใช้ร่วมกับสัญญาณนาฬิกา SPI ของ FLIR® Lepton® บนบอร์ด ไม่สามารถใช้ SWO พร้อมกับ Lepton — ต้องเลือกอย่างใดอย่างหนึ่ง

ขั้วต่อ PURE Modules Debug ตัวที่สามติดตั้งอยู่บนบอร์ด โดยแยกสัญญาณที่เน้นการดีบักออกมาหลายสัญญาณ (SWCLK, SWDIO, RST, SPI2_MISO, SPI2_MOSI, VBUS, 3.3 V, GND และพิน GPIO สองพิน) สำหรับเชื่อมต่อโมดูลเสริม พิน GPIO สองพินบนขั้วต่อนี้ขับเคลื่อนโดย I²C bus แบบ bit‑banged แทนที่จะเป็นอุปกรณ์ต่อพ่วงฮาร์ดแวร์

ขั้วต่อดีบักทั้งสาม (หัวต่อ SWD แบบแนว, ขั้วต่อ ARM 10‑pin SWD และขั้วต่อ PURE Modules Debug) อ้างอิง 3.3 V — ตรวจสอบให้แน่ใจว่าอะแดปเตอร์ดีบักของคุณถูกตั้งค่าสำหรับลอจิก 3.3 V ก่อนเชื่อมต่อ

อุปกรณ์ต่อพ่วงบนบอร์ด

LED

Pure Thermal มี LED สามดวงบนบอร์ด:

  • LED RGB สำหรับผู้ใช้ — ควบคุมด้วยซอฟต์แวร์ เข้าถึงได้ผ่าน LED_RED, LED_GREEN และ LED_BLUE

    from machine import LED
    
    LED("LED_RED").on()
    LED("LED_GREEN").on()
    LED("LED_BLUE").on()
    
  • หลอด LED สีขาว — ขับเคลื่อนผ่าน LED_WHITE LED_WHITE ต่อสายแบบ active high ในฮาร์ดแวร์ ในขณะที่เฟิร์มแวร์ถือว่า LED บนบอร์ดอื่น ๆ ทุกดวงเป็น active low ดังนั้นใช้ 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
    
  • LED ชาร์จ — ขับเคลื่อนโดยตรงจากฮาร์ดแวร์จัดการพลังงานบนบอร์ด ไม่มีการควบคุมด้วยซอฟต์แวร์ ทำงานได้ไม่ว่ารางระบบจะเปิดหรือปิด (คือไม่ว่าปุ่มเปิด/ปิดจะอยู่ตำแหน่งใด)

    สี

    ความหมาย

    น้ำเงิน

    กำลังชาร์จ — ดูข้อผิดพลาด: อาจไม่ดับเมื่อชาร์จเสร็จ

    เขียว

    ชาร์จเสร็จแล้ว — ดูข้อผิดพลาด: อาจไม่ทำงานได้อย่างน่าเชื่อถือ

    แดง

    แบตเตอรี่เหลือน้อย (≤ 3.2 V, เฉพาะเมื่อไม่ได้ชาร์จอยู่)

บัซเซอร์

บัซเซอร์ piezo บนบอร์ดต่อสายไปยังช่องตัวจับเวลา — ขับด้วย 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) และ 5MP เต็ม csi.WQXGA2 (2592×1944) สตรีมได้ในรูปแบบ JPEG ปรับการบีบอัดด้วย csi.CSI.quality (0-100 ค่าสูงกว่า = เฟรมใหญ่กว่า รายละเอียดมากกว่า):

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

OV5640 มีเลนส์โฟกัสอัตโนมัติแบบ voice‑coil‑actuator เรียกโฟกัสอัตโนมัติแบบครั้งเดียวผ่าน csi.CSI.ioctl พร้อม csi.IOCTL_TRIGGER_AUTO_FOCUS — เซนเซอร์จะกวาดมอเตอร์โฟกัสครั้งเดียวและล็อคที่สิ่งที่อยู่ตรงหน้า:

cam.ioctl(csi.IOCTL_TRIGGER_AUTO_FOCUS)

เรียก ioctl ซ้ำทุกครั้งที่ฉากเปลี่ยน — โฟกัสอัตโนมัติเป็นแบบครั้งเดียว ไม่ใช่ต่อเนื่อง

Note

เอาต์พุต STROBE ของ OV5640 (ใช้สำหรับแฟลช/ไฟ IR แบบซิงโครไนซ์) ต่อสายไปยัง MCU บน 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()

Note

เอาต์พุต VSYNC ของ Lepton (หนึ่งพัลส์ต่อเฟรมความร้อน) ต่อสายไปยัง MCU บน 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 --- Machine Learning รันโมเดล TFLite แบบ quantised บน Cortex‑M7 ด้วยเคอร์เนล CMSIS‑NN — เร็วพอสำหรับตัวตรวจจับขนาดกะทัดรัดที่สักสองสามเฟรมต่อวินาที โมเดลในระบบไฟล์แบบอ่านอย่างเดียว /rom โหลดโดยตรงจากแฟลชโดยไม่ต้องคัดลอกไปยัง RAM นี่คือตัวตรวจจับใบหน้า BlazeFace ขนาด 128×128 ที่ซ้อนทับใบหน้าที่ตรวจพบและจุดสำคัญ 6 จุดบนทุกเฟรมจากกล้องแสงปกติ:

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 ranger บนบอร์ดต่อสายไปยัง I²C bus 2 ใช้ไดรเวอร์แช่แข็ง vl53l1x --- ไดรเวอร์เซนเซอร์วัดระยะ VL53L1X ToF เพื่อรับค่าระยะทางสูงสุด ~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

LCD บนบอร์ดขนาด 4.3" มีความละเอียด 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

จอสัมผัส

ตัวควบคุมการสัมผัสแบบ capacitive คือ 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

เฟิร์มแวร์ยังแจกจ่าย framebuffer ของ LCD ไปยัง tfp410 --- ตัวควบคุม DVI/HDMI HDMI serializer บนบอร์ดด้วย ทำให้จอภาพภายนอกสะท้อนสิ่งที่อยู่บน LCD สร้าง tfp410.TFP410 เพื่อเปิดใช้งานเอาต์พุต HDMI:

import tfp410

hdmi = tfp410.TFP410()

หากต้องการเฉพาะเอาต์พุต HDMI และไม่สนใจ LCD บนบอร์ด ให้ปิดแบ็คไลท์และเพิ่มความละเอียด framebuffer ให้เกิน WVGA TFP410 รองรับสูงสุด 1280×720 @ 60 Hz ตัวอย่างเช่น:

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 ถูกเสียบหรือถอดออก ให้ลงทะเบียนคอลแบ็ก hot‑plug บน TFP410 คอลแบ็กจะทำงานด้วย True เมื่อเชื่อมต่อ และ False เมื่อตัดการเชื่อมต่อ:

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

hdmi.hotplug_callback(on_hotplug)

คุณยังสามารถโพลสถานะการเชื่อมต่อได้ทุกเมื่อด้วย isconnected() (เฉพาะเมื่อไม่มีการลงทะเบียนคอลแบ็ก)

พอร์ต HDMI ยังรองรับช่อง DDC (ข้อมูลการแสดงผล) และ CEC (การควบคุมอิเล็กทรอนิกส์สำหรับผู้บริโภค) เปิดเผยผ่านคลาส คลาส 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 shield

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

Note

เนื่องจากปัญหาการขาดแคลนชิ้นส่วน บาง Pure Thermal ถูกจัดส่งโดยไม่มีโมดูล WINC1500 ติดตั้ง หาก network.WINC ทำให้เกิดข้อผิดพลาดหรือเชื่อมต่อไม่ได้ ให้ตรวจสอบบอร์ดว่าขาดโมดูล Wi‑Fi — ส่วนที่เหลือของกล้องทำงานได้เหมือนกันทุกอย่างโดยไม่มีมัน

การ์ด microSD

เมื่อใส่การ์ดจะถูก mount อัตโนมัติที่ /sdcard และสามารถใช้งานผ่านระบบไฟล์ปกติ:

import os

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

ข้อมูลอ้างอิงบัส

GPIO

ใช้ machine.Pin เพื่ออ่านหรือขับพินที่มีชื่อบนบอร์ด เอาต์พุตเป็น 3.3 V CMOS รองรับ 5 V ในด้านอินพุต และสามารถ sink/source ได้สูงสุด 25 mA ต่อพิน (รวม 120 mA ทั่วทั้ง header)

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

ฮาร์ดแวร์เดียวกันยังสามารถใช้ในโหมด target (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 ถูก level-shifted เป็น 5 V ผ่านทรานซิสเตอร์แบบ open‑drain ดังนั้นบัสจำกัดเฉพาะ โหมดมาตรฐาน (100 kHz) และ โหมดเร็ว (400 kHz) เท่านั้น — อย่าพยายามรันโหมดเร็วพิเศษหรืออัตราสูงกว่าผ่าน Qwiic header

ขั้วต่อ Qwiic จ่าย 5 V เพื่อขับโมดูลที่ต่ออยู่; ไม่สามารถใช้จ่ายไฟให้ 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 เป็นพินอนาล็อกสำหรับผู้ใช้เพียงพินเดียว สามารถใช้เป็นอินพุต ADC 12 บิตหรือเอาต์พุต DAC ได้

  • ADC — สเกลเต็มที่ 3.3 V ที่พิน:

    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 V:

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

ในโหมด ADC หรือ DAC P6 รองรับเฉพาะ 3.3 V เท่านั้น — ห้ามป้อน 5 V

PWM

พิน

ตัวจับเวลา / ช่อง

P4

TIM2 CH3

P5

TIM2 CH4

P6

TIM2 CH1

P7

TIM4 CH1

P8

TIM4 CH2

Note

TIM1 ถูกสงวนไว้ โดยเฟิร์มแวร์เพื่อสร้างสัญญาณนาฬิกาพิกเซลของเซนเซอร์กล้อง ดังนั้นช่อง TIM1 ที่อยู่บน P0/P1/P2 ทางกายภาพจึงไม่สามารถใช้สำหรับ PWM ของผู้ใช้โดยไม่ทำให้กล้องเสียหาย

TIM4 ใช้ร่วมกับ pyb.Servo — การสร้าง servo จะตั้งค่าตัวจับเวลาทั้งหมดใหม่เป็นโหมด 50 Hz ดังนั้นอย่าผสม machine.PWM บน P7/P8 กับ pyb.Servo ในสคริปต์เดียวกัน

ขับใด ๆ ของพวกมันผ่าน machine.PWM

from machine import Pin, PWM

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

บัสแบบ Software bit‑banged

machine.SoftI2C และ machine.SoftSPI ทำงานบน GPIO ใด ๆ หากต้องการบัสเพิ่มเติม

เซนเซอร์ความร้อน (ภายนอกบอร์ด)

นอกเหนือจาก FLIR Lepton บนบอร์ด เฟิร์มแวร์ยังรวมไดรเวอร์ fir --- ไดรเวอร์เซนเซอร์ความร้อน (fir == far infrared) สำหรับเครื่องถ่ายภาพความร้อน I²C แบบต่อสายภายนอก:

  • MLX90621 — IR array 16 × 4

  • MLX90640 — IR array 32 × 24

  • MLX90641 — IR array 16 × 12

  • AMG8833 — IR array 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"))

ค่า Period เป็นมิลลิวินาที เรียก 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 bootloader

ในทุกการเปิดเครื่อง กล้องจะรัน bootloader สั้น ๆ (สักสองสามวินาที) ที่ให้ OpenMV IDE อัปเดตเฟิร์มแวร์โดยไม่ต้องให้ผู้ใช้เข้าโหมด DFU หลังจากหน้าต่างหมดเวลา bootloader จะส่งต่อไปยัง boot.py แล้วจึง main.py

สคริปต์ที่กำลังทำงานสามารถกลับเข้าสู่ bootloader ได้ตามต้องการโดยเรียก machine.bootloader()

ระบบไฟล์และลำดับการบูต

เฟิร์มแวร์ Pure Thermal mount ระบบไฟล์สูงสุดสามระบบเมื่อบูต:

  • แฟลชภายใน — mount ที่ /flash เสมอ เก็บ main.py และ README.txt ตามค่าเริ่มต้น; สร้างในการบูตครั้งแรก

  • การ์ด microSD — หากใส่การ์ดแล้วจะถูก mount ที่ /sdcard

  • ROMFS — ระบบไฟล์แบบ memory‑mapped อ่านอย่างเดียวที่ /rom ใช้สำหรับส่งมอบข้อมูลขนาดใหญ่ (เช่น โมเดล AI) ที่ได้ประโยชน์จากการเข้าถึงแบบ zero‑copy Mount อัตโนมัติโดย MicroPython เมื่อเริ่มต้น ก่อนที่ Python ของผู้ใช้จะทำงาน

หลังจาก mount แล้ว working directory จะถูกตั้งเป็น /sdcard เมื่อมีการ์ดอยู่ มิฉะนั้น /flash จากนั้น interpreter จะรันสคริปต์จากไดเรกทอรีนั้น:

  • boot.py ถูกรันบน ทุก การ soft reset

  • main.py ถูกรันเฉพาะ เมื่อบูตเย็น เท่านั้น ทันทีหลังจาก boot.py

การวาง boot.py หรือ main.py ลงบนการ์ด SD จะแทนที่ไฟล์ที่อยู่ใน flash โดยไม่แตะต้องมัน

เมื่อเชื่อมต่อผ่าน USB ระบบไฟล์บูต (/sdcard หากมีการ์ดอยู่ มิฉะนั้น /flash) จะแสดงเป็น USB mass‑storage drive บนโฮสต์ด้วย Eject drive ก่อนรีเซ็ตกล้อง เพื่อให้โฮสต์ flush การเขียนที่แคชไว้

Note

ไฟล์ที่สร้างหรือแก้ไขโดยโค้ดที่รันบน OpenMV Cam จะไม่แสดงบนโฮสต์จนกว่า drive จะถูก mount ใหม่ ใช้การ์ด SD สำหรับข้อมูลใด ๆ ที่สคริปต์เขียนกลับ และ mount ใหม่ก่อนอ่านไฟล์เหล่านั้นจากโฮสต์

ขนาดพื้นที่เก็บข้อมูล

Pure Thermal จัดส่งพร้อม:

  • /flash — ระบบไฟล์ FAT 24 MB อ่าน/เขียน

  • /rom — ROMFS แบบ memory‑mapped อ่านอย่างเดียว 8 MB ใช้สำหรับส่งมอบสคริปต์และโมเดล ML ที่ได้ประโยชน์จากการเข้าถึง mmap แบบ zero‑copy

  • /sdcard — ขนาดเต็มของการ์ด microSD ที่ใส่อยู่ (เมื่อมีอยู่) อ่าน/เขียน

ตัวบ่งชี้ hard‑fault

หาก LED RGB ของผู้ใช้วนรอบสีต่าง ๆ อย่างรวดเร็ว — เร็วพอที่จะดูเหมือน LED ขาวระยิบระยับ มากกว่าสีที่แตกต่างกัน — เฟิร์มแวร์ได้เกิด hard fault ที่กู้คืนไม่ได้ ให้ reflash เฟิร์มแวร์เพื่อกู้คืน

ข้อผิดพลาดของฮาร์ดแวร์

ข้อบกพร่องระดับบอร์ดบางประการมีการบันทึกไว้ใน ข้อผิดพลาดฮาร์ดแวร์ Pure Thermal รายการสำคัญที่ควรทราบ:

  • การรบกวนจากขั้วต่อแบตเตอรี่ — ชิ้นส่วนบน PCB อยู่ใต้ขั้วต่อแบตเตอรี่ LiPo โดยตรง และลิ่มที่ยื่นออกมาบนปลั๊กสายแบตเตอรี่อาจเกี่ยวชิ้นส่วนเหล่านั้นเมื่อถอดสายออก บางครั้งดึงชิ้นส่วนออกจากบอร์ด ตัดลิ่มออกจากปลั๊กสายด้วยคีมตัดแบน flush cutters ก่อนใช้งานครั้งแรก

  • RTC หยุดทำงานขณะบอร์ดปิด — ค่า load capacitance บน crystal 32 kHz (Y2) สูงเกินไป การถอด C96 และ C97 (คู่ที่อยู่ข้าง crystal ถัดจาก STM32) ช่วยให้ RTC ทำงานต่อด้วยไฟสำรอง บอร์ดส่วนใหญ่จัดส่งโดยถอด cap เหล่านี้ออกแล้ว หาก RTC ของคุณสูญเสียเวลาเมื่อถอดปลั๊ก ให้ตรวจสอบตำแหน่งเหล่านั้น ดู GitHub issues #1536 และ #1600 สำหรับเธรดเต็ม

  • LED ตัวบ่งชี้การชาร์จค้างสีน้ำเงิน — ตัวชาร์จอาจสิ้นสุดรอบการชาร์จที่แรงดันระหว่าง 4.15 V ถึง 4.19 V โดยไม่สลับตัวบ่งชี้จากสีน้ำเงิน (กำลังชาร์จ) เป็นสีเขียว (ชาร์จเสร็จ) แบตเตอรี่ยังคงชาร์จเต็มในกรณีนี้ เชื่อการวัดแรงดัน ไม่ใช่ LED

  • Silkscreen ระบุ VIN ผิดเป็น VBAT — แพดในตำแหน่ง VIN ของ header OpenMV มาตรฐานถูกพิมพ์บนบอร์ดว่า VBAT บน Pure Thermal ป้ายกำกับผิด แต่ไม่มีผลในทางปฏิบัติเพราะแพดนี้ ไม่มีการเชื่อมต่อทางไฟฟ้า ไม่ว่าทิศทางใด

ไลบรารีซอฟต์แวร์

ดู ดัชนีไลบรารี สำหรับรายการโมดูลทั้งหมด รวมถึงโมดูลที่มีเฉพาะใน Pure Thermal build