Pular para o conteúdo principal

Dispositivos seriais e Arduino

As funções internas Serial* se comunicam com qualquer coisa que apareça como porta COM: um Arduino, ESP32 ou Raspberry Pi Pico via USB, um adaptador USB-serial, um receptor GPS, um leitor de código de barras no modo serial, uma impressora de recibos ou de etiquetas, uma impressora 3D ou um controlador CNC que aceita G-code, um instrumento de laboratório, ou uma caixa de botões, um teclado de macros, um botão giratório ou um controlador de LEDs feitos em casa. O dispositivo envia e recebe linhas de texto; os seus scripts transformam essas linhas em qualquer outra coisa que o Input.Observer saiba fazer.

Algumas ideias:

  • Uma caixa de botões ou um teclado de macros. Um Arduino lê os seus botões e imprime B1, B2, ... Um monitor serial transforma cada linha em um atalho, uma tecla de mídia ou um comando de janela.
  • Um botão de volume ou de rolagem. Um potenciômetro ou um encoder rotativo imprime a sua posição; o monitor ajusta o volume ou rola a tela.
  • Luzes de status. Um gesto ou uma tecla de atalho envia LED 255 0 0 a uma placa que controla uma fita de LEDs, para que uma luz mostre que a gravação está ativa ou que a compilação falhou.
  • Sensores. Um temporizador pede a uma placa a temperatura ou o nível de luz e o mostra, registra em um arquivo ou escurece a tela.
  • Leitores e scanners. Cada código de barras ou etiqueta RFID que o leitor envia vira texto digitado, uma pesquisa ou uma consulta em um arquivo.
  • Máquinas. Envie linhas de G-code a uma impressora ou plotter, um SerialWriteTextLine por comando.

Encontrar a porta​

O Windows dá a cada dispositivo um nome como COM3. O Gerenciador de Dispositivos o lista em Portas (COM e LPT); o Arduino IDE o mostra no seu menu de placas. Um script também pode listá-los:

n = SerialEnumeratePorts();
if (n == 0) UtilityPrint("No COM ports. Is the board plugged in?");
for (i = 0; i < n; i = i + 1) {
UtilityPrint(SerialGetEnumeratedPortAt(i));
}

A lista é ordenada por número, então COM3 vem antes de COM10. O número pode mudar quando o dispositivo é conectado a outra porta USB.

Velocidade, enquadramento e terminações de linha​

  • A velocidade (baudRate) precisa ser a mesma do dispositivo: o número no Serial.begin(...) de um sketch do Arduino, geralmente 9600 ou 115200.
  • O enquadramento é quase sempre 8 bits de dados, sem paridade, um bit de parada: 8, SerialParity.None, SerialStopBits.One. Escreva as constantes: o número simples 1 como argumento de bits de parada significa um bit e meio de parada.
  • Terminações de linha. Uma linha recebida termina no terminator que você informar, que é removido da linha. Texto vazio significa CR LF, que é o que o Serial.println do Arduino envia. Para um dispositivo que termina as linhas só com LF, use "\n"; só com CR, use "\r". SerialWriteTextLine termina o que envia com o mesmo terminador, ou com CR LF quando a chamada abre a porta por conta própria. Com CR LF, um sketch do Arduino que lê com Serial.readStringUntil('\n') recebe a linha com um CR no final, que trim() remove; com "\n" não há CR para remover.
  • Sinais. Abrir uma porta liga os sinais DTR e RTS dela, como faz o Arduino IDE. Isso reinicia a maioria das placas Arduino Uno e Nano, e outras placas com o mesmo circuito de reinicialização.

Três formas de se comunicar com um dispositivo​

  • Uma pergunta, uma resposta. SerialWriteTextLine e SerialGetTextLine em uma porta que não está aberta a abrem com o baudRate da chamada, 8-N-1, só para essa chamada. O argumento baudRate é ignorado quando a porta já está aberta. Serve para um dispositivo que não reinicia quando a sua porta é aberta. Consulte Fazer uma pergunta a um dispositivo serial.

  • Uma porta mantida aberta. SerialOpenPort abre a porta com as suas configurações até SerialClosePort e reúne cada linha que o dispositivo envia para SerialGetTextLine ler. A maioria das placas Arduino Uno e Nano reinicia sempre que a sua porta é aberta, então abra a porta uma vez, no evento global Load, e deixe os gestos e as teclas de atalho escreverem nela. SerialOpenPort retorna false para uma porta que não existe, está desconectada ou aberta em outro programa; para uma porta que o Input.Observer já tem aberta, ou que um monitor ocupa, interrompe a ação com um erro.

    // Load global event: open the port once, at the sketch's speed. An Uno or Nano
    // restarts every time its port opens, so a port opened per command would lose
    // the command while the board boots.
    if (!SerialOpenPort("COM4", 115200, SerialParity.None, 8, SerialStopBits.One, "")) {
    UtilityPrint("COM4 isn't available: unplugged, or open in the Arduino IDE's Serial Monitor?");
    }

    // Any gesture or hotkey: send a command line and read the board's reply.
    // (The sketch reads a line with Serial.readStringUntil('\n') and answers OK.)
    if (SerialWriteTextLine("COM4", "LED 255 0 0", 115200)) {
    reply = SerialGetTextLine("COM4", 1, 115200);
    if (reply != "OK") UtilityPrint(StringFormat("Board said: {0}", reply, ""));
    }
  • Um monitor. SerialMonitorCreate executa um script para cada linha que o dispositivo envia, em segundo plano, até SerialMonitorDelete. A linha é ContextGetSerialTextLine(). Consulte Mapear os botões de um dispositivo serial para teclas de mídia e:

    // Arduino side: a potentiometer on A0, one reading per line.
    // void setup() { Serial.begin(115200); }
    // void loop() { Serial.println(analogRead(A0)); delay(50); }
    // Empty text as the terminator means CR LF, which Serial.println sends.
    SerialMonitorCreate("knob", "COM4", 115200, SerialParity.None, 8, SerialStopBits.One, "",
    "line = ContextGetSerialTextLine(); if (StringIsNumber(line, true)) MultimediaSetVolume(AudioEndpoint.Playback, StringToNumber(line, true) / 1023.0);");
observação

O que saber sobre os monitores.

  • As linhas são executadas uma de cada vez, na ordem em que chegaram: o script da próxima linha começa quando o script da anterior termina. Até 256 linhas aguardam; além disso, as mais antigas são descartadas.
  • Parar todas as ações interrompe o script em execução para uma linha e descarta as linhas em espera. O próprio monitor continua em execução. A Tecla de atalho para parar todas as ações vai além: ela exclui todos os monitores e fecha a porta de cada um.
  • Qualquer script, inclusive o do próprio monitor, pode enviar para uma porta monitorada com SerialWriteTextLine, então um monitor pode responder ao seu dispositivo. SerialGetTextLine não consegue ler uma porta monitorada: toda linha vai para o monitor.
  • Cada nome de monitor pertence a uma porta. SerialMonitorCreate com o nome do monitor desta porta substitui esse monitor. Ele interrompe a ação com um erro quando o nome já monitora outra porta, quando a porta está aberta com SerialOpenPort ou quando outro monitor ocupa a porta. Uma porta que não existe, está desconectada ou aberta em outro programa resulta em false.
  • Desconectar o dispositivo encerra o monitor e grava uma linha na guia Sistema do console. Nada reinicia o monitor quando o dispositivo volta. Execute o SerialMonitorCreate de novo (por uma tecla de atalho ou pelo evento Load na próxima inicialização).
aviso

Só um programa por vez pode manter uma porta COM aberta. Enquanto o Serial Monitor ou o Serial Plotter do Arduino IDE estiver aberto, o Input.Observer não consegue abrir a porta, e, enquanto o Input.Observer a mantiver aberta, o IDE não consegue carregar um sketch. Feche a porta (SerialClosePort ou SerialMonitorDelete) antes de carregar.