Week 14 (Networking and Communications)
Week 14
Week 14
While the Web Server (AP) is great for controlling the box with a phone, I wanted to use this specific assignment to experiment with something more robust for the future: device-to-device communication.
I didn't just pick this assignment at random. I have a bigger vision for this project. Right now, I control the box with my phone, but in a dusty CNC workshop, I don't always want to be touching my screen. My goal is to build a physical "Master Remote" that sits right on my CNC machine. I want to be able to press a physical button on that remote and control a network of Smart Boxes which might be on the other side of the LAB and open the correct drawer instantly. To do that, I needed a way for two ESP32 boards to talk to each other directly, instantly, and without relying on a potentially flaky shop Wi-Fi router.
To make this happen, I used ESP-NOW. If you haven't used it before, ESP-NOW is a connectionless communication protocol developed by Espressif. Unlike standard Wi-Fi, where a device has to find a router, negotiate a connection, get an IP address, and then send data (which takes seconds), ESP-NOW is like using a walkie-talkie. Implementing ESP-NOW is a two-step process because it relies on the physical hardware ID (MAC Address) of the chips, not IP addresses. First, I had to find out "who" my boards were. Every ESP32 has a unique MAC address burned into it. I wrote a simple script with the help of GEMINI to force the Wi-Fi hardware on and print this address to the Serial Monitor for the XIAO ESP32S3 Series.
//Walter Lenigan - Fab Academy 2025
#include "WiFi.h"
void setup() {
Serial.begin(115200);
// Add a small delay to give the board time to initialize.
delay(500);
Serial.println("Attempting to initialize WiFi...");
// Explicitly set the mode to Wi-Fi Station mode.
// This forces the radio hardware to turn on, which is often necessary
// to read the MAC address.
WiFi.mode(WIFI_STA);
// Disconnect any previous connection.
WiFi.disconnect();
// A small delay after mode set
delay(100);
Serial.print("ESP32 Board MAC Address: ");
Serial.println(WiFi.macAddress());
}
void loop() {
// Nothing to do here
}
Once I had the MAC addresses of my boards, I wrote the communication firmware. This code allows multiple boards (Board 1, 2, and 3) to chat with each other. The structure struct_message that contains a senderID and the text message. Change #define MY_BOARD_ID to 1, 2, or 3 for each board. To send a message you must firth type the board number (1, 2 or 3) to whom you want to communicate and then “:” (1: LENIGAN).
//Walter Lenigan - Fab Academy 2025
#include
#include
#include "esp_wifi.h"
// ======================= SETTINGS =======================
// !!! CHANGE THIS TO 1, 2, OR 3 FOR EACH BOARD !!!
#define MY_BOARD_ID 1
// Your 3 Boards:
uint8_t mac_table[3][6] = {
{0x24, 0x58, 0x7C, 0xE4, 0x0F, 0x30}, // Board 1 (ID 1)
{0x24, 0x58, 0x7C, 0xE4, 0x0E, 0x04}, // Board 2 (ID 2)
{0x64, 0xE8, 0x33, 0x50, 0x52, 0xDC} // Board 3 (ID 3)
};
#define WIFI_CHANNEL 1
// ========================================================
typedef struct struct_message {
int senderID;
char message[200];
} struct_message;
struct_message dataToSend;
struct_message receivedData;
// ========= THE FIX IS HERE =========
// Your library version (3.3.3) requires 'const wifi_tx_info_t *'
// instead of the older 'const uint8_t *'.
void OnDataSent(const wifi_tx_info_t *info, esp_now_send_status_t status) {
// We don't need to use the 'info' variable for this simple chat,
// but the function signature MUST match what the library expects.
}
// Callback when data is received
// (This one also uses the newer 'esp_now_recv_info' signature)
void OnDataRecv(const esp_now_recv_info * info, const uint8_t *incomingData, int len) {
memcpy(&receivedData, incomingData, sizeof(receivedData));
Serial.println();
Serial.print("--- Message from Board ");
Serial.print(receivedData.senderID);
Serial.println(" ---");
Serial.println(receivedData.message);
Serial.println("------------------------");
Serial.print("Enter 'TargetID:Message': ");
}
void setup() {
Serial.begin(115200);
// Wait a moment for Serial to start
delay(1000);
Serial.println();
Serial.print("Initializing Board ");
Serial.println(MY_BOARD_ID);
WiFi.mode(WIFI_STA);
WiFi.disconnect();
esp_wifi_set_promiscuous(true);
esp_wifi_set_channel(WIFI_CHANNEL, WIFI_SECOND_CHAN_NONE);
esp_wifi_set_promiscuous(false);
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
// Register Peers
esp_now_peer_info_t peerInfo = {};
peerInfo.channel = WIFI_CHANNEL;
peerInfo.encrypt = false;
for (int i = 0; i < 3; i++) {
// We register everyone except ourselves
// (Array index 'i' corresponds to Board ID 'i+1')
if ( (i + 1) != MY_BOARD_ID ) {
memcpy(peerInfo.peer_addr, mac_table[i], 6);
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.printf("Failed to add Board %d as peer\n", i+1);
} else {
Serial.printf("Board %d registered as peer.\n", i+1);
}
}
}
esp_now_register_send_cb(OnDataSent);
esp_now_register_recv_cb(OnDataRecv);
Serial.println("Ready!");
Serial.println("Syntax: Type 'TargetID:Message'");
Serial.println("Example: '2:Hello' sends to Board 2");
Serial.print("Enter 'TargetID:Message': ");
}
void loop() {
if (Serial.available() > 0) {
String input = Serial.readStringUntil('\n');
input.trim();
int separatorIndex = input.indexOf(':');
if (separatorIndex == -1) {
Serial.println("\nError: Use format 'ID:Message' (e.g. 2:Hello)");
Serial.print("Enter 'TargetID:Message': ");
return;
}
String targetStr = input.substring(0, separatorIndex);
String msgStr = input.substring(separatorIndex + 1);
int targetID = targetStr.toInt();
// Basic Validation
if (targetID < 1 || targetID > 3 || targetID == MY_BOARD_ID) {
Serial.printf("\nError: Invalid Target ID %d.\n", targetID);
Serial.print("Enter 'TargetID:Message': ");
return;
}
dataToSend.senderID = MY_BOARD_ID;
msgStr.toCharArray(dataToSend.message, sizeof(dataToSend.message));
uint8_t *targetMAC = mac_table[targetID - 1];
esp_err_t result = esp_now_send(targetMAC, (uint8_t *) &dataToSend, sizeof(dataToSend));
if (result == ESP_OK) {
Serial.printf("\nSent to Board %d: %s\n", targetID, dataToSend.message);
} else {
Serial.println("\nError sending data.");
}
Serial.print("Enter 'TargetID:Message': ");
}
}