Built-in project · Arduino Mega 2560 R3

Mega four serial ports

Three of the Mega's four USARTs, each with its TX jumpered back to its own RX, reporting on the fourth. The whole build, an Arduino Mega, 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.

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

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

The circuit runs here once the page has loaded.

What you'll learn

  • The Mega's extra hardware serial ports
  • Baud rate and why both ends must match
  • A loopback test with TX wired to RX
  • Picking a port with a pointer to HardwareSerial
  • Difficulty: Beginner
  • Time: About 10 minutes
  • Board: Arduino Mega 2560 R3
  • Parts: 1

How it works

An Uno has one hardware serial port, used for the serial monitor. A Mega has four. Serial is still the monitor, and Serial1, Serial2 and Serial3 are on header pins. Here each TX pin is jumpered to its own RX pin: 18 to 19, 16 to 17 and 14 to 15.

Once a second the sketch sends a line like ping 1 out of each port, at 9600, 19200 and 38400 baud. Each port hears its own message. The sketch reads it back and prints it in the monitor with the port that heard it.

Parts list

1 part, plus the jumper wires. They retail for about $48; 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
Arduino Mega 2560 R3 pin 15Arduino Mega 2560 R3 pin 14
Arduino Mega 2560 R3 pin 17Arduino Mega 2560 R3 pin 16
Arduino Mega 2560 R3 pin 19Arduino Mega 2560 R3 pin 18

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 Mega 2560 R3
// Four serial ports: the thing a Mega has that an Uno does not.
//
// An Uno has one USART and it is spoken for: it is the channel to the serial
// monitor. A Mega has four. Serial is still the monitor, and Serial1, Serial2
// and Serial3 come out on header pins:
//
//   Serial1   TX1 pin 18   RX1 pin 19
//   Serial2   TX2 pin 16   RX2 pin 17
//   Serial3   TX3 pin 14   RX3 pin 15
//
// Wire each TX back to the RX beside it with a short jumper and every port
// talks to itself, which is the cheapest way to see all three working at once
// with nothing else on the bench. In the simulator those are real wires and
// the bytes are real 8N1 frames on them: start bit, eight data bits least
// significant first, stop bit, at the baud rate each port was opened with.
// Change one port's baud rate and its line comes back as mojibake, exactly as
// it would on the bench.
//
// The three ports run at different rates on purpose, so the frames on the
// three wires are visibly different lengths.

const unsigned long RATE[3] = { 9600, 19200, 38400 };

HardwareSerial *port(int n) {
  if (n == 0) {
    return &Serial1;
  }
  if (n == 1) {
    return &Serial2;
  }
  return &Serial3;
}

unsigned long lastSend = 0;
int round_ = 0;

void setup() {
  Serial.begin(115200);
  Serial1.begin(RATE[0]);
  Serial2.begin(RATE[1]);
  Serial3.begin(RATE[2]);

  Serial.println("Mokxi Arduino Mega 2560: four serial ports");
  for (int n = 0; n < 3; n++) {
    Serial.print("Serial");
    Serial.print(n + 1);
    Serial.print(" at ");
    Serial.print(RATE[n]);
    Serial.println(" baud, TX jumpered to its own RX");
  }
}

void loop() {
  unsigned long now = millis();

  // Once a second, send one line out of each port.
  if (now - lastSend >= 1000) {
    lastSend = now;
    round_++;
    for (int n = 0; n < 3; n++) {
      port(n)->print("ping ");
      port(n)->println(round_);
    }
  }

  // Anything a port hears, it heard from itself. Report it with the port it
  // arrived on, so three lines a second appear in the monitor.
  for (int n = 0; n < 3; n++) {
    while (port(n)->available() > 0) {
      int c = port(n)->read();
      if (c == '\r') {
        continue;
      }
      if (c == '\n') {
        Serial.println();
        continue;
      }
      if (c == 'p') {
        // The start of a line: label it with the port it came back on.
        Serial.print("Serial");
        Serial.print(n + 1);
        Serial.print(" heard: ");
      }
      Serial.print((char)c);
    }
  }
}

Code walkthrough

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

port sketch.ino

Returns a pointer to one of the three extra serial ports for the numbers 0, 1 and 2. That lets the other code loop over the ports with one set of lines instead of repeating them for Serial1, Serial2 and Serial3.

HardwareSerial *port(int n) {
  if (n == 0) {
    return &Serial1;
  }
  if (n == 1) {
    return &Serial2;
  }
  return &Serial3;
}

setup sketch.ino

Opens the monitor at 115200 and starts the three ports at 9600, 19200 and 38400 baud. Then it prints a line for each port with its baud rate.

void setup() {
  Serial.begin(115200);
  Serial1.begin(RATE[0]);
  Serial2.begin(RATE[1]);
  Serial3.begin(RATE[2]);

  Serial.println("Mokxi Arduino Mega 2560: four serial ports");
  for (int n = 0; n < 3; n++) {
    Serial.print("Serial");
    Serial.print(n + 1);
    Serial.print(" at ");
    Serial.print(RATE[n]);
    Serial.println(" baud, TX jumpered to its own RX");
  }
}

loop sketch.ino

Once a second it prints ping and the round number out of each port. Every pass it reads any bytes each port has heard. When a line starts with p, it prints the label Serial1, Serial2 or Serial3 first, then the text, and ends the line on a newline.

void loop() {
  unsigned long now = millis();

  // Once a second, send one line out of each port.
  if (now - lastSend >= 1000) {
    lastSend = now;
    round_++;
    for (int n = 0; n < 3; n++) {
      port(n)->print("ping ");
      port(n)->println(round_);
    }
  }

  // Anything a port hears, it heard from itself. Report it with the port it
  // arrived on, so three lines a second appear in the monitor.
  for (int n = 0; n < 3; n++) {
    while (port(n)->available() > 0) {
      int c = port(n)->read();
      if (c == '\r') {
        continue;
      }
      if (c == '\n') {
        Serial.println();
        continue;
      }
      if (c == 'p') {
        // The start of a line: label it with the port it came back on.
        Serial.print("Serial");
  // ... 7 more lines in the editor

Troubleshooting

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

  • A port hears nothing This project
    Check its jumper: TX1 18 to RX1 19, TX2 16 to RX2 17, TX3 14 to RX3 15.
  • A port's line comes back as garbage This project
    The baud rate on that port is wrong. Each port reads at the same rate it was started with.
  • No output in the monitor at all This project
    Open the monitor at 115200 baud to match Serial.begin.
  • The upload fails or nothing runs. Arduino Mega 2560 R3
    Under Tools, choose Arduino Mega or Mega 2560 as the board, then pick the right port. An Uno selection uploads the wrong build.
  • I2C, SPI, or serial pins are in the Uno spots and do not work. Arduino Mega 2560 R3
    On the Mega, SDA is pin 20 and SCL is 21. SPI uses 50 to 53, not 10 to 13. Serial1 to Serial3 are on pins 19 to 14.

Change it and keep it

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