12.6. Canais nomeados¶
O ID de canal no cabeçalho de cada pacote permite que até 32 fluxos independentes compartilhem o mesmo transporte físico. A camada de canais transforma esses IDs numéricos em endpoints nomeados e visíveis à aplicação, aos quais o código do host pode se referir por uma string.
12.6.1. Os quatro canais integrados¶
A câmera registra quatro canais na inicialização, antes de qualquer código de aplicação ser executado:
stdin– bytes de script que o host envia à câmera para executar. A IDE usa esse canal para enviar o script que está sendo editado;exec()no SDK do host é a chamada equivalente a partir de um programa Python.stdout– bytes das chamadasprint()da câmera e tracebacks de exceções não capturadas. O console serial da IDE lê esse canal.stream– o canal de pré-visualização ao vivo. A IDE extrai quadros JPEG dele; qualquer script do host pode fazer o mesmo comread_frame().profile– eventos de profiler, presentes apenas quando a câmera foi compilada com profiling habilitado. A maioria das builds de release o omite.
O código de aplicação raramente precisa tocar em qualquer um dos canais integrados; o trabalho interessante acontece em canais que a própria aplicação registra.
12.6.2. Registrando um canal¶
Um script do lado da câmera registra um novo canal chamando protocol.register() com um nome e um objeto Python de backend
import json
import protocol
import time
trigger_count = 0
class StatusChannel:
def size(self):
# Refresh the snapshot on every host query.
self._buf = json.dumps({
'uptime_s': time.ticks_ms() // 1000,
'triggers': trigger_count,
}).encode()
return len(self._buf)
def read(self, offset, size):
return self._buf[offset:offset + size]
protocol.register(name='status', backend=StatusChannel())
Os métodos do objeto de backend decidem o que o canal pode fazer. Um backend com apenas size e read é um canal de dados somente leitura; adicione write e ele se torna bidirecional; adicione poll e o host pode perguntar se há dados novos prontos antes de pagar por uma leitura. Amostrar os dados dentro de size é o padrão mais simples quando o payload é pequeno o suficiente para caber em um único fragmento – o buffer é gerado sob demanda, nunca em cache, nunca em condição de corrida. Payloads maiores – quadros de imagem, traços de sensor – precisam de um padrão de travamento que segura o buffer até o host concluir sua leitura de múltiplos fragmentos, abordado no canal de quadros.
Uma pequena quantidade de contabilidade acontece automaticamente:
A biblioteca atribui o próximo ID de canal livre (entre 0 e 31).
Os flags de capacidade são derivados dos métodos presentes:
CHANNEL_FLAG_READsereadestiver definido,CHANNEL_FLAG_WRITEsewriteestiver definido,CHANNEL_FLAG_LOCKselock/unlockestiverem definidos.Um pacote de evento
CHANNEL_REGISTEREDé enviado a qualquer host conectado para que sua lista de canais se atualize.
O valor de retorno é um handle protocol.ProtocolChannel que a aplicação pode manter. O método send_event() do handle é o gancho do lado da câmera para dizer ao host “algo aconteceu neste canal sem alterar os dados legíveis” – um gatilho disparou, um botão foi pressionado, um marco de contagem de amostras foi atingido.
12.6.3. Lendo canais a partir do host¶
O SDK do host é distribuído como o pacote openmv no PyPI (pip install openmv), construído sobre pyserial para o transporte. Sua classe openmv.camera.Camera expõe os canais nomeados da câmera por meio de métodos de alto nível:
from openmv.camera import Camera
with Camera('/dev/ttyACM0', baudrate=921600) as cam:
cam.update_channels()
if cam.has_channel('status'):
size = cam.channel_size('status')
data = cam.channel_read('status', size)
Aviso
O pacote openmv requer CPython 3.12 ou mais recente. Interpretadores anteriores não possuem recursos dos quais o SDK depende; instale uma build 3.12+ antes de pip install openmv.
Algumas coisas a observar sobre a configuração:
A string da porta serial –
/dev/ttyACM0aqui – é no estiloCOM3no Windows,/dev/cu.usbmodemXXXXno macOS e/dev/ttyACM*no Linux. O número real depende de qual porta a câmera enumerou.A taxa de transmissão (baud rate) é o valor mágico do protocolo
921600, que a pilha USB-CDC da câmera reconhece como “este cliente fala o protocolo, não o REPL”. Qualquer outra taxa cai de volta para uma linha serial comum.O gerenciador de contexto
with Camera(...) as cam:abre o transporte, executaPROTO_SYNC, troca capacidades e, ao sair, fecha a porta de forma limpa. A chamada explícita aupdate_channels()após a entrada atualiza a lista local de canais com quaisquer canais que a aplicação registrou após a inicialização.
channel_size() e channel_read() são os métodos de trabalho principais; channel_write() faz a ida e volta de um buffer para a câmera se o backend tiver um método write; has_channel() é a forma segura de verificar se um nome está registrado antes de usá-lo. O nome do canal é resolvido uma vez para o ID de canal que a câmera atribuiu durante register e usado em cada pacote a partir de então.
Cada par channel_size() / channel_read() custa duas idas e voltas: um pacote para pedir o tamanho, outro para pedir os bytes. Por USB-CDC, ambos terminam em cerca de um milissegundo combinados; por UART, a mesma troca leva mais tempo em proporção à taxa de transmissão (baud rate) da linha serial. Código de aplicação que lê em um laço apertado deve chamar channel_size() apenas quando o tamanho pode realmente mudar – para dados de tamanho fixo, o tamanho da primeira chamada pode ser armazenado em cache.
12.6.4. Independência entre canais¶
Três coisas valem a pena saber sobre como os canais interagem:
Controle de fluxo independente. Cada canal tem seu próprio estado de leitura pendente, seus próprios dados e seus próprios callbacks
size/read/write. Uma leitura demorada no canalstreamnão bloqueia leituras no canalconfigda aplicação.Sequencial por canal. Dentro de um único canal, os pacotes são entregues em ordem. A camada de confiabilidade garante isso mesmo quando há retransmissões envolvidas.
Transporte compartilhado, orçamento de retransmissão compartilhado. Todos os canais compartilham o único enlace físico, então uma torrente de tráfego em um canal desacelera os outros ao monopolizar o fio. O mecanismo
CHANNEL_LOCKpermite que um canal reserve o fio para uma leitura atômica de múltiplos pacotes; o backend adere implementando os callbackslock/unlock.
Um canal é a área de superfície mínima sobre a qual um programa do host e um programa da câmera concordam em cooperar. O nome, a direcionalidade (leitura, escrita ou ambas), os métodos de callback no lado da câmera e as chamadas de método correspondentes no lado do host constituem todo o contrato.