Assignment Description

Week 14 (Networking and Communications)

Assignment

Weekly Assignment

  • Group Assignment: Send a message between two projects.
  • Individual Assignments: Design, build, and connect wired or wireless node(s) with network or bus addresses.

Group Assignment

Week 14

Wroked by myself...

Individual Assignments

Week 14

This section covers the "invisible" side of the project....

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.

Arduino Code Display

Arduino Code (XIAO_ESP-32S3_MAC_Address)

  
//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
}
  


  1. MAC_B1: {0x24, 0x58, 0x7C, 0xE4, 0x0F, 0x30}

  2. MAC_B2: {0x24, 0x58, 0x7C, 0xE4, 0x0E, 0x04}

  3. MAC_B3: {0x64, 0xE8, 0x33, 0x50, 0x52, 0xDC}







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).

Arduino Code Display

Arduino Code (ESP-NOW Network Code (CHAT))

  
//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': ");
  }
}