Need advice for communication protocol between arduinos

For a project i'm working on, i need 2 arduino's to talk with each other.

Arduino 1
Will have a couple of sensors connected to it measuring different voltages, currents, and doing some basic math. This will mainly be using I2C to communicate with the arduino.

The code on the arduino will be a program with a lot of timers that calls functions that read the I2C sensors or GPIO pins and store these in different variables.

There will also be a few digital outputs switching a relay once a command for this is received.

Arduino 2
Will be a user interface with a OLED display, buzzer, light sensor, some lights, and a lot of buttons.

This module must display the variables generated by arduino 1, do some math, and send commands to switch the digital outputs off arduino 1

Network requirements
A cable length of 10m is plenty. I want it to communicate over every possible kind of cable/wire. I can either use 2 of 4 wires for this.

I will not be using any shields. I will design a custom PCB. It would be preferred not to use to much components and keep the needed components cheap. (2-3 $ max)

High speed is not required. If al the readout variables form arduino 1 are send 2-4 times per second, that is fast enough for me. On the other hand, when i press a button on arduino 2, i want it to switch a digital output on arduino 1 in 200ms or less.

The idea is to only have 2 arduinos talking to each other, but if there are easy ways of incorporating a 3rd or a 4th arduino that also displays values or add additional sensors that would be a nice bonus.

Suitable protocol
I'm looking for a protocol and library that are easy to work with. I'm not very good with writing code so something simple is preferred.

I just want something that can easily transmit and receive global variables so that the same values are available in both arduino's.

In case of multiple arduino's i would like simple addressing trough digital inputs and jumpers. 2 or 3 bits should be plenty.

All of the above requires duplex communication. If simplex (only arduino 1 sending data to arduino 2) is much simpler this might be a option too. I can also control the relays at arduino 1 with the digital output of arduino 2. The wires are already there.

Hope you guys can recommend some protocols and good library's!

10 metres seems a long distance for I2C - but I have little personal experience of it.

It might even be difficult for Serial communication. I know that works over distances of a few metres but I have never tried 10m.

RS485 should be suitable but that requires extra modules to link the RS485 to the Arduinos.

Have you considered using wireless communication - perhaps with a pair of nRF24L01+ modules?

...R
Simple nRF24L01+ Tutorial

Try with RS232 link.

Please spend some time and consider what will happen if there is an error in the communications. Then some protocols can be suggested.

Paul

10 metres seems a long distance for I2C - but I have little personal experience of it.

Robin is right and wrong....

AFAIK I2C is certainly not designed to work over 10m. I have it working reliably over roughly that distance reading DS1624 temperature sensors. Been like that for years. The trick is to run it slowly; I have it running around 15kHz. I have not tried increasing the speed to see at what frequency it starts to give problems. Maybe it would be OK at 100kHz, maybe not. By all means experiment but keep in mind both that it is not supposed to work and that it can be made to work.

I'm not aware of any libraries that transparently copy data between multiple Arduinos making it available to each one as if it were local data.

Modbus and Can bus are two common protocols for moving bytes between multiple devices, but it's up to you to decide how you want the data to be packaged for transmission and how it gets turned back into usable data at the receiving end. I wouldn't describe this process as simple.

mikb55:
I'm not aware of any libraries that transparently copy data between multiple Arduinos making it available to each one as if it were local data.

SerialTransfer.h and EasyTransfer.h are two examples. They both make transferring data from one Arduino to another super simple

Power_Broker:
SerialTransfer.h and EasyTransfer.h are two examples. They both make transferring data from one Arduino to another super simple

SerialTransfer is point to point only. The OP mentioned a 4 device scenario.

EasyTransfer is unreliable due to the absence of bit stuffing.

Thank you all very much for your replies.

Robin2:
10 metres seems a long distance for I2C - but I have little personal experience of it.

It might even be difficult for Serial communication. I know that works over distances of a few metres but I have never tried 10m.

RS485 should be suitable but that requires extra modules to link the RS485 to the Arduinos.

Have you considered using wireless communication - perhaps with a pair of nRF24L01+ modules?

...R
Simple nRF24L01+ Tutorial

I dont think I2C is the preferred solution. Its mainly a inter-chip or inter-chip interface. There already will be quite a bit communication on I2C for the different chips and the display. I also want it to run over any kind of cable and some noise may be pressend form heavy inverters and switching power supplies.

RS485 will definitely be suitable for the physical layer, but i fear it may be much more complicated with the programming and additional components.

The nRF24L01+ i like for some solutions, but not for this one. I dont think its reliable enough.

GolamMostafa:
Try with RS232 link.

This might be suitable, but only allows for communications between 2 devices if i'm not mistaking. A bit advantage is that i dont need a lot of additional hardware. Maybe only a buffer IC?

However, how do i send variables? I guess i know how to sent a variable tough serial.print, but how do i receive and store this variable on the receiving end? Are there any library's or tutorials for this?

Paul_KD7HB:
Please spend some time and consider what will happen if there is an error in the communications. Then some protocols can be suggested.

Paul

You are very right for asking this. The variables that are sent from arduino 1 to aruino 2 are not that important as these are only to display. If i send all the variables 4 times each second, and one of these transmissions doesnt work, i only loose a 250ms update on the display so thats fine.

The data that is send from arduino 2 to arduino 1 (switching a relay on and off) should be always be received, but delays up to seconds are acceptable. If 1 "package" gets dropt but the next one is received, that is fine with me. I could either work with some form of confirmation, or just sent the "turn on" or "turn off" package multiple time and assume at least one of them will be received correctly.

mikb55:
I'm not aware of any libraries that transparently copy data between multiple Arduinos making it available to each one as if it were local data.

Modbus and Can bus are two common protocols for moving bytes between multiple devices, but it's up to you to decide how you want the data to be packaged for transmission and how it gets turned back into usable data at the receiving end. I wouldn't describe this process as simple.

I'm looking for the most simple solution as i'm not great with programming. They way i usually work is with a lot of global variables.

I call different functions that write a value to a global variable. Next i use another function (like writing to the display) to use that variable. All functions will be on a timer.

In this way of working its a logical solution for me to transfer these global variables from 1 arduino to another. If this is a bad way of working i would appreciate some tutorials on better ways to accomplish this.

What is the usual way to transfer data between 2 arduinos?

Power_Broker:
SerialTransfer.h and EasyTransfer.h are two examples. They both make transferring data from one Arduino to another super simple

Thanks! these are the kind of library's i'm looking for. I think these could easily transfer global variables as described above.

EasyTransfer is using a lot of delays in the example. I dont like this as the receiving end will not be responding to button input. It also says i need to run the receive function more often than the transmit function. The arduino 2 write display function needs some processing time. Approx 60ms if i remember correctly.

SerialTransfer is looking a little more difficult in the programming, but maybe is more suitable?

superkris:
The nRF24L01+ i like for some solutions, but not for this one. I dont think its reliable enough.

[....]

You are very right for asking this. The variables that are sent from arduino 1 to aruino 2 are not that important as these are only to display. If i send all the variables 4 times each second, and one of these transmissions doesnt work, i only loose a 250ms update on the display so thats fine.

How do you reconcile those two statements?

What do you envisage as the cause of the unreliability of the nRF24 ?

...R

Robin2:
How do you reconcile those two statements?

What do you envisage as the cause of the unreliability of the nRF24 ?

...R

I'm not saying its a unreliable part. I using multiple of these in my house trough the MySensors protocol. Although most of then work fine, i occasionally have troubles reaching some sensors. This may be not related to my NRF module, itself.

My application is automotive. A power management system for the leisure battery's to be exact. The funny thing is that in my previous camper there actually was a simple aliexpress power module using the same NRF. This one often had connection problem too.

I really don't want to start any discussion about using NRF modules. I know them, love them, but choose to not use them in this project. I require a cable between the 2 arduinos for other purposes anyway so there is no added value in going wireless.

superkris:
I really don't want to start any discussion about using NRF modules. I know them, love them, but choose to not use them in this project.

That's fine. I was not aware of all that from your earlier posts.

...R

mikb55:
SerialTransfer is point to point only. The OP mentioned a 4 device scenario.

If you use an MCU with multiple UART ports, it's very possible with SerialTransfer...

superkris:
SerialTransfer is looking a little more difficult in the programming, but maybe is more suitable?

SerialTransfer is more configurable, flexible, and robust than EasyTransfer. That does mean, however, it's not quite as simple to use - but it is still much easier than trying to replicate the logic yourself.

If you transfer structs with SerialTrasnfer.h, I think you'll find a very nice medium between code simplicity and ease of use. I have a few examples in the library that demonstrate how to do that. You can post here if you have any questions about it.

"This might be suitable, but only allows for communications between 2 devices if i'm not mistaking."

Well, in a TTL rs232 style setup, a tx can probably communicate with as many rx devices as the tx can pull to a detectable low state. That being said, do you have the arduinos so you can start doing some testing? Time to move from hanger flying to some field testing.

Arduino MEGA could be a choice as it has 4 hardware UART Ports + at least one Software UART Port. Post an example of a typical data item that you (the OP) want to send from source to destination over UART Port. We may show you the codes/procedures of transmission and reception.

Power_Broker:
If you use an MCU with multiple UART ports, it's very possible with SerialTransfer...

SerialTransfer is more configurable, flexible, and robust than EasyTransfer. That does mean, however, it's not quite as simple to use - but it is still much easier than trying to replicate the logic yourself.

If you transfer structs with SerialTrasnfer.h, I think you'll find a very nice medium between code simplicity and ease of use. I have a few examples in the library that demonstrate how to do that. You can post here if you have any questions about it.

I did not notice that you created these libraries. Thank you very much for making these!

As a creator of these libraries i'm guessing you are very much able to judge if they are suitable for my requirements. If you dont mind i have a few questions.

What example should i be looking at? full_duplex_simple.ino? Can you give me some tips on how to send, receive and store sent (multiple) variables?

Are there any timing issues that could become a problem? I cant use any delays. Currently i'm using millis to time and call different functions that measure something I typicaly call these every 250...1000ms. These functions write to global variables. On arduino 1 i want to sent all global variables every 250ms to arduino 2. Meanwhile i want arduino 1 to be ready to pick up messages form arduino 2 to switch a relay. I guess i should call the "receive" function at the end of every loop?

On arduino 2 there will also be a loop running looking for status changes in push buttons. There will be a timed function (every 500ms?) that writes to display. This function will needs some time. Approx 60ms in my current beta version.

Also, do you recommend certain hardware like buffer IC's or RS485 converters?

zoomkat:
"This might be suitable, but only allows for communications between 2 devices if i'm not mistaking."

Well, in a TTL rs232 style setup, a tx can probably communicate with as many rx devices as the tx can pull to a detectable low state. That being said, do you have the arduinos so you can start doing some testing? Time to move from hanger flying to some field testing.

This might be true. I need at least 1 duplex connection. Future applications ai'm thinking of are a additional display without controls. This one will only need to sniff the transmissions between Arduino 1 and 2. The other application is another sensor board. This one will require a duplex system but a 2nd serial connection to arduino 2 is very acceptable for this purpose.

This also eliminates any needs for addressing making both hardware and software design a bit more simple.

I have plenty of arduinos laying around so testing will not be a problem but i need to make some choices on possible hardware and librarys first.

GolamMostafa:
Arduino MEGA could be a choice as it has 4 hardware UART Ports + at least one Software UART Port. Post an example of a typical data item that you (the OP) want to send from source to destination over UART Port. We may show you the codes/procedures of transmission and reception.

The main arduino (Arduino 2) will probably be a Mega. I need the memory for the U82lib and the 265x64 OLED. So i guess i will have multiple serial ports available.

superkris:
I did not notice that you created these libraries. Thank you very much for making these!

In all fairness I only wrote SerialTransfer.h, EasyTransfer.h was written by Bill Porter.

superkris:
What example should i be looking at? full_duplex_simple.ino? Can you give me some tips on how to send, receive and store sent (multiple) variables?

That's a good example to look at for a basic understanding of how to use the library, but you should look at tx_multiple_objects.ino and rx_multiple_objects.ino instead. They show how you can transfer structs (among other things) with the library.

To help you better understand how to use it, I'll explain how you should write your code below:

Background:


The library SerialTransfer.h, like most Arduino libraries, has a main class that is used to access all public features of the library. In order to create an instance of this class, you need to call the following line before setup():

SerialTransfer myTransfer; // <---- You can replace "myTransfer" with a different class name

Note that you can instantiate as many classes as you wish. Normally, you will instantiate a single class per UART (or softwareserial) port you want to use.

In addition to the class, you can access certain global constants that are defined in the library's header file. The most important are the packet error definitions:

const int8_t CRC_ERROR        = -1;
const int8_t PAYLOAD_ERROR    = -2;
const int8_t STOP_BYTE_ERROR  = -3;

If the library can't parse a packet for a specific reason, it will return an error code that will match one of the negative numbers in the constant list above. You can then use these constants to figure out what exactly went wrong when parsing the packet.

Initialization:


Create a struct that you would like to transfer - NOTE: the structs must be identical between Arduinos!

Here is an example code snippet that is called before setup() which creates a struct that will be transferred. You can change the contents to be anything you would like with the exception of "String"s - do not use "Strings".

struct STRUCT {
  char z;
  float y;
} testStruct;

Next, initialize all your serial ports and the library class(es) in setup():

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

When calling myTransfer.begin(), the argument is the serial port you would like to transfer data with. You can pass a hardware or software serial port class as the argument - both will work.

Sending Data:


First, make sure the data within the struct is updated with the values you want. Once the struct is updated, you can call the following whenever you want to transfer the struct's contents from one Arduino to the other:

myTransfer.txObj(testStruct, sizeof(testStruct));

That's literally it.

Receiving Data:


On the receiving Arduino, call .available() as often as possible to poll for new packets. The return value for .available() will be a bool - true if a new packet was successfully parsed and false if no packet was parsed. If no packet was parsed, check the class's status attribute to see if an error occurred - remember from the "Background" section that errors are always negative in this library.

If a new packet was successfully parsed, however, you can simply call .rxObj(obj, sizeof(obj)) to copy the data in the receive buffer directly into the struct/float/int/etc. that you passed as the argument.

Here's an example:

if(myTransfer.available())
{
  myTransfer.rxObj(testStruct, sizeof(testStruct)); // <---- directly update "testStruct" here
}
else if(myTransfer.status < 0)
{
  Serial.print("ERROR: ");
  Serial.println(myTransfer.status);
}

Lastly:


superkris:
Are there any timing issues that could become a problem? I cant use any delays.

The wonderful thing about SerialTransfer.h is that it's non-blocking and the use of delay() is not only unnecessary for the library, it's strongly discouraged.

As long as you call .available() as fast as your program can handle, you shouldn't have any timing issues. The one case where you might have trouble is if you are streaming really large amounts of data and the receiving Arduino isn't fast enough to parse it all - causing input buffer overflow which results in packet parsing errors.

Look at this if you haven't already done so.

Also, each instance of the library's class can handle full duplex communication on a given hardware serial port.

I also wrote a pip-installable Python package that is compatible with the Arduino version of the library in case you're interested...

I don't know these libraries, they might be better suited. But concerning normal serial:
I also think serial should be possible. Make sure to use a low frequency but since you don't need fast data transmission that should be fine.
Concerning errors: Maybe send a checksum with each message. One or two letters of ckecksum should be fine (Like adding all the characters as ints together and then sending result%255 as the checksum).
A board with multiple UARTs is the ESP32 or the STM32. They also give you a lot more performance than most Arduinos while remaining fairly cheap (cheaper than most UNO clones). All that with a lot more flash than most Arduinos.

Example checksum generator:

char checksum(String serialData){
    uint_32 dataSum = 0;
    for (int i = 0; i < serialData.length(); i++){
        dataSum += (uint_32) (serialData.charAt(i));
    }
    char checksumChar = dataSum%255;
    return checksumChar;
}
...
Serial.print("whatever");
Serial.println(ckechsum("whatever"));

Bastian2001:
I don't know these libraries, they might be better suited. But concerning normal serial:
I also think serial should be possible. Make sure to use a low frequency but since you don't need fast data transmission that should be fine.

It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. This can happen after the program has been running perfectly for some time. Just use cstrings - char arrays terminated with '\0' (NULL).

...R