Home / References / ESP32 Library / AsyncUDP Library
Description
The remoteMac() method is used to retrieve the MAC (Media Access Control) address of the sender of a UDP packet. This method is part of the AsyncUDPPacket class and copies the 6-byte MAC address of the device that sent the UDP packet into a provided buffer. It’s particularly useful for device identification and authentication, implementing MAC-based access control and filtering, creating wake-on-LAN applications, building network security protocols that validate sender hardware addresses, and developing network diagnostic tools that analyze device-specific communication patterns. The method works at the data link layer and provides hardware-level identification of the sending device, making it essential for applications requiring device-specific security, network asset management, and low-level network analysis.
Syntax and Usage
The remoteMac() method has a simple syntax and is called on an AsyncUDPPacket object:
Get remote MAC address:
packet.remoteMac(macBuffer)Copies the MAC address of the device that sent the UDP packet into the provided 6-byte buffer.
This page is part of the Comprehensive Guide to the ESP32 Arduino Core Library, accessible on AvantMaker.com.
Arguments
The remoteMac() method takes one argument:
- mac (uint8_t*): A pointer to a 6-byte buffer where the MAC address will be copied. The buffer must be at least 6 bytes long to store the complete MAC address (6 octets).
Return Value
The remoteMac() method has a void return type and does not return any value. Instead, it copies the 6-byte MAC address directly into the provided buffer. The MAC address is stored in the buffer as 6 consecutive bytes in the format [byte0, byte1, byte2, byte3, byte4, byte5], where each byte represents one octet of the MAC address. If the packet information is not available or the MAC address cannot be determined, the buffer contents may be undefined or contain zeros.
Example Code
Simple Example: MAC Address Monitor
This example demonstrates how to use the remoteMac() method to monitor and display MAC addresses of devices sending UDP packets. The application listens for UDP packets and displays detailed MAC address information including formatted display, vendor identification hints, and device tracking capabilities.
How to use this example: Upload this code to your ESP32 and replace the WiFi credentials. The ESP32 will listen for UDP packets on port 9999 and display the MAC address of each sender. This is useful for network device monitoring, access control implementation, and understanding which specific devices are communicating with your ESP32.
/*
* Author: Avant Maker
* Date: January 15, 2025
* Version: 1.0
* License: MIT
*
* Description:
* Simple MAC Address Monitor that demonstrates remoteMac() usage
* for tracking and identifying UDP packet senders by their hardware addresses.
* This example provides basic MAC address monitoring, formatted display,
* device tracking, and vendor identification hints. It's particularly useful
* for network security monitoring, device identification, access control
* implementation, and understanding network communication patterns at the
* hardware level.
*
* How to use this example:
* Upload this code to your ESP32 and replace the WiFi credentials.
* The ESP32 will listen for UDP packets on port 9999 and display the MAC
* address of each sender along with formatted information and device tracking.
* This is useful for network device monitoring, access control implementation,
* and understanding which specific devices are communicating with your ESP32.
*
* Test with netcat: echo "Hello ESP32" | nc -u ESP32_IP_ADDRESS 9999
* Or use any UDP client to send packets to port 9999.
*
* Features:
* - MAC address extraction and display
* - Formatted MAC address presentation
* - Device tracking and identification
* - Vendor identification hints
* - Access control demonstration
* - Network security monitoring
*
* Code Source:
* This example code is sourced from the Comprehensive Guide
* to the ESP32 Arduino Core Library, accessible on AvantMaker.com.
* For additional code examples and in-depth documentation related to
* the ESP32 Arduino Core Library, please visit:
*
* https://avantmaker.com/home/all-about-esp32-arduino-core-library/
*
* AvantMaker.com, your premier destination for all things
* DIY, AI, IoT, Smart Home, and STEM projects. We are dedicated
* to empowering makers, learners, and enthusiasts with
* the resources they need to bring their innovative ideas to life.
*/
#include <WiFi.h>
#include <AsyncUDP.h>
#include <map>
const char* ssid = "your_wifi_ssid";
const char* password = "your_wifi_password";
AsyncUDP udp;
// Structure to track device information
struct DeviceInfo {
String macAddress;
String lastMessage;
IPAddress lastIP;
unsigned long firstSeen;
unsigned long lastSeen;
unsigned long packetCount;
String deviceName;
};
// Map to store device information by MAC address
std::map<String, DeviceInfo> deviceMap;
// Function to format MAC address as human-readable string
String formatMacAddress(uint8_t* mac) {
String macStr = "";
for (int i = 0; i < 6; i++) {
if (mac[i] < 16) {
macStr += "0";
}
macStr += String(mac[i], HEX);
if (i < 5) {
macStr += ":";
}
}
macStr.toUpperCase();
return macStr;
}
// Function to get vendor hint from MAC address OUI (first 3 bytes)
String getVendorHint(uint8_t* mac) {
String oui = "";
for (int i = 0; i < 3; i++) {
if (mac[i] < 16) {
oui += "0";
}
oui += String(mac[i], HEX);
}
oui.toUpperCase();
// Common OUI prefixes (first 3 bytes of MAC address)
if (oui.startsWith("B8:27:EB") || oui.startsWith("DC:A6:32") || oui.startsWith("E4:5F:01")) {
return "Raspberry Pi Foundation";
} else if (oui.startsWith("AC:BC:32") || oui.startsWith("24:62:AB") || oui.startsWith("30:AE:A4")) {
return "Espressif Systems (ESP32/ESP8266)";
} else if (oui.startsWith("00:50:56") || oui.startsWith("00:0C:29") || oui.startsWith("00:05:69")) {
return "VMware Virtual Device";
} else if (oui.startsWith("F0:18:98") || oui.startsWith("A4:83:E7") || oui.startsWith("40:A3:6B")) {
return "Apple Inc.";
} else if (oui.startsWith("00:15:5D") || oui.startsWith("00:03:FF")) {
return "Microsoft Corporation";
} else if (oui.startsWith("52:54:00")) {
return "QEMU Virtual Machine";
}
return "Unknown Vendor";
}
// Function to generate a simple device name based on MAC
String generateDeviceName(String macAddr) {
// Use last 4 characters of MAC for simple identification
String suffix = macAddr.substring(macAddr.length() - 5);
suffix.replace(":", "");
return "Device_" + suffix;
}
void setup() {
Serial.begin(115200);
Serial.println("ESP32 UDP MAC Address Monitor");
// Connect to WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("WiFi connected!");
Serial.print("Station IP: ");
Serial.println(WiFi.localIP());
Serial.println("Starting UDP MAC Monitor on port 9999");
// Start UDP listener on port 9999
if (udp.listen(9999)) {
Serial.println("UDP MAC Monitor listening on port 9999");
Serial.println("Send UDP packets to monitor MAC addresses");
udp.onPacket([](AsyncUDPPacket packet) {
// Buffer to store MAC address (6 bytes)
uint8_t senderMac[6];
// Get MAC address using remoteMac() method
packet.remoteMac(senderMac);
// Format MAC address for display
String macAddress = formatMacAddress(senderMac);
// Get other packet information
IPAddress senderIP = packet.remoteIP();
uint16_t senderPort = packet.remotePort();
// Get packet data
String message = "";
size_t packetSize = packet.length();
for (size_t i = 0; i < packetSize && i < 100; i++) {
char c = packet.data()[i];
message += (c >= 32 && c <= 126) ? c : '.';
}
unsigned long currentTime = millis();
// Update or create device information
if (deviceMap.find(macAddress) == deviceMap.end()) {
// New device
DeviceInfo newDevice;
newDevice.macAddress = macAddress;
newDevice.lastMessage = message;
newDevice.lastIP = senderIP;
newDevice.firstSeen = currentTime;
newDevice.lastSeen = currentTime;
newDevice.packetCount = 1;
newDevice.deviceName = generateDeviceName(macAddress);
deviceMap[macAddress] = newDevice;
Serial.println("╔══════════════════════════════════════════════════════════════════╗");
Serial.println("║ NEW DEVICE DETECTED ║");
Serial.println("╚══════════════════════════════════════════════════════════════════╝");
} else {
// Existing device
deviceMap[macAddress].lastMessage = message;
deviceMap[macAddress].lastIP = senderIP;
deviceMap[macAddress].lastSeen = currentTime;
deviceMap[macAddress].packetCount++;
Serial.println("╔══════════════════════════════════════════════════════════════════╗");
Serial.println("║ KNOWN DEVICE ║");
Serial.println("╚══════════════════════════════════════════════════════════════════╝");
}
DeviceInfo device = deviceMap[macAddress];
// Display detailed MAC and device information
Serial.println("Device Information:");
Serial.printf(" Device Name: %s\n", device.deviceName.c_str());
Serial.printf(" MAC Address: %s\n", macAddress.c_str());
Serial.printf(" Vendor Hint: %s\n", getVendorHint(senderMac).c_str());
Serial.printf(" Current IP: %s:%d\n", senderIP.toString().c_str(), senderPort);
Serial.printf(" Message: %s%s\n", message.c_str(), packetSize > 100 ? "..." : "");
Serial.printf(" Packet Size: %d bytes\n", packetSize);
// Display MAC address in different formats
Serial.println("MAC Address Formats:");
Serial.printf(" Standard: %s\n", macAddress.c_str());
Serial.printf(" Raw Hex: ");
for (int i = 0; i < 6; i++) {
Serial.printf("%02X", senderMac[i]);
}
Serial.println();
Serial.printf(" Decimal: ");
for (int i = 0; i < 6; i++) {
Serial.printf("%d", senderMac[i]);
if (i < 5) Serial.print(".");
}
Serial.println();
// Display device statistics
Serial.println("Device Statistics:");
Serial.printf(" Total Packets: %lu\n", device.packetCount);
Serial.printf(" First Seen: %lu ms ago\n", currentTime - device.firstSeen);
Serial.printf(" Last Seen: %lu ms ago\n", currentTime - device.lastSeen);
Serial.printf(" Active Duration: %lu ms\n", device.lastSeen - device.firstSeen);
// Check for MAC address patterns
Serial.println("Analysis:");
if (senderMac[0] & 0x02) {
Serial.println(" MAC Type: Locally Administered");
} else {
Serial.println(" MAC Type: Universally Administered");
}
if (senderMac[0] & 0x01) {
Serial.println(" Address Type: Multicast");
} else {
Serial.println(" Address Type: Unicast");
}
// Simple access control demonstration
String vendor = getVendorHint(senderMac);
if (vendor.indexOf("Espressif") != -1) {
Serial.println(" Access Level: ESP32 Device - Full Access");
} else if (vendor.indexOf("Apple") != -1 || vendor.indexOf("Raspberry Pi") != -1) {
Serial.println(" Access Level: Trusted Device - Limited Access");
} else {
Serial.println(" Access Level: Unknown Device - Restricted Access");
}
Serial.println(" ────────────────────────────────────────────────────────────────");
// Send acknowledgment back to sender
String ackMessage = "MAC " + macAddress + " identified - Device: " + device.deviceName;
packet.printf("ACK: %s", ackMessage.c_str());
});
} else {
Serial.println("Failed to start UDP listener");
}
}
void loop() {
// Display periodic device summary
static unsigned long lastSummary = 0;
if (millis() - lastSummary > 60000) { // Every 60 seconds
lastSummary = millis();
Serial.println("╔══════════════════════════════════════════════════════════════════╗");
Serial.println("║ DEVICE SUMMARY ║");
Serial.println("╚══════════════════════════════════════════════════════════════════╝");
Serial.printf("Total Devices Observed: %d\n", deviceMap.size());
Serial.printf("WiFi Signal Strength: %d dBm\n", WiFi.RSSI());
Serial.printf("Free Heap Memory: %d bytes\n", ESP.getFreeHeap());
if (!deviceMap.empty()) {
Serial.println("\nActive Devices:");
Serial.println("MAC Address | Device Name | Packets | Last Seen | Vendor");
Serial.println("------------------|--------------|---------|-----------|------------------");
unsigned long currentTime = millis();
for (auto& device : deviceMap) {
uint8_t mac[6];
// Parse MAC string back to bytes for vendor lookup
sscanf(device.second.macAddress.c_str(), "%02X:%02X:%02X:%02X:%02X:%02X",
(unsigned int*)&mac[0], (unsigned int*)&mac[1], (unsigned int*)&mac[2],
(unsigned int*)&mac[3], (unsigned int*)&mac[4], (unsigned int*)&mac[5]);
String vendor = getVendorHint(mac);
if (vendor.length() > 15) vendor = vendor.substring(0, 15) + "...";
Serial.printf("%-17s | %-12s | %-7lu | %-9lu | %-18s\n",
device.second.macAddress.c_str(),
device.second.deviceName.c_str(),
device.second.packetCount,
(currentTime - device.second.lastSeen) / 1000,
vendor.c_str());
}
}
Serial.println("══════════════════════════════════════════════════════════════════");
}
delay(1000);
}Testing Example: UDP Sender for MAC Address Testing
This companion example demonstrates how to create a UDP sender using another ESP32 to test the MAC address monitoring functionality. This sender ESP32 will periodically send UDP packets to the receiver (running the previous example), allowing you to observe how the remoteMac() method captures and displays the sender’s MAC address information.
How to test with two ESP32 boards:
- Setup the Receiver: Upload the previous “MAC Address Monitor” code to your first ESP32 and note its IP address from the Serial Monitor.
- Setup the Sender: Upload this “UDP Sender” code to your second ESP32 and update the
receiverIPvariable with the first ESP32’s IP address. - Connect both ESP32s: Ensure both ESP32 boards are connected to the same WiFi network.
- Monitor the results: Open the Serial Monitor for the receiver ESP32 to see detailed MAC address information of the sender ESP32.
- Experiment: Try different message types, intervals, and observe how the receiver tracks the sender device.
Expected Results: The receiver ESP32 will display the sender’s MAC address, identify it as an Espressif device, assign it a device name, and track communication statistics. This demonstrates real-world MAC address monitoring and device identification capabilities.
/*
* Author: Avant Maker
* Date: January 15, 2025
* Version: 1.0
* License: MIT
*
* Description:
* UDP Sender for testing remoteMac() functionality. This example creates
* a UDP client that sends periodic packets to another ESP32 running the
* MAC Address Monitor example. It demonstrates practical UDP communication
* between ESP32 devices and allows testing of MAC address extraction and
* device identification features. The sender includes various message types,
* device information, and configurable sending intervals to provide
* comprehensive testing scenarios for MAC address monitoring applications.
*
* Hardware Requirements:
* - ESP32 development board
* - WiFi network access
* - Another ESP32 running the MAC Address Monitor example
*
* Setup Instructions:
* 1. Upload the MAC Address Monitor code to your first ESP32 (receiver)
* 2. Note the IP address of the receiver ESP32 from its Serial Monitor
* 3. Update the receiverIP variable below with the receiver's IP address
* 4. Upload this code to your second ESP32 (sender)
* 5. Connect both ESP32s to the same WiFi network
* 6. Monitor the receiver's Serial output to see MAC address detection
*
* Testing Scenarios:
* - Basic MAC address detection and device identification
* - Vendor identification through OUI (Organizationally Unique Identifier)
* - Device tracking and communication pattern analysis
* - Access control based on MAC address filtering
* - Network security monitoring and device authentication
*
* Code Source:
* This example code is sourced from the Comprehensive Guide
* to the ESP32 Arduino Core Library, accessible on AvantMaker.com.
* For additional code examples and in-depth documentation related to
* the ESP32 Arduino Core Library, please visit:
*
* https://avantmaker.com/home/all-about-esp32-arduino-core-library/
*
* AvantMaker.com, your premier destination for all things
* DIY, AI, IoT, Smart Home, and STEM projects. We are dedicated
* to empowering makers, learners, and enthusiasts with
* the resources they need to bring their innovative ideas to life.
*/
#include <WiFi.h>
#include <AsyncUDP.h>
// WiFi credentials
const char* ssid = "your_SSID"; // Replace with your Wi-Fi SSID
const char* password = "your_PASSWORD"; // Replace with your Wi-Fi password
// Receiver ESP32 configuration
IPAddress receiverIP(192, 168, 0, 123); // UPDATE THIS with your receiver ESP32's IP
const uint16_t receiverPort = 9999; // Port where receiver is listening
AsyncUDP udp;
unsigned long packetCounter = 0;
String deviceName = "ESP32_Sender";
// Message templates for testing
String messageTemplates[] = {
"Hello from ESP32 Sender!",
"MAC Address Test Packet",
"Device Identification Test",
"Network Security Test",
"IoT Communication Test",
"AvantMaker ESP32 Demo",
"UDP Protocol Test",
"Hardware Address Test"
};
void setup() {
Serial.begin(115200);
Serial.println("ESP32 UDP Sender for MAC Address Testing");
// Display device information
Serial.println("=== ESP32 Sender Information ===");
Serial.printf("Device Name: %s\n", deviceName.c_str());
Serial.printf("Chip Model: %s\n", ESP.getChipModel());
Serial.printf("CPU Frequency: %d MHz\n", ESP.getCpuFreqMHz());
Serial.printf("Flash Size: %d bytes\n", ESP.getFlashChipSize());
Serial.printf("Free Heap: %d bytes\n", ESP.getFreeHeap());
// Get and display MAC address
uint8_t macAddr[6];
WiFi.macAddress(macAddr);
Serial.printf("Sender MAC Address: %02X:%02X:%02X:%02X:%02X:%02X\n",
macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]);
Serial.println("================================");
// Connect to WiFi
Serial.printf("Connecting to WiFi network: %s\n", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("\nWiFi connected successfully!");
Serial.printf("Sender IP Address: %s\n", WiFi.localIP().toString().c_str());
Serial.printf("Gateway: %s\n", WiFi.gatewayIP().toString().c_str());
Serial.printf("Subnet Mask: %s\n", WiFi.subnetMask().toString().c_str());
Serial.printf("DNS Server: %s\n", WiFi.dnsIP().toString().c_str());
Serial.printf("WiFi Signal: %d dBm\n", WiFi.RSSI());
// Display receiver information
Serial.println("\n=== Receiver Configuration ===");
Serial.printf("Target IP: %s\n", receiverIP.toString().c_str());
Serial.printf("Target Port: %d\n", receiverPort);
Serial.println("===============================");
// AsyncUDP doesn't require begin() for sending
// The UDP object is ready to use after construction
Serial.println("UDP sender ready - no initialization required");
Serial.println("\nStarting UDP packet transmission...");
Serial.println("The receiver should display this device's MAC address");
Serial.println("Monitor is the receiver's Serial output for MAC detection");
Serial.println("=====================================\n");
}
void loop() {
static unsigned long lastSend = 0;
static int messageIndex = 0;
// Send packet every 10 seconds
if (millis() - lastSend > 10000) {
lastSend = millis();
packetCounter++;
// Create comprehensive test message
String message = createTestMessage(messageIndex);
// Send UDP packet to receiver
if (udp.writeTo((uint8_t*)message.c_str(), message.length(), receiverIP, receiverPort)) {
Serial.printf("Packet #%lu sent successfully\n", packetCounter);
Serial.printf("Message: %s\n", message.c_str());
Serial.printf("Size: %d bytes\n", message.length());
Serial.printf("Target: %s:%d\n", receiverIP.toString().c_str(), receiverPort);
// Display sender MAC again for reference
uint8_t macAddr[6];
WiFi.macAddress(macAddr);
Serial.printf("Sender MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]);
Serial.println("Check receiver's Serial Monitor for MAC address detection");
Serial.println("─────────────────────────────────────────────────────────");
} else {
Serial.printf("Failed to send packet #%lu\n", packetCounter);
Serial.println("Check network connection and receiver IP address");
}
// Cycle through different message templates
messageIndex = (messageIndex + 1) % (sizeof(messageTemplates) / sizeof(messageTemplates[0]));
}
// Display periodic status
static unsigned long lastStatus = 0;
if (millis() - lastStatus > 60000) { // Every 60 seconds
lastStatus = millis();
Serial.println("\n=== Sender Status Report ===");
Serial.printf("Runtime: %lu seconds\n", millis() / 1000);
Serial.printf("Packets sent: %lu\n", packetCounter);
Serial.printf("WiFi status: %s\n", WiFi.status() == WL_CONNECTED ? "Connected" : "Disconnected");
Serial.printf("Signal strength: %d dBm\n", WiFi.RSSI());
Serial.printf("Free heap: %d bytes\n", ESP.getFreeHeap());
Serial.printf("Receiver target: %s:%d\n", receiverIP.toString().c_str(), receiverPort);
Serial.println("============================\n");
}
delay(1000);
}
// Function to create comprehensive test messages
String createTestMessage(int templateIndex) {
String baseMessage = messageTemplates[templateIndex];
// Add device and network information
String fullMessage = baseMessage + " | ";
fullMessage += "Packet#" + String(packetCounter) + " | ";
fullMessage += "Device:" + deviceName + " | ";
fullMessage += "IP:" + WiFi.localIP().toString() + " | ";
fullMessage += "RSSI:" + String(WiFi.RSSI()) + "dBm | ";
fullMessage += "Heap:" + String(ESP.getFreeHeap()) + "b | ";
fullMessage += "Time:" + String(millis() / 1000) + "s";
return fullMessage;
}Complete Testing Setup Instructions
To fully test the remoteMac() method functionality with two ESP32 boards, follow these comprehensive steps:
Hardware Requirements
- Two ESP32 development boards
- USB cables for programming and power
- Access to a WiFi network
- Computer with Arduino IDE and ESP32 board support
Step-by-Step Setup
- Prepare ESP32 #1 (Receiver):
- Upload the first example code (“MAC Address Monitor”)
- Update WiFi credentials in the code
- Open Serial Monitor at 115200 baud
- Note the IP address displayed after WiFi connection
- Verify it’s listening on port 9999
- Prepare ESP32 #2 (Sender):
- Upload the second example code (“UDP Sender”)
- Update WiFi credentials to match the receiver
- Update the
receiverIPvariable with ESP32 #1’s IP address - Open Serial Monitor at 115200 baud
- Network Configuration:
- Ensure both ESP32s connect to the same WiFi network
- Verify both devices can communicate (same subnet)
- Check that UDP port 9999 is not blocked by firewall
- Testing and Verification:
- Monitor the receiver’s Serial output for MAC address detection
- Verify the sender’s MAC address is correctly displayed
- Check vendor identification (should show “Espressif Systems”)
- Observe device tracking and statistics
- Test different message types and frequencies
Expected Test Results
When both ESP32 boards are running correctly, you should observe:
- MAC Address Detection: The receiver displays the sender’s MAC address in standard format (XX:XX:XX:XX:XX:XX)
- Vendor Identification: Shows “Espressif Systems (ESP32/ESP8266)” as the vendor
- Device Tracking: Assigns a unique device name and tracks packet statistics
- Communication Analysis: Displays packet count, timing, and communication patterns
- Access Control: Demonstrates MAC-based access level assignment
- Network Information: Shows IP addresses, ports, and signal strength
Troubleshooting Tips
- No MAC address received: Check network connectivity and IP addresses
- Wrong vendor displayed: Verify the MAC address OUI database in the code
- Packets not received: Ensure both ESP32s are on the same network subnet
- Connection issues: Check WiFi credentials and signal strength
- Port conflicts: Verify port 9999 is available and not blocked
For more ESP32 development resources and tutorials, visit the All About ESP32 Resources Hub on AvantMaker.com
ESP32 Library Index
- ESP32 WiFi Library
- ESP32 WiFiClient Library
- ESP32 HTTPClient Library
- ESP32 WiFiClientSecure Library
- ESP32 WebServer Library
- ESP32 AsyncUDP Librarry
- Connection Management
- Data Transimission
- Broadcast Multicast
- Network Information
- Which ESP32 Boards are Recommended for Learners
- How to Copy Codes from AvantMaker.com
- What is SPIFFS and how to upload files to it?
- What is LIttleFS and how to upload files to it?
Ready to experiment and explore more about ESP32? Visit our website’s All About ESP32 Resources Hub, packed with tutorials, guides, and tools to inspire your maker journey. Experiment, explore, and elevate your skills with everything you need to master this powerful microcontroller platform!