13.1.10. Окремі вікна терміналу

Tools → Open Terminal відкриває незалежні вікна терміналу – кожне з них є мініатюрним сеансом OpenMV IDE у власному вікні з переглядачем кадрового буфера, гістограмою та інтерактивним терміналом, підключеним через обраний транспорт. Підключення головного вікна не зачіпається, тому окремий термінал дозволяє спостерігати за другою камерою, поки перша залишається підключеною, або налагоджувати камеру на іншому кінці мережі.

A standalone terminal window: interactive terminal on the left with its toolbar, frame buffer and histogram on the right

Окреме вікно терміналу через послідовний порт: інтерактивний термінал ліворуч із панеллю інструментів запуску / зупинки / скидання, кадровий буфер і гістограма праворуч – вони активуються, коли камера передає кадри вбудовано в потік.

New Terminal запитує один із трьох транспортів:

  • Послідовний порт – будь-який послідовний порт на будь-якій швидкості передачі (за замовчуванням 115 200 бод). Це охоплює USB-порт другої камери, камеру, підключену через USB-UART міст, Bluetooth-послідовний зв’язок (що відображається як звичайний послідовний порт) або будь-який не-OpenMV послідовний пристрій, якому потрібен термінал. На USB-порту швидкість передачі не обмежує швидкість – дані завжди рухаються зі швидкістю USB-з’єднання – але на USB-порту камери уникайте 921 600 і 12 000 000, що перемикають камеру з REPL у протокол налагодження IDE.

  • TCP – підключення до сервера за обраним хостом і портом або прослуховування як сервер на обраному порту.

  • UDP – та сама пара режимів, але через UDP.

IDE запам’ятовує останні десять конфігурацій і перераховує їх у підменю Open Terminal для відкриття одним кліком; Clear Menu видаляє їх.

На відміну від панелі виводу головного вікна, окремий термінал є повністю інтерактивним: це REPL. Вводьте команди, і Python виконуватиме їх на підключеній камері рядок за рядком, з підтримкою історії та автодоповнення від самого MicroPython. Панель інструментів додає однокнопкові еквіваленти поширених керуючих послідовностей – запустити поточний скрипт редактора, зупинити запущений скрипт і м’яке скидання – а також ті самі елементи керування очищенням, збереженням і перенесенням рядків, що й у головній панелі терміналу.

Кадровий буфер над терміналом також відображає дані в реальному часі. Коли підключена камера передає стиснуті кадри вбудовано в потік – вкладені в той самий потік, що й вивід print – термінал декодує та відображає їх, а кнопки Record і Zoom працюють так само, як і в головному вікні. Це поєднання є засобом мережевого налагодження в IDE: камера, що надає REPL через Wi-Fi, отримує повний цикл редагування-запуску-перегляду без USB-кабелю.

13.1.10.1. Передача кадрів вбудовано в потік

Кодування, яке розуміє термінал, просте: байт 0xFE відкриває кадр, другий 0xFE закриває його, і кожен байт корисного навантаження між ними має встановлений старший біт і переносить шість бітів стиснутого зображення – три байти зображення стають чотирма байтами навантаження. Звичайний текст ніколи не використовує ці значення байтів, тому кадри та вивід print() спільно використовують потік без колізій: термінал відображає текст і показує кадри.

Скрипт нижче захоплює кадри, конвертує кожен у JPEG та виводить у такому вигляді. Пакування бітів виконується через ulab (у якому немає операторів зсуву, тому зсуви записані як множення та ділення), і він достатньо швидкий, щоб встигати за камерою:

import csi
import sys
import time
from ulab import numpy as np

csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)

clock = time.clock()


def encode_for_ide(data):
    n = len(data)
    n3 = n - (n % 3)
    m = (n3 // 3) * 4
    out = bytearray(((n * 8) + 5) // 6 + 2)
    out[0] = 0xFE
    out[-1] = 0xFE
    if n3:
        src = np.frombuffer(data, dtype=np.uint8)
        dst = np.frombuffer(out, dtype=np.uint8)
        b0 = src[0:n3:3]
        b1 = src[1:n3:3]
        b2 = src[2:n3:3]
        dst[1:m + 1:4] = (b0 & 0x3F) | 0x80
        dst[2:m + 2:4] = (b0 // 64) | ((b1 & 0x0F) * 4) | 0x80
        dst[3:m + 3:4] = (b1 // 16) | ((b2 & 0x03) * 16) | 0x80
        dst[4:m + 4:4] = (b2 // 4) | 0x80
    if n % 3 == 2:
        x = data[n - 2] | (data[n - 1] << 8)
        out[m + 1] = 0x80 | (x & 0x3F)
        out[m + 2] = 0x80 | ((x >> 6) & 0x3F)
        out[m + 3] = 0x80 | ((x >> 12) & 0x3F)
    elif n % 3 == 1:
        out[m + 1] = 0x80 | (data[n - 1] & 0x3F)
        out[m + 2] = 0x80 | (data[n - 1] >> 6)
    return out


while True:
    clock.tick()
    img = csi0.snapshot().to_jpeg(quality=80)
    sys.stdout.write(encode_for_ide(img.bytearray()))
    print(clock.fps())

Це працює в окремому терміналі, тому що виведення REPL чекає: камера блокується до того часу, поки термінал не прийме дані, тому кожен байт кадру надходить, і JPEG швидко передається через USB full-speed або high-speed з’єднання. Той самий скрипт без змін працює через TCP-термінал, що є описаним вище шляхом мережевого налагодження. Єдине місце, де це не працює – головне з’єднання налагодження IDE, канал виводу якого скидається при переповненні замість очікування – там переглядач кадрового буфера вже нативно відображає кадри камери, тому нічого не втрачається.