Microcontroller boards require data communication with many other devices that include sensors, actuators, other microcontrollers and microcomputers. This data transfer or exchange is carried out through serial interfaces using standard protocols such as UART, I2C, SPI, 1-wire, etc. UART is one of the common serial data communication protocols available in almost all microcontrollers. UART is a point-to-point bidirectional serial data communication protocol. Sensors generally communicate data with microcontrollers and microcomputers using I2C or SPI protocols, as sensors always behave as slave devices that generally only require data transmission to the microcontrollers/microcomputers. There is almost no need for two-way data communication via sensors.
However, any data communication with other microcontrollers or microcomputers needs to be strictly point-to-point and bidirectional. UART is the most useful serial communication protocol in this scenario. The UART protocol is useful when a microcomputer or other master microcontroller drives networked microcontroller systems such as ESP8266 or ESP32. This article will look at the MicroPython machine module UART class and explore how it can be used for serial data communication in ESP8266 and ESP32.
MicroPython Machine Module
In addition to the reimplemented Python3 modules, MicroPython has several native modules for handling hardware functions and devices. One of these modules is a machine responsible for handling many vital functions of the microcontroller. The module includes classes to control digital input/output, control output signals from external devices, pulse width modulation, analog to digital conversion, control peripherals ADC, UART, SPI, I2C, I2S, timer, RTC, Watchdog timer and SD card management.
UART Class
The UART class is part of the machine module. It implements the standard UART/USART protocol. UART requires two lines – Rx and Tx for full-duplex communication. The Rx and Tx of one microcontroller are connected to the Tx and Rx of the other respectively. This class is imported into a MicroPython script using the following instructions.
from UART machine import
The instantiation of a UART object must follow this. The constructor function of the UART object is as follows.
class machine.UART(id,…)
The constructor method essentially requires specifying the UART ID as an argument. Many other arguments can be passed inside the constructor method. These arguments are the same as those defined for the UART.init method. We will discuss them soon. Some of the valid examples of instantiating a UART object are as follows.
uart = UART(0)
uart = UART(0, baud rate=9600)
uart1 = UART(1, baud rate=9600, tx=33, rx=32)
The other methods available in the UART class are as follows.
UART.init : This method is used to initialize the UART object. It has the following prototype.
UART.init(baud rate=9600, bits=8, parity=None, stop=1, *,…)
The following parameters are passed to the method call.
baud rate: This is the baud rate in bps. Must be passed as an argument.
bits: It is the number of bits per character. It can be 7, 8 or 9. The default is 8.
parity: specifies the parity bit. It can be even (0) or odd (1).
stop: specifies the number of stop bits. It can be 1 or 2.
tx: Specifies the transmit pin on the port to be used.
rx: Specifies the receiver pin on the port to be used.
rtc: specifies the rtc pin on the port to be used. The RTC is the output pin used for hardware receive flow control.
cts: specifies the cts pin on the port to be used. The CTS is the input pin used for hardware transmission flow control.
txbuf: Specifies the transmitter buffer size in multiple characters.
rxbuf: Specifies the receiver buffer size in multiple characters.
timeout: specifies the time in milliseconds to wait to receive the first character.
timeout_char: specifies the time in milliseconds to wait between characters.
invert: specifies the line to be inverted. Can be set to 0, UART.INV_TX, UART.INV_RX and UART.INV_TX UART.INV_RX. If set to 0, no lines will be flipped. If set to UART.INV_TX, the transmission line will be inverted. If set to UART.INV_RX, the receiver line will be inverted. If set to UART.INV_TX UART.INV_RX, the transmitter and receiver lines are reversed. Inversion inverts the TTL logic levels, that is, from positive logic to negative logic or from negative logic to positive logic.
flow: specifies the hardware flow control. Can be set to 0, UART.RTS, UART.CTS and UART.RTS UART.CTS.. If set to 0, hardware flow control signals will be ignored. If set to UART.RTS, enables the RTS signal as long as the receiver FIFO has enough space to accommodate more data. If set to UART.CTS, the CTS signal will be enabled. When the CTS signal, transmission is paused if the receiver is low on buffer space. If set to UART.RTS UART.CTS, RTS and CTS signals are enabled.
Clearly, MicroPython allows precise control of UART communication. This is evident from the number of configuration parameters allowed in the UART.init method.
UART.deinit : When calling this method, the UART bus is disabled.
UART.read((nbytes)) : This method reads the number of characters specified as an argument from the receiver buffer. If no arguments are passed, it reads all characters in the receiver's buffer. It may return nothing or return early if the timeout is reached.
UART.readinto(buf(,nbytes)) : This method reads the number of characters specified as an argument into the receiver's buffer. If no arguments are passed, it reads all available characters in the receiver's buffer. It may not read anything or return early if the timeout is reached.
UART.readline : This method reads a line of characters. A newline character ends a line. May not return any if timeout is reached.
UART.write(buf) : This method writes specified characters to the transmission line.
UART.sendbreak : This method sends an interrupt condition on the bus. It reduces lines for a longer period.
UART.irq(trigger, priority=1, handler=None, wake=machine.IDLE) : This method invokes a callback (function) when data is received on the UART. Accepts four parameters. The trigger can only be UART.RX_ANY. Priority defines the priority level which can be between 1~7. A higher number indicates higher priority. The handler is the callback function. It's optional. wake can be the only machine.IDLE.
UART.any : This method returns the number of characters available for reading. It returns 0 if no characters are available for reading. May return 1 or the exact number of characters available for reading if one or more characters are available. This depends on the specific port.
UART on ESP8266
In ESP8266, there are two UART peripherals available – UART0 and UART1. UART0 has lines on GPIO1 (UART0 TX) and GPIO3 (UART0 RX) pins. UART1 has lines on GPIO2 (UART1 TX) and GPIO8 (UART1 RX) pins.
GPIO8 is internally connected to the flash chip; only UART1 is the transmitter. UART0 is attached by default to the Web REPL. Using UART0 for serial data communication must first be disconnected from the REPL. UART0 can be detached from the REPL using the following code snippet.
import operating system
os.dupterm(None, 1)
After disconnecting UART0 from the REPL, it can be used for serial data communication. The constructor method of the UART class can be used to instantiate a UART object. The init method can be used on the object to configure the UART. The read and readline methods can be used to read from the UART, and the write method can be used to transmit serial data over the UART.
from UART machine import
uart = UART(0, baud rate=9600)
uart.write('Hello')
uart.read
Once the serial communication requirement is completed, UART0 must be reconnected to the REPL. UART0 can be reattached to the REPL using the following code snippet.
import operating system, machine
uart = machine.UART(0, 115200)
os.duterm(uart, 1)
UART on ESP32
There are three UART peripherals on the ESP32 – UART0, UART1 and UART2. Each UART is connected to standard pins as listed in the table below.
As with ESP32-DevKit-V1, only UART0 and UART2 are exposed. Depending on the ESP32 variant, some of these pins may be shared or reserved for flash, PSRAM or other peripherals. This is shown in the image below.
MicroPython allows you to multiplex any GPIO with the ESP32's hardware UARTs. So regardless of which standard pins are exposed or not, all three UARTs can be used in a MicroPython script. When instantiating a UART object for ESP32, the Rx and Tx pins must also be passed as arguments.
Using UART on ESP8266 and ESP32
ESP8266 and ESP32 are primarily used in networking applications with Wi-Fi support. In many of these applications, the master microcontroller that manages built-in functions is another microcontroller, while the ESP8266 or ESP32 plays a secondary role in handling just the communication and network control. In these applications, UART is the only option for data communication between the ESP8266/ESP32 and the device's main controller. Some sensors and modules may also provide a UART interface along with I2C or SPI to communicate sensor data or control signals. Here again, UART on ESP8266/ESP32 can be used.
UART Serial Data Communication with MicroPython
Following is an example MicroPython script to test UART communication on ESP8266.
import operating system, machine
from UART machine import
os.dupterm(None, 1)
uart = UART(0, baud rate=9600)
uart.write('Hello')
uart.read(5)
os.duterm(uart, 1)
Following is an example MicroPython script to test UART communication on ESP32.
from UART machine import
uart = UART(1, baud rate=9600, tx=33, rx=32)
uart.write('hello')
uart.read(5)