Electronics Software Development

Controlling An Arduino From A Raspberry Pi

Connect Arduino To Raspberry Pi Graphic
Written by John Woolsey

Skill Level: Intermediate

Table Of Contents

Introduction

This tutorial will teach you how to control an Arduino from a Raspberry Pi over the USB serial port. Specifically, we will be sending commands to the Arduino from the Raspberry Pi and receiving data back from the Arduino. A basic understanding of electronics and programming is expected along with some familiarity with both the Arduino and Raspberry Pi platforms. If you are new to these platforms or would just like to refresh your knowledge, please see our Blink: Making An LED Blink On An Arduino Uno and Blink: Making An LED Blink On A Raspberry Pi tutorials before proceeding with this one. You may also find the Using The Arduino Command Line tutorial useful depending on how you want to compile and upload your Arduino code.

The resources created for this tutorial are available on GitHub for your reference.

What Is Needed

Background Information

Since the Raspberry Pi runs an actual operating system, it is great at performing a wide variety of tasks. The Arduino, on the other hand, runs a specific program, provides true analog inputs, and tends to be better at real time processing and monitoring since it does not have to manage the complexities of running an OS. For this reason, it can be quite beneficial to include an Arduino acting as a submodule within a larger design with a Raspberry Pi acting as the main controller. There are a variety of ways where a Raspberry Pi could control an Arduino, either through a direct hardware connection (e.g. serial ports or custom GPIO links) or wirelessly (e.g. WiFi or Bluetooth).

It is possible for an Arduino Uno and a Raspberry Pi to communicate with each other using the standard USB, UART, I2C, or SPI serial ports (protocols). However, all of them, other than USB, typically require some form of level shifting circuitry since the Arduino Uno boards utilize 5 V GPIO and the Raspberry Pi uses 3.3 V. The USB interface, available on both boards, handles the appropriate level shifting for us making this simple approach our preferred option and the one we will use in this tutorial. Other Arduino boards are available that utilize 3.3 V GPIO and could be used without the extra level shifting circuitry, but may require other technical considerations to implement.

The Arduino board can be programmed in a variety of ways. You can use either the standard Arduino IDE or the Arduino Command Line Interface to compile and upload the code from either your main computer or directly from the Raspberry Pi itself. If using your main computer, you will need to unplug the Arduino from the USB port once the code has been uploaded and then plug it into the Raspberry Pi each time you need to update the code. Regardless of the method you choose, the vast majority of this tutorial should still apply, however, some minor changes may be necessary.

My development system consists of an Arduino Uno WiFi Rev2 along with a Raspberry Pi 3 Model B running the Raspbian operating system. Since I am using my Raspberry Pi in a headless fashion, I chose to use the Arduino Command Line Interface to program my Arduino directly from the Raspberry Pi.

If you need assistance with your particular setup, post a question in the comments section below and I, or someone else, can try to help you.

Writing The Arduino Software

Create a sketch named USB with the code shown below. As mentioned above, whether this is created on your main computer or on your Raspberry Pi depends on the system and tool options you choose.

// USB - USB.ino
//
// Description:
// Executes commands retrieved from serial (USB) port.
//
// Created by John Woolsey on 12/17/2019.
// Copyright (c) 2019 Woolsey Workshop.  All rights reserved.


void setup() {
   Serial.begin(9600);  // start serial communication at 9600 baud
}


void loop() {
   // Read and execute commands from serial port
   if (Serial.available()) {  // check for incoming serial data
      String command = Serial.readString();  // read command from serial port
      if (command == "led_on") {  // turn on LED
         digitalWrite(LED_BUILTIN, HIGH);
      } else if (command == "led_off") {  // turn off LED
         digitalWrite(LED_BUILTIN, LOW);
      } else if (command == "read_a0") {  // read and send A0 analog value
         Serial.println(analogRead(A0));
      }
   }
}

The code should be fairly straightforward. It just enables the serial port, reads commands (strings) from the serial port, and then executes those commands. In the case of the last command, it also sends data back over the serial port.

Compile and upload the program to your Arduino and then attach it, via USB, to your Raspberry Pi if it is not attached already.

If you are uploading directly from the Raspberry Pi, you may find that you do not have permissions to access the USB port. In that case, you will see an error like the following when you try to upload the sketch to the Arduino.

avrdude: usbdev_open(): cannot open device: Permission denied
avrdude: jtag3_open_common(): Did not find any device matching VID 0x03eb and PID list: 0x2145

Typically this just means you need to add yourself (username) to the dialout group on the Raspberry Pi. This can be achieved by running the following command and then log out and back in again for the change to take effect. Make sure to change the <user> placeholder to your username.

$ sudo usermod -aG dialout <user>

If you try to upload the sketch again and still have a permission issue, you may have to resort to the following. This is not typically required, but may be necessary for your particular system or setup.

$ sudo su
$ echo 'SUBSYSTEM=="usb", MODE="0660", GROUP="dialout"' > /etc/udev/rules.d/00-usb-permissions.rules
$ udevadm control --reload-rules

You will need to reboot the Raspberry Pi after performing the above operation.

You should now be able to access the USB port and upload your sketch. You may see the following warning, but you can ignore it.

avrdude: usbdev_open(): WARNING: failed to set configuration 1: Device or resource busy

Writing The Raspberry Pi Software

Create a Python program named usb.py on the Raspberry Pi with the code shown below.

#!/usr/bin/env python3
"""Control an Arduino over the USB port."""

# usb.py
# Created by John Woolsey on 12/17/2019.
# Copyright (c) 2019 Woolsey Workshop.  All rights reserved.


# USB_PORT = "/dev/ttyUSB0"  # Arduino Uno R3 Compatible
USB_PORT = "/dev/ttyACM0"  # Arduino Uno WiFi Rev2


# Imports
import serial


# Functions
def print_commands():
   """Prints available commands."""
   print("Available commands:")
   print("  a - Retrieve Arduino value")
   print("  l - Turn on Arduino LED")
   print("  k - Turn off Arduino LED")
   print("  x - Exit program")


# Main

# Connect to USB serial port at 9600 baud
try:
   usb = serial.Serial(USB_PORT, 9600, timeout=2)
except:
   print("ERROR - Could not open USB serial port.  Please check your port name and permissions.")
   print("Exiting program.")
   exit()

# Send commands to Arduino
print("Enter a command from the keyboard to send to the Arduino.")
print_commands()
while True:
   command = input("Enter command: ")
   if command == "a":  # read Arduino A0 pin value
      usb.write(b'read_a0')  # send command to Arduino
      line = usb.readline()  # read input from Arduino
      line = line.decode()  # convert type from bytes to string
      line = line.strip()  # strip extra whitespace characters
      if line.isdigit():  # check if line contains only digits
         value = int(line)  # convert type from string to int
      else:
         print("Unknown value '" + line + "', setting to 0.")
         value = 0
      print("Arduino A0 value:", value)
   elif command == "l":  # turn on Arduino LED
      usb.write(b'led_on')  # send command to Arduino
      print("Arduino LED turned on.")
   elif command == "k":  # turn off Arduino LED
      usb.write(b'led_off')  # send command to Arduino
      print("Arduino LED turned off.")
   elif command == "x":  # exit program
      print("Exiting program.")
      exit()
   else:  # unknown command
      print("Unknown command '" + command + "'.")
      print_commands()

Let’s take a look at some of the more interesting parts of the code.

Lines 9-10 assign the USB_PORT variable to the name of the USB serial port associated with your Arduino. This can be easily determined by running

$ ls /dev/tty*

on the command line before and after plugging the Arduino into the USB port on your Raspberry Pi and seeing which one shows up. Change the value to the name used by your system.

Lines 18-24 define the print_commands() function that just prints the available commands on the screen. These commands, just single letters, will be typed on the keyboard and then executed when the ENTER key is pressed.

Line 31 initializes serial communication. Feel free to change the baud rate (speed) of the serial port, 9600 in this case, to something faster, like 115200, but make sure it matches the rate defined in the Arduino sketch or the boards will not be able to communicate with each other.

The endless loop (while True:) starting on line 40 continually reads and executes the keyboard commands typed on the keyboard. The b' ' construct transforms the encompassed string (command) to bytes for transmitting over the serial port.

Even though I chose to use short, single letter, commands to be typed on the keyboard for quick and easy operation, I decided to use more descriptive names for the actual textual commands being sent over the USB serial port. This was done in order to be completely clear about the expected behavior across both programs. This also provides the extra benefit of being able to add many more, and perhaps more complex, commands in the future and still be able to keep everything straight between the two platforms.

After sending the read_a0 command to the Arduino, lines 44-52 wait for a response back, transforms the response to something understandable by the Python program, and then shows the result on the screen. While I technically did not have to convert the result to an integer, I chose to do so since in a real world application, the value will most likely need to be used in a follow-on operation.

If there is something that needs further explanation, please let me know in the comment section and I will try to answer your question.

Running And Testing The System

Now that our software is written for both platforms, it is time to run and test our project. Make sure the sketch is uploaded and that the Arduino is plugged into the Raspberry Pi USB port. The Arduino code will begin running automatically once it is plugged in.

Within a Raspberry Pi’s terminal window, change the permissions of the usb.py program to be an executable by running the following command within the same directory as the program.

$ chmod 755 usb.py

This enables us to run the python code directly as a script like the following.

$ ./usb.py

Alternatively, you could just run the code as an argument to the python3 executable.

$ python3 usb.py

Now run the program.

If you get an error stating that the program could not find the serial module, it can be installed with the following command.

$ sudo pip3 install pyserial

If you get the following error,

ERROR - Could not open USB serial port.  Please check your port name and permissions.
Exiting program.

verify and update the name of the USB port within your Python program that has your Arduino attached. If the port name looks correct, you may not have permission to access the serial port. In that case, please see the bottom part of the Writing The Arduino Software section for how to grant access.

Once your program is running, you should find yourself at the Enter command: prompt. Press the letter l (lower case L) on your keyboard followed by the ENTER key and the built-in LED on the Arduino should light. Likewise, enter the k command and the LED should turn off.

Enter the a command and you should get an analog value back from the Arduino. Note, since we did not actually connect anything to the A0 analog input of the Arduino, you should see what appears to be random values between 0 and 1023 being returned. Feel free to add a potentiometer or some other sensor and check the values.

When you are done using the program, enter the x command to exit.

Summary

In this tutorial, we learned how to communicate with and control an Arduino Uno directly from a Raspberry Pi over the USB port. We sent commands from the Raspberry Pi and even received data back from the Arduino.

Even though we only used a keyboard interface for user input and did not connect any extra hardware, e.g. buttons, sensors, etc., in this simple setup, hopefully you can see how valuable this general approach is to extending functionality to your larger designs.

The final source code and schematic used for this tutorial are available on GitHub.

Thank you for joining me in this journey and I hope you enjoyed the experience. Please feel free to share your thoughts in the comments section below.

About the author

John Woolsey

John is an electrical engineer who loves science, math, and technology and teaching it to others even more.
 
He knew he wanted to work with electronics from an early age, building his first robot when he was in 8th grade. His first computer was a Timex/Sinclair 2068 followed by the Tandy 1000 TL (aka really old stuff).
 
He put himself through college (The University of Texas at Austin) by working at Motorola where he worked for many years afterward in the Semiconductor Products Sector in Research and Development.
 
John started developing mobile app software in 2010 for himself and for other companies. He has also taught programming to kids for summer school and enjoyed years of judging kids science projects at the Austin Energy Regional Science Festival.
 
Electronics, software, and teaching all culminate in his new venture to learn, make, and teach others via the Woolsey Workshop website.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.