Manual

do

Maker

.

com

Configurar a UART do ESP32

Configurar a UART do ESP32

Esse post é simples e curto, apenas pra que possamos eliminar a última coisa que utilizavamos nativo da IDE do Arduino; a conexão serial. Vou mostrar brevemente como configurar a UART do ESP32 para que não utilizemos mais **Serial.begin()**e assim possamos utilizar uma IDE mais bacana para programar o ESP32 utilizando apenas o ESP-IDF. Vamos lá?

Onde comprar

Bem, para programar um ESP32 você precisará de...

...um ESP32. Recomendo a CurtoCircuto.

Abstração

Quando iniciamos a UART utilizando Serial.begin(), todas as configurações necessárias são executadas sem que vejamos. O único parâmetro que precisamos passar é a velocidade em kbauds. Já a configuração UART nativa do ESP-IDF é um pouco mais complicada. Mesmo se utilizarmos a IDE do Arduino, devemos seguir 5 passos para fazer a configuração, mas temos muito mais flexibilidade.

Como configurar a UART do ESP32

Os passos que devemos seguir são:

  • incluir a biblioteca uart.h
  • definir os parâmetros de comunicação
  • configurar os pinos de comunicação
  • instalar o driver
  • enviar e receber dados

Com isso, já conseguimos fazer uma comunicação serial sem problemas, mas podemos ir além, configurando as interrupções, descartando dados entrantes etc. Comecemos pelo básico.

Incluir a biblioteca UART

À primeira vista pode parecer chato ter que fazer isso, mas tem uma razão. Quanto mais bibliotecas você incluir, mas recursos de hardware são utilizados. Incluir as bibliotecas manualmente permitem flexibilizar o consumo desses recursos. Se seu projeto não utiliza UART, simplesmente não inclua a biblioteca. Mas se precisar, faça assim:

#include "driver/uart.h"

Configurar os parâmetros de comunicação

Existem configurações específicas da comunicação serial que, conforme o dispositivo com o qual se está comunicando, pode variar bastante. Esses parâmetros normalmente estão disponíveis em programas que utilizamos para fazer a comunicação serial, como o CuteCom, que tem uma interface amigável.

Esses parâmetros de configuração  no header do programa são as possibilidades que temos para modificar, mas destes, mudamos normalmente apenas a velocidade, os demais parâmetros já vem na configuração padrão que utilizamos para a maioria das comunicações seriais. Não precisamos nos preocupar com os detalhes, poderemos sempre utilizar um padrão como acima, mas caso deseje se aprofundar em comunicação serial, recomendo esse link, que utilizei em meados dos anos 2000 para fazer a comunicação de um teclado numérico com o Linux para um sistema de mercado.

Voltando ao ESP32, devemos agora configurar esses parâmetros. Para isso, devemos definir previamente uma estrutura do tipo uart_config_t passando o seguinte padrão:

uart_config_t uart_config = {
  .baud_rate = 115200,
  .data_bits = UART_DATA_8_BITS,
  .parity    = UART_PARITY_DISABLE,
  .stop_bits = UART_STOP_BITS_1,
  .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};

Dessa estrutura, o único parâmetro que provavelmente estaremos mudando é o baud rate.

Definida a estrutura, configuramos agora os parâmetros através da função uart_param_config().

uart_param_config(UART_NUM_0, &uart_config);

Os parâmetros da função são estes:

  • Número da serial a configurar
  • Endereço da estrutura com os parãmetros que definimos

Agora podemos ir para o passo 2.

Configurar os pinos de comunicação

Acredito que seja possível fazer softserial com o ESP32 porque de outro modo não faria muito sentido flexibilizar a configuração dos pinos, mas eu me ative a configurar a serial pretendida. Os parâmetros dessa função são:

  • Número da serial a configurar
  • TX
  • RX
  • RTS
  • CTS

Mas existe uma configuração padrão, de modo que não precisaremos nos preocupar com os valores a passar para a função. Para isso, simplesmente passamos a macro UART_PIN_NO_CHANGE em todos os pinos:

uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

E agora podemos ir para o passo 3.

Instalação do driver

Essa função aloca os recursos requeridos pela UART. Como citei mais acima, a flexibilidade que o ESP-IDF nos oferece permite fazer muito mais coisas que normalmente fariamos utilizando a IDE do Arduino. Não quero dizer que faltem recursos na IDE do Arduino, mas enquanto não sabemos que um determinado recurso existe, não o utilizamos!

Os parâmetros para a instalação do driver são:

  • Tamanho do buffer de envio
  • Tamanho do buffer de recepção
  • Manipulador da fila de eventos e tamanho
  • Flags para alocar uma interrupção

Não se preocupe com esses detalhes agora. Vamos iniciar uma comunicação serial simples, como fazemos com o Arduino. Para isso, podemos utilizar sempre esse padrão:

uart_driver_install(UART_NUM_0, BUF_SIZE * 2, 0, 0, NULL, 0);

A macro BUF_SIZE podemos colocar logo após o include da biblioteca UART, desse modo:

#define BUF_SIZE (1024)

Isso significa que teremos um buffer de 1024 Bytes. Agora podemos iniciar a comunicação serial.

Ler e escrever dados na serial

A tarefa se resume em ler e escrever dados dos respectivos buffers e para isso temos duas funções; uart_read_bytes()uart_write_bytes().

uart_read_bytes

Essa função lê do buffer e retorna o tamanho do array de dados alocados no buffer. Seu formato básico:

int len = uart_read_bytes(UART_NUM_0, data, BUF_SIZE, 20 / portTICK_RATE_MS);

Se definirmos um buffer menor que 256 Bytes, podemos usar unsigned char para guardar o retorno e assim economizamos 1 Byte. Repare que sempre passamos o número da serial em questão. Nessa função, passamos outros 2 parâmetros que normalmente manipulamos; o buffer que conterá os dados e o tamanho do buffer. Depois, lemos os dados desse buffer.

uart_write_bytes

Escrever dados na serial é a mesma coisa, exceto que não precisamos de um retorno, pois supostamente sabemos o tamanho do buffer que estamos passando e não faria sentido "descobrir" o tamanho do buffer para envio. Desse modo, a função normalmente é chamada da seguinte maneira:

uart_write_bytes(UART_NUM_0, (const char *) data, len);

Pronto, já temos uma comunicação serial!

Na prática

Agora que já configuramos todos os parâmetros, podemos iniciar a comunicação de teste.

#include "driver/uart.h"

#define BUF_SIZE (1024)

uint8_t *data = (uint8_t *) malloc(BUF_SIZE);


uart_config_t uart_config = {
  .baud_rate = 115200,
  .data_bits = UART_DATA_8_BITS,
  .parity    = UART_PARITY_DISABLE,
  .stop_bits = UART_STOP_BITS_1,
  .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};

void setup() {
  uart_param_config(UART_NUM_0, &uart_config);
  uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
  uart_driver_install(UART_NUM_0, BUF_SIZE * 2, 0, 0, NULL, 0);
}

void loop() {
  int len = uart_read_bytes(UART_NUM_0, data, BUF_SIZE, 20 / portTICK_RATE_MS);
  if (data[0] == 'd') {
    bzero(data,1024);
    uart_write_bytes(UART_NUM_0, (const char *) "www.DobitAoByte.com.br\n", 23);
  }
  else if (len >3){
    uart_write_bytes(UART_NUM_0, (const char *) data, len);
  }
}

No loop eu fiz uma brincadeira lendo o primeiro Byte do buffer e se esse Byte for 'd', então o buffer é apagado e a serial devolve a URL do site. De outro modo, se for escrito apenas 1 Byte na serial, ignora o que foi escrito.

Dá pra simplificar?

Não posso dizer a quantidade de pessoas que gostam de codificar, mas certamente a maioria deve estar odiando essa montoeira de código. No Arduino usamos 3 linhas; uma inicializa a serial, outra lê e outra escreve. Daí você pode questionar se dá para simplificar e a resposta é simples: Claro!

Se estivermos utilizando a IDE do Arduino para programar o ESP32, não podemos utilizar Serial.begin()? Aliás, não foi o que utilizei em todos os artigos anteriores relacionados ao ESP32? A API do Arduino para programar o ESP32 faz a abstração necessária para deixar a utilização da serial identica à função serial do Arduino. Se estivermos programando fora da IDE do Arduino, podemos simplesmente definir os parâmetros necessários e criar funções que se alimentem deles, de modo que passemos apenas os dados como parâmetro para essa função; exatamente como é feito na IDE do Arduino. Veremos isso em outro artigo, onde utilizaremos outra IDE para programar o ESP32.

Outros recursos

Para cada uma das funções *_set_* temos uma correspondente para *_get_*. Não sei se em algum momento realmente utilizarei essas funções, mas é bom saber que existem.

Existe também uma função para aguardar até que os dados do buffer sejam enviados e que o buffer esteja vazio, e pode ser usado conjuntamente com uma função básica que escreve Bytes para o buffer.

uart_tx_chars()

Essa função escreve o que couber no espaço vazio do buffer e sai, retornando o número de Bytes escritos.

uart_wait_tx_done()

Essa função é a que aguarda o fim da transmissão dos dados e o esvaziamento do buffer. Mas sem dúvidas, o caminho mais fácil para trabalhar com essa função é o uart_write_bytes().

uart_get_buffered_data_len()

Essa função verifica previamente o tamanho dos dados disponíveis no buffer de recepção. Pode ser bom para quando se espera um dado de tamanho fixo, assim evita-se processamento desnecessário ou direciona-se o resultado para uma função específica para o tamanho do buffer armazenado...

uart_flush()

...mas suponhamos que o tamanho dos dados contidos no buffer não são o esperado. Podemos simplesmente descartá-lo com a função uart_flush().

Interrupções

Relacionado às interrupções da UART, também são simples de gerenciar, mas vou deixar isso para outro artigo porque esse já ficou um pouco maior do que eu esperava.

Está pronto para uma nova IDE? Então acompanhe os próximos artigos relacionados!

Inscreva-se no nosso canal Manual do Maker no YouTube.

Também estamos no Instagram.

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.