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.

Leave a Reply

Your email address will not be published.