RS485 board/First messages/08. One ESP32, two boards
First messages · 08 of 11

One ESP32, two boards

One ESP32, two MAX3485 boards and three short wires between their terminals. One board talks on UART1, the other listens on UART2, and the Serial Monitor prints what crossed the pair. It proves the boards, the wiring and the direction pin before any second microcontroller is involved.

What you need

  • An ESP32 or ESP32-S3 dev board.
  • Two MAX3485 3V3 boards, with their bottom headers soldered.
  • A breadboard, jumper wires, and three short wires for A, B and GND.

Leave the 120 Ω fitted on both. Two boards on one cable are its two ends.

Wire it

Board AESP32Board BESP32
GNDGNDGNDGND
3V33V33V33V3
TXGPIO 16TXGPIO 18
RXGPIO 17RXGPIO 5
DEGPIO 4DEGPIO 21

Then the bus: board A's A to board B's A, B to B, and GND to GND, through the screw terminals.

The one line that matters

When the direction pin goes LOW
byte cut off
The pin goes LOW
One byte takes
1.0 ms
write() returns after
a few µs
Bits lost
9
The byte is cut off. write() only puts the byte in the UART's buffer and returns in microseconds; the bits take a millisecond to leave. Switch the driver off then and the listener gets nothing, or a byte that is not the one you sent.

Serial1.println() only hands the text to the UART and returns. The bits take about a millisecond per character to leave. Drop the direction pin then and the driver switches off mid-message. flush() waits until the last stop bit has gone, and the pin goes LOW straight after, so the bus is free again as soon as it can be.

What it prints

A talks, B listens
A sent  hello 1
B heard hello 1
A sent  hello 2
B heard hello 2

Each "B heard" line crossed the pair: out of GPIO 17, into board A, onto A and B as a difference, into board B, and back into GPIO 18. If you see these, both boards, their direction pins and the wiring all work.

The receiver prints only lines that start with hello, which is the idle-bus rule from When nobody talks in two lines of code.

The code

rs485_two_boards.ino

Board A sends a numbered line once a second. Board B listens and prints each complete line it hears, so a stray byte on the idle bus never reaches the Serial Monitor.

// One ESP32, two MAX3485 (3V3) boards: A talks, B listens.
//
// Wiring. Both boards: 3V3 -> ESP32 3V3, GND -> ESP32 GND.
//   Board A  TX -> GPIO16   (the board's TX is the chip's RO)
//            RX -> GPIO17
//            DE -> GPIO4    (RE is joined to DE on the board)
//   Board B  TX -> GPIO18
//            RX -> GPIO5
//            DE -> GPIO21
//   Terminals: A to A, B to B, GND to GND.
//   Keep both 120R fitted: two boards are the two ends.
//
// Arduino IDE: Tools > Board > esp32 > ESP32 Dev Module
// (or ESP32S3 Dev Module), then Tools > Port.
// Serial Monitor at 115200. No libraries.

const int A_RO = 16, A_DI = 17, A_DIR = 4;
const int B_RO = 18, B_DI = 5, B_DIR = 21;
const long BAUD = 9600;

String line;
int count = 0;
uint32_t last = 0;

void setup() {
  // Direction pins first: listen until there is
  // something to say.
  pinMode(A_DIR, OUTPUT);
  digitalWrite(A_DIR, LOW);
  pinMode(B_DIR, OUTPUT);
  digitalWrite(B_DIR, LOW);

  Serial.begin(115200);
  Serial1.begin(BAUD, SERIAL_8N1, A_RO, A_DI);
  Serial2.begin(BAUD, SERIAL_8N1, B_RO, B_DI);
  delay(500);
  Serial.println("A talks, B listens");
}

void loop() {
  if (millis() - last >= 1000) {
    last = millis();
    count++;
    digitalWrite(A_DIR, HIGH);     // A takes the bus
    delayMicroseconds(100);        // a clean idle first
    Serial1.print("hello ");
    Serial1.println(count);
    Serial1.flush();               // wait for the stop bit
    digitalWrite(A_DIR, LOW);      // let go
    Serial.printf("A sent  hello %d\n", count);
  }

  // B collects bytes and prints only whole lines.
  while (Serial2.available()) {
    char c = Serial2.read();
    if (c == '\n') {
      line.trim();
      if (line.startsWith("hello")) {
        Serial.print("B heard ");
        Serial.println(line);
      }
      line = "";
    } else if (line.length() < 40) {
      line += c;
    }
  }
}

Serial1 and Serial2 both accept any pins in begin(), on the classic ESP32 and the ESP32-S3 alike, and these six are free on both. flush() waits for the last stop bit, so the direction pin drops the moment the line has left.

When it does not work

A sent appears, B heard never does.

Check board B's TX goes to GPIO18 and board A's RX to GPIO17; TX to TX is the usual cause. Then check A, B and GND run terminal to terminal, and that GPIO4 really reaches board A's DE or RE hole.

B heard prints, but some numbers are skipped.

A line was damaged on the way, and the sketch threw it away rather than print garbage. On a short wire that is usually a loose jumper or a direction pin that drops early. If you changed the sketch, make sure flush() still comes before the pin goes LOW.

Every line arrives as nonsense.

A and B are crossed between the terminals, which turns every bit over. Swap the two wires at one terminal. If the text is still wrong, both Serial1 and Serial2 must be at the same BAUD.

It will not compile: Serial2 is not declared.

An ESP32-C3 or C6 has only two UARTs, one of them the USB serial. Use a classic ESP32 or an ESP32-S3 for this build, or run each board on its own microcontroller as in the Uno build.

Where this goes next

The 5 V boards, two microcontrollers, and a reply coming back the other way.

Two Unos over one pair

Edit this page — content/books/rs485/one-esp32-two-boards.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

This page covers several products. Choose yours to see the right discussions.

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum