Electronics Software Development

Controlling An Arduino From A Raspberry Pi

Connect Arduino To Raspberry Pi Graphic
Written by John Woolsey

Last Updated: September 18, 2024
Originally Published: February 5, 2020

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
   pinMode(LED_BUILTIN, OUTPUT);  // set built-in LED as an output
}


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.

26 Comments

  • Many thanks for the Pi and Arduino code. Worked perfectly first time, very pleased, now I have more time to get into the meat of the project.

  • Right before the while statement on the raspberry pi section, I put usb.write(b’led_on’) to imitate a python script that just writes to the Arduino. So like every time you run usb.py, it turns on the led.

    However, it is not working as intended. Any thoughts? I got your version working as intended and I am trying to build from it. Any help would be appreciated.

    • It is nice to hear you are playing around and testing things. I believe the problem is related to the USB serial port not yet being available when you are trying to send the led_on command. Try adding a small delay to the code before sending the first command.

      First, add the following line after the import serial line
      from time import sleep
      to import the sleep command.

      Then, add the following line after the Send commands to Arduino comment line
      sleep(1)
      to add a one second delay.

      This worked for me.

  • Hey,
    Right now I have a HDMI Splitter connected to a HDMI to AV which is connected to the rasperry via a video grabber and I got 4 arrays with color values saved, so can I send the values from the arrays to the led strips which are connected to the arduino?

    • It seems doable to me. Depending on the size of your arrays, I suspect you could just send the color values separated by delimiters, like commas, as a single string that you then parse out and process on the Arduino side.

  • Hi John, thank you for the tutorial. I am receiving the USB port / permissions error, with Traceback:


    self.fd = os.open(self.portstr, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK)
    FileNotFoundError: [Errno 2] No such file or directory: ‘/dev/ttyACMO’

    During handling of the above exception, another exception occurred:


    raise SerialException(msg.errno, “could not open port {}: {}”.format(self._port, msg))
    serial.serialutil.SerialException: [Errno 2] could not open port /dev/ttyACMO: [Errno 2] No such file or directory: ‘/dev/ttyACMO’

    However, I have checked the port in the Arduino Tools, and it is correct. I have also tried different ports just incase.

    Also, I have pySerial installed, but cannot CHMOD or run usb.py, as there is ‘no such file or directory’.

    Any help would be very much appreciated. Thanks

    • From what I can tell, it appears you are using the capital letter ‘O’ in ACMO, instead of the number zero ‘0’ in ACM0. Change the port name to use a zero and hopefully that should fix the issue. Make sure to verify the port name actually being used by running ls /dev/tty* before and after plugging in the Arduino.

      As for the CHMOD problem, usb.py is just the name of the Python program you wrote as part of the tutorial. You can change the file name used in the chmod command if you used a different program name.

  • Thanks! Very good tutorial. I especially liked the fact that you took the time to go over all common hiccups and errors one might encounter and explaining the solutions. Well thought through, with your readers in mind. Well done!

  • Hello John, greetings from Argentina. Regarding i2c communication, do you think it is better to use usb? No interference or noise issues of any kind? Do you have any idea whether it affects the type of usb cable? Raspberry 4 doesn’t affect usb 3.0 usage? Thank you so much

    • I’m not sure I understand your question. However, USB communication works well between an Arduino and a Raspberry Pi because it handles the 3.3V and 5V level shifting for you. Noise interference should not be a problem with a good quality USB cable.

  • This doesn’t seem to be working for me. Maybe an update broke it?
    on aduino boot the LED comes on and stays on. the commands do nothing.

    any ideas?

    • I don’t have my equipment with me, but I can try to help.

      Let’s first take a look at the Arduino side. You can test if the Arduino code is functioning correctly by entering the commands into the Serial Monitor. You may need to alter the Line Ending option in the Serial Monitor to get the commands passed in correctly.

  • I’m using this script along with a bluetooth controller input to send information to the arduino. Currently, I can use the buttons on the controller to turn on and off the lights, but I really want to be able to send joystick values over to the arduino so it can run something like a motor. I’m very lost in what the code would look like to do this. Do you what I would need to do?

    • I believe something like the following should work for you.

      Use the usb.write() function in the Python program to send the command and value as a single string, e.g. usb.write(b'jx234') to pass the joystick’s x-axis value of 234.

      Use the startsWith(), substring(), and toInt() String functions in the Arduino sketch to parse the command and retrieve the integer value, e.g.

      if (command.startsWith("jx")) {  // retrieve joystick x-axis value
         int value = command.substring(2).toInt();
         // Do something with the value
      }
    • The programs are fairly minimal already so I don’t believe there is much that can be done to make them quicker. The only things I can recommend are to change the baud rate from 9600 to something quicker (like 38400 or 115200) and shorten the serial command text (i.e. led_on, led_off, and read_a0) that gets sent over the serial port. Whatever changes you do make, however, need to be applied to both programs.

Leave a Comment

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