Arduino Serial Programming

Some background history and why it is useful for controlling robots and plotters

The Arduino is great at interfacing directly with various forms of hardware unlike a PC or Mac. Also typically better than a Raspberry Pi. But why is that?

Because when dealing with hardware you want to time things accurately. You don’t want some operating system suddenly taking over control and running another program just while you are busy altering he signal output on some pin. With Arduino only your program is ever running, consuming every clock cycle. You know that next clock cycle, the only instruction executed will be the next one in your program. On a PC, Mac or Raspberry Pi with a multitasking operating system, any program could be executing instructions on the CPU at any given time.

So Arduino is great for interfacing with hardware, but it is also rather weak. You don’t use it for showing big Applications with graphical user interfaces.

This is where serial communications comes in. You can have a program running on your PC or Mac sending data over a serial interface to the Arduino to make it do stuff.

One example would be e.g. getting an Arduino to draw a complicated vector graphics picture with a plotter. That requires a lot of instructions and you don’t want to hardwire them into your Arduino. It is better to have the Arduino listen for drawing instruction from the serial port. Then on the PC you can use a program to select a drawing and send instructions for drawing it over to the Arduino.

Now that we got the rational for serial port communications, let us look at the technical details.

Virtual Serial Ports using USB

One thing that is confusing today is that our computer hardware has evolved from far more primitive types of hardware, but a lot of the old physical parts of that old hardware still exists in modern Macs and PCs. Except the hardware often exists in virtual form.

One example related to TTY and PTY devices on Unix machines. Back in the day before desktop computers we had mainframe computers. Big boxes that several people in a company or university used. We are used to normal computers having one keyboard and one screen connected to them. But these computers essentially had multiple keyboards and screens connected.

More specifically they connected teletype machines, which are essentially type writers. Paper was their screen. They did not have the power to maintain a whole screen for multiple users. Instead a serial RS-232 cable sent keystrokes from the teletyper to the mainframe. On the same cable responses would come back from the mainframe and type characters on the typewriter, which would show on the paper. This is what we call a terminal or console.

On Unix, every connection point to such a terminal was a TTY device (TeleTYpe), represented as a /dev/tty* device in the operating system. Unix was written on such a machine, the PDP-7. You can the teletyper on the right side in the picture below (the device looking like a type writer).

For those puzzled by the ed editor, it is easily understood when you realize that these early mainframe computers could not show a screen continuously which you could move a cursor around and get feedback. The “screen” was just what you saw on paper. Hence you had to look at what was printed and give a command to change, say line two, to something else.

But why are things like /dev/tty* still around when we don't use teletype machines? Because a lot of software was built around this idea. Software running on Unix would be communicating with /dev/tty* assuming there was a teletype machine at the other end.

When we got electronic displays, we started faking these TTY devices instead. On Linux e.g. you can switch between multiple virtual terminals, which to programs running on the operating system just looks like people sitting on different physical teletype machines communicating over RS-232 with the computer.

To make matters even more complex, PTY devices were invented. These are Pseudo TTY devices, with names like /dev/pty*. These are associated mostly with the X-window system. Then you could have multiple programs running, each would could simulate a TTY device. So e.g. a terminal application window would be associated with a PTY which would communicate with a "real" TTY, which is also fake. Same happens when you SSH to another computer. Seen from the applications point of view, it is just communicating with a TTY (faked by a PTY) which translates into a remote communication over the network.

What About USB, RS-232 and TTY?

Okay this was a long winded path to get to talk about USB. I just wanted to get across the idea that it is useful at times for the operating system to trick software running on it into thinking specific hardware is attached to it, which no longer physically exists, such as Teletyper.

Likewise modern computers don’t have RS-232 style connection points anymore. For those too young to remember when they were attached to PCs, they looked like this:

However computers have USB ports. So what the operating system will do, is to create a fake TTY which you can communicate with as if you had an actual serial port. The data is then transmitted over a USB cable. However the software doing the communication has no idea that it is sending data over USB. The Operating system simulates the hardware behavior of a TTY over a RS-232 cable.

One confusing thing is that serial ports are represented by two different devices on Unix, a /dev/tty* and a /dev/cu* device. This is due to a historical distinction between ports connecting directly to a TTY terminal (teletyper) and connections to modems, which may have been used for sending email. The former used TTY and the latter used CU. Apart from the initial setup, they are almost identical, and you can often use them interchangeable.

The key difference is the use of the DTR (Data Terminal Ready) control signal on the RS-232 port. A program talking to DTR would block until DTR signals. A teletyper would send a signal to DTR to indicate it is ready to communicate.

For modems it is a bit different because initially you are giving commands to the modem about who to connect to and how. So in this case DTR is used to signal that a connection has been made. So modems would use the CU device.

The practical consequence of this is that if you are communicating with an Arduino, you can simply cat a /dev/cu* device, and get all the data the Arduino is transmitting. However if you do that with /dev/tty* it will block and you get nothing.

How to Discover Virtual USB Serial Devices

On macOS we can find the serial devices emulated by our USB port like below. However keep in mind, that the serial port to communicate with an Arduino, does not actually get created until you plug the Arduino in. So after you have plugged in the Arduino you can issue these commands:

$ ls /dev/tty.*

$ ls /dev/cu.*

I have not investigate all these ports, but I’ve found out that I am able to communicate with my Arduino using the /dev/cu.wchusbserial10 and /dev/tty.wchusbserial10 devices. I assume the first device is faking serial communication over wireless bluetooth.

Program to Test Communication with Arduino

Loading up the Arduino IDE, we can type in this program.

void setup() {

void loop() {
for (int i = 0; i < 255; ++i) {
Serial.print(" count: ");

In the Tools > Port menu you select the correct serial port. In my case that would be /dev/cu.wchusbserial10. The Mac then use the USB cable with virtual serial communication to actually transmit the program using /dev/cu.wchusbserial10. Once the program is transmitted, the Arduino will start running it.

It will then communicate over the same serial communication, due to calling Serial.begin(9600) on setup. In the main loop we keep sending text again and again. You can see this text with e.g.

$ cat /dev/cu.wchusbserial10
count: 0
count: 1
count: 2
count: 3

Just remember to replace /dev/cu.wchusbserial10 with whatever name youre fake USB serial port gets.

Alternatively you can go to Tools > Serial monitor in your Arduino IDE to watch the output.

Yet another alternative is the Unix screen command, which communicates with a TTY through a PTY.

This specifies device to connect to and the baud rate. Make sure the baud rate is the same as the one you used in your Arduino setup() function.

$ screen /dev/tty.wchusbserial10 9600

IMPORTANT This command is a bit weird. You cannot exit with the usual Ctrl-C or Ctrl-D. Rather you hit Ctrl+A and then Ctrl+\`. If you don't get this right, you will seemingly exit but cannot connect again, because the PTY is being used.

You need to get back to your previous connection. You do that with lsof which lists all open files, and programs keeping them open. You can then find screen and and a ID to use for disconnecting.

$ lsof | grep usbserial
$ screen -x 8722

You will then get the chance of existing using Ctrl+A plus Ctrl+\` again.

If nothing works, I’ll just unplug the USB cable.

Echo Example

Here is another example to demonstrate reading and writing to the serial port.

void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps

void loop() {
// reply only when you receive data:
if (Serial.available() > 0) {
// read the incoming bytes:
String s = Serial.readString();

// echo back what was received
Serial.print("echo: ");

Below is an example interaction in the serial monitor window under the Tools menu. Further details of the serial programming interface can be found here.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Erik Engheim

Geek dad, living in Oslo, Norway with passion for UX, Julia programming, science, teaching, reading and writing.