Hi all… Now we will see ESP32 IDF Serial Communication Tutorial. ESP32 has three Serial ports. UART0, UART1, and UART2.
ESP32 IDF Serial Communication Tutorial
Suggest to Read
First, we will take the example Program from the example directory. Before coding, I will explain the API and Structures which we are using in our code.
ESP32 IDF Serial Communication
APIs Used
- uart_param_config()
- uart_set_pin()
- uart_driver_install()
- uart_read_bytes()
- uart_write_bytes()
uart_param_config()
This API is used to set the UART configuration like baud rate, stop bits, etc. This API contains Two arguments. Have a look at this API below.
esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config)
Return
- ESP_OK Success
- ESP_FAIL Parameter error
Parameters
uart_num
: UART_NUM_0, UART_NUM_1 or UART_NUM_2uart_config
: UART parameter settings
Here Second argument is a structure’s address. That structure is used to set the configuration settings.
structuart_config_t
Members | Description |
int baud_rate |
UART baudrate |
uart_word_length_t data_bits |
UART byte size |
uart_parity_t parity |
UART parity mode |
uart_stop_bits_t stop_bits |
UART stop bits |
uart_hw_flowcontrol_t flow_ctrl |
UART HW flow control mode(cts/rts) |
uint8_t rx_flow_ctrl_thresh |
UART HW RTS threshold |
uart_set_pin()
This function is used to configure the UART pin in ESP32. Using this function we can select any pin as a TX, RX, CTS, or RTS. This function contains five arguments. The internal signal can be output to multiple GPIO pads. Only one GPIO pad can connect with the input signal.
esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num)
Return
- ESP_OK Success
- ESP_FAIL Parameter error
Parameters
uart_num
: UART_NUM_0, UART_NUM_1 or UART_NUM_2tx_io_num
: UART TX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin.rx_io_num
: UART RX pin GPIO number if set to UART_PIN_NO_CHANGE, use the current pin.rts_io_num
: UART RTS pin GPIO number if set to UART_PIN_NO_CHANGE, use the current pin.cts_io_num
: UART CTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin.
uart_driver_install()
This API is used to install the uart_driver. This API contains six arguments. UART ISR handler will be attached to the same CPU core that this function is running on. Users should know which CPU is running and then pick an INUM that is not used by the system. We can find the information on INUM and interrupt level in soc.h.
esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void *uart_queue)
Return
- ESP_OK Success
- ESP_FAIL Parameter error
Parameters
uart_num
: UART_NUM_0, UART_NUM_1 or UART_NUM_2rx_buffer_size
: UART RX ring buffer sizetx_buffer_size
: UART TX ring buffer size. If set to zero, the driver will not use the TX buffer, TX function will block the task until all data have been sent out.queue_size
: UART event queue size/depth.uart_intr_num
: UART interrupt number, check the info insoc.h
, and please refer tocore-isa.h f
or more detailsuart_queue
: UART event queue handle, if set NULL, the driver will not use an event queue.
uart_read_bytes()
This API is used to read the data from the UART buffer. This contains four arguments.
int uart_read_bytes(uart_port_t uart_num, uint8_t *buf, uint32_t length, TickType_t ticks_to_wait)
Return
- (-1) Error
- Others return char data from uart fifo.
Parameters
uart_num
: UART_NUM_0, UART_NUM_1 or UART_NUM_2buf
: pointer to the buffer.length
: data lengthticks_to_wait
: timeout, count in RTOS ticks
uart_write_bytes()
This API is used to write the data into the UART port from a given buffer and length. This has three arguments.
int uart_write_bytes(uart_port_t uart_num, const char *src, size_t size)
If parameter tx_buffer_size is set to zero: This function will not return until all the data have been sent out or at least pushed into TX FIFO.
Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to the tx ring buffer, then, UART ISR will move data from the ring buffer to TX FIFO gradually.
Return
- (-1) Parameter error
- OTHERS(>=0) The number of data that were pushed to the TX FIFO
Parameters
uart_num
: UART_NUM_0, UART_NUM_1 or UART_NUM_2src
: data buffer addresssize
: data length to send
Example Code 1
This is an example that echos any data it receives on UART1 back to the sender, with hardware flow control turned off. It does not use the UART driver event queue.
- port: UART1
- RX buffer: on
- TXbuffer: off
- flow control: off
- event queue: off
- pin assignment: txd(io4), rxd(io5)
#include <stdio.h> #include <string.h> #include <stdlib.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_system.h" #include "nvs_flash.h" #include "driver/uart.h" #include "freertos/queue.h" #include "esp_log.h" #include "soc/uart_struct.h" #define ECHO_TEST_TXD (4) #define ECHO_TEST_RXD (5) #define BUF_SIZE (1024) //an example of echo test without hardware flow control on UART1 static void echo_task() { const int uart_num = UART_NUM_1; 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, .rx_flow_ctrl_thresh = 122, }; //Configure UART1 parameters uart_param_config(uart_num, &uart_config); //Set UART1 pins(TX: IO4, RX: I05) uart_set_pin(uart_num, ECHO_TEST_TXD, ECHO_TEST_RXD, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); //Install UART driver (we don't need an event queue here) //In this example we don't even use a buffer for sending data. uart_driver_install(uart_num, BUF_SIZE * 2, 0, 0, NULL, 0); uint8_t* data = (uint8_t*) malloc(BUF_SIZE); while(1) { //Read data from UART int len = uart_read_bytes(uart_num, data, BUF_SIZE, 20 / portTICK_RATE_MS); //Write data back to UART uart_write_bytes(uart_num, (const char*) data, len); } } void app_main() { //A uart read/write example without event queue; xTaskCreate(echo_task, "uart_echo_task", 1024, NULL, 10, NULL); }
Output
Example Code 2
In this example, I’m going to use two UARTs. Whatever I’m typing in UART 0 it sends to UART1 and UART0.
UART 0:
- port: UART0
- rx buffer: on
- tx buffer: off
- flow control: off
- event queue: off
- pin assignment: txd(default), rxd(default)
UART 1:
- port: UART1
- rx buffer: on
- tx buffer: off
- flow control: off
- event queue: off
- pin assignment: txd(io4), rxd(io5)
#include <stdio.h> #include <string.h> #include <stdlib.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_system.h" #include "nvs_flash.h" #include "driver/uart.h" #include "freertos/queue.h" #include "esp_log.h" #include "soc/uart_struct.h" #define ECHO_TEST_TXD (4) #define ECHO_TEST_RXD (5) #define BUF_SIZE (1024) //an example of echo test with hardware flow control on UART1 static void echo_task() { const int uart_num0 = UART_NUM_0; uart_config_t uart_config0 = { .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, //UART_HW_FLOWCTRL_CTS_RTS, .rx_flow_ctrl_thresh = 122, }; const int uart_num1 = UART_NUM_1; uart_config_t uart_config1 = { .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, //UART_HW_FLOWCTRL_CTS_RTS, .rx_flow_ctrl_thresh = 122, }; //Configure UART1 parameters uart_param_config(uart_num0, &uart_config0); uart_param_config(uart_num1, &uart_config1); uart_set_pin(uart_num0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); uart_set_pin(uart_num1, ECHO_TEST_TXD, ECHO_TEST_RXD, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); //Install UART driver (we don't need an event queue here) //In this example we don't even use a buffer for sending data. uart_driver_install(uart_num0, BUF_SIZE * 2, 0, 0, NULL, 0); uart_driver_install(uart_num1, BUF_SIZE * 2, 0, 0, NULL, 0); uint8_t* data = (uint8_t*) malloc(BUF_SIZE); while(1) { //Read data from UART int len = uart_read_bytes(uart_num0, data, BUF_SIZE, 20 / portTICK_RATE_MS); //Write data back to UART uart_write_bytes(uart_num1, (const char*) data, len); uart_write_bytes(uart_num0, (const char*) data, len); } } void app_main() { //A uart read/write example without event queue; xTaskCreate(echo_task, "uart_echo_task", 1024, NULL, 10, NULL); }
Output
Embedded Software | Firmware | Linux Devic Deriver | RTOS
Hi, I’m SLR. I am a tech blogger and an Embedded Engineer. I am always eager to learn and explore tech-related stuff! also, I wanted to deliver you the same as much in a more straightforward way with more informative content. I generally appreciate learning by doing, rather than only learning. If you want to help support me on my journey, consider sharing my articles, or Buy me a Coffee! Thank you for reading my blog! Happy learning!