Introduction: Arduino Serial Communication

About: Electrical Engineer

Many Arduino projects rely on transmitting data between several Arduinos.

Whether you're a hobbyist that is building an RC car, an RC airplane, or designing a weather station with a remote display, you will need to know how to reliably transfer serial data from one Arduino to another. Unfortunately, it is difficult for hobbyists to get serial data communication working in their own projects.This is because serial data is sent as a stream of bytes.

Without any sort of context within the stream of bytes, it is nearly impossible to interpret the data. Without being able to interpret the data, your Arduinos will not be able to reliably communicate. The key is to add this context data to the byte stream by using a standard serial packet design.

Serial packet design, packet stuffing, and packet parsing is complex and difficult to achieve. Luckily for Arduino users, there are libraries available that can do all of this complex logic behind the scenes so you can focus on getting your project to work without extra headace. This Instructable will use the library SerialTransfer.h for serial packet processing.

In short: this Instructable will go over how you can implement robust serial data easily in any project using the library SerialTransfer.h. If you want to learn more about the low-level theory on robust serial communication, see this tutorial.

Supplies

  • 2 Arduinos
    • It is highly encouraged that you use Arduinos that have multiple hardware UARTs (i.e. Arduino Mega)
  • Hookup wire
  • Install SerialTransfer.h
    • Available via the Arduino IDE's Libraries Manager

Step 1: Physical Connections

When using serial communication, a few wiring points need to be kept in mind:

  • Make sure all grounds are connected!
  • Arduino TX (Transmit) pin needs to be connected to the other Arduino's RX (Receive) pin

Step 2: How to Use the Library

SerialTransfer.h allows you to easily send large amounts of data using a custom packet protocol. Below is a description of all of the library's features - many of which we will use later in this tutorial:

SerialTransfer.txBuff

This is a byte array where all payload data to be sent over serial is buffered before transmission. You can stuff this buffer with bytes of data to send to another Arduino.

SerialTransfer.rxBuff

This is a byte array where all payload data received from the other Arduino is buffered.

SerialTransfer.bytesRead

The number of payload bytes received by the other Arduino and stored in SerialTransfer.rxBuff

SerialTransfer.begin(Stream &_port)

Initializes an instance of the library's class. You can pass any "Serial" class object as a parameter - even "SoftwareSerial" class objects!

SerialTransfer.sendData(const uint16_t &messageLen)

This makes your Arduino send "messageLen" number of bytes in the transmit buffer to the other Arduino. For example, if "messageLen" is 4, the first 4 bytes of SerialTransfer.txBuff will be sent via serial to the other Arduino.

SerialTransfer.available()

This makes your Arduino parse any received serial data from the other Arduino. If this function returns the boolean "true", it means a new packet has been successfully parsed and the newly received packet's data is stored/available in SerialTransfer.rxBuff.

SerialTransfer.txObj(const T &val, const uint16_t &len, const uint16_t &index=0)

Stuffs "len" number of bytes of an arbitrary object (byte, int, float, double, struct, etc...) into the transmit buffer starting at the index as specified by the argument "index".

SerialTransfer.rxObj(const T &val, const uint16_t &len, const uint16_t &index=0)

Reads "len" number of bytes from the receive buffer (rxBuff) starting at the index as specified by the argument "index" into an arbitrary object (byte, int, float, double, struct, etc...).

NOTE:

The easiest way to transmit data is to first define a struct that contains all data you want to send. The Arduino on the receiving end should have an identical struct defined.

Step 3: Transmit Basic Data

The following sketch transmits both the ADC value of analogRead(0) and value of analogRead(0) converted to voltage to Arduino #2.

Upload the following sketch to Arduino #1:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

struct STRUCT {
  uint16_t adcVal;
  float voltage;
} data;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}

void loop()
{
  data.adcVal  = analogRead(0);
  data.voltage = (data.adcVal * 5.0) / 1023.0;
  
  myTransfer.txObj(data, sizeof(data));
  myTransfer.sendData(sizeof(data));
  delay(100);
}

Step 4: Receive Basic Data

The following code prints the ADC and voltage values received from Arduino #1.

Upload the following code to Arduino #2:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

struct STRUCT {
  uint16_t adcVal;
  float voltage;
} data;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}

void loop()
{
  if(myTransfer.available())
  {
    myTransfer.rxObj(data, sizeof(data));
    Serial.print(data.adcVal);
    Serial.print(' ');
    Serial.println(data.voltage);
    Serial.println();
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");

    if(myTransfer.status == -1)
      Serial.println(F("CRC_ERROR"));
    else if(myTransfer.status == -2)
      Serial.println(F("PAYLOAD_ERROR"));
    else if(myTransfer.status == -3)
      Serial.println(F("STOP_BYTE_ERROR"));
  }
}

Step 5: Testing

Once both sketches have been uploaded to their respective Arduinos, you can use the Serial Monitor on Arduino #2 to verify you are receiving data from Arduino #1!