Fundamentos · 12 min de leitura

O que é UART? Guia de ligação TX/RX, baud rate e comunicação serial com Arduino

O que é UART e como funciona? Ligação cruzada TX/RX, baud rate, bits de início/parada, conexão entre dois Arduinos, SoftwareSerial, níveis de 5V/3,3V, Serial2 do ESP32 e UART do Pico.

A resposta curta para o que é UART: UART (Universal Asynchronous Receiver-Transmitter) é o hardware de comunicação serial que permite a dois dispositivos trocar dados byte a byte por apenas dois fios de dados (TX e RX) e um GND comum. Cada linha que você vê no Monitor Serial quando seu Arduino está conectado ao computador chega por UART. Dezenas de módulos, como receptores GPS, o módulo Bluetooth HC-05, o módulo Wi-Fi ESP-01, o módulo GSM SIM800L, leitores de impressão digital e tocadores de MP3, também se comunicam com o Arduino por UART.

Neste artigo, explicamos passo a passo por que UART não precisa de uma linha de clock, o que é baud rate, como um byte é enquadrado com bits de início, dados e parada, por que TX e RX se cruzam, como fazer dois Arduinos se comunicarem primeiro em uma direção e depois nas duas, os limites de SoftwareSerial, como conectar placas de 5 V e 3,3 V com segurança, as três UARTs do ESP32 e os pinos UART do Raspberry Pi Pico. Na última seção, você verá como montar pacotes com caracteres de início e fim para evitar que os dados se misturem, além dos erros mais comuns.

Conversor USB-TTL CP2102
Conversor USB-TTL CP2102: transforma a porta USB do computador em uma UART com pinos TX, RX e GND; o chip USB do Arduino Uno faz o mesmo trabalho.

O que é UART e como funciona?

A palavra “assíncrono” no nome UART é a chave: em I2C e SPI, um sinal de clock é enviado junto com os dados, enquanto UART não tem linha de clock. Em vez disso, os dois lados combinam previamente uma velocidade, chamada baud rate. Em repouso, a linha fica em HIGH (1). Para enviar um byte, o transmissor leva a linha a LOW pelo tempo de um bit; esse é o bit de início, que diz ao receptor “comece a contar agora”. Em seguida vêm os 8 bits de dados, começando pelo bit menos significativo (LSB), podendo haver depois um bit de paridade opcional; por fim, a linha fica em HIGH por pelo menos o tempo de um bit; esse é o bit de parada. A configuração padrão do Arduino é 8N1: 8 bits de dados, sem paridade, 1 bit de parada.

BitInícioD0D1D2D3D4D5D6D7Parada
Letra 'A' (0x41 = 0100 0001)0100000101
Nível da linhaLOWHIGHLOWLOWLOWLOWLOWHIGHLOWHIGH

Cada bit dura 1 / baud segundo. Em 9600 baud, um bit leva 104,2 microssegundos; como um quadro 8N1 tem 10 bits por byte (início + 8 dados + parada), é possível enviar no máximo 960 bytes por segundo. Depois de detectar a borda de descida do bit de início, o receptor amostra a linha no meio de cada bit. Mesmo que os dois clocks sejam um pouco diferentes, os dados são lidos corretamente desde que o erro acumulado em 10 bits fique abaixo de meio bit; por isso, o erro total de baud deve ficar abaixo de aproximadamente 2–3%. Com seu cristal de 16 MHz, o Arduino Uno não consegue produzir exatamente 115200 baud, e a taxa real tem um desvio de cerca de 2,1%; ainda assim funciona porque está dentro do limite.

BaudTempo de um bitMáximo de bytes por segundo (8N1)Onde é usado
9600104,2 µs960GPS NEO-6M, modo de dados do HC-05, DFPlayer Mini, configuração clássica do Monitor Serial
1920052,1 µs1920Alguns sensores e modems antigos
3840026,0 µs3840Modo de comandos AT do HC-05
5760017,4 µs5760Bootloaders antigos do Arduino, limite superior de SoftwareSerial
1152008,68 µs11520ESP32 e ESP-01, saída rápida de depuração
1.000.000 ou mais1 µs ou menos100.000+Conexões curtas entre placas, registro rápido de dados

UART vs I2C vs SPI

Os três métodos de comunicação serial mais comuns no Arduino são UART, I2C e SPI. UART funciona apenas ponto a ponto entre dois dispositivos, e os dois lados podem falar quando quiserem (full duplex). I2C reúne dezenas de dispositivos nos mesmos dois fios usando endereços; SPI é muito mais rápido com quatro fios, mas precisa de um pino de seleção separado para cada dispositivo. Para mais detalhes, veja os artigos O que é I2C e O que é SPI.

CaracterísticaUARTI2CSPI
Fios2 (TX, RX) + GND2 (SDA, SCL) + GND4 (SCK, MOSI, MISO, CS) + GND
Linha de clockNenhuma (assíncrona)SimSim
Número de dispositivos2 (ponto a ponto)Até 127, por endereçoTantos quanto os pinos CS
Velocidade típica9600–115200 baud100–400 kHz1–80 MHz
Nas duas direções ao mesmo tempoSim (full duplex)NãoSim
Módulos típicosGPS, Bluetooth, GSM, ESP-01, impressão digitalOLED, LCD, RTC, BME280Cartão SD, RFID, TFT, LoRa

Ligação cruzada de TX e RX e GND comum

A regra de ouro da ligação UART: o pino TX (transmissão) de um dispositivo se conecta ao pino RX (recepção) do outro, ou seja, os fios se cruzam. Pense em boca e ouvido: onde um fala, o outro deve escutar. Se você conectar TX a TX, duas saídas ficam ligadas entre si e nenhum dado passa. O terceiro fio, GND, dá às duas placas a mesma referência de tensão; sem ele, “HIGH” e “LOW” perdem o significado e os dados recebidos viram caracteres estranhos. Mesmo que as placas sejam alimentadas por fontes separadas, seus terras devem estar conectados.

Arduino AFioArduino BObservação
TX (D11, SoftwareSerial)→RX (D10, SoftwareSerial)O que A envia vai para B
RX (D10, SoftwareSerial)←TX (D11, SoftwareSerial)A resposta de B chega a A
GND—GNDGND comum, obrigatório
5Vopcional5VApenas se só uma placa estiver alimentada por USB
No Arduino Uno e Nano, os pinos da UART de hardware D0 (RX) e D1 (TX) também estão conectados ao chip USB. Se você tentar carregar o código com um módulo conectado a esses pinos, o carregamento falha com o erro “not in sync”, e no Monitor Serial os dados do módulo se misturam aos do computador. Desconecte os fios de D0/D1 durante o carregamento ou use outros pinos com SoftwareSerial, como nos exemplos deste artigo.

Comunicação entre dois Arduinos: uma direção

Arduino Uno
Arduino Uno: como sua única UART de hardware (D0/D1) está reservada para USB, conectar dois Unos com SoftwareSerial em D10/D11 é a forma mais fácil.

No primeiro exemplo, a placa A envia o valor de um potenciômetro em A0 para a placa B dez vezes por segundo, e a placa B ajusta o brilho do LED em D9 de acordo com esse valor. As duas placas usam SoftwareSerial em D10 (RX) e D11 (TX), mantendo livres a conexão USB e o Monitor Serial. Como os valores são enviados com println(), cada número termina com os caracteres “\r\n” (final de linha); o receptor sabe que o número está completo quando os vê. Ligação: D11 de A ao D10 de B, D10 de A ao D11 de B e os terras unidos.

C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11 (diğer karta / to the other board)
4
5void setup() {
6  Serial.begin(9600);           // USB: bilgisayar / computer
7  link.begin(9600);             // iki tarafta aynı baud / same baud on both sides
8}
9
10void loop() {
11  int deger = analogRead(A0);   // potansiyometre 0-1023 / potentiometer 0-1023
12  link.println(deger);          // ör. "512\r\n" / e.g. "512\r\n"
13  Serial.println(deger);
14  delay(100);                   // saniyede 10 ölçüm / 10 readings per second
15}
16
C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11
4const int LED_PIN = 9;          // PWM pini / PWM pin
5
6char tampon[8];                 // en fazla 7 karakter + '\0' / up to 7 chars + '\0'
7byte konum = 0;
8
9void setup() {
10  Serial.begin(9600);
11  link.begin(9600);
12  pinMode(LED_PIN, OUTPUT);
13}
14
15void loop() {
16  while (link.available()) {               // gelen her baytı oku / read every incoming byte
17    char c = link.read();
18    if (c == '\n') {                       // satır bitti / line complete
19      tampon[konum] = '\0';
20      int deger = atoi(tampon);
21      analogWrite(LED_PIN, map(deger, 0, 1023, 0, 255));
22      Serial.println(deger);
23      konum = 0;
24    } else if (c != '\r' && konum < sizeof(tampon) - 1) {
25      tampon[konum++] = c;                 // satıra ekle / append to the line
26    }
27  }
28}
29

Há duas coisas a observar no código do receptor. Primeiro, os dados nunca chegam “de uma vez”: “512” chega como três bytes separados, com cerca de 1 milissegundo entre eles. Por isso, acumulamos os bytes em um buffer e os convertemos em número quando chega o final de linha. Segundo, o código nunca fica esperando: funções como readString() ou parseInt() esperam 1 segundo por padrão quando nenhum dado chega e deixam o programa lento. Ler os bytes conforme chegam é a abordagem mais confiável e rápida. Também limitamos o tamanho do buffer para que dados corrompidos não sejam escritos além do fim do array.

Comunicação bidirecional: comando e resposta

Projetos reais geralmente precisam de uma conversa nas duas direções: uma placa envia um comando, a outra o executa e responde. No exemplo abaixo, a placa A funciona como uma ponte: encaminha para B cada caractere que você digita no Monitor Serial e imprime cada caractere recebido de B. A placa B entende os comandos “LED ON”, “LED OFF” e “DURUM?” (estado), responde a cada um e também informa por conta própria quando o botão em D2 é pressionado. Não se esqueça de definir o final de linha do Monitor Serial como Nova linha; B usa esse caractere para saber que o comando terminou.

C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11
4
5void setup() {
6  Serial.begin(9600);
7  link.begin(9600);
8  Serial.println("Komut yaz / type a command: LED ON, LED OFF, DURUM?");
9}
10
11void loop() {
12  if (Serial.available()) link.write(Serial.read());   // bilgisayar -> B kartı / computer -> board B
13  if (link.available()) Serial.write(link.read());     // B kartı -> bilgisayar / board B -> computer
14}
15
C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11
4const int LED_PIN = 13;         // kart üstündeki LED / on-board LED
5const int BUTON_PIN = 2;        // buton GND'ye / button to GND
6
7String satir = "";
8int oncekiButon = HIGH;
9
10void komutCalistir(String k) {
11  k.trim();                                  // \r ve boşlukları at / strip \r and spaces
12  if (k == "LED ON") {
13    digitalWrite(LED_PIN, HIGH);
14    link.println("OK LED=1");
15  } else if (k == "LED OFF") {
16    digitalWrite(LED_PIN, LOW);
17    link.println("OK LED=0");
18  } else if (k == "DURUM?") {                // durum sorgusu / status query
19    link.print("LED=");
20    link.print(digitalRead(LED_PIN));
21    link.print(" BUTON=");
22    link.println(digitalRead(BUTON_PIN) == LOW ? 1 : 0);
23  } else {
24    link.print("HATA / unknown: ");
25    link.println(k);
26  }
27}
28
29void setup() {
30  link.begin(9600);
31  pinMode(LED_PIN, OUTPUT);
32  pinMode(BUTON_PIN, INPUT_PULLUP);
33  satir.reserve(32);
34}
35
36void loop() {
37  while (link.available()) {
38    char c = link.read();
39    if (c == '\n') {
40      komutCalistir(satir);
41      satir = "";
42    } else if (satir.length() < 31) {
43      satir += c;
44    }
45  }
46
47  int buton = digitalRead(BUTON_PIN);        // değişince kendiliğinden bildir / report changes unprompted
48  if (buton != oncekiButon) {
49    oncekiButon = buton;
50    link.println(buton == LOW ? "BUTON BASILDI / pressed" : "BUTON BIRAKILDI / released");
51    delay(20);                               // basit debounce / simple debounce
52  }
53}
54

A chamada trim() na placa B remove o caractere \r que chega quando o Monitor Serial está configurado como “Ambos, NL e CR”, além dos espaços extras, para que os comandos sejam reconhecidos com qualquer uma das configurações. Usar reserve(32) no objeto String evita que a memória seja realocada toda vez que um caractere é acrescentado e reduz a fragmentação dos 2 KB de RAM do Uno. Essa estrutura também é a base para conversar com o Arduino a partir de um programa Python no computador; veja os detalhes no artigo Comunicação serial entre Arduino e Python.

Os limites de SoftwareSerial

SoftwareSerial imita uma UART por software em vez de hardware: o próprio processador controla o tempo de cada bit. Isso é muito útil, mas tem um custo. Durante a transmissão, as interrupções ficam desativadas por todo o tempo de cada byte; em 9600 baud, isso dá cerca de 1 milissegundo por byte, período em que millis() pode acumular desvio, os pulsos do servo podem variar e dados que chegam por outra porta serial podem ser perdidos. Apenas uma porta SoftwareSerial pode escutar por vez; se abrir uma segunda, você precisa escolher qual escuta com listen(). Em placas como Mega e Leonardo, só determinados pinos com suporte a interrupções podem ser usados como RX.

CaracterísticaUART de hardware (Serial)SoftwareSerialAltSoftSerial
Pinos no UnoD0 (RX), D1 (TX), compartilhados com USBQuase qualquer pino (PCINT para RX)Fixos: D8 (RX), D9 (TX)
Maior velocidade confiável1–2 Mbaud57600 baud (115200 fica no limite)Estável em velocidades moderadas (9600–38400)
Carga da CPUMuito baixa (buffer de hardware)Alta, interrupções desativadas ao enviarMédia, usa Timer1
Enviar e receber ao mesmo tempoSimNãoSim
Mais de uma porta4 no MegaSó uma escuta por vezPorta única
ConflitosCarregamento e Monitor SerialBibliotecas sensíveis à temporização, como Servo e NeoPixelPWM em D10 e Servo (Timer1)
Se você for usar mais de um módulo UART (por exemplo, GPS + Bluetooth), escolha um Arduino Mega com quatro UARTs de hardware (Serial1–Serial3 além de Serial) ou um ESP32 com três. No Leonardo e no Pro Micro, USB é separado, então Serial1 em D0/D1 fica completamente livre.

Adequação dos níveis de 5V e 3,3V

Em UART, o nível “HIGH” é a tensão de operação da placa: 5 V no Arduino Uno, 3,3 V no ESP32, Raspberry Pi Pico, ESP-01 e na maioria dos módulos mais novos. Conectar uma saída TX de 5 V diretamente a uma entrada RX de 3,3 V expõe a entrada a uma sobretensão e danifica o pino com o tempo ou imediatamente; o ESP32 e o Pico baseado no RP2040 não toleram 5 V. Na outra direção, geralmente não há problema: o Uno considera 3,0 V ou mais como HIGH, então pode ler um TX de 3,3 V, mas, como isso fica perto do limite, um conversor de nível lógico é mais robusto com fios longos.

ConexãoO que você precisaExemplo
Uno TX (5 V) → ESP32 / Pico RX (3,3 V)Divisor de tensão ou conversor de nível lógico1 kΩ em série + 2 kΩ para GND: 5 × 2/3 ≈ 3,3 V
ESP32 / Pico TX (3,3 V) → Uno RX (5 V)Geralmente diretaO Uno considera acima de 3,0 V como HIGH
Uno TX → HC-05 RXDivisor de tensão recomendadoO RX do HC-05 é uma entrada com nível de 3,3 V
Uno TX → SIM800L RXDivisor de tensãoO nível do sinal do SIM800L é de cerca de 2,8 V
Ambas as placas de 5 V ou ambas de 3,3 VDiretaUno ↔ Nano, ESP32 ↔ Pico
Um divisor de tensão só é adequado para sinais unidirecionais de velocidade baixa a média (por exemplo, até 115200 baud). Para linhas bidirecionais ou muito rápidas, use um conversor bidirecional de nível lógico baseado no BSS138. Você pode calcular os valores dos resistores na calculadora de divisor de tensão; os conversores de nível são abordados em detalhes no artigo Conversor de nível lógico.

UART do ESP32: Serial, Serial1 e Serial2

O ESP32 tem três UARTs de hardware, e a melhor parte é que, graças à sua matriz de GPIO, os pinos UART podem ser movidos para quase qualquer pino que você quiser. UART0 (Serial) está conectada ao chip USB-serial e é usada para o Monitor Serial. UART2 (Serial2) fica por padrão nos GPIO 16 (RX) e 17 (TX). Os antigos pinos padrão de UART1 (GPIO 9 e 10) estão conectados à memória flash da placa e não podem ser usados diretamente, então sempre inicialize Serial1 informando os pinos: begin(baud, SERIAL_8N1, RX, TX). O código abaixo monta uma ponte bidirecional entre o Monitor Serial e um dispositivo na UART2 (um GPS, outro Arduino, um sensor); é ideal para acompanhar as sentenças NMEA de um módulo GPS.

UARTNome no ArduinoRX / TX padrãoObservação
UART0SerialGPIO 3 / GPIO 1Conectada ao chip USB-serial, carregamento e Monitor Serial
UART1Serial1GPIO 9 / GPIO 10 (flash, não use)Informe outros pinos em begin(), por exemplo, 25 / 26
UART2Serial2GPIO 16 / GPIO 17Nas placas WROVER, 16/17 são usados pela PSRAM; escolha outros pinos
C++ (Arduino IDE)
1// ESP32: UART0 = Serial (USB), UART2 = Serial2 (GPIO 16/17)
2const int RXD2 = 16;            // karşı cihazın TX'i buraya / the other device's TX goes here
3const int TXD2 = 17;            // karşı cihazın RX'ine / to the other device's RX
4
5void setup() {
6  Serial.begin(115200);                          // USB Seri Monitör / USB Serial Monitor
7  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);   // baud, çerçeve, RX, TX / baud, frame, RX, TX
8  // UART1'i de istediğin pinlere taşıyabilirsin / UART1 can be moved to any free pins:
9  // Serial1.begin(115200, SERIAL_8N1, 25, 26);
10  Serial.println("UART2 hazir / ready");
11}
12
13void loop() {
14  while (Serial2.available()) Serial.write(Serial2.read());   // UART2 -> USB
15  while (Serial.available()) Serial2.write(Serial.read());    // USB -> UART2
16}
17

As UARTs do ESP32 podem chegar a 5 Mbaud e cada uma tem um buffer de hardware; o buffer de recepção padrão tem 256 bytes e pode ser ampliado antes de begin() com uma chamada como Serial2.setRxBufferSize(1024). Isso importa para módulos GPS que enviam dezenas de sentenças por segundo ou módulos GSM com respostas longas. Como os pinos do ESP32 são de 3,3 V, não se esqueça do divisor da seção anterior ao conectar um Arduino de 5 V. Você pode ver a pinagem do ESP32 na ferramenta Pinagem.

Pinos UART do Raspberry Pi Pico e MicroPython

O Raspberry Pi Pico tem duas UARTs de hardware (uart0 e uart1), e cada uma pode ser direcionada a vários pares de pinos diferentes. No MicroPython, o REPL e a saída de print() chegam por uma porta serial virtual separada no USB, então UART0 fica completamente livre; não é preciso desconectar fios ao carregar o código, como no Arduino. Os pinos do Pico são de 3,3 V e, no Pico baseado no RP2040, não toleram 5 V. O código abaixo faz no Pico o que a placa B fazia no Arduino: lê linhas de UART0, liga ou desliga o LED integrado com os comandos “LED ON” e “LED OFF” e responde. Se quiser que ele converse com o sketch de ponte do Uno, não se esqueça de um divisor de tensão na linha TX do Uno.

UARTPinos TXPinos RXExemplo em MicroPython
uart0GP0, GP12, GP16GP1, GP13, GP17UART(0, 9600, tx=Pin(0), rx=Pin(1))
uart1GP4, GP8GP5, GP9UART(1, 9600, tx=Pin(4), rx=Pin(5))
MicroPython
1from machine import UART, Pin
2import time
3
4uart = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))   # UART0: GP0 = TX, GP1 = RX
5led = Pin("LED", Pin.OUT)                             # Pico ve Pico W dahili LED / on-board LED
6
7tampon = b""
8while True:
9    if uart.any():
10        tampon += uart.read()                         # gelen baytlar / incoming bytes
11        while b"\n" in tampon:                        # her tam satırı işle / handle each full line
12            satir, tampon = tampon.split(b"\n", 1)
13            try:
14                komut = satir.decode().strip()
15            except UnicodeError:
16                continue                              # bozuk bayt: atla / corrupt byte: skip
17            if komut == "LED ON":
18                led.on()
19                uart.write("OK LED=1\n")
20            elif komut == "LED OFF":
21                led.off()
22                uart.write("OK LED=0\n")
23            else:
24                uart.write("HATA / unknown: " + komut + "\n")
25    if len(tampon) > 64:                              # satır sonu hiç gelmezse taşmayı önle / avoid overflow
26        tampon = b""
27    time.sleep_ms(5)
28

Enquadramento de pacotes: caracteres de início e fim

UART é um fluxo de bytes; não sabe onde algo começa ou termina. Quando o transmissor escreve “23.5,48,812”, o receptor pode ter começado a escutar no meio, um byte pode ter se perdido por ruído ou duas leituras podem se juntar. A solução é transformar os dados em um pacote: se cada pacote começa com “<” e termina com “>”, o receptor ignora tudo até o primeiro “<”, processa o pacote quando chega “>” e descarta os pacotes incompletos que aparecem no meio. O transmissor só precisa imprimir os valores entre Serial.print('<'), vírgulas e Serial.print('>'). Você pode testar imediatamente o código do receptor abaixo digitando <23.5,48,812> no Monitor Serial.

C++ (Arduino IDE)
1// Paket biçimi / packet format: <sicaklik,nem,isik>  ör. / e.g. <23.5,48,812>
2const byte MAKS = 32;
3char tampon[MAKS];
4byte konum = 0;
5bool paketIcinde = false;                    // '<' görüldü mü? / have we seen '<'?
6
7void paketiIsle(char* p) {
8  char* parca = strtok(p, ",");              // ilk alan / first field
9  if (!parca) return;
10  float sicaklik = atof(parca);
11  parca = strtok(NULL, ",");
12  if (!parca) return;                        // eksik alan: paketi at / missing field: drop
13  int nem = atoi(parca);
14  parca = strtok(NULL, ",");
15  if (!parca) return;
16  int isik = atoi(parca);
17
18  Serial.print("Sicaklik / temp: ");
19  Serial.print(sicaklik, 1);
20  Serial.print("  Nem / humidity: ");
21  Serial.print(nem);
22  Serial.print("  Isik / light: ");
23  Serial.println(isik);
24}
25
26void setup() {
27  Serial.begin(9600);                        // Seri Monitör'den <23.5,48,812> yazarak dene / test from the Serial Monitor
28}
29
30void loop() {
31  while (Serial.available()) {
32    char c = Serial.read();
33    if (c == '<') {                          // yeni paket başladı / a new packet starts
34      paketIcinde = true;
35      konum = 0;
36    } else if (c == '>' && paketIcinde) {    // paket bitti / packet complete
37      tampon[konum] = '\0';
38      paketIcinde = false;
39      paketiIsle(tampon);
40    } else if (paketIcinde) {
41      if (konum < MAKS - 1) tampon[konum++] = c;
42      else paketIcinde = false;              // çok uzun, bozuk: at / too long, corrupt: drop
43    }                                        // paket dışındaki baytlar yok sayılır / bytes outside packets are ignored
44  }
45}
46
MétodoExemploVantagensDesvantagens
Texto com final de linha512\nMais simples, legível no Monitor SerialNo meio de uma linha, não dá para saber onde ela começa
Caracteres de início e fim<23.5,48,812>Recupera-se sozinho após perder a sincronizaçãoNão permite usar < e > nos dados
Texto com soma de verificação (como NMEA)$GPGGA,...*47Pacotes corrompidos são detectadosUm pouco mais de cálculo e código
Pacote binário0xAA, comprimento, dados, CRCMais curto e rápidoDifícil de ler e depurar

Em ambientes com ruído ou com fios longos, você pode ir além e acrescentar uma soma de verificação ao pacote. O formato NMEA usado pelos módulos GPS é um bom exemplo: todos os caracteres entre “$” e “*” passam por uma operação XOR entre si, e o resultado é escrito no final como um número hexadecimal de dois dígitos. O receptor faz o mesmo cálculo e descarta o pacote se o resultado não coincidir. Isso protege contra erros discretos, como um “812” corrompido ser processado como “872”.

O transmissor não deve ser mais rápido que a capacidade de processamento do receptor. O buffer de recepção de hardware do Arduino Uno tem apenas 64 bytes; se você usar chamadas longas de delay() em loop(), os bytes recebidos serão perdidos quando o buffer encher. No receptor, use millis() em vez de delay() e verifique Serial.available() a cada ciclo do laço.

Ideias de projetos com UART

  • Controle pelo celular: o módulo Bluetooth HC-05 é, na verdade, uma UART sem fio; controle um LED, um relé ou um carrinho robô pelo celular com o HC-05.
  • Registro de posição GPS: analise as sentenças NMEA que um módulo GPS NEO-6M envia por UART e grave a posição em um cartão SD.
  • Sistema com duas placas: conecte por UART um Uno que lê os sensores a um ESP32 que envia os dados para a internet por Wi-Fi.
  • Alarme por SMS: envie comandos AT a um módulo SIM800L por UART e mande um SMS para você quando uma porta abrir.
  • Painel de controle no computador: envie comandos para o Arduino a partir de um app para computador escrito em Python e exiba os dados dos sensores em gráficos.
  • Tocador de MP3: escolha faixas, ajuste o volume e reproduza alertas de voz por UART com um DFPlayer Mini.

Erros comuns e soluções

SintomaCausa provávelSolução
Caracteres estranhos no Monitor SerialOs baud rates não coincidemUse o mesmo baud rate no código e no Monitor Serial
Nenhum dado recebidoTX ligado a TX, RX a RXCruze os fios: TX → RX, RX → TX
Caracteres corrompidos aleatóriosGND não conectadoConecte os terras das duas placas
Falha no carregamento (not in sync)Há um módulo conectado a D0/D1Desconecte os fios durante o carregamento ou use SoftwareSerial
Os comandos não são reconhecidosO final de linha é \r\n ou está ausenteConfigure o Monitor Serial como Nova linha e limpe o texto com trim()
O final de mensagens longas desapareceO buffer de 64 bytes transbordouEvite delay(), leia os dados conforme chegam e reduza a velocidade
O pino RX do ESP32 não funciona ou está danificadoTX de 5 V conectado diretamenteAcrescente um divisor de tensão ou conversor de nível lógico
SoftwareSerial falha em 115200A temporização por software não consegue acompanharUse 57600 ou menos ou mude para uma UART de hardware
Se um módulo UART não responder, primeiro conecte-o diretamente ao computador com um conversor USB-TTL e envie comandos por um terminal serial. Se o módulo funcionar ali, o problema está no código do Arduino ou na fiação; se também não funcionar, confira o baud rate, a alimentação e o próprio módulo.

UART é o primeiro método de comunicação a aprender em eletrônica: bastam dois fios, um GND comum e o mesmo baud rate, e, quando configurado corretamente, funciona por anos sem problemas. Depois de dominar a ligação cruzada, a adequação de níveis e o enquadramento de pacotes, você poderá usar com facilidade dezenas de módulos, de GPS a GSM. No app BlueGrays, você encontra projetos passo a passo com esquemas do circuito e código para comunicação unidirecional e bidirecional entre dois Arduinos, acendimento de um LED pelo celular via Bluetooth e um carrinho controlado por Bluetooth.

Perguntas frequentes

O que significa UART e para que serve?

UART significa Universal Asynchronous Receiver-Transmitter, ou receptor-transmissor assíncrono universal. Permite que dois dispositivos troquem dados byte a byte sem uma linha de clock, usando apenas os fios TX e RX e um baud rate combinado previamente. O Monitor Serial do Arduino e os módulos GPS, Bluetooth e GSM usam UART.

Como conectar TX e RX?

O pino TX de um dispositivo vai para o RX do outro, e seu RX vai para o TX do outro, formando uma ligação cruzada. Os pinos GND dos dois dispositivos também devem estar conectados. Ligar TX a TX bloqueia completamente os dados.

O que é baud rate e qual devo escolher?

Baud rate é o número de bits enviados por segundo e deve ser igual nos dois lados. Os valores mais comuns nos módulos usados com Arduino são 9600 e 115200; 9600 é o padrão na maioria dos módulos GPS e Bluetooth, enquanto 115200 é preferido para ESP32 e saídas rápidas. Comece com a velocidade padrão indicada na documentação do módulo.

UART é a mesma coisa que porta serial?

No uso cotidiano, os termos costumam ter o mesmo sentido. UART é a unidade de hardware que serializa os dados; porta serial é a conexão COM ou /dev/tty de um computador. RS-232 e RS-485 são padrões que usam níveis de tensão diferentes para transportar sinais UART por distâncias maiores.

Como usar mais de uma UART em um Arduino?

O Uno tem uma única UART de hardware compartilhada com USB; para portas extras, você usa SoftwareSerial, mas apenas uma pode escutar por vez. O Arduino Mega tem três UARTs de hardware extras (Serial1, Serial2 e Serial3), o ESP32 tem três no total e o Raspberry Pi Pico tem duas.

Qual pode ser o comprimento de um cabo UART?

UART em nível TTL de 3,3 V ou 5 V funciona de forma confiável por até alguns metros em baud rates baixos, e a distância diminui conforme a velocidade aumenta. Para distâncias maiores, transceptores RS-485 (como o MAX485) alcançam centenas de metros, e RS-232, cerca de 15 metros.

Ver todos os projetos
Comunicação unidirecional entre 2 Arduinos
#57Intermediário
Premium

Comunicação unidirecional entre 2 Arduinos

Faça dois Arduinos conversarem por um único fio: ao pressionar o botão de um, o LED do outro acende; ao pressionar de novo, ele apaga. A placa transmissora informa que o botão foi pressionado com uma

4.4(37)
50
Comunicação bidirecional entre 2 Arduinos
#58Intermediário
Premium

Comunicação bidirecional entre 2 Arduinos

Dois Arduinos se conectam por fios e conversam entre si: ao pressionar o botão de um, a luz do outro acende; ao pressionar novamente, ela apaga. As duas placas executam o mesmo programa, então ambas e

4.5(20)
26
Controle de LED pelo celular via Bluetooth
#34Iniciante
Premium

Controle de LED pelo celular via Bluetooth

Um pequeno ensaio para acender e apagar as luzes do seu quarto pelo celular. O módulo Bluetooth repassa ao Arduino as letras enviadas pelo celular; uma letra acende o LED vermelho, outra acende o verd

4.8(68)
89
Carrinho controlado por Bluetooth com L298N
#80Avançado
Premium

Carrinho controlado por Bluetooth com L298N

Transforme seu celular em um controle remoto e dirija seu próprio carrinho robô. O Arduino recebe os comandos de um aplicativo de controle remoto no celular por meio de um módulo Bluetooth e aciona as

5.0(38)
65
Teclado para programar em Python
#66Intermediário
Premium

Teclado para programar em Python

Se você se cansou de digitar os mesmos padrões várias vezes ao programar, este pequeno teclado é para você. O Arduino Leonardo se conecta ao computador como um teclado, e cada tecla do teclado matrici

4.6(35)
45
Medição de distância com HC-SR04 (Monitor Serial)
#27Iniciante

Medição de distância com HC-SR04 (Monitor Serial)

O sensor HC-SR04 funciona como um morcego que se orienta no escuro: emite um som que você não consegue ouvir, escuta o eco e mede a distância até o objeto à sua frente. O Arduino escreve essa distânci

4.6(46)
71
Temperatura e umidade com DHT11
#55Intermediário

Temperatura e umidade com DHT11

Qual é a temperatura do seu quarto? O ar está úmido ou seco? O sensor DHT11 mede a temperatura e a umidade, e o Arduino mostra os valores em um display LCD de duas linhas, atualizando a cada poucos se

4.7(47)
73
Leitura da intensidade da luz com módulo LDR e ESP32
#113Iniciante

Leitura da intensidade da luz com módulo LDR e ESP32

Acompanhe em tempo real no Monitor Serial o valor bruto do ADC do módulo LDR e a porcentagem relativa de luz calculada a partir de referências de escuridão e claridade. Essa porcentagem não é uma medida em lux. O projeto ensina a lógica de calibração do módulo. É ideal fazê-lo antes do projeto 10, pois ajuda a determinar o valor do limiar.

4.6(36)
55