Skip to content

Arduino Serial Communication

Jeremy edited this page Mar 6, 2019 · 2 revisions

Serial Overview

This page documents the behavior of the serial communication between the Raspberry Pi and the Arduino.

The communication uses the default Arduino Serial library on the Arduino side and PySerial on the Raspberry Pi side. Communication uses UART with a baud rate of 9600.

Packets

Each packet consists of up to 500 characters. It contains:

  • A command of up to 20 characters, corresponding to a function you want to call on the Arduino side.

  • Up to twenty parameters, corresponding to arguments you want to pass to that function.

The command and parameters are separated by colons, and the entire packet is terminated with a semicolon. As such, the strings used as commands and parameters cannot contain these characters.

Here are a few examples of what commands might look like:

SET_LED_BRIGHTNESS:0.5;

SET_LEG_POSITION:3:0.8;

SOFT_ESTOP;

Adding new functionality

There are a few places you need to change in code to add new commands.

  • Make your function: this function will be called by the parser when a new serial command comes in.

  • Update the serial parser: there is a section in the serial handler script on the Arduino side (currently called test_serial.ino) that reads incoming serial communication and calls functions accordingly. It should look something like this:

////////////////////////////////////////////////////////
//               PARSING FUNCTION CALLS               //
////////////////////////////////////////////////////////

if (strncmp(command, "MOVE", 4) == 0) sMove(s2i(params[0]));
else if (strncmp(command, "ROTATE", 6) == 0) sRotate(s2d(params[0]));
else if (strncmp(command, "LED", 3) == 0) sLED(s2i(params[0]));

To have your function be called by the serial handler, you need to add a line to this section. Note that strncmp compares a certain number of characters; as such, you'll need to pass in the length of your command as the second argument. The call after the if statement should be to the function you made previously.

The examples shown above use helper functions s2i and s2d to convert the argument to an integer or double, respectively. These were made specifically for strings ending in colons or semicolons; you will have to be careful when adding similar helper functions for different types.

Finally, the parser only looks as far into params as you specify in your function call. As such, extra parameters will be ignored by the parser, and it will not throw an exception.

Serial listener and ROS

Serial commands are sent to the Arduino from a Raspberry Pi running rospy and connected via USB. There is a PiSerial object that, when instantiated, begins to listen to the serial rostopic and push information to the Arduino.

You can send serial commands by either calling the send_raw(string) or send(command, *params) methods of the PiSerial object, or by publishing a string to serial that meets the specifications described in the previous section.

Potential errors

Based on the way serial communication was implemented, there are a few failure cases that might not be obvious for debugging.

  • The Arduino serial parser ignores extra parameters without throwing an exception.

  • The Arduino serial parser has a maximum packet length, number of parameters, and command length. If these values are exceeded, it may or may not cause a runtime error.

  • Nothing happens on the Arduino side until a semicolon character is received. This means that errors in the Pi side, or lapses/errors in UART, could result in the total omission of a packet.

  • The port of the USB device needs to be specified manually in the PiSerial object. This is likely going to be /dev/ttyUSB0 or /dev/ttyACM0, but it could be different based on the configuration of the Pi. Referring to an unused port should raise an exception in the python code. Trying to access the wrong USB device may not.