Built-in project · Arduino Uno R3

RS-485 link on an Arduino Uno

Two MAX485 modules on one A/B pair carry a numbered ping from the Uno’s TX to pin 8, with pin 4 switching the sender between talk and listen. The whole build, an Arduino Uno and 2 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate About 20 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $29. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • How differential signaling uses two wires
  • Direction control with DE and RE pins
  • Why a sender waits for its last bit
  • Reading a line of text from software serial
  • Combining hardware serial and SoftwareSerial
  • Difficulty: Intermediate
  • Time: About 20 minutes
  • Board: Arduino Uno R3
  • Parts: 5

How it works

RS-485 sends each bit as the difference between two wires, A and B. That makes it tough against noise over long cables. A MAX485 module converts the Uno's serial signal into that pair and back.

Here one Uno plays both ends. Module 1 sends from the Uno's TX pin. Module 2 listens and passes the bits to pin 8. The A and B wires join the two modules.

Once a second, the sketch raises pin 4 to talk and sends ping with a number. It waits until the last bit is out, then drops pin 4 to listen. Module 2 hears the message and the sketch prints it.

Parts list

5 parts, plus the jumper wires. They retail for about $29; on Mokxi they're free.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
RS-485 module, MAX485 (1) pin GNDArduino Uno R3 pin GND
RS-485 module, MAX485 (2) pin ROArduino Uno R3 pin 8
RS-485 module, MAX485 (1) pin REArduino Uno R3 pin 4
RS-485 module, MAX485 (1) pin DEArduino Uno R3 pin 4
RS-485 module, MAX485 (1) pin DIArduino Uno R3 pin 1
RS-485 module, MAX485 (1) pin VCC5 V
RS-485 module, MAX485 (2) pin BRS-485 module, MAX485 (1) pin B
RS-485 module, MAX485 (2) pin ARS-485 module, MAX485 (1) pin A
RS-485 module, MAX485 (2) pin REGround
RS-485 module, MAX485 (2) pin DEGround
RS-485 module, MAX485 (2) pin VCC5 V
RS-485 module, MAX485 (2) pin GNDGround

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Two MAX485 modules on one twisted pair: RS-485 in one direction.
//
// RS-485 sends each bit as the difference between two wires, A and B, so it
// runs hundreds of meters through electrical noise. A MAX485 module turns a
// UART into that pair and back. Here one Uno does both ends: the sender on
// its hardware Serial, the receiver on SoftwareSerial, with the pair between.
//
//   sender    DI -> TX (pin 1)   DE and RE -> pin 4    VCC -> 5V, GND -> GND
//   receiver  RO -> pin 8        DE and RE -> GND      VCC -> 5V, GND -> GND
//   the pair  A -> A, B -> B
//
// Every second the sketch raises pin 4 (talk), sends "ping" and a number,
// waits for the last bit with Serial.flush(), then drops pin 4 (listen). The
// receiver hears it on pin 8 and the sketch prints what came back. The
// serial monitor shows both, because hardware Serial is also the USB port.
//
// THINGS TO TRY. Swap A and B on one module: every bit arrives inverted and
// nothing comes back. Or take the Serial.flush() out: pin 4 drops while the
// UART is still sending, and the end of each message is cut off.

#include <SoftwareSerial.h>

const int DIR = 4;              // the sender's DE and RE, tied together
SoftwareSerial bus(8, 9);       // RX on pin 8 from the receiver's RO; 9 unused

unsigned long count = 0;
unsigned long last = 0;
char line[32];
uint8_t len = 0;

void setup() {
  pinMode(DIR, OUTPUT);
  digitalWrite(DIR, LOW);
  Serial.begin(9600);
  bus.begin(9600);
  Serial.println("RS-485 link: sender on TX, receiver on pin 8");
}

void loop() {
  if (millis() - last >= 1000) {
    last = millis();
    count++;
    digitalWrite(DIR, HIGH);    // talk
    Serial.print("ping ");
    Serial.println(count);
    Serial.flush();             // wait for the last bit before letting go
    digitalWrite(DIR, LOW);     // listen
  }
  while (bus.available()) {
    char c = bus.read();
    if (c == '\r') continue;
    if (c == '\n') {
      line[len] = 0;
      if (strncmp(line, "ping ", 5) == 0) {
        Serial.print("received over the pair: ");
        Serial.println(line);
      }
      len = 0;
    } else if (len < sizeof(line) - 1) {
      line[len++] = c;
    }
  }
}

Code walkthrough

The key functions of the sketch, and what each one does.

setup sketch.ino

Makes pin 4 an output and sets it LOW so the sender starts out listening. Then it starts the hardware serial for the sender and bus, the software serial on pin 8, both at 9600 baud.

void setup() {
  pinMode(DIR, OUTPUT);
  digitalWrite(DIR, LOW);
  Serial.begin(9600);
  bus.begin(9600);
  Serial.println("RS-485 link: sender on TX, receiver on pin 8");
}

loop sketch.ino

Once a second, it raises DIR, prints a numbered ping, calls Serial.flush and lowers DIR. A second part reads bytes from bus into a line buffer. At the newline, if the line starts with ping, it prints it as received over the pair.

void loop() {
  if (millis() - last >= 1000) {
    last = millis();
    count++;
    digitalWrite(DIR, HIGH);    // talk
    Serial.print("ping ");
    Serial.println(count);
    Serial.flush();             // wait for the last bit before letting go
    digitalWrite(DIR, LOW);     // listen
  }
  while (bus.available()) {
    char c = bus.read();
    if (c == '\r') continue;
    if (c == '\n') {
      line[len] = 0;
      if (strncmp(line, "ping ", 5) == 0) {
        Serial.print("received over the pair: ");
        Serial.println(line);
      }
      len = 0;
    } else if (len < sizeof(line) - 1) {
      line[len++] = c;
    }
  }
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • Nothing comes back over the pair This project
    Check that A goes to A and B goes to B between the two modules. Swapping them inverts every bit.
  • Messages arrive cut off This project
    The sender has to finish before it stops talking. Keep Serial.flush before DIR goes LOW.
  • The receiver hears nothing at all This project
    The receiver's RE and DE pins go to ground. Its RO pin goes to pin 8.
  • No data arrives, or it garbles. RS-485 module
    Join DE and RE together. Drive them high to send and low to listen. Match baud rate, and keep A with A and B with B across all nodes.
  • A long bus is noisy or unreliable. RS-485 module
    Put a 120 ohm termination resistor across A and B at both ends only. Use twisted pair, and join the grounds of the nodes.
  • Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
    Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected.
  • Pins misbehave or the board resets when you power motors or servos from it. Arduino Uno R3
    Each I/O pin gives about 20 mA, 40 mA max. Power motors and servos from their own supply and join its ground to the Uno's GND.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.