How to use UART in ESP32 || ESP-IDF

This is second tutorial in the ESP32 Series, and today we will see how to use UART to send and receive data. As I have already mentioned in my previous tutorial,  I will be using the  ESP32 WROOM Devkit for these tutorials along with the Espressif IDE (ESP-IDF).

I hope you guys know what UART is and how it works. If you don’t I would recommend you to first study the basics of these protocols as this website generally focuses on how to implement these protocols in microcontrollers.

ESP32 has three UART interfaces, i.e., UART0, UART1, and UART2, which provide asynchronous communication (RS232 and RS485) and IrDA support, communicating at a speed of up to 5 Mbps. UART provides hardware management of the CTS and RTS signals and software flow control (XON and XOFF). All of the interfaces can be accessed by the DMA controller or directly by the CPU.

In this tutorial we will use the UART2, and using the FT232 USB to UART module, we will communicate with the computer. Also note that we will first use the simplest process of transmitting and receiving data, and if possible, later we will also see the more complicated ones, for eg- using interrupt or DMA.

Let’s start with the IDE and we will create a new project.

Setting up the Project

We will first create a project using the examples provided in the IDE by default. Later we will modify it according to our need.

  • Fist we will create a new Espressif IDF project
  • Then give some name to this project and click next

  • check “Create project using one of the templates”
  • goto Peripherals -> uart and select the “uart_async_rxtxtasks”
  • The name of the project will be modified after this, so make sure you change it back
  • Click finish to generate the project

The example we chose above is simplest example to start with uart. The ESP32 also supports interrupt and DMA for uart, but in this tutorial we will do the simplest method of sending and receiving data, i.e using the blocking mode.

The code will generate and you can find the main.c file in the main folder. The pregenerated code will also work pretty well, but here I will modify it a little and explain you the entire code.






Connection

ESP32 have 3 uarts, UART0, 1 and 2. I am going to use the UART2 as the pins for UART2 are defined clearly on the board. This is shown in the picture below

As shown in the image above, the RX2 and TX2 pins, GPIO16 and GPIO17 respectively, represents the UART2 pins. I am going to connect them to the FT232 USB to UART device. The pins must be cross connected, Rx to TX and TX to RX. This is shown in the picture below.

  • We have the TX2 (GPIO_17) connected to the RX pin of the FTDI
  • The RX2 (GPIO_16) is connected to the TX pin of the FTDI
  • As I mentioned before, for the UART to communicate, the devices must be cross connected.


The CODE

Defines

static const int RX_BUF_SIZE = 1024;

#define TXD_PIN (GPIO_NUM_17)
#define RXD_PIN (GPIO_NUM_16)

#define UART UART_NUM_2

int num = 0;
  • First the Rx buffer size is defined as 1 Kilobytes
  • I have modified the Tx and Rx Pins as per my setup
  • As I mentioned before, I am going to use the UART2 in this tutorial, so I have defined the UART as UART_NUM_2
    • Now we can just simply use the UART wherever the uart number is needed to be provided
  • num is an integer, which we will modify and send via the UART.

Initialize the UART

void init(void) 
{
    const 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,
        .source_clk = UART_SCLK_APB,
    };

    // We won't use a buffer for sending data.
    uart_driver_install(UART, RX_BUF_SIZE * 2, 0, 0, NULL, 0);
    uart_param_config(UART, &uart_config);
    uart_set_pin(UART, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}
  • Here First of all we will install the UART Driver. The uart_driver_install function takes the following argument
    • @uart_port_t, The UART_NUM you are using, UART2 in this example
    • @int rx_buffer_size, The size of the RX Buffer, 1024 Bytes in this example
    • @int tx_buffer_size, The size of the TX Buffer, 0 in this example
    • @int queue_size, The UART Queue size, 0 in this case since we are not using any queue in this tutorial
    • @QueueHandle_t *uart_queue, The UART queue, NULL in this example since we are not using the queue
    • @int intr_alloc_flags, The interrupt FLAG, 0 in this example since we are not using interrupt
  • Then we will configure the UART parameters. The configuration used is 8-N-1 with the baud rate of 115200. The UART source clock is set to APB.
  • Finally we will set the pins for the UART. The uart_set_pin function takes the following parameters
    • @uart_port_t, The UART_NUM you are using, UART2 in this example
    • @int tx_io_num, The TX Pin number, GPIO_NUM_17 in this example
    • @int rx_io_num, The RX Pin number, GPIO_NUM_16 in this example
    • @int rts_io_num and @int cts_io_num, The RTS and CTS Pin numbers. We are not using hardware flow control so these parameters are set to UART_PIN_NO_CHANGE (-1).

The TX Task

static void tx_task(void *arg)
{
	char* Txdata = (char*) malloc(100);
    while (1) {
    	sprintf (Txdata, "Hello world index = %d\r\n", num++);
        uart_write_bytes(UART, Txdata, strlen(Txdata));
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}
  • The TX task will send the data via the UART every 2 seconds
  • First we will create a buffer, where we will store the data to be transmitted.
  • Next we will store the data into the Txdata buffer. Here we will increment the value of the num variable so that the data will be different each time
  • uart_write_bytes will be used to write the data. The function takes the following parameters
    • @uart_port_t, The UART_NUM you are using, UART2 in this example
    • @const void* src, The pointer to the source address, Txdata in this example
    • @size_t size, The length of the data to be transmitted


The RX Task

static void rx_task(void *arg)
{
    static const char *RX_TASK_TAG = "RX_TASK";
    esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
    uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE+1);
    while (1) {
        const int rxBytes = uart_read_bytes(UART, data, RX_BUF_SIZE, 500 / portTICK_RATE_MS);
        if (rxBytes > 0) {
            data[rxBytes] = 0;
            ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
        }
    }
    free(data);
}
  • RX Task will be used to receive the data from the UART. Here we will first create a buffer to store the received data
  • We will receive the data using the function uart_read_bytes. It takes the following parameters
    • @uart_port_t, The UART_NUM you are using, UART2 in this example
    • @void* buf, The pointer to the buffer where the data will be saved, data in this example
    • @uint32_t length, The length of the buffer, 1024 bytes in this example
    • @TickType_t ticks_to_wait, The time to wait for the data to arrive, 500 ms in this example
    • @ESP_LOGI, is used to log the received data to the terminal.

The main function

void app_main(void)
{
    init();
    xTaskCreate(rx_task, "uart_rx_task", 1024*2, NULL, configMAX_PRIORITIES-1, NULL);
    xTaskCreate(tx_task, "uart_tx_task", 1024*2, NULL, configMAX_PRIORITIES-2, NULL);
}
  • In the main function, we will first initialize the UART
  • Then we are creating 2 tasks, rx_task and tx_task
    • The stack size for both the tasks is set to 2 Kilobytes
    • The priority of the receiver task is highest, and that of the transmitter task is 1 below the receiver task
  • After the Tasks has been created, the transmitter task will start sending the data to the UART and Receiver task will wait for the data to be received.


Result

Below is the output of the serial Terminal and the ESP32 Terminal

As you can see the Serial terminal is receiving the data sent by the ESP32 every 2 seconds. The data is updated with the new number each time it is sent.
When we send some data from the terminal to the ESP, it gets logged into the ESP terminal


Check out the Video Below










Info

You can help with the development by DONATING
To download the code, click DOWNLOAD button and view the Ad. The project will download after the Ad is finished.

3 Comments. Leave new

  • i am trying to communicate with ec200 ucn gsm module, and sending AT commands via esp32 uart 0, getting response back but , not able to read proper reading of gsm module response because it takes time to respond back, so i am not able to when to send next at command , I simply wanted to send data to server via gsm moudle

    Reply
  • aaaaaaaaaaaa
    March 30, 2023 1:51 PM

    where is the free of Txdata ?

    Reply
  • Hi, Thanks for this. There is an interesting observation which I like to share:

    static const int RX_BUF_SIZE = 1024;
    

    My application had only 10bytes of rx data so I redefined RX_BUF_SIZE to 10. Apparently ESP32 driver install API doesnot like this. RX_BUF_SIZE must be always >128 bytes else it throws error.

    This might help someone!

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

keyboard_arrow_up

Adblocker detected! Please consider reading this notice.

We've detected that you are using AdBlock Plus or some other adblocking software which is preventing the page from fully loading.

We don't have any banner, Flash, animation, obnoxious sound, or popup ad. We do not implement these annoying types of ads!

We need money to operate the site, and almost all of it comes from our online advertising.

Please add controllerstech.com to your ad blocking whitelist or disable your adblocking software.

×