13.1.10. Janelas de terminal independentes¶
Tools → Open Terminal abre janelas de terminal independentes – cada uma é uma sessão em miniatura da OpenMV IDE em sua própria janela, com um visualizador de frame buffer, um histograma e um terminal interativo, conectados por um transporte de sua escolha. A conexão da janela principal não é afetada, então um terminal independente permite observar uma segunda câmera enquanto a primeira permanece conectada, ou depurar uma câmera do outro lado de uma rede.
Uma janela de terminal independente sobre uma porta serial: o terminal interativo à esquerda com sua barra de ferramentas de executar / parar / reiniciar, o frame buffer e o histograma à direita – eles se acendem quando a câmera transmite quadros embutidos no fluxo.¶
New Terminal solicita um de três transportes:
Serial port – qualquer porta serial em qualquer taxa de transmissão (baud rate) (padrão 115.200). Isso abrange a porta USB de uma segunda câmera, uma câmera ligada por uma ponte USB-para-UART, um enlace serial Bluetooth (que aparece como uma porta serial comum) ou qualquer dispositivo serial não OpenMV que precise de um terminal. Em uma porta USB, a taxa de transmissão (baud rate) não limita a velocidade – os dados sempre se movem na velocidade do enlace USB –, mas na porta USB de uma câmera evite 921.600 e 12.000.000, que alternam a câmera do REPL para o protocolo de depuração da IDE.
TCP – conecte-se a um servidor em um host e porta escolhidos, ou escute como um deles em uma porta escolhida.
UDP – o mesmo par de papéis, sobre UDP.
A IDE lembra as últimas dez configurações e as lista no submenu Open Terminal para reabertura com um clique; Clear Menu as esquece.
Diferentemente do painel de saída-apenas da janela principal, um terminal independente é totalmente interativo: é um REPL. Digite no prompt e o Python é executado na câmera conectada linha por linha, com histórico e autocompletar por tabulação fornecidos pelo próprio MicroPython. A barra de ferramentas adiciona equivalentes de um clique para as sequências de controle comuns – executar o script atual do editor, parar o script em execução e fazer soft reset – e os mesmos controles de limpar, salvar e quebrar de linha do painel de terminal principal.
O frame buffer acima do terminal também é ao vivo. Quando a câmera conectada transmite quadros comprimidos embutidos no fluxo – inseridos no mesmo fluxo de sua saída de impressão –, o terminal os decodifica e exibe, e os botões Record e Zoom funcionam exatamente como na janela principal. Essa combinação é a proposta de depuração em rede da IDE: uma câmera que expõe seu REPL por Wi-Fi obtém o ciclo completo de editar-executar-pré-visualizar sem nenhum cabo USB envolvido.
13.1.10.1. Transmitindo quadros embutidos no fluxo¶
A codificação que o terminal entende é simples: um byte 0xFE abre um quadro, um segundo 0xFE o fecha, e cada byte de carga útil entre eles tem seu bit mais alto definido e carrega seis bits da imagem comprimida – três bytes de imagem tornam-se quatro bytes de carga útil. O texto puro nunca usa esses valores de byte, então quadros e a saída de print() compartilham o fluxo sem colidir: o terminal mostra o texto e exibe os quadros.
O script abaixo captura, converte cada quadro para JPEG e o imprime nessa forma. O empacotamento de bits passa pelo ulab (que não possui operadores de deslocamento, então os deslocamentos são escritos como multiplicações e divisões) e é rápido o suficiente para acompanhar a câmera:
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())
Isso funciona em um terminal independente porque a impressão do REPL espera: a câmera bloqueia até que o terminal tenha recebido os dados, então cada byte do quadro chega, e um JPEG se move rapidamente sobre um enlace USB full-speed ou high-speed. O mesmo script funciona sem alterações sobre um terminal TCP, que é o caminho de depuração em rede descrito acima. O único lugar onde ele não funciona é a conexão de depuração principal da IDE, cujo canal de saída se reinicia ao transbordar em vez de esperar – ali o visualizador de frame buffer já mostra os quadros da câmera nativamente, então nada é perdido.