13.1.10. Fenêtres de terminal autonomes¶
Tools → Open Terminal ouvre des fenêtres de terminal indépendantes – chacune est une session miniature de l’OpenMV IDE dans sa propre fenêtre, dotée d’une visionneuse de tampon d’image, d’un histogramme et d’un terminal interactif, connectée via un transport de votre choix. La connexion de la fenêtre principale n’est pas affectée : un terminal autonome vous permet ainsi de surveiller une seconde caméra tandis que la première reste connectée, ou de déboguer une caméra située à l’autre bout d’un réseau.
Une fenêtre de terminal autonome via un port série : le terminal interactif à gauche avec sa barre d’outils run / stop / reset, le tampon d’image et l’histogramme à droite – ils s’animent lorsque la caméra diffuse des trames dans le flux.¶
New Terminal demande l’un des trois transports suivants :
Serial port – n’importe quel port série à n’importe quel débit en bauds (115 200 par défaut). Cela couvre le port USB d’une seconde caméra, une caméra câblée via un pont USB-vers-UART, une liaison série Bluetooth (qui apparaît comme un port série ordinaire), ou tout périphérique série non-OpenMV nécessitant un terminal. Sur un port USB, le débit en bauds ne limite pas la vitesse – les données circulent toujours à la vitesse de la liaison USB – mais sur le port USB d’une caméra, évitez 921 600 et 12 000 000, qui font passer la caméra du REPL au protocole de débogage de l’IDE.
TCP – se connecter à un serveur sur un hôte et un port choisis, ou écouter en tant que serveur sur un port choisi.
UDP – la même paire de rôles, mais via UDP.
L’IDE mémorise les dix dernières configurations et les liste dans le sous-menu Open Terminal pour une réouverture en un clic ; Clear Menu les oublie.
Contrairement au volet de sortie en lecture seule de la fenêtre principale, un terminal autonome est entièrement interactif : c’est un REPL. Tapez à l’invite et Python s’exécute sur la caméra connectée ligne par ligne, avec l’historique et la complétion par tabulation fournis par MicroPython lui-même. La barre d’outils ajoute des équivalents en un clic des séquences de contrôle courantes – exécuter le script de l’éditeur courant, arrêter le script en cours et réinitialiser à chaud – ainsi que les mêmes commandes d’effacement, d’enregistrement et de retour à la ligne que le volet de terminal principal.
Le tampon d’image au-dessus du terminal est lui aussi en direct. Lorsque la caméra connectée diffuse des trames compressées dans le flux – intégrées au même flux que sa sortie d’impression – le terminal les décode et les affiche, et les boutons Record et Zoom fonctionnent exactement comme dans la fenêtre principale. Cette combinaison constitue la solution de débogage réseau de l’IDE : une caméra qui expose son REPL via Wi-Fi bénéficie de la boucle complète édition-exécution-aperçu sans aucun câble USB.
13.1.10.1. Diffuser des trames dans le flux¶
L’encodage que le terminal comprend est simple : un octet 0xFE ouvre une trame, un second 0xFE la ferme, et chaque octet de charge utile entre les deux a son bit de poids fort à 1 et transporte six bits de l’image compressée – trois octets d’image deviennent quatre octets de charge utile. Le texte brut n’utilise jamais ces valeurs d’octet, de sorte que les trames et la sortie de print() partagent le flux sans collision : le terminal affiche le texte et affiche les trames.
Le script ci-dessous capture, convertit chaque trame en JPEG et l’imprime sous cette forme. Le compactage des bits passe par ulab (qui ne possède pas d’opérateurs de décalage, donc les décalages sont écrits sous forme de multiplications et de divisions), et il est suffisamment rapide pour suivre le rythme de la caméra
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())
Cela fonctionne dans un terminal autonome car l’impression REPL attend : la caméra se bloque jusqu’à ce que le terminal ait récupéré les données, de sorte que chaque octet de la trame arrive, et un JPEG circule rapidement sur une liaison USB full-speed ou high-speed. Le même script fonctionne sans modification via un terminal TCP, ce qui constitue le chemin de débogage réseau évoqué ci-dessus. Le seul cas où il ne fonctionne pas est la connexion de débogage principale de l’IDE, dont le canal de sortie se réinitialise en cas de dépassement au lieu d’attendre – là, la visionneuse de tampon d’image affiche déjà nativement les trames de la caméra, donc rien n’est manqué.