A Progression of USART Drivers

Arduino provides a USART driver for treating the data as a stream, basically a never ending sequence of bytes. This follows along a tradition of sorts, basically popularized by UNIX in the 1970’s. However traditional (at the time) computers did I/O by records, blocks or packets, of data. And this is the basic operating method of other interfaces like SPI and I2C as well as protocols like MODBUS, BACNet, Ethernet TCP/IP and many others.

So looking forward to a MODBUS example in the new book, I decided to implement a sequence of USART drivers progressing toward MODBUS. MODBUS is popular in industrial control systems allowing for multiple devices connected over building-sized distances with high reliability.

I will point out that the ATmega328P based Arduino boards like the Uno and Nano have a disadvantage in that they have only one USART, and that USART is also used for program loading. This makes them difficult to use, especially for development, in projects that utilize the USART for data transmission. Save yourself some frustration and buy Arduino Leonardo or Micro (ATmega32U4), Mega (ATmega2560), or Nano Every (ATmega4809) boards. You can thank me by buying my books!

STEP 1 Conventional USART Stream Driver. Works like the one Arduino supplies but implements the C language stdio interface — think scanf and printf. This is implemented directly at the lowest level like in my earlier books, or as the Arduino library does, for that matter. But in the case of ATmega32U4, since the stream interface is likely used only for the USB interface to a terminal program, the stdio interface is built on top of the existing Serial class.

STEP 2 Now it starts to get interesting. Receiving and transmitting are done by blocks of data, I refer to as packets in the book. Each direction of operation has two functions, one that returns a boolean that the driver is ready or not and the other that starts a transfer. Following the standard design in the book, these functions do not block if the interface is idle. When the transfer completes, an optional callback function is called, or the application can poll for completion by calling the boolean function.

STEP 3 What happens if the data packet is never received or there are errors? This revised driver adds two timeout counters. The first one causes the receive function to abort (and optionally call the callback function) if no data is received before the timer runs out. The second also causes the function to abort if two much time passes after a character is received. This is needed to handle packets that are short — data is missing. It also makes it possible to receive packets of varying sizes as the callback function simply gets called when the data stops arriving. This step also adds error detection (but not correction) for parity, framing, and buffer overrun.

STEP 4 As a move toward MODBUS, the driver of step 3 is modified for half duplex transmission over RS-485. This move is actually quite simple as long as we have a protocol that prevents more than one device trying to transmit at the same time. There are inexpensive boards that convert between TTL and RS-485 signaling that use the MAX-485 part.

STEP 5 A software layer is added that performs the communication protocol. It could be MODBUS, but I wanted something simpler to implement and present while offering nearly the same functionality. I have written several MODBUS implementations in the past, but keeping it simple cut the size by two thirds! I call this Mock-Modbus because I like the alliteration. Others might want to call it Modbus-Lite. The major difference is there are only two commands, read multiple and write multiple, and only one data type, a byte. And error detection is slightly more lax in that Mock-Modbus uses a checksum byte instead of a 16-bit CRC.

The driver could be enhanced for full MODBUS if desired, or the existing Arduino MODBUS library could be used. But in many cases, especially for the experimenter, Mock-Modbus does give most of the functionality at much less cost in memory use. Either has a single client device and one or more server devices which can be separated by thousands of feet. Think of the servers being remote sensors and actuators with a reliable wired connection.