3.19. UART em código

machine.UART encapsula um canal UART de hardware. Construa um com o id do barramento e uma taxa de transmissão; todo o resto tem valores padrão razoáveis:

from machine import UART

uart = UART(3, baudrate=115200)

O id seleciona qual UART de hardware usar; o valor depende da placa (consulte a Placas OpenMV para os números de barramento disponíveis e as atribuições de pinos em uma determinada câmera). O formato de quadro padrão é 8 bits de dados, sem paridade, um bit de parada – o “8N1” que todos esperam.

3.19.1. Escrevendo e lendo

A saída UART passa por write():

uart.write("hello\n")
uart.write(b"\x01\x02\x03")

write aceita tanto um str (codificado como UTF-8) quanto um bytes / bytearray. Ele retorna imediatamente assim que os bytes são enfileirados no buffer de TX; o hardware termina de transmiti-los em segundo plano.

As leituras acontecem por meio de três métodos, dependendo do que é necessário:

n = uart.any()              # bytes available to read right now
data = uart.read(8)         # up to 8 bytes, or None on timeout
line = uart.readline()      # bytes ending in '\n', or None on timeout

any() verifica o buffer de RX sem bloquear. read() lê um número fixo de bytes, retornando None se o timeout (configurável por meio do argumento timeout no construtor) expirar primeiro. readline() lê até a próxima quebra de linha, inclusive, útil para protocolos baseados em linhas.

Um laço simples que ecoa tudo o que recebe:

uart = UART(3, baudrate=115200, timeout=100)

while True:
    if uart.any():
        data = uart.read()
        uart.write(data)

read() sem argumento de comprimento continua lendo bytes até que a linha de recepção permaneça em silêncio pelo timeout configurado, e então retorna o que foi acumulado. Com timeout=100, cada chamada aqui retorna uma rajada de bytes – tudo o que o remetente transmitiu sem um intervalo de 100 ms entre os bytes. Sem um timeout, a chamada não teria nenhum sinal de que o remetente terminou e poderia travar indefinidamente.

3.19.2. Dados binários com struct

Enviar inteiros e floats pela linha é a finalidade do módulo struct. Ele empacota valores de largura fixa em um objeto bytes usando uma string de formato que nomeia a ordem dos bytes e o tipo de cada campo:

import struct

uart.write(struct.pack("<lhb", count, temperature_x100, status))

O "<" inicial escolhe a ordem de bytes little-endian; "l" é um inteiro de 32 bits com sinal, "h" é um inteiro de 16 bits com sinal, "b" é um inteiro de 8 bits com sinal. O outro lado desempacota com a mesma string de formato:

payload = uart.read(7)        # 4 + 2 + 1 = 7 bytes
count, temperature_x100, status = struct.unpack("<lhb", payload)

A string de formato é o contrato entre as duas extremidades. Qualquer incompatibilidade – ordem de bytes errada, tamanhos de tipo errados, ordem de campos errada – produz valores sem sentido.

3.19.3. Buffer e leituras de alta taxa

Um laço de polling que chama any() e read() acompanha sozinho a maior parte do tráfego, desde que o laço principal itere rápido o suficiente para esvaziar o buffer de recepção antes que ele encha. Duas opções do construtor importam quando a taxa sobe ou o laço está ocupado.

rxbuf define o tamanho do buffer de RX por software. O padrão é de algumas centenas de bytes; para sensores que emitem centenas de bytes por rajada, ou quando o laço principal realiza um trabalho longo entre os pollings, ampliar o buffer evita que os bytes recebidos sejam descartados enquanto o laço está ocupado em outra coisa:

uart = UART(3, baudrate=115200, timeout=100, rxbuf=4096)

Qualquer coisa que chegue enquanto o buffer está cheio é perdida; dimensione rxbuf para cobrir o maior intervalo entre os esvaziamentos.

Para taxas altas sustentadas, readinto() lê para um buffer pré-alocado em vez de retornar um novo objeto bytes a cada chamada:

buf = bytearray(256)

while True:
    n = uart.readinto(buf)
    if n:
        process(buf, n)

Nenhuma alocação acontece no caminho de leitura, o que importa quando o heap está fragmentado ou quando a latência de alocação ultrapassaria o orçamento de temporização entre bytes.