Raspberry Pi and Arduino USB Serial Communication

We often come across the need to communicate between Raspberry Pi and Arduino though Serial Communication. Arduino is easier than Raspberry Pi for driving motors or reading sensors.

I came across this scenario while working on my autonomous robot project. The image processing and logic were implemented on a Raspberry Pi and the motor driver, GPIO and sensor components were attached to an Arduino Leonardo. We created a separate process for the serial communication and used FIFOs (Named pipes) for Inter Process Communication, refer to this post to learn more about FIFOs.

Arduino serial communication process

We will look at an implementation of serial communication running as a background process in the Raspberry Pi using the pySerial library. Identify the port that corresponds to the USB port connected to the Arduino, if the port keeps switching to different ports list all of them in the ‘ports’ array.

import serial, os, errno, time

input_p = "p_serial"; #path to the FIFO for accessing communication
output_p = "p_output"; #path to the FIFO for putting output data

ports = ('ACM1', 'ACM0'); #specify multiple ports if it keeps changing
index = 0;

def Connect(): #loop through the ports listed and try connecting
	global index;
	try:
		conn = serial.Serial(port='/dev/tty' + ports[index], baudrate=9600, timeout=0);
		return conn;
	except serial.serialutil.SerialException as err:
		index += 1;
		if(index > (len(ports) - 1))
			exit();
		else
			return Connect();

ser = Connect();

while 1:
	try: #read input from pipe to pass to arduino
		pipe = os.open(path, os.O_RDONLY | os.O_NONBLOCK);
		input = os.read(pipe,100);
	except OSError as err:
		if err.errno == 11: #refer to the post on FIFOs to understand this error exception
			continue;
		else:
			raise err;
		
	if input:
		try:
			ser.write(input);
			time.sleep(0.1);
		except serial.serialutil.SerialException as err: #if the serial communication stopped during operation attempt to reconnect
			ser = Connect();
			ser.write(input);
	os.close(pipe);
	
	read_data = "";
	
	try:
		read_data = ser.read(1);
	except serial.serialutil.SerialException as err: #if data not available ignore error
		pass;
		
	if read_data:
		try:
			pipe = os.open(output, os.O_WRONLY | os.O_NONBLOCK);
			os.write(pipe, read_data);
			os.close(pipe);
		except OSError as err:
			if err.errno == 6:
				pass;
			else:
				raise err;

I am using a non-blocking FIFO to receive inputs to be written to the Arduino and write the data read from the Arduino into another FIFO to be read by the other process. When data write to the port fails I try to reconnect because Arduino reset causes the serial port to change.

Consuming the service

Lets look at the C++ code that accesses the Arduino serial communication process using FIFO.

#include <unistd.h>
#include <sys/syscall.h>
#include <stdio.h>

#define PATH "p_serial"
void serial(char input);

int main ()
{
	while(1){
		char input = getchar();
		serial(input);
	}

	return (0);
}

void serial(char input)
{
	FILE* fp;
	fp = fopen(PATH, "w");
	fputs(input, fp);
	fclose(fp);
}

The C++ code takes an input from the terminal and writes it to the FIFO which is read by the python script and sent to the Arduino.

Run the Python script in one terminal with the Arduino Connected through USB and run the C++ code in the other terminal to test the code.

Optical mouse odometer for robots using Raspberry Pi

One of my university projects involved developing a search and rescue autonomous robot using a Raspberry Pi. The logic deployed for the application required the precise position of the robot. The usual methods for determining the position of the robot is to use a stepper motor (not cheap) or an encoder (didn’t want to interpret the IO signals on a Raspberry Pi). Using an optical mouse odometer would be a cheap solution for the problem as the surface of the game area for the robot is flat.

John Graham’s post about using a optical mouse odometer served as an inspiration. However, he suggests using the signals directly from the sensors, again interpreting the signals on a Raspberry Pi and it requires a mouse with a particular optical sensor.

Mouse data

The file /dev/input/mice provides data in a PS/2 format for the USB mouse. The python code demonstrates the methods of accessing the position of the mouse. The file access is “blocking” and returns a relative position value whenever there is a mouse event.

import struct, os

file = open( "/dev/input/mice", "rb" );

point_x = 0;
point_y = 0;

class Point:
	x = 0.0
	y = 0.0

def getMouseEvent():
  	buf = file.read(3); 
  	x,y = struct.unpack( "bb", buf[1:] );
  	dis = Point();
  	dis.x = x;
  	dis.y = y;
  	return dis;

while( 1 ):
  	dis = getMouseEvent();
	point_x = point_x + dis.x;
	point_y = point_y + dis.y;
	print ("%d  %d" % (point_x, point_y));
file.close();

This method will work for any standard USB Mouse connected to a Raspberry Pi.

Running as a service

Accessing the mouse /dev/input/mice from multiple locations could result in a conflict in the file access and missing of a mouse event. So I created a background process that acquires the relative position data, converts into an absolute value with a scaling and puts it onto one or more FIFOs (Named pipes) for access by other applications.

The python code writes the calculated position onto a FIFO. Refer to this post for better understanding of FIFOs.

import struct, math, os, errno

file = open( "/dev/input/mice", "rb" );
output = "mouse_FIFO";

point_x = 0;
point_y = 0;
scaling = 0.046875; #determine the scaling based on trial and calibration

class Point:
	x = 0.0
	y = 0.0

def getMouseEvent():
  	buf = file.read(3);
  	x,y = struct.unpack( "bb", buf[1:] );
  	dis = Point();
  	dis.x = x;
  	dis.y = y;
  	return dis;

while( 1 ):
  	dis = getMouseEvent();
	point_x = point_x + (scaling * dis.x);
	point_y = point_y + (scaling * dis.y);
	
	try:
		pipe = os.open(output, os.O_WRONLY | os.O_NONBLOCK);
		os.write(pipe, "%d %d" % (point_x,point_y));
		os.close(pipe);
	except OSError as err:
		if err.errno == 6:
			pass;
		else:
			raise err;
file.close();

The following C++ function demonstrates accessing the FIFO for determining the absolute position of the robot. However note the data can be accessed from any other programming language.

Point robot_position(){
    FILE* fp;
    char readbuf[10];

    fp = fopen("mouse_FIFO", "r");
    fgets(readbuf, 10, fp);
    fclose(fp);

    char *pch;

    pch = strtok(readbuf," ");
    int x = atoi(pch);
    pch = strtok (NULL, " ");
    int y = atoi(pch);
    
    return Point(x, y);
}

Hope this examples provided you with a cheap solution for determining the position of a robot. Mind that the cheapness comes with its restrictions, an optical mouse requires a proper surface to achieve proper movement data.

Using Blocking and Non-Blocking Linux FIFOs in Python

FIFOs (Named pipes) is one of the Inter Process Communication (IPC) functionalities built into the Linux platform for one-way communication between processes. The FIFO appears as a file which can be accessed by normal file IO libraries for exchanging data.

FIFO allows running micro-services that communicate with each other rather than a single monolithic code, helping in maintaining the code. Running FIFO allows processes written in different languages to access each other.

Creating Pipes

mkfifo “pipe” command in bash creates a FIFO named “pipe”

mkfifo pipe

Lets test the FIFO we created, opening two terminals and entering the following commands

Terminal 1:

echo "Hello World!" > pipe

Terminal 2:

cat pipe

The blocking nature of the FIFO can be observed, the ‘data write’ to the pipe from Terminal 1 doesn’t complete until the data is read from Terminal 2. Now we are done with the bash commands lets see the implementation of FIFOs in Python.

Blocking FIFOs

A read call to a FIFO is blocked until a data is available in the pipe and a write call is blocked until the data is read from another process. The following python code demonstrates a blocking FIFO

#fifo.py
import os

bufferSize = 100;
PATH = "pipe";

pipe = os.open(PATH, os.O_RDONLY);
input = os.read(pipe,bufferSize);
print(input);
os.close(pipe);

Lets run this code in the background and write some data into the “pipe” FIFO file.

 sudo python fifo.py&;
echo "Hello World!" > pipe 

Note: Adding an ampersand (&) to the end of the command runs it in the background but still prints to the console.

The os.open() function blocks the process when data is unavailable rather than the os.read() function. Observe this by interrupting the code before writing data into the FIFO.

The blocking nature of FIFOs is useful only when the code is executed after a data is received. Scenarios where there are other functions that need to run continuously independent of the data received  requires the use of non-blocking FIFO operations.

Non-Blocking FIFOs

The following python code implements a non-blocking FIFO.

#fifo.py
import os, errno, time

bufferSize = 100;
PATH = "pipe";
    while 1:
    try:
        pipe = os.open(PATH, os.O_RDONLY | os.O_NONBLOCK);
        input = os.read(pipe,bufferSize);
    except OSError as err:
        if err.errno == 11:
            continue;
        else:
            raise err;
    if input:
        print(input);

os.close(pipe);

#Other functions
print "Sleep 500 ms"
time.sleep(0.5);

The code when executed is not blocked when there is no data and proceeds to print a string and sleep for 500ms

bash fifos
Bash output for writing and reading FIFO data using non-blocking method

Implementing a non-blocking FIFO in a loop would throw out a “Resource temporarily unavailable” (Error number :11) error and returns an empty string (“”) when there is no data on the FIFO. Implement an exception block for the os_open() and os_read() functions to ignore the particular error.

Refer to this post and this post for an example of using a non-blocking FIFO for inter process communication.

The code is accessible in my Github page.