Skip to content
[繁]

Advanced ESP32: Wireless Communication and Web-Controlled RGB ​

Goal: use two representative IoT tasks to understand the core patterns behind router-free peer-to-peer communication and a front end that controls physical hardware.

Topic A: ESP-NOW wireless messages and encryption
Topic B: an ESP32 web server controlling an RGB LED with a colour picker

1. What Is ESP-NOW? ​

Besides connecting to the internet through Wi-Fi, an ESP32 supports a protocol that communicates without an access point or router:

ESP-NOW = very low latency + no router + peer-to-peer or broadcast communication using MAC addresses

Think of it as:

  • a short-message network for ESP32 devices;
  • local messages exchanged directly between machines;
  • communication that does not pass through the internet; and
  • a fast, reliable link for small packets.

Common applications include:

  • exchanging data between IoT nodes without an access point;
  • robot-to-robot communication;
  • sensor-to-controller links;
  • games, remote vehicles and distributed systems; and
  • advanced mesh-style networks.

2. Build a Teacher Sender and Student Receiver ​

Two ESP32 boards can communicate locally. Start by sending a plain-text message; encryption comes next.

In this exercise, the teacher broadcasts a message through ESP-NOW. Your ESP32 receives it, and you report the text when the teacher checks your result.

2.1 Teacher's Sender ​

Function: broadcast a text message every five seconds. You do not need to upload this program; the teacher has already prepared the sender.

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

uint8_t broadcastAddress[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; // Broadcast

typedef struct struct_message {
  char msg[50];
} struct_message;

struct_message packet;

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);

  if (esp_now_init() != ESP_OK) {
    Serial.println("ESP-NOW Init Failed");
    return;
  }

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

void loop() {
  strcpy(packet.msg, "HELLO STUDENTS");
  esp_now_send(broadcastAddress, (uint8_t *)&packet, sizeof(packet));
  Serial.println("Sent: HELLO STUDENTS");
  delay(5000);
}

2.2 Student's Receiver ​

Function: display the received message.

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

typedef struct struct_message {
  char msg[50];
} struct_message;

struct_message incoming;

// Runs whenever a message arrives ============================================
void onReceive(const esp_now_recv_info *info, const uint8_t *data, int len) {
  memcpy(&incoming, data, sizeof(incoming));
  Serial.print("Received: ");
  Serial.println(incoming.msg);
}
// Runs whenever a message arrives ============================================

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);

  if (esp_now_init() != ESP_OK) {
    Serial.println("ESP-NOW Init Failed");
    return;
  }
  // Register once. ESP32 invokes onReceive() when a message arrives.
  esp_now_register_recv_cb(onReceive);
}

void loop() {}  // Other normal work can continue here

3. 🔥 Advanced ESP-NOW: Built-In AES-128 Encryption ​

The next step upgrades communication to a secure IoT pattern. ESP-NOW supports AES-128 encryption. Add a 16-byte key while registering the peer; the sender encrypts and receiver decrypts automatically, so application code does not implement the cryptography itself.

3.1 What Is ESP-NOW Encryption? ​

ModeSecure?Key required?Appropriate use
Open❌ Can be intercepted❌ NoDemonstrations and simple tests
AES-128 encrypted✅ Yes✅ 16-byte keyIoT communication and paired devices

The encryption and decryption are handled below the application layer:

Sender → encryption → radio transmission → receiver firmware decrypts → onReceive() gets plaintext

3.2 Handling the 16-Byte Key ​

ESP-NOW encryption requires a fixed 16-byte (128-bit) key. You may use:

  • a byte array such as uint8_t key[16] = {0x01, 0x02, ...};; or
  • a 15-character C string such as "1234567890ABCDE". Its terminating \0 occupies the sixteenth byte. A 16-character string plus its terminator needs 17 bytes and will not fit.

Example 15-character string:

cpp
"LOKCM is GENIUS"

Or an explicit 16-byte character array:

cpp
uint8_t myKey[16] = {
  'L','O','K','C','M',' ','i','s',' ','G','E','N','I','U','S','!'
};

3.3 Encryption Restrictions ​

Encrypted ESP-NOW traffic cannot use a broadcast or multicast MAC address:

  • FF:FF:FF:FF:FF:FF → ❌ cannot be encrypted.
  • A group address with its least-significant group bit set → ❌ cannot be encrypted.

Encrypted ESP-NOW therefore uses one-to-one MAC-addressed peers; one encrypted message cannot simply be broadcast to the whole class.

3.4 Overall Process ​

Each pair of students needs two ESP32 boards:

  1. Read each board's local MAC address.
  2. Enter the receiver's MAC in the sender program.
  3. Enter the sender's MAC in the receiver program.
  4. Use the same 16-byte AES key on both boards.
  5. One board calls send() and the other's onReceive() callback receives automatically decrypted data.

3.5 Read the MAC Address ​

Upload this program to each board:

cpp
#include <WiFi.h>
#include <esp_wifi.h>

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);

  uint8_t mac[6];
  esp_wifi_get_mac(WIFI_IF_STA, mac);   // Reliably reads the station MAC

  Serial.printf("ESP32 MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
    mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

void loop(){}

Record the output, for example:

  • Student A: 7C:DF:A1:12:34:56
  • Student B: B4:E6:2D:98:76:54

3.6 Complete Receiver Program ​

Replace the placeholder in senderMAC[] with the sender's MAC. The receiver only receives; ESP-NOW decrypts the packet automatically.

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

typedef struct struct_message {
  char msg[50];
} struct_message;

struct_message incoming;

// Student A (receiver): enter Student B's sender MAC
uint8_t senderMAC[6] = {0xB4,0xE6,0x2D,0x98,0x76,0x54}; // Replace this example

// AES key: a 15-character string plus its null terminator occupies 16 bytes
uint8_t myKey[16] = "LOKCM is Genius"; // Replace with your shared key
// Or use an explicit 16-byte array:
// uint8_t myKey[16] = {'L','O','K','C','M',' ','i','s',' ','G','E','N','I','U','S','!'};

void onReceive(const esp_now_recv_info *info, const uint8_t *data, int len) {
  memcpy(&incoming, data, sizeof(incoming));
  Serial.print("Received: ");
  Serial.println(incoming.msg);
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);

  if (esp_now_init() != ESP_OK) {
    Serial.println("ESP-NOW Init Failed");
    return;
  }

  // Register the sender as a peer
  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, senderMAC, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = true;
  memcpy(peerInfo.lmk, myKey, 16);

  if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println("Add Peer Failed");
    return;
  }

  esp_now_register_recv_cb(onReceive);
}

void loop(){}

3.7 Complete Sender Program ​

Replace the placeholder in receiverMAC[] with the receiver's MAC. send() encrypts and the receiver automatically decrypts the payload.

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

typedef struct struct_message {
  char msg[50];
} struct_message;

struct_message packet;

// Student B (sender): enter Student A's receiver MAC
uint8_t receiverMAC[6] = {0x7C,0xDF,0xA1,0x12,0x34,0x56}; // Replace this example

// Both devices must use exactly the same key
uint8_t myKey[16] = "LOKCM is Genius"; // Replace with your shared key
// uint8_t myKey[16] = {'L','O','K','C','M',' ','i','s',' ','G','E','N','I','U','S','!'};

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);

  if (esp_now_init() != ESP_OK) {
    Serial.println("ESP-NOW Init Failed");
    return;
  }

  // Register the receiver as a peer
  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, receiverMAC, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = true;
  memcpy(peerInfo.lmk, myKey, 16);

  if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println("Add Peer Failed");
    return;
  }
}

void loop() {
  strcpy(packet.msg, "Hello from Sender!");

  esp_now_send(receiverMAC, (uint8_t*)&packet, sizeof(packet));
  Serial.println("Encrypted message sent.");

  delay(2000); // Send every two seconds
}

3.8 Settings Both Students Must Match ​

StepReceiverSender
Start Wi-Fi station mode✅✅
Enter the other board's MAC✅ senderMAC[]✅ receiverMAC[]
Use the same AES key✅✅
Call esp_now_add_peer()✅✅
Set encrypt = true✅✅
Copy the key to peerInfo.lmk✅✅

If any one of these settings is wrong, the receiver gets no message.

3.9 Advanced Challenge ​

These are problems you will encounter frequently in later projects.

Advanced ESP-NOW challenge

4. ESP32 Web Server: Control an RGB LED from a Browser ​

Core model: ESP32 = server providing the page. Browser = client selecting a colour, POSTing JSON and controlling the LED.

Previously, the front end ran on your computer through VS Code's Live Server. That arrangement is convenient for local testing, but other devices cannot use the page when the computer is unavailable.

For a simple interface, the website can run directly on the ESP32 while the board acts as an access point (AP). This has several benefits:

  1. Independent of school Wi-Fi or a home router. The ESP32 creates its own hotspot, avoiding school-network passwords, client isolation and access-point limits.
  2. Very low deployment cost. A USB cable powers the complete system; there is no separate server, router or cloud service.
  3. Low latency and little external interference. Only the ESP32 and the connected device exchange local traffic.
  4. Convenient classroom demonstrations. Each group can run an independent access point and hardware setup.
  5. The website and hardware live on one device. This makes the data flow concrete: ESP32 server → HTML and JavaScript → browser client → HTTP request → hardware.
  6. Well suited to control panels. Colour pickers, buttons, sliders, LEDs and motor switches need little storage and respond quickly.

4.1 Complete Flow: Colour Picker × fetch × ESP32 ​

4.1.1 ESP32 Program: Web Server and RGB PWM ​

In AP mode:

  • the ESP32 creates its own Wi-Fi hotspot;
  • a phone or notebook connects directly;
  • no external Wi-Fi or router is required; and
  • the program does not call WiFi.begin().
cpp
#include <WiFi.h>
#include <WebServer.h>

WebServer server(80);

// Give every group a unique SSID to avoid dozens of identical networks
const char* ap_ssid = "ESP32-RGB-GROUP-01";
const char* ap_pass = "12345678";   // At least 8 characters for WPA2

int r_pin = 14, g_pin = 12, b_pin = 13;

String webpage = R"=====(
<!DOCTYPE html>
<html>
<body>
<h2>RGB LED Control (AP Mode)</h2>
<input type="color" id="picker">
<script>
document.getElementById("picker").addEventListener("input", e => {
    fetch("/set", {
        method: "POST",
        headers: {"Content-Type":"application/json"},
        body: JSON.stringify({color: e.target.value})
    });
});
</script>
</body>
</html>
)=====";

void handleRoot(){
  server.send(200, "text/html", webpage);
}

void handleSet(){
  String body = server.arg("plain");
  int idx = body.indexOf("#");
  String hex = body.substring(idx+1, idx+7);

  long rgb = strtol(hex.c_str(), NULL, 16);
  int r = (rgb >> 16) & 255;
  int g = (rgb >> 8) & 255;
  int b = rgb & 255;

  analogWrite(r_pin, r);
  analogWrite(g_pin, g);
  analogWrite(b_pin, b);

  server.send(200, "text/plain", "OK");
}

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

  // Create an access point; WiFi.begin() is not needed
  WiFi.mode(WIFI_AP);
  WiFi.softAP(ap_ssid, ap_pass);

  Serial.print("AP started. SSID: ");
  Serial.println(ap_ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.softAPIP());   // Normally 192.168.4.1

  pinMode(r_pin, OUTPUT);
  pinMode(g_pin, OUTPUT);
  pinMode(b_pin, OUTPUT);

  server.on("/", handleRoot);
  server.on("/set", HTTP_POST, handleSet);
  server.begin();
}

void loop(){
  server.handleClient();
}

4.1.2 Operating in AP Mode ​

  1. Power the ESP32. It creates a hotspot with the example settings:
    • SSID: ESP32-RGB-GROUP-01
    • password: 12345678
  2. Connect a phone or notebook to that hotspot.
  3. Open this address in a browser:
text
http://192.168.4.1/

The colour-picker page should appear.

4.2 Advanced Challenge ​

Advanced web-controlled RGB challenge

5. Summary ​

This chapter introduced two widely used IoT patterns.

1. Encrypted ESP-NOW Peer-to-Peer Communication ​

  • Connects a specific MAC address to another MAC address.
  • Both peers must be registered and use the same 16-byte key.
  • ESP32 firmware encrypts and decrypts the transmission automatically.
  • Appropriate for short control messages and low-latency local links.

2. ESP32 Access Point and Web Server Controlling an RGB LED ​

  • The ESP32 creates a Wi-Fi hotspot to which a phone connects directly.
  • No external router is required, making classroom use more reliable.
  • A browser loads the hosted page and uses fetch() to POST JSON that controls the hardware.
  • It demonstrates the complete front end → server → hardware data flow.

You have now practised secure peer-to-peer communication, a basic /set web API, and HTML colour-picker control of RGB PWM. Together, device-to-device communication and browser-to-hardware control form a foundation for later interactive IoT projects.

Translation notice: This English page was generated automatically by AI and has not yet been reviewed by a human. It will be checked and revised page by page when time permits.

Last updated: