Last Updated: September 18, 2024
Originally Published: February 5, 2020
Skill Level: Intermediate
Table Of Contents
- Introduction
- What Is Needed
- Background Information
- Writing The Arduino Software
- Writing The Raspberry Pi Software
- Running And Testing The System
- Summary
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
- Linux, macOS, Or Windows Based Computer With A USB Port (optional)
- Either Arduino IDE or Arduino Command Line Interface
- Arduino Uno (R3 available on Arduino and SparkFun; WiFi R2 on Arduino and SparkFun) With Compatible USB Cable
- Raspberry Pi Running Raspbian Or Similar Linux Based OS (available on Raspberry Pi Foundation and Adafruit)
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.
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.
You are very welcome!
me too !!!!
Thank you!
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
linefrom time import sleep
to import the sleep command.
Then, add the following line after the
Send commands to Arduino
comment linesleep(1)
to add a one second delay.
This worked for me.
thank you
You are very welcome.
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.
this is great thank you.
You are very welcome.
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!
Thank you so much for taking the time to let us know how much you appreciate our work! I love hearing these types of comments and they keep us going.
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()
, andtoInt()
String
functions in the Arduino sketch to parse the command and retrieve the integer value, e.g.That worked all good for me. But i need it to be faster. Can you help me?
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.
In the Arduino setup(), I had to add
pinmode(LED_BUILTIN, OUTPUT)
to get it to work
Thank you for pointing that out. It is such a glaringly obvious mistake, I am surprised it even worked for me. How embarrassing.