Serial Commands
Serial CommandsIntroduction
Serial communication through USB can be used to communicate between a host computer (like a laptop, or Raspberry Pi), and an Arduino. Doing so allows:
- Use of the faster hardware of the host computer for calculations
- Synchronization of multiple Arduinos
- Use of other information provided by the host computer, like internet connections or larger data files
- Creation of a graphical interface using the host's display or web server
Communication
Communication is easy. Arduino and Python have simple methods for establishing serial communication.
Arduino
void setup() { Serial.begin(9600); //Initialize serial communication at 9600 baud } void loop() { Serial.println('hello'); }
Python
from serial import * ser = Serial( "/dev/ttyACM0" , 9600 ) while 1: print ser.readline()
Incomplete Messages
One of the most difficult issues with this sort of serial communication is ensuring that commands are received in entirety. If you send "1000" from Python, and want Arduino to read it as shown below, Arduino might read "10" and then in another loop() right afterward read "00".
char incoming[11] = " "; int i = 0; while (Serial.available() > 0) { // read the incoming byte: char[i] = Serial.read(); i++; } //Convert the incoming string into an integer! int newNumber = atoi(incoming);
Fixed Length Packets
One way to solve this in an efficient manner is to use a fixed length for commands and wait until there are enough bytes waiting in the buffer. The downside is that no commands may be issued with a length longer than the fixed length, and requires remaining length be wasted.
Python
ser.write('%10d' % 1000 ) #pad the number so it is 10 characters long
Now just add a condition so that no reading is done unless a full command is waiting in the serial buffer.
Arduino
if ( Serial.available() >= 10 ) { char incoming[11] = " "; int i = 0; while (Serial.available() > 0) { // read the incoming byte: char[i] = Serial.read(); i++; } //Convert the incoming string into an integer! newNumber = atoi(incoming); }
Delineation
Another way to solve this problem is to delineate commands by placing a special character at the end of each command to signify that the command is complete. The problem with this way is that the length of the incoming string is unknown, and a long or improperly formed command could overflow a fixed length array. To overcome this limitation, a dynamic class is usually used. The String object is a good solution as it can be easily converted back into a character array. Maintaining data types with C++ strings can quickly become confusing, and it is extremely important that the delineation character is never used in the command. There are some special command characters in ASCII that would be good choices.
Multiple Command Types
You can send different commands over the same communication line by attaching a prefix to the command. For example, speed and position could be sent as "p000001000" and "s000000200" to tell the Arduino to go to position 1000 at a speed of 200.
Python
ser.write('p%9d' % 1000 ) #send position, padded to 9 (plus 1 command char) ser.write('s%9d' % 200 ) #send speed, padded to 9 (plus 1 command char)
Arduino
int position, speed; if ( Serial.available() >= 10 ) { //read the first character as the command char command = Serial.read(); char incoming[10] = " "; int i = 0; // read the incoming byte: char[i] = Serial.read(); i++; } //Convert the incoming string into an integer, based on command if ( command == 'p' ) { position = atoi(incoming); } else if ( command == 's' ) { speed = atoi(incoming); } else { //The command was not one of the recognized characters Serial.println("COMMAND ERROR - Not recognized"); } }
Now that your communication is established, it is up to you to figure out what to do with the data on either end.