Getting Arduinos to communicate with each other

I have seen in the past numerous questions along the lines of "How can I get two Arduinos to talk to each other".

So, I decided I would package up some code I often use in a little library. This allows multiple Arduino boards to communicate with each other using the built in Serial device. I have tested it between two Arduinos directly connected, but I have coded it to handle multiple Arduinos on a shared RS-485 bus (untested as yet - I don't have an RS-485 bus to test it with). In theory 255 Arduinos could communicate with each other using this code, but of course that depends on your RS-485 driving hardware. It could probably also work with some serially connected RF modules, though again I haven't tested it.

Communication takes the form of small packets including a command code (which is linked to a function by the user at the receiving end), data segment of up to 255 bytes (limited in software to 50 bytes to save memory - can be easily increased if you want), and a simple checksum to ensure proper transmission.

It is rugged enough that you can dump debugging information over the serial connection and it won't interfere with the serial communications between the two Arduinos - you just get to see the serial packets in the Serial Console as well as your debugging information.

It only handles the primary hardware serial device at the moment - I could do with enhancing it to use the other serial ports available on the Mega boards, but that can come later (I don't have a mega board at the moment to play with it on). If someone else wants to enhance it to do that then please go right ahead.

The library is still in its infancy, and I have yet to write examples for it (but they will come, don't worry), but feel free to take it, play with it, break it, and let me know what should be changed / improved.

Oh, you'll want the URL... It's on Sourceforge: Arduino Inter-Chip Serial Communication download | SourceForge.net

Hi,
I played with it just a bit and I like the idea. Obviously it would be more exciting to test it on a proper serial bus, which I don't have.
I'll see if I can break it by implementing a sort of request/response protocol - sending back a command within a callback function. I'm not really expecting to break it, but I believe that at this stage you will be more interested in bugs or limitations than in positive results.

Since I have a Uno and a Leonardo I added this to the library code.

#ifdef __AVR_ATmega32U4__
#define Serial Serial1
#endif

It doesn't look much safe, both because Serial is not guaranteed to be unique and because I don't know whether all ATmega32U4 devices have the same TX/RX assignment. This arrangement however allows using the #defines already used by the preprocessor and is transparent to the client code. There will certainly be a better way to do that.

OK, I think I have a nice way of doing multiple serial ports. You can now specify the serial port during the ICSC.begin() call:

ICSC.begin(1, 9600, &Serial1);

It should work with any HardwareSerial device.

Oh, and if you want to communicate on 2 busses at once, you can - just create yourself a new _ICSC object:

_ICSC ICSC1;
...
ICSC1.begin(1, 115200, &Serial2);

If you don't specify a serial port it will default to Serial.

ICSC.begin(1, 9600, &Serial1);

It should work with any HardwareSerial device.

Both HardwareSerial and SoftwareSerial derive from the same base, Stream. If this method is designed to take a Stream object, it would work with SoftwareSerial, too.

PaulS:

ICSC.begin(1, 9600, &Serial1);

It should work with any HardwareSerial device.

Both HardwareSerial and SoftwareSerial derive from the same base, Stream. If this method is designed to take a Stream object, it would work with SoftwareSerial, too.

You have pre-empted my next question - how can I get it to go with SoftwareSerial? :slight_smile:

At the moment the object pointer is stored as HardwareSerial *_serial; - will it be enough to just use Stream *_serial, or will it complain?

In answer to my own question: error: 'class Stream' has no member named 'begin'

So, no.

I can have two overloaded functions for HardwarSerial and SoftwareSerial, but how do I then get that stored in the same pointer (*_serial) and using it as a serial object?

but how do I then get that stored in the same pointer (*_serial) and using it as a serial object?

One way is to have each constructor store the pointer is a different field, and set a flag (bool hard;) to true or false.

Then, each time you need to print, check the flag and use the appropriate instance.

It's unfortunate that there is not a Serial class between Stream and HardwareSerial/SoftwareSerial.

PaulS:

but how do I then get that stored in the same pointer (*_serial) and using it as a serial object?

One way is to have each constructor store the pointer is a different field, and set a flag (bool hard;) to true or false.

Then, each time you need to print, check the flag and use the appropriate instance.

It's unfortunate that there is not a Serial class between Stream and HardwareSerial/SoftwareSerial.

Do-able... Kind of messy, and adds extra overheads, but doable.

A generic Serial object that covered both hardware and software transparently would be the ultimate perfect object of course, but we have to work with what we have, and I'm not about to try combining the two into one generic Serial object...

Hmmm... Small problem... You need to include SoftwareSerial.h in your main sketch whether you are using SoftwareSerial or not...

It would be nice if I could include the SoftwareSerial handling code only if SoftwareSerial were included in the main sketch, but no, that's not on.

Without it included in the sketch I get:

In file included from RemoteLEDSender.cpp:2:
/home/matt/arduino-1.0.1/libraries/ICSC/ICSC.h:66: error: ISO C++ forbids declaration of 'SoftwareSerial' with no type
/home/matt/arduino-1.0.1/libraries/ICSC/ICSC.h:66: error: expected ';' before '*' token
/home/matt/arduino-1.0.1/libraries/ICSC/ICSC.h:76: error: 'SoftwareSerial' has not been declared

Even though it's included in the ICSC.h file.

I'm thinking a SoftwareSerial specific class might be better, so you have the choice of whether or not to include the SoftwareSerial support or not.

I have now written a Linux C library to go with this so you can use it to communicate between your PC, Pi, etc (running Linux) and the Arduino. And yes, I have had one Arduino communicating with another at the end of a serial link, whilst at the same time communicating with the PC through the USB connection - through the same physical Serial device.

... nice :wink:

New version released. R13. This adds support for a DE/RE# signal for RS-485 driver chips. I have had Arduinos communicating on a 2-node half-duplex RS-485 line at 230400 baud.

Hi,
I am using your ICSC library and it for the most part works wonderful. I have run into a few glitches however. First if use the number 1 for a sender station address and a receiver tries to reply to that address it seems to get mistaken for a SOH and the state engine gets confused and will not recognize the message. That problem is easily solved by using a character 1 ie. ‘1’ instead of and integer 1. The last problem I am having is that when I tried to port the code from an Arduino MEGA 2560 to an Arduino Due using the Adruino IDE 1.5.2 I got the following compile error:

ICSC\ICSC.cpp.o: In function _ICSC::begin(unsigned char, unsigned long, HardwareSerial*, int)': C:\Users\DickEue\Documents\arduino-1.5.2\libraries\ICSC/ICSC.cpp:58: warning: undefined reference to HardwareSerial::begin(unsigned long)'

If I try running the program it hangs in ICSC.begin('1', 57600, &Serial1, 3);

I tired to track this down but my C++ skills are almost 20 years old and I’m a bit rusty. The interesting thing is that when I change the line of code in ICSC.cpp from:
_serial->begin(baud);
to
Serial1.begin(baud);

It compiles and runs fine.

The other modification I had to make was to add the following lines to void _ICSC::waitForTransmitToComplete() routine:

#if ARDUINO >= 150
delayMicroseconds((20000000UL/_baud)+1);
#elif ARDUINO >= 104

Because the EOT didn’t get sent.

Any help figuring out the problem with Serial1 would be greatly appreciated.
Thanks for writing a very useful library.

reue:
Hi,
I am using your ICSC library and it for the most part works wonderful. I have run into a few glitches however. First if use the number 1 for a sender station address and a receiver tries to reply to that address it seems to get mistaken for a SOH and the state engine gets confused and will not recognize the message. That problem is easily solved by using a character 1 ie. ‘1’ instead of and integer 1.

This is a known problem, yes. The simple answer is "don't use 1" :wink: The complex answer is the protocol needs changing, but I am loth to do that at this stage in the game. A proper start "pattern" would be better than just SOH - Maybe replacing the SOH with a few SYN followed by a single SOH, so the header is identified by SYN,SYN,SOH,x,x,x,x,SOT. Either that or make proper use of the DLE character, so any character from the envelope character set that's transmitted as part of the header or data content is prefixed by DLE. Makes processing somewhat harder, but the whole protocol becomes more rugged I guess.

The last problem I am having is that when I tried to port the code from an Arduino MEGA 2560 to an Arduino Due using the Adruino IDE 1.5.2 I got the following compile error:

ICSC\ICSC.cpp.o: In function _ICSC::begin(unsigned char, unsigned long, HardwareSerial*, int)': C:\Users\DickEue\Documents\arduino-1.5.2\libraries\ICSC/ICSC.cpp:58: warning: undefined reference to HardwareSerial::begin(unsigned long)'

If I try running the program it hangs in ICSC.begin('1', 57600, &Serial1, 3);

I tired to track this down but my C++ skills are almost 20 years old and I’m a bit rusty. The interesting thing is that when I change the line of code in ICSC.cpp from:
_serial->begin(baud);
to
Serial1.begin(baud);

It compiles and runs fine.

Can't help on the Due, I'm afraid - never used one, and haven't seen 1.5.x yet myself. I should look at it at some point...

The other modification I had to make was to add the following lines to void _ICSC::waitForTransmitToComplete() routine:

#if ARDUINO >= 150
delayMicroseconds((20000000UL/_baud)+1);
#elif ARDUINO >= 104

Because the EOT didn’t get sent.

Any help figuring out the problem with Serial1 would be greatly appreciated.
Thanks for writing a very useful library.

That is, as the code says, a bit of a fudgy hack. Ideally the flush() routine should wait for the last character to leave the hardware and be sent down the wire, but different IDEs and core versions, and even different chips, do things differently. The "belt and braces" way is to delay long enough for all buffered characters to be sent, and that depends on how many levels of FIFO the chip has.

has anyone tested this with long distances and multiple nodes? say > 200m and > 5 nodes?