Make two ESP32s talk to each other through Arduino Cloud

Two ESP32s, in two rooms or two homes, share variables through Arduino Cloud. Press the button on one and the other one lights up and plays a tune: a doorbell or a "come for dinner" call.
  • Level:Advanced
  • Duration: 1 h 10 min
  • Ages 12+
  • Platform: ESP32

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Make two ESP32s talk to each other through Arduino Cloud – EEMENTOR STEM™ lesson

Arduino Cloud can do more than connect a device to your phone: it can connect two devices to each other, anywhere in the world. You'll build two little boxes, A and B. Press the button on box A, and box B lights up and plays a tune; press the button on box B to answer, and box A lights up. Use it as a doorbell between the front door and your room, as a "dinner is ready" call, or between your home and a friend's. The trick is a feature called variable sync.

What you'll learn

  • How two Things can share a variable with variable sync
  • Why each box both sends and receives
  • How to tell a press of your own button from a message from the other box
  • How to design the same sketch for two devices

What you need

  • ESP32 board, expansion board and USB cable (ESP32 IoT Starter Kit)
  • A second ESP32 with its expansion board and USB cable (a second ESP32 IoT Starter Kit, or a friend's)
  • 2 push buttons (one from each kit)
  • 2 LEDs and 2 resistors, 220 Ω
  • 2 passive buzzers
  • A 2.4 GHz Wi-Fi network for each box
  • The printed parts from the 3D printing box below (two buzzer boxes), or the cardboard version described there
  • Breadboard and jumper wires (ESP32 IoT Starter Kit)

Wiring (the same for both boxes)

Breadboard wiring diagram: Make two ESP32s talk to each other through Arduino Cloud
Breadboard view. Check each connection against the wiring table below.
From To
Push button, one leg ESP32 GPIO 27
Push button, the diagonally opposite leg GND
ESP32 GPIO 25 220 Ω resistor, then the LED's long leg
LED short leg GND
Passive buzzer + ESP32 GPIO 26
Passive buzzer − GND

Set up Arduino Cloud

  1. Add the second ESP32 as a new device, as in lesson 1. Keep both secret keys.
  2. Create a Thing called Box A with its device, and two Boolean variables, ringA and ringB, both Read & Write, On change.
  3. Create a Thing called Box B with the second device and the same two variables.
  4. In Box B, open the ringA variable, click Sync with other Things and pick ringA of Box A. Do the same for ringB. Now both Things share the two variables.

This uses both of the free plan's Things. Box A rings Box B by setting ringA; Box B answers with ringB.

The sketch

Arduino Cloud writes the sketch's first version for you, with a thingProperties.h tab that lists your variables and a Secret tab with your Wi-Fi details and device key. In the Thing, open the Sketch tab, click Open full editor, replace the main sketch with the code below and upload. Leave the other tabs as they are.

Upload the same sketch to both boxes. Change one line: ME is 'A' for Box A and 'B' for Box B.

#include "thingProperties.h"

const char ME = 'A';                 // 'A' on box A, 'B' on box B
const int BUTTON_PIN = 27, LED_PIN = 25, BUZZER_PIN = 26;
bool lastButton = HIGH;
unsigned long glowUntil = 0;

// My own bell variable, and the other box's
bool& myRing() { return ME == 'A' ? ringA : ringB; }
bool& theirRing() { return ME == 'A' ? ringB : ringA; }

void tune() {
  const int notes[] = {784, 988, 1175, 1568};
  for (int n : notes) {
    tone(BUZZER_PIN, n, 120);
    delay(140);
  }
}

void incoming() {                   // the other box pressed its button
  if (!theirRing()) return;           // only react to the "ring", not the reset
  Serial.println("Ring from the other box!");
  glowUntil = millis() + 10000;       // glow for 10 seconds
  tune();
}

void setup() {
  Serial.begin(115200);
  delay(1500);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);

  initProperties();
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  setDebugMessageLevel(2);          // connection messages in the Serial Monitor
  ArduinoCloud.printDebugInfo();
}

void loop() {
  ArduinoCloud.update();
  bool button = digitalRead(BUTTON_PIN);
  if (button == LOW && lastButton == HIGH) {   // my button: ring the other box
    myRing() = true;
    Serial.println("Ringing...");
    tone(BUZZER_PIN, 2000, 60);
    delay(30);
  }
  if (button == HIGH && lastButton == LOW) myRing() = false;   // let go: ready for the next ring
  lastButton = button;
  digitalWrite(LED_PIN, millis() < glowUntil && (millis() / 300) % 2);
}

void onRingAChange() {
  if (ME == 'B') incoming();
}

void onRingBChange() {
  if (ME == 'A') incoming();
}

For reference, this is the thingProperties.h that Arduino Cloud creates from the variables above. Yours has your own device ID, and may use an older but equally valid style such as addProperty(led, READWRITE, ON_CHANGE, onLedChange).

// Code generated by Arduino Cloud (yours has your own device ID)
#include <ArduinoIoTCloud.h>
#include <Arduino_ConnectionHandler.h>

const char DEVICE_LOGIN_NAME[] = "YOUR_DEVICE_ID";
const char SSID[] = SECRET_SSID;              // from the Secret tab
const char PASS[] = SECRET_OPTIONAL_PASS;
const char DEVICE_KEY[] = SECRET_DEVICE_KEY;

void onRingAChange();
void onRingBChange();

bool ringA;
bool ringB;

void initProperties() {
  ArduinoCloud.setBoardId(DEVICE_LOGIN_NAME);
  ArduinoCloud.setSecretDeviceKey(DEVICE_KEY);
  ArduinoCloud.addProperty(ringA, Permission::ReadWrite).onUpdate(onRingAChange);
  ArduinoCloud.addProperty(ringB, Permission::ReadWrite).onUpdate(onRingBChange);
}

WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS);

How it works

With variable sync, ringA in Box A and ringA in Box B are really the same variable. When Box A sets it to true, Arduino Cloud passes the change to Box B, and Box B's onRingAChange() runs. Box A also gets its own change back, which is why each box only reacts to the other box's variable.

The button sets the variable to true while it is pressed and back to false when you let go. The other box only plays the tune when the value becomes true, so letting go doesn't ring twice.

myRing() and theirRing() return a reference (the &) to the right variable, so one sketch works for both boxes: only the letter in ME changes.

Try this next

  • Add a second button for a different message, like "come for dinner" with a different tune.
  • Count how many times each box was rung today.
  • Add a dashboard so you can ring both boxes from your phone too.

Troubleshooting

  • The device stays "Offline": open the Serial Monitor at 115200 baud. Check the Wi-Fi name and password in the Secret tab (2.4 GHz only) and that the secret device key is the one you saved when you added the device.
  • Compile error about a variable: the variable names in the sketch must match the ones in your Thing exactly, including capital letters. Rename the Thing's variable or the sketch's to match.
  • Nothing happens on the other box: check that both variables are synced in the Thing settings, and that both devices are online.
  • A box rings itself: check that ME is different on the two boxes.
  • A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

ESP32 IoT Starter Kit | Arduino IDE Compatible | Free Tutorials $57.00

Shop the kit