11. Networking and Communications

This week I implemented wireless communication between two ESP32 development boards using ESP-NOW — Espressif's peer-to-peer, connection-less radio protocol. Because our lab's Snapmaker CNC mill currently lacks a stable workholding fixture, I could not mill a custom PCB this week. Instead I used two off-the-shelf ESP32 DevKit boards and designed the supporting circuits in Cirkuit Designer.

The system works as follows: pressing a push-button on Board A (the sender) causes it to broadcast an ESP-NOW packet to Board B (the receiver), which then commands a SG90 servo to rotate 90 °. Releasing the button sends another packet and the servo returns to 0 °.

Student's checklist – 4 / 7

  • ☐  Linked to the group assignment page
  • ☐  Documented the project and what I learned from implementing networking / communication
  • ☐  Explained the programming process(es) I used
  • ☐  Ensured and documented that addressing for boards works
  • ☐  Outlined problems and how I fixed them
  • ✓  Included design files (Cirkuit Designer circuits)
  • ☐  Included a hero shot of the network / communications setup

Group Assignment

As a group we surveyed multiple communication protocols and interfaces available in the lab — including UART, I²C, SPI, Wi-Fi (TCP/IP), and ESP-NOW — and compared their speed, range, power consumption, and ease of use. The full group documentation is on the lab page:

Week 11 Group Assignment – Networking and Communications (Chaihuo Lab)

Communication Protocol Comparison

Before choosing a protocol, I compared the main options available on the ESP32 platform. The table below summarises the key characteristics of each method and explains why I selected ESP-NOW for this project.

Method Common Pins Wired / Wireless Typical Use Speed Range Good For Main Limitation
UART / Serial TX, RX Wired Board-to-board, PC debugging, modules like GPS / DFPlayer Medium Short Simple communication between two devices Usually one-to-one
I²C SDA, SCL Wired Sensors, OLEDs, RTC, multiple devices Medium Short Many devices on just 2 signal wires Short distance, address conflicts possible
SPI MOSI, MISO, SCK, CS Wired Displays, SD cards, fast sensors, radio modules Fast Short High-speed communication Uses more pins
Wi-Fi Built into ESP32 / ESP8266 Wireless IoT, web server, remote control, sending data online Fast Medium Internet, phone / web dashboard Higher power use
Bluetooth / BLE Built into ESP32 Wireless Phone control, apps, nearby device connection Medium Short Easy phone-to-board communication Shorter range than Wi-Fi
Radio (RF) Often via SPI modules Wireless Remote control, board-to-board wireless Varies Medium to long No Wi-Fi needed Needs extra module
CAN Bus CAN TX/RX + transceiver Wired Automotive, robotics, robust multi-device systems Medium Long Reliable in noisy environments Extra hardware needed
ESP-NOW ✓ ESP32 / ESP8266 wireless Wireless Direct ESP-to-ESP communication Fast Medium No router needed, low latency Mostly for ESP family only

Why I chose ESP-NOW — and ruled out the alternatives

My project needs two ESP32 boards to communicate in real time: a button press on Board A must move the servo on Board B with no noticeable delay. Here is why I rejected each alternative:

ESP-NOW wins because: both boards are already ESP32s (no extra hardware), the latency is <5 ms (servo feels instant), no router or infrastructure is needed, and the API is straightforward — register a peer by MAC address and call esp_now_send(). For a robotic arm lamp that needs real-time local control, it is the right tool for the job.

Note: if the lamp later needs phone app control, I would add BLE on top of ESP-NOW. If it needs to log data to a server, I would add Wi-Fi. ESP-NOW and Wi-Fi can even run simultaneously on the same ESP32.

What is the first principle behind ESP-NOW?

At the most fundamental level, ESP-NOW works because two ESP32 boards use their built-in 2.4 GHz Wi-Fi radio hardware to send and receive specially formatted wireless frames directly, without needing a router or a normal Wi-Fi network connection.

So the core idea is:

First-principles view: what is wireless communication?

Wireless communication is simply this:

  1. One device converts digital data (0s and 1s) into electromagnetic signals.
  2. Those signals are transmitted through the air using a radio frequency.
  3. Another device listening on the same frequency receives the signal.
  4. It converts the signal back into digital data.

For ESP32, this happens using its 2.4 GHz Wi-Fi radio. So when two ESP32 boards communicate with ESP-NOW, they are really doing this:

How do two ESP32 boards communicate without a router?

Normally, Wi-Fi communication looks like this:

Device → Router → Network → Another device

But ESP-NOW skips the router. Instead, it works more like this:

ESP32 A → direct wireless packet through the air → ESP32 B

So ESP-NOW is a peer-to-peer communication method. That is why it is fast and lightweight.

What happens step by step?

Here is the real process:

On ESP32 A:

  1. Wi-Fi hardware is turned on.
  2. ESP-NOW is initialized.
  3. ESP32 B is added as a peer using its MAC address.
  4. A program calls esp_now_send().
  5. The ESP32 packages the data into a special wireless frame.
  6. Its antenna transmits that frame through the air.

On ESP32 B:

  1. Its Wi-Fi hardware is listening on the same channel.
  2. It receives the frame.
  3. It checks whether the address matches.
  4. If valid, the received data is passed to the receive callback function.

So from first principles: ESP-NOW is direct device-to-device wireless packet delivery using Wi-Fi radio hardware, but without traditional networking layers.

Why is ESP-NOW so useful?

Because it avoids a lot of extra overhead.

With normal Wi-Fi, you usually need:

With ESP-NOW, you can just send short data directly.

That makes it very useful for:

System Architecture

Board A — Sender
ESP32 DevKit
Push-button on GPIO 4
Built-in LED feedback
ESP-NOW (2.4 GHz)
Unicast packet
Peer MAC address
No router needed
Board B — Receiver
ESP32 DevKit
SG90 servo on GPIO 18
Serial debug output

I chose ESP-NOW over standard Wi-Fi for three reasons:

Circuit Design (Cirkuit Designer)

Because I could not mill a custom PCB this week, I used Cirkuit Designer to design and simulate both circuits before wiring them on breadboards. The schematics are shown below.

Board A — Button Sender

A momentary push-button is wired between GPIO 4 and GND. GPIO 4 is configured with the ESP32's internal pull-up resistor, so it reads HIGH at rest and LOW when pressed. The built-in LED (GPIO 2) blinks to confirm each transmission.

Board B — Servo Receiver

The SG90 servo signal wire is connected to GPIO 18, which supports LEDC PWM on the ESP32. The servo is powered from the board's 5 V pin (USB-sourced), with a 100 µF capacitor across the supply to absorb the inrush current when the servo stalls.

Cirkuit Designer schematic – Board B (Servo Receiver)

Board Addressing

ESP-NOW uses the standard Wi-Fi MAC address of each ESP32 as its unique network identifier. There is no IP layer or DHCP — the sender simply registers the receiver's 6-byte MAC address as a "peer" and sends packets directly to it.

How I found the MAC addresses

I uploaded the small sketch below to each board and read the MAC address from the Serial Monitor at 115200 baud:

get_mac_address.ino

#include <WiFi.h>

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  Serial.print("MAC Address: ");
  Serial.println(WiFi.macAddress());
}

void loop() {}
        

The addresses I recorded are:

Board Role MAC Address GPIO used
Board A Sender (button) XX:XX:XX:XX:XX:XX (replace with your value) GPIO 4 (button), GPIO 2 (LED)
Board B Receiver (servo) XX:XX:XX:XX:XX:XX (replace with your value) GPIO 18 (servo PWM)

Board B's MAC address is hard-coded into Board A's firmware as the receiverMac[] array (see source code below). This ensures that Board A only sends to the correct peer and no other ESP32 in range can accidentally receive the packets.

Programming Process

Both sketches were written in Arduino IDE 2 with the esp32 by Espressif Systems board package (v3.x). The ESP-NOW API is included in the package; no extra library is needed.

  1. 1 Find MAC addresses — Upload get_mac_address.ino to each board and record the output in the Serial Monitor.
  2. 2 Write the sender sketch (Board A) — Initialise Wi-Fi in station mode, initialise ESP-NOW, register Board B as a peer using its MAC, then in loop() poll the button and call esp_now_send() on state changes.
  3. 3 Write the receiver sketch (Board B) — Initialise ESP-NOW and register an OnDataRecv callback. Inside the callback parse the message and move the servo accordingly.
  4. 4 Flash and test — Upload Board B's sketch first (receiver must be ready), then Board A. Open the Serial Monitor on Board B to confirm packets are arriving.

Board A – sender.ino (Button → ESP-NOW)

#include <esp_now.h>
#include <WiFi.h>

// ── Replace with Board B's actual MAC address ──────────────────────────────
uint8_t receiverMac[] = {0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX};
// ──────────────────────────────────────────────────────────────────────────

const int BUTTON_PIN = 4;
const int LED_PIN    = 2;

typedef struct Message {
  bool buttonPressed;
} Message;

Message outgoing;

void onSent(const uint8_t *macAddr, esp_now_send_status_t status) {
  Serial.print("Send status: ");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "OK" : "FAIL");
}

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

  WiFi.mode(WIFI_STA);
  if (esp_now_init() != ESP_OK) {
    Serial.println("ESP-NOW init failed");
    return;
  }
  esp_now_register_send_cb(onSent);

  esp_now_peer_info_t peer = {};
  memcpy(peer.peer_addr, receiverMac, 6);
  peer.channel = 0;
  peer.encrypt = false;
  esp_now_add_peer(&peer);

  Serial.println("Board A ready — press button to trigger servo");
}

bool lastState = HIGH;

void loop() {
  bool current = digitalRead(BUTTON_PIN);

  if (current != lastState) {
    lastState = current;
    outgoing.buttonPressed = (current == LOW);  // LOW = pressed (pull-up)

    digitalWrite(LED_PIN, outgoing.buttonPressed ? HIGH : LOW);
    esp_now_send(receiverMac, (uint8_t *)&outgoing, sizeof(outgoing));

    Serial.print("Button ");
    Serial.println(outgoing.buttonPressed ? "PRESSED → sending" : "RELEASED → sending");
  }

  delay(20);  // simple debounce
}
        

Board B – receiver.ino (ESP-NOW → Servo)

#include <esp_now.h>
#include <WiFi.h>
#include <ESP32Servo.h>   // install "ESP32Servo" library by Kevin Harrington

const int SERVO_PIN = 18;

Servo myServo;

typedef struct Message {
  bool buttonPressed;
} Message;

void onDataRecv(const uint8_t *mac, const uint8_t *data, int len) {
  Message incoming;
  memcpy(&incoming, data, sizeof(incoming));

  Serial.print("Packet received. buttonPressed = ");
  Serial.println(incoming.buttonPressed);

  if (incoming.buttonPressed) {
    myServo.write(90);   // rotate to 90 °
    Serial.println("Servo → 90°");
  } else {
    myServo.write(0);    // return to 0 °
    Serial.println("Servo → 0°");
  }
}

void setup() {
  Serial.begin(115200);

  myServo.attach(SERVO_PIN);
  myServo.write(0);

  WiFi.mode(WIFI_STA);
  if (esp_now_init() != ESP_OK) {
    Serial.println("ESP-NOW init failed");
    return;
  }
  esp_now_register_recv_cb(onDataRecv);

  Serial.println("Board B ready — waiting for packets");
}

void loop() {
  // All work is done in the onDataRecv callback
}
        

What I Learned

The biggest insight from this week was that networking does not require complex infrastructure. ESP-NOW's peer-to-peer model means the two boards can communicate across a room with no router, no DHCP, and no TCP/IP stack overhead. Once I understood that it is essentially just "send a byte array to this MAC address", the API became very approachable.

Hero Shot

The two ESP32 boards communicating: pressing the button on Board A (left) causes the servo on Board B (right) to rotate to 90 °.

Two ESP32 boards — button sender on the left, servo receiver on the right

Design Files

(Files will be uploaded once the Cirkuit Designer exports are finalised.)

← Week 10 Back to Assignments Week 12 →