Sending simple serial commands to an Arduino is the easiest way to communicate between an Arduino and a computer. The computer could be a PC, a Raspberry Pi, or any device that communicates with serial.
By sending and “decoding” a single character it is easy to add a simple debug menu or even serial menu. Plus, it is easy to extend.
Single Character vs. Full Words
The mistake I see many people make is that they try to send full-text strings as serial commands. For example, to turn on a LED, I have seen (silly) commands like “RED LED ON” or “RED LED OFF.” While you could use something like strcmp(), as I showed on the Multiple MQTT Topics example, that tends to be overkill for most serial commands.
Humans like words, computers like binary. Just send one character over serial.
switch (variable) {
case 'a':
// A Stuff
case 'b':
case 'c':
// B and C Stuff
break;
}
Serial is asynchronous
It is easy to get caught in the trap of thinking that typing a full word in the Serial Monitor and then clicking Send, means the entire word is sent immediately. However, it is not. Only one character gets sent at a time. Depending on the line ending settings, a few extra characters might be sent automatically.
Asynchronous sending means that to capture multiple character commands requires you to buffer incoming characters. While not difficult, it does tend to start getting complicated.
Using switch statement
In C, the switch statement executes code based on the value of a variable. The structure is much cleaner than endless if-else statements and even allows a limited amount of “stacking.”
switch (variable) {
case 'K':
// Do something
break;
case 'm':
case 'M':
// Do something with either character
break;
default:
// When all else fails
break;
}
The conditions for the switch-statement are wrapped in curly braces. Each “case” is like saying “if (variable==case).” Inside of the switch block, any code matching the switch case executes and continues to run until the end of the block or a “break” statement.
Stacking Cases
It is also possible to “stack” case. In the example above both, the characters ‘m’ and ‘M’ will execute the same code. You could also repeat cases if you want multiple actions to happen based on a character.
You must include a “break” at the end of the “case.” The code will not check any more conditions until the next time it sees a “break!”
switch (variable) {
case 'a':
// A Stuff
case 'b':
case 'c':
// B and C Stuff
break;
}
In this example, if “variable” contains the character ‘a,’ then the code for “A Stuff” will execute, but so will “B and C Stuff!”
Sending Simple Serial Commands to Arduino
Using a switch-block, sending simple commands is easy. In my code, I always create a function called “handleSerial()” and then in the loop() I call handleSerial() on each iteration.
This bit is a style choice, but I like to put my handleSerial() at the bottom of my code. It makes adding new commands easy since I can just scroll to the bottom of the screen.
void loop() {
handleSerial();
// Everything else loop() does…
}
void handleSerial() {
while (Serial.available() > 0) {
char incomingCharacter = serial.Read();
switch (incomingCharacter) {
case '+':
pwmValue = pwmValue + 5;
If (pwmValue >= pwmMax)
pwmValue = pwmMax;
break;
case '-':
pwmValue = pwmValue - 5;
If (pwmValue <= 0)
pwmValue = 0;
break;
}
}
}
First, handleSerial() checks for any received characters. If there is nothing in the receive buffer, then the while-loop exits immediately. Why a while-loop()? Style choice, explained later.
Next, we capture a character from the buffer. You do not need to put the character into a variable; you could put the serial.Read() inside of the switch() statement. However, if you want to do any debugging with the received character, it is easier to have a variable setup ahead of time.
Now the switch-block will execute any code that matches the received character. If none of the code matches, then the character is dropped and the while-loop continues. One critical advantage here is that if you have “line endings” turned on in the serial monitor, this code ignores them!
Why a while()-loop?
For me, it is a style choice. I’d rather handle all of the serial characters at once. However, in a program where I have “time sensitive code”, I’ll only process 1 character each time handleSerial() is called. Just change the while-loop into an if-statement.
Couple of notes
Make sure you call handleSerial() often to check for commands. For example if you have a very long loop, call handleSerial() every so often inside that loop.
One simple trick
One simple serial command trick you won’t believe
Here is a neat little trick when modifying values. In this Larson Scanner Tutorial, I am modifying a variable called pwmValue. So I tend to send multiple characters in a row.
In the serial monitor, I will often put multiple characters in before hitting send.
While they will not all send immediately, it just saves me some time.
Conclusion
Limiting serial commands to a single character could mean limiting your commands. However, the tradeoff is the code to handle these commands is very simple. Check out my tutorial on
Measuring PWM Current with a modified moving average. I used handleSerial() in that one.
26 Comments
There is a typo, line 8. It’s serial.read it should be Serial.read
I made this same comment three years ago. I hope he fixes it this time!
Strangely it seems hard to find a library to simplify making menus on the Serial Monitor Arduino console. That’s how I found this blog.
I’ve found some libraries that make it complicated :).
It’s definitely something one should not waste time coding as it’s not the value add in a project.
I decided to write one which eliminates the need to handle the case statement, or the asynchronicity, by using callback functions. I just released it.
https://github.com/sonyhome/SerialMenu
Getting a “stray ‘\342’ in program” error when I copy and paste your code into Arduino
There was an issue with the quote marks. I’ve updated all of the code, so it should be fixed now.
An interesting project that I will use as a base fpr my RPio 3 to Arduino project.
Basically I want to use the Uno as an intelligent IO. Reading/writing the GPIO ,setting PWM and analogue to digital converters,
The – as yet unrefined – protocol may look like this
SIO 1 0 Set IO pin to Input
RIO 1 read Port
WIO 1 x Write Port
RAN 1 read analogue 1
RPA 1 Read a 10 entry fload parameter table (Used to refine A2D converters)
WPA1 Write a 10 Entry Parameter Table to The Uno
SPW 1 x Set PWM to x
etc
Has any one has done something simular? This does not appear to be difficult but I have no experience of RPI 3 and Arduino serial.
I wonder if SPI/I2C would work.
USB even?
Have a good day!
3D
Serial is the easiest. It is no different than computing between a PC and Arduino. SPI or I2C would be next in terms of difficulty. I2C may be a better protocol for this application. “USB” doesn’t make sense unless you mean Serial to USB, like how the Arduino works today. If you’re talking about creating your own profile that certainly isn’t worth it.
25.9.2017
Robert Zacharias suggested I use firmdata – which I had never heard of – but looks exactly what I want
I then found this mini tutorial : https://raspberrypi-aa.github.io/session3/firmata.html
Thank you all for your tips.
3D
USB is a serial connection pro definition. When one is talking rpi arduino , serial USB is meant.
Not necessarily. Since the Pi GPIO header breaks out a UART serial port, one could be connecting an Arduino’s UART (or SoftSerial) directly to the Pi’s UART. Completely bypassing the USB-to-Serial option.
It sounds a bit like you’re looking to write the RPi equivalent of Firmata. I’d recommend looking at that protocol before you blaze a trail ten feet away from one that already exists. (In fact, since Processing/Java are available for Linux, what you want is probably already achieved on your platform of choice.)
Hi James,
Google brought me to your site again:) I am enjoying reading your posts and colorful webpage pictures.
Do you use cmdMessenger LIbrary (https://playground.arduino.cc/Code/CmdMessenger) for handling serial communcation between Arduino and PC?
I am using a solution similar to the link below:
https://adnbr.co.uk/articles/parsing-simple-usart-commands
Hi Buli, glad to hear from you again!
I have not used this library before. I don’t know too much about Mono / .NET. However, based on the project page, this library does look good.
Hi
I have been trying to use this page to help me connect hyperterminal on a PC to arduino.
Send a character to the Arduino and have it do something.
So far I have been unable to get any talking happening.
I am using a usb to serial adaptor and then a db9 232 to ttl adaptor after that and connecting tx and rx to arduino.
Gnd is connected and I hav tried with and without 5v connected on the 232-TTL adaptor.
Ive swapped Tx Rx round in case Im making a dummy mistake
Have set serial settings same at both ends. Do I have to do anything special to begin comms?
Is what Im trying to do possible.
Thanks.
If you have an actual arduino, why are you connecting anything to RX/TX? No external adapter is needed. If you want to use something other than pins 0 and 1, you would need to use SoftwareSerial.
I am just doing it for testing. I want to have a third party controller (which I havnt got yet) to control the Arduino via rs232. So just wanting to have a play around first and get the concept going. Pins 0 and 1 is fine
The only other thought I have is to disconnect the Arduino’s USB cable. That way the onboard usb to serial controller won’t be trying to drive the RX/TX pins of the 328p.
Additionally, connect your adapters TX to it’s RX for a “loopback” test. Make sure you can send characters to itself.
This is, of course, assuming you’ve already tested your code using the Serial Monitor to send/receive data.
Thanks for your reply. Tried those things. Loopback OK but just cant get anything when I try to connect to UNO.
Seems to be very little on it when I search internet
Update. I got this going. It was a series of issues that tripped me up.
2 main things: on my(Chinese) converter you cant use a null modem cable on the PC-converter side.
Also the Tx Rx pins on the TTL side going to the Arduino, needed to be connected Rx-Rx and Tx-Tx. Seemed odd, and stumped me for a while. Maybe to do with whether the system considered the units a DTE or DCE device?
Also note it works fine on pins 0 and 1 too, but get some garbage when you restart UNO or try upload a sketch to it. So best to stick to software serial on other pins. Hope this helps someone.
I use “serialevent”, in that case I don’t need to worry to lose data.
https://www.arduino.cc/en/Tutorial/SerialEvent
SerialEvent is called with each iteration of loop(). In fact if you look at the C-Code that the IDE compiles, it looks something like this:
[arduino firstline=””]
while(1) {
serialEvent();
loop();
}
[/arduino]
(Not the exact code, it is actually a for-loop.)
Checking the serial buffer at least once from loop(), or from a function called on each iteration of loop(), like in my example is no different from using SerialEvent. In my code, handleSerial() is called each iteration of loop, just like serialEvent().
That being the case, there is almost no reason to ever use “SerialEvent”.
Nice post and I like the idea of using switch case for serial handling—think I’ll give it a shot.
There are a few small capitalization typos in the handleSerial codeblock: line 7 Serial.read replaces serial.read; lines 11 and 17 the ifs have to be lowercased or it won’t compile.
spot on. a variant would be when you don’t control both ends and you need to look for a string (e.g “RED LED ON”) in the incoming stream. in this case you may want to have a FIFO stack of the appropriate length into which goes the serial data and on every received character, you check the string starting at the top of the stack against the list of commands/strings you want to capture. the length of the stack should be at least as much as your longest command + 1 (in case there’s a null terminator).
Exactly why I like using single character. It is limiting for sure, but reduce a lot of overhead.
I think I would prefer an line input programme that buffers each character until a CR/LF is detected…One can use the programme then to read single character and longer lines. I like using a longer word to describe an action like
LR11 for red led 1 on (LR10 for off) This makes the documentation later much easier. Perhaps send a special sequence to start single char processing (X) to stop/start the buffering so one has the best of both worlds.
I want to use the Arduino with a Raspberry Pi to access sensor data from the Arduino. Perhaps the serial communication would be the best method.
I only use CR/LF (or any delimiter) detection when single characters won’t work. (e.g. sending the value of a LED.) Then my preference is to use fixed lengths with a delimiter. If you get the delimiter before the “buffer” is filled, you know a character was dropped.