# Long distance (and I think low frequency) serial communication with Arduino [closed]

I need to implement a long distance (up to 15 kilometers) communication between some electronic devices. The devices is some boards on AVR chips with Arduino library and a Raspberry pi as the mainframe for displaying aquired data. Standarts I know don't support so long distances. For instance, RS-485 can be used on only up to 1200 meters.

I'm aware that the allowed distance rices with lowering the data transfer speed, and I have no claims for transfer time. 1 byte per 1, 10, or 20 seconds will work fine. Therefore it was considered to write a custom functions to communicate at very low speed. I also have a question, may be there is a readily avalailable library for this issue?

I've found the simple software serial implementation for Arduino in the web. It consists of two functions: read and write and the timing constant. I used it with a few fixes, and it worked with custom ATTiny board as transmitter and arduino as receiver. When I've made a custom receiver board with ATMega1284P, the bugs appeared. Some of them were fixed by me. The current state of affairs is odd, some channels work and some don't.

The transmitter code:

void sendCode(int pin, int data)
{
// startbit
digitalWrite(pin, LOW);
delayMicroseconds(ONE_BIT_TIMING);

if (data & mask) { // choose bit
digitalWrite(pin, HIGH); // send 1
}
else {
digitalWrite(pin, LOW); // send 0
}
delayMicroseconds(ONE_BIT_TIMING);
}
//stop bit
digitalWrite(pin, HIGH);
// pause between packages
delayMicroseconds(ONE_BIT_TIMING * 10);
}

void loop() {
sendCode(GND_SIG_PIN, GND_SIG_CODE);
sendCode(BROWN_PIN, BROWN_CODE);
sendCode(BLUE_PIN, BLUE_CODE);
sendCode(BLACK_PIN, BLACK_CODE);
sendCode(WHITE_PIN, WHITE_CODE);
sendCode(GREEN_PIN, GREEN_CODE);
sendCode(RED_PIN, RED_CODE);
sendCode(GND_INT_PIN, GND_INT_CODE);
sendCode(RED_INT_PIN, RED_INT_CODE);
sendCode(BLACK_INT_PIN, BLACK_INT_CODE);
sendCode(WHITE_INT_PIN, WHITE_INT_CODE);
sendCode(GREEN_INT_PIN, GREEN_INT_CODE);
}


It works fine and emits a signal exactly as I want.

The reciever code:

int SWread(int pin)
{
byte val = 0;
//wait for start bit

delayMicroseconds(ONE_BIT_TIMING);
for (int offset = 0; offset < 8; offset++) {
// Get 100 pin states during the one bit period
// and write the most common to the variable
int highCount = 0;
int lowCount = 0;
for (int c = ONE_BIT_TIMING; c > 0;) {
highCount++;
} else {
lowCount++;
}
c = c - (ONE_BIT_TIMING / 100);
delayMicroseconds(ONE_BIT_TIMING / 100);
}
if (highCount > lowCount) {
val |= 1 << offset;
}
}
return val;
}
}

// turn on one led if recieved the expected code and the other if not
void swithLed(int sigPin, int code, int okPin, int failPin) {
if (val == code) {
digitalWrite(okPin, HIGH);
digitalWrite(failPin, LOW);
} else {
digitalWrite(okPin, LOW);
digitalWrite(failPin, HIGH);
}
}

void loop() {
swithLed(GND_SIG_PIN, GND_SIG_CODE, GND_SIG_OK, GND_SIG_FAIL);
swithLed(BROWN_PIN, BROWN_CODE, BROWN_OK, BROWN_FAIL);
swithLed(BLUE_PIN, BLUE_CODE, BLUE_OK, BLUE_FAIL);
swithLed(BLACK_PIN, BLACK_CODE, BLACK_OK, BLACK_FAIL);
swithLed(WHITE_PIN, WHITE_CODE, WHITE_OK, WHITE_FAIL);
swithLed(GREEN_PIN, GREEN_CODE, GREEN_OK, GREEN_FAIL);
swithLed(RED_PIN, RED_CODE, RED_OK, RED_FAIL);
swithLed(GND_INT_PIN, GND_INT_CODE, GND_INT_OK, GND_INT_FAIL);
swithLed(RED_INT_PIN, RED_INT_CODE, RED_INT_OK, RED_INT_FAIL);
swithLed(BLACK_INT_PIN, BLACK_INT_CODE, BLACK_INT_OK, BLACK_INT_FAIL);
swithLed(WHITE_INT_PIN, WHITE_INT_CODE, WHITE_INT_OK, WHITE_INT_FAIL);
swithLed(GREEN_INT_PIN, GREEN_INT_CODE, GREEN_INT_OK, GREEN_INT_FAIL);
}


The last two channels always receive the wrong code, while the other receive the right one and work as expected.

## closed as too broad by Chris Stratton, Elliot Alderson, Dmitry Grigoryev, Chetan Bhargava, BimpelrekkieNov 29 '18 at 13:21

Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

• Just a remark: you're now very much concentrating on the protocol you will use for this. You should also consider the physical side, I mean the hardware / electronics which connect that wire to the uC. I do not expect that connecting a 15 km wire directly to a uC will be without any issues. At the very least you would want some protection against induced voltage spikes. – Bimpelrekkie Nov 21 '18 at 11:21
• Yes, you are right, the device now in a prototype stage and uC connected to each other directly with a short cable. Advices on this part are also welcome. – Sun Tzun Nov 21 '18 at 11:26
• 15 km is a lot, if possible I would use existing telecommunication infrastructure, such as GSM or internet if available. – Rokta Nov 21 '18 at 12:18
• @Minatko "Must work all the time" doesn't exist in engineering. You'll have to get used to the idea of error and outage probabilities, especially in the context of communication. 15km is not something you can bridge without a dedicated transceiver hardware system, and what software you write depends on what hardware you'll be using. So, you're approaching this from the wrong end. You can't start working on a protocol before defining the medium. – Marcus Müller Nov 21 '18 at 13:22
• You need to broaden your scope and consider methods other than baseband communication. Use some sort of modem to create a signal that is much easier to transport long distances reliably. – Dave Tweed Nov 21 '18 at 14:24

As others have pointed out, you are approaching the problem from the wrong side. If your major design constraint is reliability, you have to take many more aspects into account.

Although it is true that slower data speeds would allow for more reliable communications, other requirements for the physical layer might impose additional restrictions that set the lowest desirable baud rate. At a slow data rate, you will get more reliable communications with a somewhat faster highly-redundant and error corrected bit-stream, than with an equivalent low-speed one.

A 15km line will have interactions with the surrounding environment in very predictable ways. If it is laid any way along 60Hz/50Hz power lines, it will act both as the secondary of a transformer (and a plate of a capacitor) in which high common-mode voltages would be induced. If a lighting strikes anything near that line (not to mention the line itself) extremely high-energy, high-voltage, high-current pulses will be present. To deal with this you need isolation and surge protection. These are not a major consideration for short-distance lines, but are still present.

For example. Ethernet, which is designed to work just inside buildings and for much shorter distances, has a specification of 2.5kV of required device isolation. And Ethernet devices being blown-up by nearby lighting strikes are not that rare.

Although isolation is possible to achieve with baseband communications, this will require isolated power supplies and other elements that must lay across the isolation barrier, making this barrier more likely to be breached, and thus less reliable. The simplest and most reliable method to provide isolation is to AC-couple through a toroidal core transformer, this allows for a large gap between primary and secondary windings that can sustain tens of kV. However, AC-coupling removes the possibility of baseband communications. And, to keep the transformer at a reasonable size, higher frequencies become desirable. This also allows you to stay away from the very large 50/60Hz interference that will be present on such long line.

To allow for unterminated, unmatched wiring, you need to limit your maximum carrier frequency to avoid dealing with reflections and excessive attenuation. For a 15km line you probably don't want to go much higher than 2kHz. This puts you within standard low-frequency audio modem territory. But here is another compromise, toroidal cores for those frequencies will be hard to come by or extremely large, so a more common, less-isolated transformer might be necessary unless you get clever with the design.

As phone modems IC might have become a rarity (particularly of the 300baud or 1200baud variety) and if an existing protocol such as the Highway Addressable Remote Transduce Protocol (HART), which was designed for similar constraints, does not work for you. At these frequencies it is rather easy to design your own software modem using ASK or FSK. Take a look at the old computers that used to store data in cassette tapes (the Apple II had very efficient code running at >5kBaud using RS232 symbols over FSK).

You will need some analog circuitry to filter the channel and receive the analog signal, but it's nothing that can't be put together with a few passives an op-amp and a Schmidt trigger.

If bi-directional communications are necessary, additional issues will arise.

If you're stuck on baseband, you could do it similar to how Sam Morse did it back in 1844. He sent the message "WHAT HAS GOD WROUGHT" over some 70+ km using Morse Code. A more modern implementation might be to use a solid state relay as your telegraph "key" driven by your arduino acting as the "hand" operating the key. Commercially available, optically isolated relays and input modules would offer some protection to the sensitive cpu modules on both ends. A Morse Code library for each cpu would keep you from having to learn code.

I sketched out what a simple implementation might look like. It implies bidirectional communication; retransmitting back what was received, while not very efficient, would be a good way to assure accuracy of the message. Transmission wire resistance would need to be sized to assure the drop doesn't exceed the minimum threshold of the input module.

• That's just wrong at so many levels. But now I am wondering how to bridge the distance by controlling a fire pit and a remote smoke detector from an Arduino. – Edgar Brown Nov 21 '18 at 18:40