Overview of the Serial Communication Protocol
Setting Up the Arduino
Notice that the Arduino code (Listing 1) does not have much logic. The only thing it does is respond to incoming messages. In this case, whatever program is controlling the device makes all the decisions, so the logic lives elsewhere. The Arduino just follows the instructions it receives over the serial port and reports changes to the attached inputs.
Listing 1
Arduino Code
01 #include <Servo.h> 02 03 Servo servo; 04 int oldSwitch = 0; 05 int oldButton = 0; 06 07 void setup() 08 { 09 pinMode(2, INPUT_PULLUP ); 10 pinMode(3, INPUT_PULLUP ); 11 pinMode(13, OUTPUT); 12 Serial.begin ( 9600 ); 13 Serial.setTimeout ( 5000 ); 14 15 servo.attach ( 11 ); 16 } 17 18 void loop() 19 { 20 char incoming=0; 21 int readState=0; 22 23 if ( Serial.available() ) 24 { 25 incoming = Serial.read(); 26 if ( incoming == 'L' ) digitalWrite ( 13 , HIGH ); 27 else if ( incoming == 'l' ) digitalWrite ( 13 , LOW ); 28 else 29 { 30 int i=0; 31 String fullNumber = String ( incoming ); 32 String number; 33 34 number = Serial.readStringUntil ( '.' ); 35 fullNumber.concat ( number ); 36 i = fullNumber.toInt(); 37 38 if ( i >= 0 && i <= 180 ) servo.write ( i ); 39 } 40 } 41 42 readState = digitalRead ( 2 ); 43 if ( readState != oldButton ) 44 { 45 oldButton = readState; 46 Serial.print ( "Button:" ); 47 Serial.println ( readState ); 48 } 49 50 readState = digitalRead ( 3 ); 51 if ( readState != oldSwitch ) 52 { 53 oldSwitch = readState; 54 Serial.print ( "Switch:" ); 55 Serial.println ( readState ); 56 } 57 }
Lines 1-5 bring in the appropriate library and initialize global variables: servo
from the Servo.h
library represents a servo motor, and the integers oldSwitch
and oldButton
store previous states of the button and switch.
The Arduino calls the setup
function (lines 7-16) once when the program is first run. As the name implies, it sets up any Arduino features that will be used and initializes any hardware that needs to be set up before the main loop runs. In this case, it sets up input and output pins and the serial port.
Lines 9-11 tell the Arduino whether the specified pin is used as an input or an output. The first argument is the pin number, and the second argument is the mode. The INPUT_PULLUP
mode not only sets the pin to an input but also enables the Arduino's pull-up resistor.
Buttons and switches are passive components (not electrically active on their own) so the pull-up resistor provides the electrical equivalent of a default value. The pull-up resistor is relatively weak, so when the button or switch is connected to ground, the pin will see that instead, which guarantees a clean 0 or 1 each time the button or switch changes.
The Arduino Uno has one serial port located on pins 0 and 1. You'll notice, though, that nothing is connected to those pins. It also has a USB-to-serial converter as part of its onboard hardware, but everything happens behind the scenes, so you don't have to worry about it. I'll be using serial from the attached computer over the USB port.
The Serial.begin
function tells the Arduino to turn on the serial port (as opposed to being a general-purpose I/O), and 9600
is the baud rate. Serial.timeout
tells the Arduino that any code waiting for input from the serial port should stop waiting after 5,000msec, which prevents the program from stalling if a proper signal isn't received. Eventually, it will time out and move on with the program.
Servo Setup
The servo
variable created in line 3 hasn't been connected to anything yet, so the servo.attach
line tells the Arduino which pin the servo is on (11 in this case).
The special Arduino loop()
function starts in line 18 and does exactly what its name implies: It runs continuously until power is removed from the Arduino.
After initializing two local variables, incoming
and readState
, a checking routine looks for any characters that are available from the serial port. Serial.available
returns the number of characters waiting in the buffer. If it is not zero, the processing inside the if
statement proceeds.
The Serial.read
gets the first character from the serial port and saves it in the incoming
variable. If the incoming character is an uppercase L, a call to digitalWrite
turns the LED on; otherwise, the program checks for a lowercase l, which turns off the LED.
By line 28, if nothing has happened, the Arduino has probably received a number, which is processed in lines 29-39. After initializing the local variable i
to 0, lines 31 and 32 set up strings for the incoming data from the serial port. The fullNumber
variable is given an initial value of the string version of incoming
– the first character received (i.e., the first digit).
The variable number
is initialized to a blank string. Line 34 requests characters from the serial port until it receives a period. The received characters go into number
, which is concatenated to fullNumber
in the next line. Now fullNumber
really does have the full number received from the port. Line 36 uses toInt
to turn the string into an integer and stores it in i
.
Finally, if i
is greater than 0 and less than 180 (the valid range for a servo), servo.write
asks the servo to move to the angle specified in i
.
Buttons and Switches
Variable readState
stores the state of Arduino pin 2, which determines whether the button is pressed or not. Note that a value of 0 is pressed and 1 is not pressed, which might seem backward.
If readState
is different from oldButton
, the button has changed state. In that case, line 45 updates oldButton
, line 46 sends the string Button:
out the serial port, and line 47 sends the new state of the button; Serial.println
then tells the Arduino to add a newline after printing the readState
variable.
Checking the switch works exactly the same as checking the button, except on pin 3 (line 50) and by checking and updating oldSwitch
(lines 51 and 53) instead of oldButton
and printing Switch:
instead of Button:
(line 54).
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you've found an article to be beneficial.
News
-
Fedora 39 Beta is Now Available for Testing
For fans and users of Fedora Linux, the first beta of release 39 is now available, which is a minor upgrade but does include GNOME 45.
-
Fedora Linux 40 to Drop X11 for KDE Plasma
When Fedora 40 arrives in 2024, there will be a few big changes coming, especially for the KDE Plasma option.
-
Real-Time Ubuntu Available in AWS Marketplace
Anyone looking for a Linux distribution for real-time processing could do a whole lot worse than Real-Time Ubuntu.
-
KSMBD Finally Reaches a Stable State
For those who've been looking forward to the first release of KSMBD, after two years it's no longer considered experimental.
-
Nitrux 3.0.0 Has Been Released
The latest version of Nitrux brings plenty of innovation and fresh apps to the table.
-
Linux From Scratch 12.0 Now Available
If you're looking to roll your own Linux distribution, the latest version of Linux From Scratch is now available with plenty of updates.
-
Linux Kernel 6.5 Has Been Released
The newest Linux kernel, version 6.5, now includes initial support for two very exciting features.
-
UbuntuDDE 23.04 Now Available
A new version of the UbuntuDDE remix has finally arrived with all the updates from the Deepin desktop and everything that comes with the Ubuntu 23.04 base.
-
Star Labs Reveals a New Surface-Like Linux Tablet
If you've ever wanted a tablet that rivals the MS Surface, you're in luck as Star Labs has created such a device.
-
SUSE Going Private (Again)
The company behind SUSE Linux Enterprise, Rancher, and NeuVector recently announced that Marcel LUX III SARL (Marcel), its majority shareholder, intends to delist it from the Frankfurt Stock Exchange by way of a merger.