Using a 2-wire serial connection we can communicate from a Rapsberry Pi (RPi) to an Arduino. For the Rasperry Pi we’ll use the built in UART accessed through a tty, and the Arduino we’ll use the Serial Communication Library.

What this Example Does?

This example will use a RPi and Arduino to send a fixed message from the RPi to the Arduino. The Arduino will then echo what it receives back the RPi. The hardware used was a RPi B+ and the Arduino Due. The code is all in C/C++.

How this Post is Organized

  • Pre-Requisites of the setup. This is mostly preparing your tty in Raspbian and proper file permissions
  • Wiring. The physical wiring between the Raspberry Pi and the Arduino.
  • Terminals (tty) in Unix, some theory, how to test, and get information
  • C Code for the Raspberry Pi
  • Arduino Sketch to echo what is sent from the Raspberry Pi to the Arduino
  • Running and downloading the example

Pre-Requisites

For the pre-requisites in this example a Rasbperry Pi B+ was used with the following version of Raspbian.

pi@raspberrypi:~ $ uname -a
Linux raspberrypi 4.19.93+ #1290 Fri Jan 10 16:34:37 GMT 2020 armv6l GNU/Linux
pi@raspberrypi:~ $

The Arduino is an Arduino Due with Arduino Genuino 1.18.2.

Note: There really isn’t any reason you cannot use any of the Raspberry Pi and Arduino product line, it’s just a matter of mapping the pins, files, functionality and hardware to the product. I’d actually be surprised if it wasn’t easy, or even no real changes to use something else.

Wiring - Do you have a 3.3V to 5V Issue?

Before you even start the RPi I/O operates at +3.3V and most, not all Arduinos operate at +5V. You will need to use one of the various mechanisms to translate +3.3V to +5V. If you don’t you’ll damage the pins on the RPi. For this I used an Arduino Due which has +3.3V I/O Pins, and it’s easy since you can directly connect to the Raspberry Pi.

Wiring two UARTs together requires connecting Tx and Rx pins from the Raspberry Pi to the Rx and Tx pins on the Arduino. The Tx pin on one, goes to the Rx pin on the other. On my RPi B+ these are the TXD and RXD pins which map to Pins 8 and 10, which map to BCM 14 an BCM 15. Refer to this excellent Raspberry Pi Pinout. For the Arduino Due the pins 18(TX) and 19(RX) for Serial1.

Theory Using a UART in Unix

A Universal Asynchronous Receiver/Transmitter (UART) does serial communications. There is also a Universal Synchronous/Asynchronous Receiver/Transmitter (USART). For this example we actually will do synchronous serial communications, although, it could be extended to do asynchronous where the RPi and Arduino could both send and receive asynchronously.

To communicate with a UART in Unix you use a Terminal. This is also referred to as a tty for TeleTYpewriter and goes WAY back. To communicate with a tty we do basic file I/O where the file is a Unix Character Device. This device on the RPi happens to be /dev/ttyAMA0 on the RPi. In order to do file I/O on a terminal we have to configure it properly using the termios sturcture. An example will be shown below on how this termios structure is setup.

There are lot of example to use a tty in Unix online. However, I highly recommend reading all of man termios. It’s a VERY good authoritative reference.

The UART as a Terminal in Raspbian

As mentioned we’ll be configuring /dev/ttyAMA0 as a Terminal. The amount of attributes to configure terminals can be overwhelming because there are so many. These attributes relate to input, output, control, local modes, as well as, special characters. Terminals in Unix handle so many things, from USB, UARTs, VIM, SSH, Shells, Serial Ports, … They need to be very flexible and hence have a lot of attributes.

You can easily see the attributes or a terminal device. First let’s get the permissions of our character tty device correct so we don’t have to be root.

pi@raspberrypi:~ $ ls -l /dev/ttyAMA0
crw--w---- 1 root tty 204, 64 Feb  3 22:12 /dev/ttyAMA0
pi@raspberrypi:~ $y

We don’t want to be root to do everything. let’s add ourself to the tty group and change the permissions of the character device to have group read privileges.

pi@raspberrypi:~ $ sudo usermod -a -G tty pi
pi@raspberrypi:~ $ sudo chmod g+r /dev/ttyAMA0
pi@raspberrypi:~ $ exit

We have to exit and log back in for the group privileges to be applied.

Now let’s check out our terminal settings.

pi@raspberrypi:~ $ stty -F /dev/ttyAMA0 -a
speed 115200 baud; rows 24; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff
-iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt
-echoctl -echoke -flusho -extproc
pi@raspberrypi:~ $

There is a LOT of output here from the stty tool. The attributes at the bottom that start with a - means they are not enabled. For example ICANON - canonical mode - is disabled because we see -icanon. The attribute cs8 is enabled. We can see that the baud rate is 115200. All of this is documented. It can be seen by doing a man termios. To search for attributes using upper case. E.g. ICANON not icanon as displayed by stty.

Terminal Cooked mode versus Raw Mode

There are two modes for a Terminal in Linux. Cooked Mode - also known as Canonical Mode, and Raw mode - also known as non-canonical mode. The difference really is Cooked mode will read a line at a time, and Raw mode will read a character at a time. Here is a great explanation of this. For this example we’ll use Raw Mode so we’re sending a character (byte) at a time from the Rasberry Pi to the Arduino.

C Header to Communicate with the UART

A C Header to communicate with the UART for the Raspberry Pi. It’s simple, we’ll just define one function called uart_open that will return a file descriptor. If it’s less than 1 then it’s an error.

// file: uart.h
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include "error.h"

#define UART0 "/dev/ttyAMA0"

int uart_open();

C Implementation to set up the UART

Here is where the main challenge is. We will have to open our terminal device, set up the terminal in raw mode and return the file descriptor. From here, we can treat the UART as a normal file. This means we can use the read and write functions to simple read and write to our UART.

// file uart.c
#include "uart.h"

static int
tty_open(char *ptyName)
{
  int UART;
  struct termios ttyOrig;

  UART = open(ptyName, O_RDWR | O_NOCTTY);
  if(UART == -1)
  {
    err_output("error opening terminal %s\n", ptyName);
    close(UART);
    return UART;
  }

  if(tcgetattr(UART, &ttyOrig) == -1)
  {
    err_output("unable to get tty attributes for %s\n", ptyName);
    close(UART);
    return -1;
  }

  cfsetispeed(&ttyOrig, B115200);
  cfsetospeed(&ttyOrig, B115200);
  cfmakeraw(&ttyOrig);

  ttyOrig.c_cflag |= CREAD | CLOCAL;
  ttyOrig.c_cc[VMIN] = 1;
  ttyOrig.c_cc[VTIME] = 0; /* non-zero won't timeout on read anyways */
  if(tcsetattr(UART, TCSANOW, &ttyOrig) == -1)
  {
    err_output("error setting terminal attributes");
    close(UART);
    return -1;
  }

  tcflush(UART, TCIFLUSH);
  tcdrain(UART);

  return UART;
}

int
uart_open()
{
  int FILE;
  /* check the uart exits */
  if(access(UART0, F_OK) == -1)
    return -2;
  FILE = tty_open(UART0);
  return FILE;
}

Running the UART Example

To run the example we have our main method. Note, you must pass in an argument for it to read back. This is mainly for debugging purposes.

// file: main.c
#include "../config.h"
#include "uart.h"
#include <stdio.h>

int
main(int argc, char *argv[])
{
  int UART;
  ssize_t bytes, total_bytes, write_bytes;
  char msg[] = "hello world\n";
  char buf[4096];

  UART = uart_open();

  if(UART == -1)
    err_exit("unable to open UART");
  else if(UART == -2)
    _exit(77);

  write_bytes = strlen(msg);
  bytes = write(UART, msg, write_bytes);
  if(bytes == -1)
  {
    close(UART);
    err_exit("unable to write to UART\n");
  }

  /* we'll only readback bytes if an argument is specified */
  if(argc == 1)
  {
    close(UART);
    return EXIT_SUCCESS;
  }

  do
  {
    bytes = read(UART, &buf, sizeof(buf));
    total_bytes += bytes;
    if(bytes == -1)
    {
      close(UART);
      err_exit("unable to read from UART\n");
    }
    for(int i=0;i<bytes;i++)
      printf("%c", buf[i]);
  }
  while(bytes > 0 && total_bytes < write_bytes);

  close(UART);
  return EXIT_SUCCESS;
}

After this code is run it will set-up our terminal with a bunch of new attributes. We can see them with the stty command. This is different than what we see at the start of this post with the default attributes.

pi@raspberrypi:~ $ stty -F /dev/ttyAMA0 -a
speed 115200 baud; rows 0; columns 0; line = 0;
intr = <undef>; quit = <undef>; erase = <undef>; kill = <undef>; eof = <undef>; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = <undef>; stop = <undef>;
susp = <undef>; rprnt = <undef>; werase = <undef>; lnext = <undef>; discard = <undef>; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8
-opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke -flusho -extproc
pi@raspberrypi:~ $

Downloading the Example

Download the rpi-arduino-uart example. Then to run do the following:

$ tar zxf rpi-arduino-uart-1.0.tar.gz
$ cd rpi-arduino-uart-1.0/
$ ./configure
$ make
$ uart 1
hello world
$

Here hello world was sent out the UART of the Raspberry Pi to the Arduino and from the Arduino back to the Raspberry Pi and printed to the terminal.

Arduino Sketch to Echo Back through the UART

The following sketch will echo back what is sent on Serial1 which corresponds to a UART back out to what was received. All the characters that come in through Serial1 will go back out Serial1. This is so when we run the C on our Raspberry Pi we’ll see our message back on the terminal.

// file: uart_echo.ino

int incomingByte;

void setup() {
  // Serial is to debug through the Serial Monitor
  Serial.begin(9600);

  // The UART
  Serial1.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.println("starting");
}

void loop() {
  // if nothing is available get out
  if(Serial1.available() == 0) {
    digitalWrite(LED_BUILTIN, LOW);
    return;
  }
  digitalWrite(LED_BUILTIN, HIGH);

  // read the incoming byte:
  incomingByte = Serial1.read();

  // cast it to a char
  char c = (char) incomingByte;

  // output to the Serial Monitor
  Serial.print(c);

  // Echo it back out the UART
  Serial1.write(c);
}

Where to go from Here?

Depending on what you want to send and receive this example needs to be adapted. Perhaps, it’s just output to the terminal of the Raspberry Pi so you want to just display it to the terminal. You might want to look at Cooked mode and have Arduino send lines ending with a new line. Perhaps, you want to send commands to the Arduino and based on what it reads from the UART it will do something. Another use would be asynchronous where at any given time the Raspberry Pi would be sending or receiving. Here you’d need to look at man select and do Asynchronous I/O in Unix. Doing this when the terminal is opened be sure to open with O_NONBLOCK and likely some other changes need to be done. For example VMIN could be 0 or we could have a deadlock. Which I’ve seen EDEADLK errno come while playing around with it.

It should be noted to use USB instead of the UART it’s very easy. You can simply replace /dev/ttyAMA0 with /dev/ttyACM0 in the C code and Serial1 to SerialUSB in the Arduino code.