Arduino Serial Part 3: Getting started with serial communication

This page has been updated. Please see the following newer guides:

Introduction, Using The Serial Monitor, and More
A Look at the Different Serial Libraries
Serial Commands Explained
Serial Data
Getting Started With Using Serial Communication To Send Commands
ASCII Data and Using Markers to Separate Data

 
 
 
 

In the last post I briefly talked about different data formats and how I recommend keeping things as simple as possible. With this is mind for a first project let’s create a simple blinking LED. We will have one Arduino controlling an LED on a second Arduino. Controls to turn the LED on or off will be sent via serial from the first Arduino to the second Arduino. This is as basic as it gets. Arduino Blink by remote control. The LED has only two states so simple control codes can be used and to start I am using 1 of on and 0 for off.

In these examples I am using Arduino Nanos but any kind of Arduino can be used and for this series I am using Arduino to Arduino communication. The techniques are exactly the same for any UART to UART device. For example, in Arduino to Arduino by Bluetooth I use exactly the same serial communication techniques wirelessly over Bluetooth.

 

Arduino Serial Example #1: Remote Control Blink 1

Simply circuit consisting of 2 Arduinos with the following connections:
Pin 0 (RX) on the Arduino #1 goes to pin 1 (TX) on Arduino #2.
Pin 1 (TX) on the Arduino #1 goes to pin 0 (RX) on Arduino #2.
Arduino #1 GND to Arduino #2 GND
Arduino #2 D2 to LED + Resistor (220 or 330 ohm)

Arduino Serial Example #1_LED_OFF_538

Arduino Serial Example #1 Remote Control Blink: Master

The master Arduino transmits the commands:

// Arduino Serial Example #1 Remote Control Blink - Master
 
void setup() 
{
    Serial.begin(9600);  
    // wait for the serial port to connect. Required for Leonardo/Micro native USB port only
    while (!Serial) {  ;  }
}
 
 
void loop() 
{
  Serial.print(1);
  delay(1000);
  Serial.print(0);
  delay(1000);
}

Arduino Serial Example #1 Remote Control Blink: Slave

The slave Arduino receives the commands. If it receives a 1 it turns on the lED and if it receives a 0 it turns off the LED.

// Arduino Serial Example #1 Remote Control Blink - Slave
 
char c  = ' ';
byte LED = 2;
 
void setup() 
{
   pinMode(LED, OUTPUT);
   Serial.begin(9600);
   Serial.println("START");
}
 
 
void loop()
{
   if(Serial.available())
   {
      char c = Serial.read();
      if (c=='0') { digitalWrite(LED, LOW); }
      if (c=='1') { digitalWrite(LED, HIGH); }
      Serial.println(c);
   }
 
}

This is as simple as it gets.
Arduino #1 transmits a “1” waits a second then transmits a “0” then waits another second and starts again. It is worth mentioning that the master Arduino will happily keep transmitting the values whether or not there is anything listening.

Remember when you use Serial.print() numbers are converted to ascii. This means the actual values transmitted are 48 for 0 and 49 for 1. This is important to know for when you create the code for the slave Arduino.

The sketch on Arduino #2 initializes a hardware Serial channel, prints a “START” message to the serial monitor, and then keeps checking for incoming data. The loop() function is where the magic happens.

if(Serial.available())
   {
    char c = Serial.read();
    Serial.println(c);
 
    if (c=='0') { digitalWrite(LED, LOW); }
    if (c=='1') { digitalWrite(LED, HIGH); }
  }

First a check is made to see if there is any data in the Serial buffer

if(Serial.available())

If there is data in the buffer a single character is read and copied to the char variable called “c”. If you are not familiar with char and byte look up Arduino data types.

char c = Serial.read();

At this point we have a value in “c”. We are only interested is “0” and “1” so the next bit checks for these values.

if (c=='0') { digitalWrite(LED, LOW); }
if (c=='1') { digitalWrite(LED, HIGH); }

If c==’0′ the LED is turned off with digitalWrite(LED, LOW); and if c==’1′ the LED is turned on with digitalWrite(LED, HIGH);. Any other value is ignored.
Notice that 0 and 1 are inside single quotes. This is because they are the ascii characters and I am using a char variable. With chars use single quotes ‘0’ and ‘1’ not double quotes such as “0” and “1”. Because I am using a char I could have also checked the actual value rather than the ascii character

if (c==48) { digitalWrite(LED, LOW); }
if (c==49) { digitalWrite(LED, HIGH); }

This works in exactly the same way but is slightly harder to read.

Since we are only interested is “1”s and “0”s everything else is ignored. It is possible for the master device to send any character but the slave only acts on the “1”s and “0”s.

 
If you have already built the circuit you will need to remove the serial connections before uploading the sketches. If you forget to remove the serial connections the IDE will attempt to upload, fail and eventually timeout. Of course, after the upload you need to reconnect. This is one of the problems with using hardware serial and the disconnection / reconnection quickly gets annoying.

Upload the sketches and you should have Remote Blink. Remember it is the master Arduino that is controlling the LED.
ArduinoSerial_001_LED

You can also check the received data by opening the serial monitor on the slave Arduino.
Arduino Serial Example #1_SerialMonitor

 

Arduino Serial Example #2: Remote Control Blink 2

Using this technique it is fairly simply to add a second LED so let’s try it. Connect a second LED (plus resistor) to the Slave Arduino on pin D3.
ArduinoSerialPart3_002_Circuit_01

ArduinoSerialPart3_002_BreadBoard_800

Arduino Serial Example #2 Remote Control Blink: Master

// Arduino Serial Example #2 Remote Control Blink - Master
 
void setup() 
{
    Serial.begin(9600);  
    // wait for the serial port to connect. Required for Leonardo/Micro native USB port only
    while (!Serial) {  ;  }
}
 
 
void loop() 
{
  Serial.print(1);
  Serial.print(3);
  delay(1000);
  Serial.print(0);
  Serial.print(2);
  delay(1000);
}

I have added 2 more commands used to control the second LED:
“3” to turn on the LED, and
“2” to turn it off.

Arduino Serial Example #2 Remote Control Blink: Slave

// Arduino Serial Example #2 Remote Control Blink - Slave
 
char c  = ' ';
byte LED1 = 2;
byte LED2 = 3;
 
void setup() 
{
   pinMode(LED1, OUTPUT);
   pinMode(LED2, OUTPUT);
 
   Serial.begin(9600);
   Serial.println("START");
}
 
void loop()
{
   if(Serial.available())
   {
      char c = Serial.read();
      if (c=='0') { digitalWrite(LED1, LOW); }
      if (c=='1') { digitalWrite(LED1, HIGH); }
      if (c=='2') { digitalWrite(LED2, LOW); }
      if (c=='3') { digitalWrite(LED2, HIGH); }
      Serial.println(c);
   }
 
}

The two new commands are taken care of with

if (c=='2') { digitalWrite(LED2, LOW); }
if (c=='3') { digitalWrite(LED2, HIGH); }

ArduinoSerialPart3_002_LEDS_Flashing

You should be able to see that more LEDS can be added simply by using more commands.

 

Arduino Serial Example #3: Remote Control Blink 3

The above works well and is very reliable but if all you want to do is turn something on and off at regular intervals there is no need to use two Arduinos. So, next we make things a little more complex by adding a couple of switches to control the LEDs.

To the existing circuit, on Arduino #1 add a button switch on pin A0/D14 and A1/D15. If you don’t already know, analogue pins can be used as digital pins (except A5 & A6 which are analogue only). I only reason picked these pins is so the circuit diagram looked neat. No other reason.
ArduinoSerialPart3_003_Circuit

ArduinoSerialPart3_003_BreadBoard_800

I don’t spend much time on explaining the button switch code, if you are interested in learning more about this see the switching things on and off post.

Arduino Serial Example #3 Remote Control Blink: Master

// Arduino Serial Example #3 Remote Control Blink - Master
 
 
byte buttonSwitch1 = 14; 
byte buttonSwitch2 = 15; 
 
boolean buttonState1 = false;
boolean buttonState2 = false;
boolean buttonState3 = false;
 
boolean buttonSwitch1_State_old = false;
boolean buttonSwitch2_State_old = false; 
 
 
void setup() 
{
    Serial.begin(9600);  
    // wait for the serial port to connect. Required for Leonardo native USB port only
    while (!Serial) {  ;  }
    Serial.print("Start");
 
}
 
void loop() 
{
  // simple debounce
  buttonState1 = digitalRead(buttonSwitch1); delay(1);
  buttonState2 = digitalRead(buttonSwitch1); delay(1);
  buttonState3 = digitalRead(buttonSwitch1); delay(1);
 
  if ( (buttonState1 == buttonState2) && (buttonState1 == buttonState3) )
  {
     // has the button switch state changed?
     if (buttonState1 != buttonSwitch1_State_old)
     {
        buttonSwitch1_State_old = buttonState1;
        if (buttonSwitch1_State_old == HIGH) { Serial.print(1);}  else  { Serial.print(0);}
     }
  }
 
 
  buttonState1 = digitalRead(buttonSwitch2); delay(1);
  buttonState2 = digitalRead(buttonSwitch2); delay(1);
  buttonState3 = digitalRead(buttonSwitch2); delay(1);
 
  if ( (buttonState1 == buttonState2) && (buttonState1 == buttonState3) )
  {
     // has the button switch state changed?
     if (buttonState1 != buttonSwitch2_State_old)
     {
        buttonSwitch2_State_old = buttonState1;
        if (buttonSwitch2_State_old == HIGH) { Serial.print(3);} else { Serial.print(2);}
     }
  }
 
}

The state of the button switches are checked and if the state has changed the control code is sent. The main bit is that the codes are only sent when the switch position changes.

Arduino Serial Example #3 Remote Control Blink: Slave

The slave skecth has not changed.

// Arduino Serial Example #3 Remote Control Blink - Slave
 
char c  = ' ';
byte LED1 = 2;
byte LED2 = 3;
 
void setup() 
{
   pinMode(LED1, OUTPUT);
   pinMode(LED2, OUTPUT);
 
   Serial.begin(9600);
   Serial.println("START");
}
 
 
void loop()
{
   if(Serial.available())
   {
      char c = Serial.read();
      if (c=='0') { digitalWrite(LED1, LOW); }
      if (c=='1') { digitalWrite(LED1, HIGH); }
      if (c=='2') { digitalWrite(LED2, LOW); }
      if (c=='3') { digitalWrite(LED2, HIGH); }
      Serial.println(c);
   }
 
}

Now, instead of using a timer to send the commands button switches are used. Close a switch and an LED comes on. Open the switch and the LED turns off.

ArduinoSerialPart3_003_Animation

I find it a little annoying that the switches turn on the wrong LEDs but I will leave you to change it if you so wish.

 

Arduino Serial Example #4: Remote Control Blink Using Software Serial

If you followed along with the examples I suspect you were, at least a little, annoyed that you had to keep removing and reconnecting the wires to the serial pins. When writing this guide I forgot at least a couple of times. Although hardware serial is always the best choice if it is available to you it can be frustrating when developing the code. In the rest of the examples I will start using one of the software serial libraries. If you remember from part 1, the best option is AltSoftSerial followed by NeoSerial with the default SoftwareSerial at the end. In this example I change the example 3 from hardware serial to software serial using the software serial library that comes with the Arduino IDE. Not the best choice but useful is this scenario.

Technically you can use pins 0 and 1 for software serial but that would defeat what I am wanting to do, so, on the master Arduino I am using pins 2 and 3 (2 for TX and 3 for RX) and on the slave Arduino I am using pins 11 and 12 (11 for RX and 12 for TX).

ArduinoSerialPart3_004_Circuit_800

ArduinoSerialPart3_004_BreadBoard

Arduino Serial Example #4 Remote Control Blink Using Software Serial: Master

// Arduino Serial Example #4 Remote Control Blink Using Software Serial - Master
 
 
#include <SoftwareSerial.h> 
SoftwareSerial SoftSerial(2, 3); // RX, TX
 
 
byte buttonSwitch1 = 14; 
byte buttonSwitch2 = 15; 
 
boolean buttonState1 = false;
boolean buttonState2 = false;
boolean buttonState3 = false;
 
boolean buttonSwitch1_State_old = false;
boolean buttonSwitch2_State_old = false; 
 
 
void setup() 
{
    SoftSerial.begin(9600);
}
 
void loop() 
{
  // simple debounce
  buttonState1 = digitalRead(buttonSwitch1); delay(1);
  buttonState2 = digitalRead(buttonSwitch1); delay(1);
  buttonState3 = digitalRead(buttonSwitch1); delay(1);
 
  if ( (buttonState1 == buttonState2) && (buttonState1 == buttonState3) )
  {
     // has the button switch state changed?
     if (buttonState1 != buttonSwitch1_State_old)
     {
        buttonSwitch1_State_old = buttonState1;
        if (buttonSwitch1_State_old == HIGH) { SoftSerial.print(1);}  else  { SoftSerial.print(0);}
     }
  }
 
 
 
  buttonState1 = digitalRead(buttonSwitch2); delay(1);
  buttonState2 = digitalRead(buttonSwitch2); delay(1);
  buttonState3 = digitalRead(buttonSwitch2); delay(1);
 
  if ( (buttonState1 == buttonState2) && (buttonState1 == buttonState3) )
  {
     // has the button switch state changed?
     if (buttonState1 != buttonSwitch2_State_old)
     {
        buttonSwitch2_State_old = buttonState1;
        if (buttonSwitch2_State_old == HIGH) { SoftSerial.print(3);} else { SoftSerial.print(2);}
 
     }
  }
 
 
 
 
}

You should be able to see that I have removed the line that initialised the hardware serial and replaced it with

SoftSerial.begin(9600);

Where did “SoftSerial” come from? It is what I called the instance of the software serial I am using and this is done when you declare the library

#include <SoftwareSerial.h> 
SoftwareSerial SoftSerial(2, 3); // RX, TX

I could have used any name (within reason), for example SWS or sSerial. When declaring the software serial library you need to specify what pins it is to use. Here I am telling it to use pin D2 for receive and D3 for transmit.

Arduino Serial Example #4 Remote Control Blink Using Software Serial: Slave

// Arduino Serial Example #4 Remote Control Blink Using Software Serial - Slave
 
 
#include <SoftwareSerial.h> 
SoftwareSerial SoftSerial(11, 12); // RX, TX
 
 
char c  = ' ';
byte LED1 = 2;
byte LED2 = 3;
 
void setup() 
{
   pinMode(LED1, OUTPUT);
   pinMode(LED2, OUTPUT);
 
   SoftSerial.begin(9600);
}
 
 
void loop()
{
   if(SoftSerial.available())
   {
      char c = SoftSerial.read();
      if (c=='0') { digitalWrite(LED1, LOW); }
      if (c=='1') { digitalWrite(LED1, HIGH); }
      if (c=='2') { digitalWrite(LED2, LOW); }
      if (c=='3') { digitalWrite(LED2, HIGH); }
   }
 
}

In the slave sketch, pins D2 and D3 are already used (bad planning I know) so I have used D11 and D12

#include <SoftwareSerial.h> 
SoftwareSerial SoftSerial(11, 12); // RX, TX

If you give this a try you will find it works exactly the same as example 3 above. The difference is we do not need to remove the serial connections to upload a new sketch.

 

 
This has been a very gentle introduction in to using serial communication on the Arduino. Using single character codes or controls allows you to keep the code simple. A single character can be loaded in to a char or byte variable which is easy to compare or check, a simple ==. There is no need for char arrays or strings. If the character is not something we want it can be ignored and the next character read.

   if(SoftSerial.available())
   {
      char c = SoftSerial.read();
      if (c=='0') { digitalWrite(LED1, LOW); }
      if (c=='1') { digitalWrite(LED1, HIGH); }
      if (c=='2') { digitalWrite(LED2, LOW); }
      if (c=='3') { digitalWrite(LED2, HIGH); }
   }

A key point to highlight is that the serial buffer is checked for data before attempting to read from it. This allows for non-blocking code that continues to work until there is data to process.

 
In the next post we look at using more complex commands.

 
 
 
 

11 thoughts on “Arduino Serial Part 3: Getting started with serial communication”

  1. Excelent wor man, it was very helpful to me like as a beginner in arduino.
    I want to know if you could help me, how to send an analog reading.

    Reply
  2. That looks very straightforward, much appreciated. Would triggering the Adafruit Neopixel or FastLED library on the slave work? The problem with those two is that they temporarily disable interrupts to write data to LED strips or rings, hence the idea to use a nano slave to do the “dirty” work and not mess with code running on the master.

    Reply
  3. The diagram has TX connected to TX (blue) and RX connected to RX (green). That cannot work (the explanation is alright, the diagram is wrong).

    Reply
  4. I dont understand this im sorry.. everything works well when i send commands 0 to 9 from my esp8266 module to my arduino mega2560 using sws. When trying to send more characters like 10 -> the receiving arduino does not react.. the reason i am using softwareserial is that hardwareserial is being used and occupied by Nextion HMI display..

    Sorry, i’m new to this, and tried your other examples as well, but i dont know what im doing wrong..

    I want to send atleast 0-99 commands from my esp8266 to slave arduinos to execute simple on/off tasks..

    You better persons please mock me, but i would be apreciated for help with this..

    Reply
    • In the above examples I am using serial.print() which converts values to their ascii representation. value 1 becomes ‘1’, value 2 becomes ‘2’. This means, when you send the value 10 it gets converted to ’10’, 2 characters. Rather than using 10,11,12.. use a,b,c or A,B,C.

      If you want to send the actual value, use serial.write(). But note, the max value you can send is 255 (1 byte).

      Reply

Leave a Reply to Jarno Cancel reply