Table of Contents
Blog List
Some time ago I wrote a blog about how to use ADC (Analog to Digital) converter inside ESP32 chip. ADC is one of the very basic peripherals for any microcontroller. Still, when it comes to RTOS environment it is not that simple as in Arduino programming. Here, there are many configurations as you saw in the first tutorial that needs to be done.
This has pro and cons. The advantage is that it provides user with configurability and control of the application settings. This helps when making complex products with several ADC and other peripherals at use at the same time. But also, it is about the real-time behavior of the application. The slight disadvantage is for new users and someone just trying something new with ESP32 devices. They have to scroll through the documentation and have to read long documents about peripherals and their usage such as in datasheet or technical reference manuals or api guides.
But once the user is familiar with the platform it gets easier or simpler to develop code for various chips that Espressif offers.
This tutorial will be focused around one basic peripheral that every device has and that is UART (Universal Asynchronous Receiver Transmitter).
Again, for this tutorial I am using ESP32C6 based module from DFRobot Firebeatle V2. Along with that I am using DFRobot waterproof ultrasonic UART distance sensor. I will show you how to measure distance (in mm) accurately using this sensor.
Introduction to UART in ESP32
Though this is a software tutorial it would be nice to have a look at some of the hardware aspect of the UART peripheral. Above figure is a block diagram of the UART peripheral of ESP32 device. The UART is divided in TX and RX related hardware blocks. The RX block has the circuit that can detect the start condition when some UART device is connected. It also has baud detect circuit for detecting the speed of the UART device. The TX on the other hand similar with some changes. Both these parts have FIFO inside a RAM that is around 256x8 bits each. These FIFO are for storing the received and the bytes to be transmitted.
The UART is configurable to save power. It is derived from the global clock of the SoC and divided for data rates below the clock rate to save power. It can also be increased to at most twice the APB_CLK in the system.
Now let's dive into the UART and how ESP-IDF handles the incoming data from the sensor. The API is simple if you just want to use it for receiving or transmitting some data. There are event-based callbacks that can be used for use cases where it is required. Also, the API can be used with RS232 and RS485 based devices with external hardware connection to the chip. I am not going to cover all these features at the time.
Enable UART inside the main/CMakeLists.txt file with following lines. It will help you with adding header file #include "driver/uart.h" in your main code.
idf_component_register(SRCS "main.c" REQUIRES esp_driver_uart INCLUDE_DIRS ".")
This first thing is to set some parameters to configure baud rate, stop bits, parity, flow control, etc. These parameters can be set with the struct called uart_config_t. As per the datasheet of the sensor following configurations are for receiving the data without hardware flow control. The sensor does not have pins for flow control. A software-based flow control to detect some pattern can be implemented but we will not have a look onto that.
uart_config_t config = { .baud_rate = 9600, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, };
The next task is to configure the parameters defined above and check if they are right. During run time ESP will through error if there is something wrong or parameters cannot be configured with specific chip or peripheral. The use another API function to set the pins and UART number. There are two UART devices in ESP32.
ESP_ERROR_CHECK(uart_param_config(uart_no, &config)); ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, 16, 17, -1, -1)); const int uart_buf_size = (100 * 2); ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, uart_buf_size, uart_buf_size, 10, NULL, 0));
Next part is specific to either transmitting or receiving the data. As we need to get the data from the sensor we need to write receiver specific code. In the following code snipet uart_read_bytes() line will read 8 bytes into data[] buffer. At the end I am clearing the buffer with flush() command. In between the data is processed according to the frame specified for UART.
uint8_t data[12]; int length = 0; uint16_t distance; while (1) { /* code */ uint8_t sum; length = uart_read_bytes(uart_no, data, 8, 100); printf("%u %u %u %u\n",data[0], data[1], data[2], data[3]); for(int i = 0; i<8; i++){ if (data[i] == 0xff) { sum = (data[i] + data[i+1] + data[i+2]) & 0x00FF; if(sum == data[i+2]){ printf("valid data\n"); distance = data[i+1]*256 + data[i+2]; printf("%u mm\n", distance); } } } uart_flush(uart_no); vTaskDelay(10*portTICK_PERIOD_MS); }