UART devices and serial debugging are used almost ubiquitously in embedded development. Let's demystify both terms and learn how to use them in development.

While there is an abundance of specialized software and hardware tools for debugging embedded systems, the simplest way to start is by adding "debugging statements" to your program. If you've ever written a printf() or console.log() statement to help debug a desktop application, you'll find yourself in familiar territory since electronics let you do the same.

A big difference between electronics and traditional computers is that computers have fancy screens but electronics don't! So you might find yourself wondering, "when I print something where does it go?"

Worry not dear reader, this is where UART comes to the rescue! 🚑

A UART (Universal Asynchronous Receiver/Transmitter) is a piece of circuitry that allows our electronics to talk to computers. It does this by serializing your debug statements, sending them one-bit-at-a-time to a computer. From there, the computer is tasked with displaying them, just like it does with desktop applications.

Disclaimer: UARTs are a physical piece of circuitry. They use serial communication to send and receive data. Other serial protocols like I2C and SPI do exist but in my experience, I've found the term 'serial' used colloquially to mean serial communication using UART devices.

What's in this guide

The goal of this article is to help you make the most of UARTs by going over:  

  1. UART's background and use-cases
  2. Demystifying the UART spec
  3. Examples of UART on common hardware platforms

By the end of this article, you will be able to use a serial monitor to configure a two-way serial connection between a microcontroller and your computer.

Let's jump in.🤸‍

Where did UART come from and when is it used?

UART's simplicity and flexibility has made it a mainstay for developers since it was invented almost 60 years ago. It was originally developed by Gordon Bell at Digital Equipment Corporation (DEC) in 1959 for use in the PDP-1, an early computer. For you trivia-nerds out there: in 1962, the PDP-1 ran the first computer game in history.

In the '60s UART devices were used to connect minicomputers to teletypewriter machines. Their use evolved in the '70s to allow early microcomputers to store and load programs and data from cassette tapes. In the following decades UART was used to get personal computers talking to online services via modems.

Nearly every microcontroller shipped today comes equipped with built-in UARTs – whether it's boards by Arduino, STMicro, Particle, even Raspberry Pis. You might use serial communication on these boards to connect an external peripheral such as a GPS module to your microcontroller or to debug using printf statements.

How does UART work?  

Now that we know a little about the history of UART and where it's used, let's peel back the layers on its implementation.

UART communication involves sending data over two wires — one to transmit information and the other to receive it. The information is transmitted one bit at a time making it serial. It is part of an integrated circuit and usually found integrated in microcontrollers.

UART is said to be Universal because its parameters — speed, data size, and so on — are not fixed and can be configured to meet the needs of a given communication requirement. It is Asynchronous because it doesn’t require a clock to sync data transmission and reception. Niffty!

Hardware specification

Just like a serial bus consists of just two wires, one for transmitting data and another for receiving, microcontrollers and other serial devices also have two serial pins: the receiver, RX, and the transmitter, TX.                          

Fig 1. Tip: Connect the TX of one device to the RX of the other

An important note: Those RX and TX labels on a serial device are with respect to the device itself. If you are connecting two serial devices to each other, the TX from one device should go to the RX of the other, and vice-versa. In other words, the transmitter should be talking to the receiver, not to another transmitter.

Data Packet

Before any signals are transmitted on the hardware lines, both devices on a serial bus must agree to follow a predetermined set of protocols. This ends up shaping what the transmitted data packet looks like.

Fig2. Serial data packet. Some of these parameters are configurable. 

Since UART chips don't use clock signals, a start bit is sent first to tell the receiver to get ready to listen for data.

Following the start bit comes the data bits that that make up the word being sent, with bit zero, the least significant bit (LSB), being sent first.

The bits are sent as pulses on the wire at pre-specified time intervals, set based on a value known as baud rate. Baud rate is the number of pulse changes that take place per second. In the case of serial, it's the same as the number of bits transmitted per second.

The two devices might also optionally agree to use a parity bit for rudimentary error-checking, that is calculated and sent next, in sync with the data that has been transmitted thus far. Finally, at least one stop bit, of value 1, is sent by the transmitter.

Hardware implementation

Once these bits are sent by the transmitter, the receiver watches for a logic ‘high’ to change to a logic ‘low’. In a Transistor-Transistor-Logic implementation, this means watching out for a 0V signal, which signals the start bit. The receiver then syncs its own bus clock to that bit.                                                                                          

Fig 3. Serial signal at a transistor-transistor logic (TTL) level 

The receiver looks at the voltage on the wire at these times; if it sees a logic high, it records a 1, and a 0 if it sees a logic low.

UART on common platforms

Recall that for successful communication, the baud rate, word length, parity availability and type, and the number of stop bits all have to be agreed to in advance.

This "agreement" means specifying the values for baud rate, data length, etc. on the transmitting device as well as the receiving one, for example, an Arduino and a serial monitor respectively. A serial monitor offers a way to capture and view serial debug messages on a computer.

Below are three examples for a common serial program, 'Echo'. It checks to see if there's any incoming serial data, and then writes it back. This means if you typed in a 'hello' into the message prompt in your serial monitor, your microcontroller would echo back the same 'hello' to your console.

While this might seem trivial, an echo check can be used as a form of error detection to make sure that the data received at one end of a serial line is the same as the data sent.

Arduino

int incomingChars;  
void setup() {   
	// put your setup code here, to run once:   
	Serial.begin(9600); 
}  
void loop() {   
	// if there's any available serial, read it   
	while (Serial.available()) {     
		incomingChars = Serial.read();     
		Serial.write(incomingChars);   
	}
}

Particle

void setup() { 	
	Serial.begin(9600, SERIAL_8N1); 
}  
void loop() { 	
	Serial.printlnf("Echo back what you type: \n"); 	
	while (Serial.available()) { 	
		Serial.write(Serial.read());	
	} 
}  

nRF51-DK

#include "mbed.h"
Serial pc(USBTX, USBRX);  
int main() { 
	pc.printf("Echo back what you type: \n");  
	while(1) {
		pc.putc(pc.getc()); 
	}
} 

‌To see this in action, once you've uploaded your compiled program to your dev board open up your favorite serial terminal and hit connect. If you need one, we think we made a great one.

Serial debugging is great for providing quick and early feedback, but it's important to remember that it does have its drawbacks.  Printing out variables can be a clumsy operation: you have to make explicit changes to your code to insert these statements as well as recompile your program to see the changes take effect. It can also degrade performance and slow down a program's execution (and electronics are usually resource constrained, as-is!😱).  

As you become more comfortable with using serial for debugging, I encourage you to go checkout Embedded.fm for other tools to expand your debugging repertoire.

Now, you're all set. Go on with your bad self. ✨