Sending Multiple Sensor Readings over Serial

I am starting a new project with a remotely operated submarine toy, where I will collect potentiometer and button readings with an Arduino on the surface, and send it to an Arduino in the submarine via a cable that will control the motors, lights, etc.

Because of the long distance through the cable I know I will not be able to use I2C, so I figured Serial is the way to communicate the readings from the surface to the submarine. I am absolutely unfamiliar with Serial communication even after hours of research, so I was wondering if someone would be able to help me set up a basic Serial communication code to send, for example, potentiometer reading "x", potentiometer reading "y", and button press "z". Once I have a basic code like this, I will be able to adapt it to my exact project as it develops with time.

I know how to take the sensor readings and place them in a byte on the top side Arduino, and I know how to take a byte value on the submarine side and process them to motor movements, etc, I am just missing that middle communication link. Any help would be greatly appreciated!

gwinkels:
so I was wondering if someone would be able to help me set up a basic Serial communication code to send, for example, potentiometer reading "x", potentiometer reading "y", and button press "z". Once I have a basic code like this, I will be able to adapt it to my exact project as it develops with time.

Sure they would, in fact the kind folks at Arduino already did, such serial examples are in the example sketches folder that is included with the IDE.

Transmission is easy, Serial.write() will send a byte. Receiving serial is a little bit more tricky, but there are tutorials and guides linked to in the sticky threads at the top of the forum. I suggest you spend some time there, because there may be more that you can learn, in addition to the answer to this question.

here is a very good tutorial how to receive serial data Serial Input Basics - updated - Introductory Tutorials - Arduino Forum

on the sender side, you have to "design" your protocol.

as a start I propose a
Tag=Value

for example if sensor B measures 123

B=123

on the receiver side you can split

B

123

Have a look at the examples in Serial Input Basics - simple reliable non-blocking ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.

The technique in the 3rd example will be the most reliable. It is what I use for Arduino to Arduino and Arduino to PC communication.

You can send data in a compatible format with code like this (or the equivalent in any other programming language)

Serial.print('<'); // start marker
Serial.print(value1);
Serial.print(','); // comma separator
Serial.print(value2);
Serial.println('>'); // end marker

If this was my project I would also experiment with I2C - it works with 5v signals just like Serial and it would make communication even simpler. Have a look at this Arduino to Arduino I2C Tutorial

This MegaPoints YouTube Video shows I2C working over 100m of cable. The MegaPoints boards use Atmega microprocessors.

...R

noiasca:
you have to "design" your protocol.

Instead of "designing your own protocol", you can use SerialTransfer.h to automatically packetize and parse your data for inter-Arduino communication without the headace. The library is installable through the Arduino IDE and includes many examples.

Here are the library's features:

This library:

  • can be downloaded via the Arduino IDE's Libraries Manager (search "SerialTransfer.h")
  • works with "software-serial" libraries
  • is non blocking
  • uses packet delimiters
  • uses consistent overhead byte stuffing
  • uses CRC-8 (Polynomial 0x9B with lookup table)
  • allows the use of dynamically sized packets (packets can have payload lengths anywhere from 1 to 254 bytes)
  • can transfer bytes, ints, floats, structs, and even large files like JPEGs!!

Example TX Arduino Sketch:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

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

void loop()
{
  char buff[] = "hi";

  myTransfer.txObj(buff, sizeof(buff));
  myTransfer.sendData(sizeof(buff));
  delay(100);
}

Example RX Arduino Sketch:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

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

void loop()
{
  if(myTransfer.available())
  {
    char buff[40];
    
    myTransfer.rxObj(buff, sizeof(buff));
    
    Serial.println("New Data: ");
    Serial.write(buff, sizeof(buff));
    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"));
  }
}

For theory behind robust serial communication, check out the tutorials Serial Input Basics and Serial Input Advanced.

Thanks Powerbroker for this example, i saw your good work since March with SerialTransfer library
Many original Serial comm examples was replaced with UART rx and UART tx DATA.
i was trying to make a simple sketch with two arduinos to ask a sensor reading with a string command over serial and the second arduino send back another string with readings. I though that was fullduplex example?

Its is possible ?

Power_Broker:
Instead of "designing your own protocol", you can use SerialTransfer.h to automatically packetize and parse your data for inter-Arduino communication without the headace. The library is installable through the Arduino IDE and includes many examples.

Here are the library's features:

Example TX Arduino Sketch:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

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

void loop()
{
  char buff[] = "hi";

myTransfer.txObj(buff, sizeof(buff));
  myTransfer.sendData(sizeof(buff));
  delay(100);
}





Example RX Arduino Sketch:


#include "SerialTransfer.h"

SerialTransfer myTransfer;

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

void loop()
{
  if(myTransfer.available())
  {
    char buff[40];
   
    myTransfer.rxObj(buff, sizeof(buff));
   
    Serial.println("New Data: ");
    Serial.write(buff, sizeof(buff));
    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"));
  }
}





For theory behind robust serial communication, check out the tutorials [Serial Input Basics](https://forum.arduino.cc/index.php?topic=396450.0) and [Serial Input Advanced](https://forum.arduino.cc/index.php?topic=662346.0).

TuricaT:
Thanks Powerbroker for this example, i saw your good work since March with SerialTransfer library
Many original Serial comm examples was replaced with UART rx and UART tx DATA.

Thanks! Yeah, part of the latest (large) update included refactoring the existing examples and adding more on top of those.

TuricaT:
i was trying to make a simple sketch with two arduinos to ask a sensor reading with a string command over serial and the second arduino send back another string with readings.

The functionality of requesting and sending sensor readings is possible and super easy with SerialTransfer.h. There are two things to keep in mind, however:

  • It's better to send raw data (usually in the form of structs) instead of sending strings - it makes parsing easier for you and more efficient for the library
  • You can't transfer "S"trings, but you can send char arrays instead

TuricaT:
I though that was fullduplex example?

If you can understand the uart_tx_data.ino example and the uart_rx_data.ino, you can take the concepts of both those sketches and mesh them into your project for full-duplex communication relatively easily.

Thank you vey much for yuor reply!, i will try this and ll tell you ... regards!

Hi PowerB!
i meshed two examples into one and made some code to track how many tries is need for requesting a sensor readings. Also added a unix time variable and kept the other original variables from the example for testing.
Added a new struct to ask and register the Msg ID for every request and a char variable to code the requesting instance.
The example connect to arduino Mega and set to 9600bps with a simple dupont cable. (i need to understand more about this because i want to get a lower baud to increase the distance between two arduinos (up to 30m over serial) and keep it reliable.

The example works well and between 10000 Msg attempt transmitted every 500ms occurs 141 retries.

I need to know if this is a correct use of your library, maybe there are others simple way to achieve it.

Thank you very much!

Rx Arduino request side:

#include "SerialTransfer.h"


SerialTransfer myTransfer;

struct STRUCT {
  char z;
  float y;
  float temp;
  float hum;
  unsigned long unixclock;
  unsigned long MsgID;
} testStruct;

struct STRUCT2 {

  char code;
  unsigned long MsgID;
  
} AskStruct;

char arr[6];

unsigned long countError;
int retry;
int retryacum;
unsigned long timer;
bool retryflag=false;
void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600);
  myTransfer.begin(Serial1);
  AskStruct.code='?';
  delay(5000);
}


void loop()
{

   if(millis() > (timer+500) && AskStruct.code=='?')
   {
       if(!retryflag)
       {
      AskStruct.MsgID++; // New Msg Id if no retry happend. 
       }
  
  // use this variable to keep track of how many
  // bytes we're stuffing in the transmit buffer
  uint16_t AskSize = 0;

  ///////////////////////////////////////// Stuff buffer with struct
  AskSize = myTransfer.txObj(AskStruct, AskSize);
  
   ///////////////////////////////////////// Send buffer
  myTransfer.sendData(AskSize);
  AskStruct.code='>';
  timer=millis();
   }

   if(millis()>(timer+1000) && AskStruct.code=='>')
   {
    retry++;
    retryflag=true;
    AskStruct.code='?';
    retryacum++;
    timer=millis();
   }

  
  if(myTransfer.available())
  {
    // use this variable to keep track of how many
    // bytes we've processed from the receive buffer
    uint16_t recSize = 0;
    recSize = myTransfer.rxObj(AskStruct, recSize);
    if(AskStruct.code=='<') //back code with Msg.
    {  
    recSize = myTransfer.rxObj(testStruct, recSize);
    Serial.print(" DATA: ");
    Serial.print(testStruct.z);
    Serial.print(testStruct.y);
    Serial.print(" | ");
    Serial.print(testStruct.temp);
    Serial.print(" | ");
    Serial.print(testStruct.hum);
    Serial.print(" | ");
    Serial.print(testStruct.unixclock);
    Serial.print(" |MsgID:");
    Serial.print(testStruct.MsgID);
    Serial.print(" | ");
    recSize = myTransfer.rxObj(arr, recSize);
    Serial.print(arr);
    Serial.print(" | ");
    Serial.print("Retries:");
    Serial.print(retry);
    Serial.print(" | ");
    Serial.print("Retryacum:");
    Serial.print(retryacum);
    Serial.println();
    
    AskStruct.code='?'; //set ? to ask again.
    
    if(retryflag) // if this Msg had many retries, Reset retry counter for this request.
      {retry=0;
       retryflag=false;
      }
   }
  }

}

Tx Arduino reqeuested side:

#include "SerialTransfer.h"


SerialTransfer myTransfer;

#include "DHT.h"
#include "RTClib.h"
#include <Wire.h> 
#define DHTPIN 5
#define DHTTYPE DHT22   // DHT 22  (AM2302)
DHT dht(DHTPIN, DHTTYPE);
RTC_Millis rtc;


struct STRUCT {
  char z;
  float y;
  float temp;
  float hum;
  unsigned long unixclock;
  unsigned long MsgID;
} testStruct;


struct STRUCT2 {

  char code;
  unsigned long MsgID;
  
} AskStruct;

char arr[] = "hello";
unsigned long timeloop;
unsigned long Start;

void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600);
  myTransfer.begin(Serial1);
  rtc.begin(DateTime(F(__DATE__), F(__TIME__)));

  testStruct.z = '

;
  testStruct.y = 4.5;

}

void loop()
{

if(millis() > (timeloop+3000))
{
DateTime now = rtc.now();
  testStruct.hum = dht.readHumidity();
  testStruct.temp = dht.readTemperature();
  testStruct.unixclock = now.unixtime();
  timeloop=millis();
 
}

if(myTransfer.available())
  {
    // use this variable to keep track of how many
    // bytes we've processed from the receive buffer
    uint16_t recSize = 0;

recSize = myTransfer.rxObj(AskStruct, recSize);
   
    Serial.print("Code:");
    Serial.print(AskStruct.code);
    Serial.print(" | ");
    Serial.print("MsgID:");
    Serial.print(AskStruct.MsgID);
    Serial.print(" | ");
    Serial.print("Start:");
    Serial.print(Start);
    Serial.print(" | ");
 
  }

if(AskStruct.code=='?')
  {
  // use this variable to keep track of how many
  // bytes we're stuffing in the transmit buffer
  uint16_t sendSize = 0;
 
  AskStruct.code='<'; //Send code back with response.
 
  sendSize =myTransfer.txObj(AskStruct, sendSize);

testStruct.MsgID=AskStruct.MsgID; //keep track of MsgID
 
  ///////////////////////////////////////// Stuff buffer with struct
  sendSize = myTransfer.txObj(testStruct, sendSize);

///////////////////////////////////////// Stuff buffer with array
  sendSize = myTransfer.txObj(arr, sendSize);

///////////////////////////////////////// Send buffer
  myTransfer.sendData(sendSize);
 
  Serial.println();
 
  }

}