ESP32 AsyncUDP Library – isMulticast()

Home / References / ESP32 Library / AsyncUDP Library

Description

The isMulticast() method is used to determine whether a received UDP packet was sent as a multicast message. This method examines the local IP address of the received packet and checks if it matches multicast address patterns. A multicast packet is sent to a specific group of devices that have joined the multicast group, and is typically used for streaming media, group communications, network protocols like mDNS (Multicast DNS), service discovery, and distributed applications. The method returns true if the packet was sent to a multicast address such as IPv4 multicast addresses in the range 224.0.0.0 to 239.255.255.255, or IPv6 multicast addresses that start with FF00::/8. This functionality is essential for implementing protocols like UPnP, SSDP, Bonjour/Zeroconf, streaming protocols, and any application that needs to distinguish between unicast, broadcast, and multicast communications. The method works by analyzing the destination IP address that was used when the packet was received, supporting both IPv4 and IPv6 multicast detection using the underlying lwIP stack’s ip_addr_ismulticast() function.

Syntax and Usage

The isMulticast() method is called on an AsyncUDPPacket object:

bool isMulticastPacket = packet.isMulticast()

 Returns true if the received packet was sent as a multicast.

Arguments

The isMulticast() method takes no arguments. It operates on the packet data that was already received and stored in the AsyncUDPPacket object.

For more ESP32 development resources and tutorials, visit the All About ESP32 Resources Hub on  AvantMaker.com

Return Value

The isMulticast() method returns a bool value indicating whether the received packet was sent as a multicast message. It returns true if the packet’s destination address matches any multicast address pattern: IPv4 multicast addresses (224.0.0.0 to 239.255.255.255) or IPv6 multicast addresses (FF00::/8). It returns false for unicast packets (sent to a specific device), broadcast packets (sent to all devices), or when the destination address doesn’t fall within multicast ranges. The method helps applications distinguish between different types of network communications and can be used to implement multicast-specific handling logic, group communication protocols, or service discovery mechanisms. This is particularly useful in multimedia streaming applications, network service discovery protocols like mDNS, distributed systems that use multicast for coordination, and IoT applications that need to participate in multicast groups for efficient group communications.

Example Code

Multicast Media Streaming System with Group Communication and Device Discovery

This example demonstrates how to use the isMulticast() method to create a comprehensive multicast streaming system where devices can join multicast groups for receiving streamed content, participate in group communications, and discover services using multicast protocols. The system distinguishes between multicast, broadcast, and unicast communications, maintains group memberships, and provides real-time media streaming capabilities.

Multi-Device Testing Setup Instructions

Required Hardware: 2-4 ESP32 development boards on the same WiFi network

Step-by-Step Testing Guide:

  1. Upload Streaming Server Code: Upload the code below to your first ESP32 (this will be the multicast streaming server)
  2. Upload Client Receivers Code: Upload the client code (provided after the main example) to 2-3 additional ESP32 boards
  3. Monitor Serial Outputs: Open Serial Monitor for all devices at 115200 baud
  4. Verify Network Connection: Ensure all devices connect to the same WiFi network
  5. Watch Multicast Streaming: Observe how the server sends multicast streams and clients receive them
  6. Test Group Membership: See how devices join and leave multicast groups
  7. Verify Multicast Detection: Notice how devices differentiate between multicast, broadcast, and unicast messages

What You’ll See:

  • Automatic multicast-based media streaming across the network
  • Distinction between multicast, broadcast, and unicast packet handling
  • Real-time group membership management and streaming coordination
  • Multicast service discovery and announcement systems
  • Network monitoring and streaming quality metrics

Testing Scenarios:

  • Stream Distribution: Watch the server distribute media streams to multiple receivers simultaneously
  • Group Communication: See how devices communicate within multicast groups
  • Packet Classification: Observe different handling for multicast vs broadcast vs unicast messages
  • Service Discovery: Test multicast-based service announcement and discovery
/*
 * Author: Avant Maker
 * Date: June 18, 2025
 * Version: 1.0
 * License: MIT 
 * 
 * Description: 
 * This example demonstrates how to use the isMulticast() method to create a
 * comprehensive multicast streaming system where devices can join multicast groups
 * for receiving streamed content, participate in group communications, and discover
 * services using multicast protocols. The system distinguishes between multicast,
 * broadcast, and unicast communications, maintains group memberships, and provides
 * real-time media streaming capabilities.
 *
 * How to use this example:
 * Upload this code to your ESP32 and replace the WiFi credentials. The system
 * will stream content to multicast groups, handle multicast service discovery,
 * manage group memberships, and provide streaming quality metrics. Use the 
 * isMulticast() method to differentiate between multicast streams and other
 * types of network communications.
 *
 * 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, 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 <ArduinoJson.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

// Multicast streaming configuration
const IPAddress STREAM_MULTICAST_IP(239, 255, 1, 100);  // Multicast group for streaming
const IPAddress SERVICE_MULTICAST_IP(239, 255, 1, 200); // Multicast group for service discovery
const IPAddress CONTROL_MULTICAST_IP(239, 255, 1, 300); // Multicast group for control messages
const uint16_t STREAM_PORT = 9000;
const uint16_t SERVICE_PORT = 9001;
const uint16_t CONTROL_PORT = 9002;
const uint16_t RESPONSE_PORT = 9003;

// Streaming configuration
const unsigned long STREAM_INTERVAL = 100;        // Send stream data every 100ms
const unsigned long SERVICE_ANNOUNCE_INTERVAL = 15000;  // Announce service every 15 seconds
const unsigned long GROUP_MANAGEMENT_INTERVAL = 30000;  // Manage groups every 30 seconds
const unsigned long STATS_REPORT_INTERVAL = 60000;      // Report stats every minute

// Media content types
enum ContentType {
  AUDIO_STREAM = 1,
  VIDEO_STREAM = 2,
  DATA_STREAM = 3,
  CONTROL_MESSAGE = 4,
  SERVICE_ANNOUNCEMENT = 5
};

// Stream information structure
struct StreamInfo {
  String streamId;
  String streamName;
  ContentType contentType;
  IPAddress multicastAddress;
  uint16_t port;
  String description;
  int bitrate;
  String codec;
  bool isActive;
  unsigned long startTime;
  unsigned long totalPacketsSent;
  unsigned long totalBytesSent;
  std::vector<IPAddress> subscribers;
};

// System configuration
struct MulticastStreamingSystem {
  String serverId;
  String serverName;
  AsyncUDP streamUDP;
  AsyncUDP serviceUDP;
  AsyncUDP controlUDP;
  AsyncUDP responseUDP;
  std::vector<StreamInfo> availableStreams;
  unsigned long systemStartTime;
  unsigned long lastStreamSend;
  unsigned long lastServiceAnnounce;
  unsigned long lastGroupManagement;
  unsigned long totalMulticastsReceived;
  unsigned long totalBroadcastsReceived;
  unsigned long totalUnicastsReceived;
  unsigned long totalMulticastsSent;
  unsigned long totalBroadcastsSent;
  unsigned long totalUnicastsSent;
  int currentSequenceNumber;
  bool streamingActive;
} streaming;

void setup() {
  Serial.begin(115200);
  Serial.println("Starting ESP32 Multicast Streaming Server...");
  
  // Initialize server configuration
  streaming.serverId = "ESP32_STREAM_" + String((uint32_t)ESP.getEfuseMac(), HEX);
  streaming.serverName = "ESP32 Multicast Media Server";
  streaming.systemStartTime = millis();
  streaming.currentSequenceNumber = 0;
  streaming.streamingActive = true;
  
  Serial.print("Server ID: ");
  Serial.println(streaming.serverId);
  Serial.print("Server Name: ");
  Serial.println(streaming.serverName);
  
  // Initialize WiFi
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi connected successfully!");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.print("Signal strength: ");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");
  
  // Initialize multicast listeners and streams
  setupMulticastListeners();
  initializeStreamingServices();
  
  Serial.println("🎬 Multicast Streaming Server is ready...");
}

void setupMulticastListeners() {
  Serial.println("=== Setting Up Multicast Listeners ===");
  
  // Setup streaming multicast listener
  if (streaming.streamUDP.listenMulticast(STREAM_MULTICAST_IP, STREAM_PORT)) {
    Serial.print("✅ Stream multicast listener started on ");
    Serial.print(STREAM_MULTICAST_IP);
    Serial.print(":");
    Serial.println(STREAM_PORT);
    
    streaming.streamUDP.onPacket([](AsyncUDPPacket packet) {
      handleStreamPacket(packet);
    });
  } else {
    Serial.println("❌ Failed to start stream multicast listener");
  }
  
  // Setup service discovery multicast listener
  if (streaming.serviceUDP.listenMulticast(SERVICE_MULTICAST_IP, SERVICE_PORT)) {
    Serial.print("✅ Service multicast listener started on ");
    Serial.print(SERVICE_MULTICAST_IP);
    Serial.print(":");
    Serial.println(SERVICE_PORT);
    
    streaming.serviceUDP.onPacket([](AsyncUDPPacket packet) {
      handleServicePacket(packet);
    });
  } else {
    Serial.println("❌ Failed to start service multicast listener");
  }
  
  // Setup control multicast listener
  if (streaming.controlUDP.listenMulticast(CONTROL_MULTICAST_IP, CONTROL_PORT)) {
    Serial.print("✅ Control multicast listener started on ");
    Serial.print(CONTROL_MULTICAST_IP);
    Serial.print(":");
    Serial.println(CONTROL_PORT);
    
    streaming.controlUDP.onPacket([](AsyncUDPPacket packet) {
      handleControlPacket(packet);
    });
  } else {
    Serial.println("❌ Failed to start control multicast listener");
  }
  
  // Setup response listener for unicast responses
  if (streaming.responseUDP.listen(RESPONSE_PORT)) {
    Serial.print("✅ Response listener started on port ");
    Serial.println(RESPONSE_PORT);
    
    streaming.responseUDP.onPacket([](AsyncUDPPacket packet) {
      handleResponsePacket(packet);
    });
  } else {
    Serial.println("❌ Failed to start response listener");
  }
}

void handleStreamPacket(AsyncUDPPacket packet) {
  // Extract message content
  String message = "";
  for (size_t i = 0; i < packet.length(); i++) {
    message += (char)packet.data()[i];
  }
  message.trim();
  
  // Use isMulticast() to verify this is a multicast packet
  bool isMulticastPacket = packet.isMulticast();
  bool isBroadcastPacket = packet.isBroadcast();
  
  Serial.println("🎬 === Stream Packet Received ===");
  Serial.print("From: ");
  Serial.print(packet.remoteIP());
  Serial.print(":");
  Serial.println(packet.remotePort());
  Serial.print("Message: ");
  Serial.println(message.substring(0, 50) + "...");
  Serial.print("🔍 MULTICAST DETECTION: ");
  Serial.println(isMulticastPacket ? "MULTICAST MESSAGE" : "NON-MULTICAST MESSAGE");
  Serial.print("Is Broadcast: ");
  Serial.println(isBroadcastPacket ? "YES" : "NO");
  
  // Update statistics based on packet type
  if (isMulticastPacket) {
    streaming.totalMulticastsReceived++;
    Serial.println("📊 Multicast packet counter incremented");
  } else if (isBroadcastPacket) {
    streaming.totalBroadcastsReceived++;
    Serial.println("📊 Broadcast packet counter incremented");
  } else {
    streaming.totalUnicastsReceived++;
    Serial.println("📊 Unicast packet counter incremented");
  }
  
  // Skip processing our own messages
  if (message.indexOf(streaming.serverId) >= 0) {
    Serial.println("⚠️  Ignoring own stream message");
    return;
  }
  
  // Process different types of stream messages
  if (message.startsWith("STREAM_REQUEST")) {
    if (isMulticastPacket) {
      Serial.println("🎬 Processing multicast stream request");
      processStreamRequest(message, packet);
    } else {
      Serial.println("🎬 Processing direct stream request");
      processStreamRequest(message, packet);
    }
  } 
  else if (message.startsWith("STREAM_DATA")) {
    Serial.println("📡 Processing incoming stream data");
    processIncomingStreamData(message, packet);
  }
  else if (message.startsWith("QUALITY_REPORT")) {
    Serial.println("📊 Processing quality report");
    processQualityReport(message, packet);
  }
  
  Serial.println("=== End Stream Packet Processing ===");
}

void handleServicePacket(AsyncUDPPacket packet) {
  // Extract message content
  String message = "";
  for (size_t i = 0; i < packet.length(); i++) {
    message += (char)packet.data()[i];
  }
  message.trim();
  
  // Check packet type using isMulticast()
  bool isMulticastPacket = packet.isMulticast();
  bool isBroadcastPacket = packet.isBroadcast();
  
  Serial.println("🔍 === Service Discovery Packet Received ===");
  Serial.print("From: ");
  Serial.print(packet.remoteIP());
  Serial.print(":");
  Serial.println(packet.remotePort());
  Serial.print("Message: ");
  Serial.println(message);
  Serial.print("🔍 MULTICAST DETECTION: ");
  Serial.println(isMulticastPacket ? "MULTICAST SERVICE MESSAGE" : "NON-MULTICAST SERVICE MESSAGE");
  Serial.print("Is Broadcast: ");
  Serial.println(isBroadcastPacket ? "YES" : "NO");
  
  // Update statistics
  if (isMulticastPacket) {
    streaming.totalMulticastsReceived++;
  } else if (isBroadcastPacket) {
    streaming.totalBroadcastsReceived++;
  } else {
    streaming.totalUnicastsReceived++;
  }
  
  // Skip processing our own messages
  if (message.indexOf(streaming.serverId) >= 0) {
    Serial.println("⚠️  Ignoring own service message");
    return;
  }
  
  // Process service discovery messages
  if (message.startsWith("SERVICE_DISCOVERY")) {
    if (isMulticastPacket) {
      Serial.println("🔍 Responding to multicast service discovery");
      respondToServiceDiscovery(packet);
    } else {
      Serial.println("🔍 Responding to direct service discovery");
      respondToServiceDiscovery(packet);
    }
  }
  else if (message.startsWith("SERVICE_ANNOUNCE")) {
    Serial.println("📢 Processing service announcement");
    processServiceAnnouncement(message, packet);
  }
  
  Serial.println("=== End Service Discovery Packet Processing ===");
}

void handleControlPacket(AsyncUDPPacket packet) {
  // Extract message content
  String message = "";
  for (size_t i = 0; i < packet.length(); i++) {
    message += (char)packet.data()[i];
  }
  message.trim();
  
  // Verify multicast nature using isMulticast()
  bool isMulticastPacket = packet.isMulticast();
  
  Serial.println("🎛️  === Control Packet Received ===");
  Serial.print("From: ");
  Serial.print(packet.remoteIP());
  Serial.print(":");
  Serial.println(packet.remotePort());
  Serial.print("Command: ");
  Serial.println(message);
  Serial.print("🔍 MULTICAST DETECTION: ");
  Serial.println(isMulticastPacket ? "MULTICAST CONTROL" : "NON-MULTICAST CONTROL");
  
  // Update statistics
  if (isMulticastPacket) {
    streaming.totalMulticastsReceived++;
  } else if (packet.isBroadcast()) {
    streaming.totalBroadcastsReceived++;
  } else {
    streaming.totalUnicastsReceived++;
  }
  
  // Process control commands
  if (message.startsWith("START_STREAM")) {
    Serial.println("▶️  Processing start stream command");
    processStartStreamCommand(message, packet);
  }
  else if (message.startsWith("STOP_STREAM")) {
    Serial.println("⏹️  Processing stop stream command");
    processStopStreamCommand(message, packet);
  }
  else if (message.startsWith("JOIN_GROUP")) {
    Serial.println("➕ Processing join group command");
    processJoinGroupCommand(message, packet);
  }
  else if (message.startsWith("LEAVE_GROUP")) {
    Serial.println("➖ Processing leave group command");
    processLeaveGroupCommand(message, packet);
  }
  
  Serial.println("=== End Control Packet Processing ===");
}

void handleResponsePacket(AsyncUDPPacket packet) {
  // Extract message content
  String message = "";
  for (size_t i = 0; i < packet.length(); i++) {
    message += (char)packet.data()[i];
  }
  message.trim();
  
  // Check if this is a multicast response (unusual but possible)
  bool isMulticastPacket = packet.isMulticast();
  bool isBroadcastPacket = packet.isBroadcast();
  
  Serial.println("📨 === Response Packet Received ===");
  Serial.print("From: ");
  Serial.print(packet.remoteIP());
  Serial.print(":");
  Serial.println(packet.remotePort());
  Serial.print("Message Length: ");
  Serial.println(packet.length());
  Serial.print("🔍 MULTICAST DETECTION: ");
  Serial.println(isMulticastPacket ? "MULTICAST RESPONSE" : "NON-MULTICAST RESPONSE");
  Serial.print("Is Broadcast: ");
  Serial.println(isBroadcastPacket ? "YES" : "NO");
  
  // Update statistics
  if (isMulticastPacket) {
    streaming.totalMulticastsReceived++;
  } else if (isBroadcastPacket) {
    streaming.totalBroadcastsReceived++;
  } else {
    streaming.totalUnicastsReceived++;
  }
  
  // Process responses
  if (message.startsWith("STREAM_ACK")) {
    Serial.println("✅ Processing stream acknowledgment");
    processStreamAcknowledgment(message, packet);
  }
  else if (message.startsWith("SERVICE_INFO")) {
    Serial.println("📋 Processing service information");
    processServiceInformation(message, packet);
  }
  else if (message.startsWith("QUALITY_FEEDBACK")) {
    Serial.println("📊 Processing quality feedback");
    processQualityFeedback(message, packet);
  }
  
  Serial.println("=== End Response Packet Processing ===");
}

void initializeStreamingServices() {
  Serial.println("🎬 === Initializing Streaming Services ===");
  
  // Create audio stream
  StreamInfo audioStream;
  audioStream.streamId = "AUDIO_001";
  audioStream.streamName = "Live Audio Feed";
  audioStream.contentType = AUDIO_STREAM;
  audioStream.multicastAddress = STREAM_MULTICAST_IP;
  audioStream.port = STREAM_PORT;
  audioStream.description = "High-quality audio stream";
  audioStream.bitrate = 128000;  // 128 kbps
  audioStream.codec = "MP3";
  audioStream.isActive = true;
  audioStream.startTime = millis();
  audioStream.totalPacketsSent = 0;
  audioStream.totalBytesSent = 0;
  streaming.availableStreams.push_back(audioStream);
  
  // Create video stream
  StreamInfo videoStream;
  videoStream.streamId = "VIDEO_001";
  videoStream.streamName = "Live Video Feed";
  videoStream.contentType = VIDEO_STREAM;
  videoStream.multicastAddress = IPAddress(239, 255, 1, 101);
  videoStream.port = STREAM_PORT + 1;
  videoStream.description = "HD video stream";
  videoStream.bitrate = 2000000;  // 2 Mbps
  videoStream.codec = "H.264";
  videoStream.isActive = true;
  videoStream.startTime = millis();
  videoStream.totalPacketsSent = 0;
  videoStream.totalBytesSent = 0;
  streaming.availableStreams.push_back(videoStream);
  
  // Create data stream
  StreamInfo dataStream;
  dataStream.streamId = "DATA_001";
  dataStream.streamName = "Sensor Data Stream";
  dataStream.contentType = DATA_STREAM;
  dataStream.multicastAddress = IPAddress(239, 255, 1, 102);
  dataStream.port = STREAM_PORT + 2;
  dataStream.description = "Real-time sensor data";
  dataStream.bitrate = 64000;  // 64 kbps
  dataStream.codec = "JSON";
  dataStream.isActive = true;
  dataStream.startTime = millis();
  dataStream.totalPacketsSent = 0;
  dataStream.totalBytesSent = 0;
  streaming.availableStreams.push_back(dataStream);
  
  Serial.print("✅ Initialized ");
  Serial.print(streaming.availableStreams.size());
  Serial.println(" streaming services");
}

String createStreamDataPacket(StreamInfo& stream) {
  String packet = "{";
  packet += "\"type\":\"STREAM_DATA\",";
  packet += "\"streamId\":\"" + stream.streamId + "\",";
  packet += "\"sequence\":" + String(streaming.currentSequenceNumber++) + ",";
  packet += "\"timestamp\":" + String(millis()) + ",";
  packet += "\"serverId\":\"" + streaming.serverId + "\",";
  packet += "\"contentType\":" + String((int)stream.contentType) + ",";
  packet += "\"bitrate\":" + String(stream.bitrate) + ",";
  packet += "\"codec\":\"" + stream.codec + "\",";
  
  // Simulate content based on stream type
  if (stream.contentType == AUDIO_STREAM) {
    packet += "\"audioData\":\"" + generateSimulatedAudioData() + "\",";
  } else if (stream.contentType == VIDEO_STREAM) {
    packet += "\"videoFrame\":\"" + generateSimulatedVideoFrame() + "\",";
  } else if (stream.contentType == DATA_STREAM) {
    packet += "\"sensorData\":{" + generateSimulatedSensorData() + "},";
  }
  
  packet += "\"quality\":\"HIGH\"";
  packet += "}";
  
  return packet;
}

String generateSimulatedAudioData() {
  // Simulate audio data with frequency and amplitude
  float frequency = 440.0 + random(-50, 50);  // A4 note with variation
  float amplitude = 0.8 + (random(0, 40) / 100.0);
  return "freq:" + String(frequency, 2) + ",amp:" + String(amplitude, 2);
}

String generateSimulatedVideoFrame() {
  // Simulate video frame metadata
  int frameNumber = streaming.currentSequenceNumber;
  int width = 1920;
  int height = 1080;
  String frameType = (frameNumber % 30 == 0) ? "I" : "P";  // I-frame every 30 frames
  return "frame:" + String(frameNumber) + ",res:" + String(width) + "x" + String(height) + ",type:" + frameType;
}

String generateSimulatedSensorData() {
  // Simulate sensor readings
  float temperature = 20.0 + random(-10, 15) + (random(0, 100) / 100.0);
  float humidity = 45.0 + random(-15, 25) + (random(0, 100) / 100.0);
  float pressure = 1013.25 + random(-20, 20) + (random(0, 100) / 100.0);
  
  String data = "\"temperature\":" + String(temperature, 2) + ",";
  data += "\"humidity\":" + String(humidity, 2) + ",";
  data += "\"pressure\":" + String(pressure, 2);
  return data;
}

void sendMulticastStream() {
  if (!streaming.streamingActive) {
    return;
  }
  
  Serial.println("📡 === Sending Multicast Streams ===");
  
  for (size_t i = 0; i < streaming.availableStreams.size(); i++) {
    StreamInfo& stream = streaming.availableStreams[i];
    
    if (stream.isActive) {
      String streamData = createStreamDataPacket(stream);
      
      // Send to multicast group
      size_t sentBytes = streaming.streamUDP.writeTo(
        (uint8_t*)streamData.c_str(), 
        streamData.length(), 
        stream.multicastAddress, 
        stream.port
      );
      
      if (sentBytes > 0) {
        streaming.totalMulticastsSent++;
        stream.totalPacketsSent++;
        stream.totalBytesSent += sentBytes;
        
        Serial.print("✅ Stream ");
        Serial.print(stream.streamId);
        Serial.print(" sent to multicast group ");
        Serial.print(stream.multicastAddress);
        Serial.print(" (");
        Serial.print(sentBytes);
        Serial.println(" bytes)");
        Serial.println("🔍 This multicast will be detected by isMulticast() on receiving devices");
      } else {
        Serial.print("❌ Failed to send stream ");
        Serial.println(stream.streamId);
      }
    }
  }
  
  streaming.lastStreamSend = millis();
}

void sendServiceAnnouncement() {
  Serial.println("📢 === Sending Service Announcement ===");
  
  String announcement = "{";
  announcement += "\"type\":\"SERVICE_ANNOUNCE\",";
  announcement += "\"serverId\":\"" + streaming.serverId + "\",";
  announcement += "\"serverName\":\"" + streaming.serverName + "\",";
  announcement += "\"timestamp\":" + String(millis()) + ",";
  announcement += "\"services\":[";
  
  for (size_t i = 0; i < streaming.availableStreams.size(); i++) {
    if (i > 0) announcement += ",";
    StreamInfo& stream = streaming.availableStreams[i];
    announcement += "{";
    announcement += "\"id\":\"" + stream.streamId + "\",";
    announcement += "\"name\":\"" + stream.streamName + "\",";
    announcement += "\"type\":" + String((int)stream.contentType) + ",";
    announcement += "\"multicastIP\":\"" + stream.multicastAddress.toString() + "\",";
    announcement += "\"port\":" + String(stream.port) + ",";
    announcement += "\"bitrate\":" + String(stream.bitrate) + ",";
    announcement += "\"codec\":\"" + stream.codec + "\",";
    announcement += "\"active\":" + String(stream.isActive ? "true" : "false");
    announcement += "}";
  }
  
  announcement += "],";
  announcement += "\"uptime\":" + String((millis() - streaming.systemStartTime) / 1000);
  announcement += "}";
  
  // Send to service discovery multicast group
  size_t sentBytes = streaming.serviceUDP.writeTo(
    (uint8_t*)announcement.c_str(), 
    announcement.length(), 
    SERVICE_MULTICAST_IP, 
    SERVICE_PORT
  );
  
  if (sentBytes > 0) {
    streaming.totalMulticastsSent++;
    streaming.lastServiceAnnounce = millis();
    Serial.print("✅ Service announcement sent to multicast group ");
    Serial.print(SERVICE_MULTICAST_IP);
    Serial.print(" (");
    Serial.print(sentBytes);
    Serial.println(" bytes)");
    Serial.println("🔍 This multicast will be detected by isMulticast() on receiving devices");
  } else {
    Serial.println("❌ Failed to send service announcement");
  }
}

void processStreamRequest(const String& message, AsyncUDPPacket& packet) {
  Serial.println("📤 Processing stream request");
  
  // Parse stream request
  String streamId = extractValueFromMessage(message, "streamId");
  
  // Find requested stream
  for (size_t i = 0; i < streaming.availableStreams.size(); i++) {
    if (streaming.availableStreams[i].streamId == streamId) {
      String response = "{";
      response += "\"type\":\"STREAM_ACK\",";
      response += "\"streamId\":\"" + streamId + "\",";
      response += "\"multicastIP\":\"" + streaming.availableStreams[i].multicastAddress.toString() + "\",";
      response += "\"port\":" + String(streaming.availableStreams[i].port) + ",";
      response += "\"status\":\"AVAILABLE\"";
      response += "}";
      
      // Send unicast response
      size_t sentBytes = packet.printf("%s", response.c_str());
      if (sentBytes > 0) {
        streaming.totalUnicastsSent++;
        Serial.println("✅ Stream acknowledgment sent");
      }
      return;
    }
  }
  
  // Stream not found
  String response = "{\"type\":\"STREAM_ACK\",\"streamId\":\"" + streamId + "\",\"status\":\"NOT_FOUND\"}";
  size_t sentBytes = packet.printf("%s", response.c_str());
  if (sentBytes > 0) {
    streaming.totalUnicastsSent++;
  }
}

void processIncomingStreamData(const String& message, AsyncUDPPacket& packet) {
  // Process incoming stream data from other servers
  String streamId = extractValueFromMessage(message, "streamId");
  Serial.print("📡 Processing incoming stream data for: ");
  Serial.println(streamId);
}

void processQualityReport(const String& message, AsyncUDPPacket& packet) {
  // Process quality reports from clients
  String streamId = extractValueFromMessage(message, "streamId");
  String quality = extractValueFromMessage(message, "quality");
  Serial.print("📊 Quality report for ");
  Serial.print(streamId);
  Serial.print(": ");
  Serial.println(quality);
}

void respondToServiceDiscovery(AsyncUDPPacket& packet) {
  Serial.println("📤 Responding to service discovery");
  
  String response = "{";
  response += "\"type\":\"SERVICE_INFO\",";
  response += "\"serverId\":\"" + streaming.serverId + "\",";
  response += "\"serverName\":\"" + streaming.serverName + "\",";
  response += "\"serviceCount\":" + String(streaming.availableStreams.size()) + ",";
  response += "\"multicastGroups\":[";
  response += "\"" + STREAM_MULTICAST_IP.toString() + "\",";
  response += "\"" + SERVICE_MULTICAST_IP.toString() + "\",";
  response += "\"" + CONTROL_MULTICAST_IP.toString() + "\"";
  response += "],";
  response += "\"capabilities\":\"streaming,discovery,control\"";
  response += "}";
  
  // Send unicast response
  size_t sentBytes = packet.printf("%s", response.c_str());
  if (sentBytes > 0) {
    streaming.totalUnicastsSent++;
    Serial.println("✅ Service discovery response sent");
  }
}

void processServiceAnnouncement(const String& message, AsyncUDPPacket& packet) {
  String serverId = extractValueFromMessage(message, "serverId");
  Serial.print("📢 Processing service announcement from: ");
  Serial.println(serverId);
}

void processStartStreamCommand(const String& message, AsyncUDPPacket& packet) {
  String streamId = extractValueFromMessage(message, "streamId");
  Serial.print("▶️  Starting stream: ");
  Serial.println(streamId);
  
  for (size_t i = 0; i < streaming.availableStreams.size(); i++) {
    if (streaming.availableStreams[i].streamId == streamId) {
      streaming.availableStreams[i].isActive = true;
      break;
    }
  }
}

void processStopStreamCommand(const String& message, AsyncUDPPacket& packet) {
  String streamId = extractValueFromMessage(message, "streamId");
  Serial.print("⏹️  Stopping stream: ");
  Serial.println(streamId);
  
  for (size_t i = 0; i < streaming.availableStreams.size(); i++) {
    if (streaming.availableStreams[i].streamId == streamId) {
      streaming.availableStreams[i].isActive = false;
      break;
    }
  }
}

void processJoinGroupCommand(const String& message, AsyncUDPPacket& packet) {
  String groupIP = extractValueFromMessage(message, "groupIP");
  Serial.print("➕ Device joining multicast group: ");
  Serial.println(groupIP);
}

void processLeaveGroupCommand(const String& message, AsyncUDPPacket& packet) {
  String groupIP = extractValueFromMessage(message, "groupIP");
  Serial.print("➖ Device leaving multicast group: ");
  Serial.println(groupIP);
}

void processStreamAcknowledgment(const String& message, AsyncUDPPacket& packet) {
  String streamId = extractValueFromMessage(message, "streamId");
  Serial.print("✅ Stream acknowledgment for: ");
  Serial.println(streamId);
}

void processServiceInformation(const String& message, AsyncUDPPacket& packet) {
  String serverId = extractValueFromMessage(message, "serverId");
  Serial.print("📋 Service information from: ");
  Serial.println(serverId);
}

void processQualityFeedback(const String& message, AsyncUDPPacket& packet) {
  String streamId = extractValueFromMessage(message, "streamId");
  String feedback = extractValueFromMessage(message, "feedback");
  Serial.print("📊 Quality feedback for ");
  Serial.print(streamId);
  Serial.print(": ");
  Serial.println(feedback);
}

String extractValueFromMessage(const String& message, const String& key) {
  int keyIndex = message.indexOf("\"" + key + "\":");
  if (keyIndex == -1) return "";
  
  int valueStart = message.indexOf("\"", keyIndex + key.length() + 3) + 1;
  int valueEnd = message.indexOf("\"", valueStart);
  
  if (valueStart > 0 && valueEnd > valueStart) {
    return message.substring(valueStart, valueEnd);
  }
  return "";
}

void printStreamingStatus() {
  Serial.println("📊 === MULTICAST STREAMING SYSTEM STATUS ===");
  Serial.print("Server: ");
  Serial.print(streaming.serverName);
  Serial.print(" (");
  Serial.print(streaming.serverId);
  Serial.println(")");
  Serial.print("Uptime: ");
  Serial.print((millis() - streaming.systemStartTime) / 1000);
  Serial.println(" seconds");
  
  Serial.println("=== MULTICAST vs BROADCAST vs UNICAST STATISTICS ===");
  Serial.print("📡 Multicasts Received: ");
  Serial.println(streaming.totalMulticastsReceived);
  Serial.print("📻 Broadcasts Received: ");
  Serial.println(streaming.totalBroadcastsReceived);
  Serial.print("📨 Unicasts Received: ");
  Serial.println(streaming.totalUnicastsReceived);
  Serial.print("📡 Multicasts Sent: ");
  Serial.println(streaming.totalMulticastsSent);
  Serial.print("📻 Broadcasts Sent: ");
  Serial.println(streaming.totalBroadcastsSent);
  Serial.print("📨 Unicasts Sent: ");
  Serial.println(streaming.totalUnicastsSent);
  
  Serial.println("=== ACTIVE STREAMS ===");
  for (size_t i = 0; i < streaming.availableStreams.size(); i++) {
    StreamInfo& stream = streaming.availableStreams[i];
    Serial.print("  ");
    Serial.print(stream.streamId);
    Serial.print(" (");
    Serial.print(stream.streamName);
    Serial.print(") - ");
    Serial.print(stream.isActive ? "ACTIVE" : "INACTIVE");
    Serial.print(" - Multicast: ");
    Serial.print(stream.multicastAddress);
    Serial.print(":");
    Serial.print(stream.port);
    Serial.print(" - Packets: ");
    Serial.print(stream.totalPacketsSent);
    Serial.print(" - Bytes: ");
    Serial.println(stream.totalBytesSent);
  }
  
  Serial.print("WiFi Signal: ");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");
  Serial.print("Free Heap: ");
  Serial.print(ESP.getFreeHeap());
  Serial.println(" bytes");
  Serial.println("===============================================");
}

void loop() {
  unsigned long currentTime = millis();
  
  // Send periodic multicast streams
  if (currentTime - streaming.lastStreamSend >= STREAM_INTERVAL) {
    sendMulticastStream();
  }
  
  // Send periodic service announcements
  if (currentTime - streaming.lastServiceAnnounce >= SERVICE_ANNOUNCE_INTERVAL) {
    sendServiceAnnouncement();
  }
  
  // Print system status periodically
  static unsigned long lastStatusPrint = 0;
  if (currentTime - lastStatusPrint >= STATS_REPORT_INTERVAL) {
    lastStatusPrint = currentTime;
    printStreamingStatus();
  }
  
  // Monitor WiFi connection
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("⚠️  WiFi connection lost! Attempting reconnection...");
    WiFi.reconnect();
    delay(5000);
  }
  
  delay(50);
}

Multicast Streaming Client Receiver Code

Upload this code to 2-3 additional ESP32 devices to create client receivers that will join multicast groups and receive streamed content. Each client will demonstrate multicast group membership and use isMulticast() to verify packet types.

Client Receiver Features:

  • Joins multiple multicast groups for different content types
  • Receives multicast streams (audio, video, data)
  • Uses isMulticast() to verify multicast packet reception
  • Maintains statistics of multicast vs broadcast vs unicast communications
  • Provides stream quality monitoring and feedback
/*
 * Author: Avant Maker
 * Date: June 18, 2025
 * Version: 1.0
 * License: MIT 
 * 
 * Description: 
 * This is the client receiver code for testing multicast streaming communication 
 * with multiple ESP32 devices. Upload this code to additional ESP32 boards to create 
 * multicast stream receivers that can join groups and receive streamed content.
 * Use this code together with the multicast streaming server code above.
 *
 * Testing Instructions:
 * 1. Upload the main streaming server code to one ESP32
 * 2. Upload this client receiver code to 2-3 additional ESP32 devices
 * 3. All devices will automatically join multicast groups and receive streams
 * 4. Monitor all Serial outputs to see the multicast vs broadcast vs unicast communication
 * 5. Clients will receive multicast streams and provide quality feedback
 */

#include <WiFi.h>
#include <AsyncUDP.h>

// WiFi credentials (must match streaming server)
const char* ssid = "your_wifi_ssid";
const char* password = "your_wifi_password";

// Multicast streaming configuration (must match server)
const IPAddress STREAM_MULTICAST_IP(239, 255, 1, 100);
const IPAddress SERVICE_MULTICAST_IP(239, 255, 1, 200);
const IPAddress CONTROL_MULTICAST_IP(239, 255, 1, 300);
const IPAddress VIDEO_MULTICAST_IP(239, 255, 1, 101);
const IPAddress DATA_MULTICAST_IP(239, 255, 1, 102);
const uint16_t STREAM_PORT = 9000;
const uint16_t SERVICE_PORT = 9001;
const uint16_t CONTROL_PORT = 9002;
const uint16_t RESPONSE_PORT = 9003;

// Client configuration
const unsigned long DISCOVERY_INTERVAL = 45000;      // Send discovery requests every 45 seconds
const unsigned long QUALITY_REPORT_INTERVAL = 30000; // Send quality reports every 30 seconds
const unsigned long STATS_REPORT_INTERVAL = 60000;   // Report stats every minute

// Client receiver system
struct MulticastClient {
  String clientId;
  String clientName;
  AsyncUDP streamUDP;
  AsyncUDP serviceUDP;
  AsyncUDP controlUDP;
  AsyncUDP responseUDP;
  AsyncUDP videoUDP;
  AsyncUDP dataUDP;
  unsigned long systemStartTime;
  unsigned long lastDiscovery;
  unsigned long lastQualityReport;
  unsigned long totalMulticastsReceived;
  unsigned long totalBroadcastsReceived;
  unsigned long totalUnicastsReceived;
  unsigned long totalMulticastsSent;
  unsigned long totalBroadcastsSent;
  unsigned long totalUnicastsSent;
  unsigned long totalStreamPacketsReceived;
  unsigned long totalStreamBytesReceived;
  std::vector<String> joinedGroups;
  String lastStreamContent;
  String lastServiceInfo;
  bool receivingStreams;
} client;

void setup() {
  Serial.begin(115200);
  Serial.println("Starting ESP32 Multicast Streaming Client...");
  
  // Initialize client configuration
  client.clientId = "ESP32_CLIENT_" + String((uint32_t)ESP.getEfuseMac(), HEX);
  client.clientName = "ESP32 Multicast Receiver " + String((uint32_t)ESP.getEfuseMac() & 0xFFFF, HEX);
  client.systemStartTime = millis();
  client.receivingStreams = true;
  
  Serial.print("Client ID: ");
  Serial.println(client.clientId);
  Serial.print("Client Name: ");
  Serial.println(client.clientName);
  
  // Initialize WiFi
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("✅ WiFi connected successfully!");
  Serial.print("Client IP address: ");
  Serial.println(WiFi.localIP());
  Serial.print("Signal strength: ");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");
  
  // Join multicast groups and setup listeners
  joinMulticastGroups();
  
  // Send initial service discovery
  delay(3000);
  sendServiceDiscovery();
  
  Serial.println("🎬 Multicast Streaming Client is ready to receive...");
}

void joinMulticastGroups() {
  Serial.println("=== Joining Multicast Groups ===");
  
  // Join main streaming multicast group
  if (client.streamUDP.listenMulticast(STREAM_MULTICAST_IP, STREAM_PORT)) {
    Serial.print("✅ Joined audio stream multicast group ");
    Serial.print(STREAM_MULTICAST_IP);
    Serial.print(":");
    Serial.println(STREAM_PORT);
    client.joinedGroups.push_back("AUDIO:" + STREAM_MULTICAST_IP.toString());
    
    client.streamUDP.onPacket([](AsyncUDPPacket packet) {
      handleClientStreamPacket(packet);
    });
  } else {
    Serial.println("❌ Failed to join audio stream multicast group");
  }
  
  // Join video streaming multicast group
  if (client.videoUDP.listenMulticast(VIDEO_MULTICAST_IP, STREAM_PORT + 1)) {
    Serial.print("✅ Joined video stream multicast group ");
    Serial.print(VIDEO_MULTICAST_IP);
    Serial.print(":");
    Serial.println(STREAM_PORT + 1);
    client.joinedGroups.push_back("VIDEO:" + VIDEO_MULTICAST_IP.toString());
    
    client.videoUDP.onPacket([](AsyncUDPPacket packet) {
      handleClientVideoPacket(packet);
    });
  } else {
    Serial.println("❌ Failed to join video stream multicast group");
  }
  
  // Join data streaming multicast group
  if (client.dataUDP.listenMulticast(DATA_MULTICAST_IP, STREAM_PORT + 2)) {
    Serial.print("✅ Joined data stream multicast group ");
    Serial.print(DATA_MULTICAST_IP);
    Serial.print(":");
    Serial.println(STREAM_PORT + 2);
    client.joinedGroups.push_back("DATA:" + DATA_MULTICAST_IP.toString());
    
    client.dataUDP.onPacket([](AsyncUDPPacket packet) {
      handleClientDataPacket(packet);
    });
  } else {
    Serial.println("❌ Failed to join data stream multicast group");
  }
  
  // Join service discovery multicast group
  if (client.serviceUDP.listenMulticast(SERVICE_MULTICAST_IP, SERVICE_PORT)) {
    Serial.print("✅ Joined service discovery multicast group ");
    Serial.print(SERVICE_MULTICAST_IP);
    Serial.print(":");
    Serial.println(SERVICE_PORT);
    client.joinedGroups.push_back("SERVICE:" + SERVICE_MULTICAST_IP.toString());
    
    client.serviceUDP.onPacket([](AsyncUDPPacket packet) {
      handleClientServicePacket(packet);
    });
  } else {
    Serial.println("❌ Failed to join service discovery multicast group");
  }
  
  // Join control multicast group
  if (client.controlUDP.listenMulticast(CONTROL_MULTICAST_IP, CONTROL_PORT)) {
    Serial.print("✅ Joined control multicast group ");
    Serial.print(CONTROL_MULTICAST_IP);
    Serial.print(":");
    Serial.println(CONTROL_PORT);
    client.joinedGroups.push_back("CONTROL:" + CONTROL_MULTICAST_IP.toString());
    
    client.controlUDP.onPacket([](AsyncUDPPacket packet) {
      handleClientControlPacket(packet);
    });
  } else {
    Serial.println("❌ Failed to join control multicast group");
  }
  
  // Setup response listener for unicast communications
  if (client.responseUDP.listen(RESPONSE_PORT)) {
    Serial.print("✅ Response listener started on port ");
    Serial.println(RESPONSE_PORT);
    
    client.responseUDP.onPacket([](AsyncUDPPacket packet) {
      handleClientResponsePacket(packet);
    });
  } else {
    Serial.println("❌ Failed to start response listener");
  }
  
  Serial.print("📊 Total multicast groups joined: ");
  Serial.println(client.joinedGroups.size());
}

void handleClientStreamPacket(AsyncUDPPacket packet) {
  // Extract message content
  String message = "";
  for (size_t i = 0; i < packet.length(); i++) {
    message += (char)packet.data()[i];
  }
  message.trim();
  
  // Use isMulticast() to verify this is a multicast stream - this is the key demonstration
  bool isMulticastPacket = packet.isMulticast();
  bool isBroadcastPacket = packet.isBroadcast();
  
  Serial.println("🎬 === Client Audio Stream Packet Received ===");
  Serial.print("From: ");
  Serial.print(packet.remoteIP());
  Serial.print(":");
  Serial.println(packet.remotePort());
  Serial.print("Size: ");
  Serial.print(packet.length());
  Serial.println(" bytes");
  Serial.print("🔍 MULTICAST DETECTION: ");
  Serial.println(isMulticastPacket ? "MULTICAST STREAM" : "NON-MULTICAST STREAM");
  Serial.print("Is Broadcast: ");
  Serial.println(isBroadcastPacket ? "YES" : "NO");
  
  // Update statistics based on packet type
  if (isMulticastPacket) {
    client.totalMulticastsReceived++;
    client.totalStreamPacketsReceived++;
    client.totalStreamBytesReceived += packet.length();
    Serial.println("📊 Multicast stream counter incremented");
  } else if (isBroadcastPacket) {
    client.totalBroadcastsReceived++;
    Serial.println("📊 Broadcast counter incremented");
  } else {
    client.totalUnicastsReceived++;
    Serial.println("📊 Unicast counter incremented");
  }
  
  // Skip processing our own messages
  if (message.indexOf(client.clientId) >= 0) {
    Serial.println("⚠️  Ignoring own stream message");
    return;
  }
  
  // Process stream content
  if (message.startsWith("{") && isMulticastPacket) {
    Serial.println("📡 Processing multicast stream data");
    processStreamContent(message, "AUDIO");
    client.lastStreamContent = message.substring(0, 100);
  }
  
  Serial.println("=== End Client Audio Stream Processing ===");
}

void handleClientVideoPacket(AsyncUDPPacket packet) {
  // Extract message content
  String message = "";
  for (size_t i = 0; i < packet.length(); i++) {
    message += (char)packet.data()[i];
  }
  message.trim();
  
  // Verify multicast nature using isMulticast()
  bool isMulticastPacket = packet.isMulticast();
  
  Serial.println("📹 === Client Video Stream Packet Received ===");
  Serial.print("From: ");
  Serial.print(packet.remoteIP());
  Serial.print("Size: ");
  Serial.print(packet.length());
  Serial.println(" bytes");
  Serial.print("🔍 MULTICAST DETECTION: ");
  Serial.println(isMulticastPacket ? "MULTICAST VIDEO" : "NON-MULTICAST VIDEO");
  
  // Update statistics
  if (isMulticastPacket) {
    client.totalMulticastsReceived++;
    client.totalStreamPacketsReceived++;
    client.totalStreamBytesReceived += packet.length();
    Serial.println("📊 Multicast video stream counter incremented");
  } else if (packet.isBroadcast()) {
    client.totalBroadcastsReceived++;
  } else {
    client.totalUnicastsReceived++;
  }
  
  // Process video stream content
  if (message.startsWith("{") && isMulticastPacket) {
    Serial.println("📹 Processing multicast video stream data");
    processStreamContent(message, "VIDEO");
  }
  
  Serial.println("=== End Client Video Stream Processing ===");
}

void handleClientDataPacket(AsyncUDPPacket packet) {
  // Extract message content
  String message = "";
  for (size_t i = 0; i < packet.length(); i++) {
    message += (char)packet.data()[i];
  }
  message.trim();
  
  // Check multicast nature using isMulticast()
  bool isMulticastPacket = packet.isMulticast();
  
  Serial.println("📊 === Client Data Stream Packet Received ===");
  Serial.print("From: ");
  Serial.print(packet.remoteIP());
  Serial.print("Size: ");
  Serial.print(packet.length());
  Serial.println(" bytes");
  Serial.print("🔍 MULTICAST DETECTION: ");
  Serial.println(isMulticastPacket ? "MULTICAST DATA" : "NON-MULTICAST DATA");
  
  // Update statistics
  if (isMulticastPacket) {
    client.totalMulticastsReceived++;
    client.totalStreamPacketsReceived++;
    client.totalStreamBytesReceived += packet.length();
    Serial.println("📊 Multicast data stream counter incremented");
  } else if (packet.isBroadcast()) {
    client.totalBroadcastsReceived++;
  } else {
    client.totalUnicastsReceived++;
  }
  
  // Process data stream content
  if (message.startsWith("{") && isMulticastPacket) {
    Serial.println("📊 Processing multicast sensor data stream");
    processStreamContent(message, "DATA");
  }
  
  Serial.println("=== End Client Data Stream Processing ===");
}

void handleClientServicePacket(AsyncUDPPacket packet) {
  // Extract message content
  String message = "";
  for (size_t i = 0; i < packet.length(); i++) {
    message += (char)packet.data()[i];
  }
  message.trim();
  
  // Verify multicast service using isMulticast()
  bool isMulticastPacket = packet.isMulticast();
  
  Serial.println("🔍 === Client Service Discovery Packet Received ===");
  Serial.print("From: ");
  Serial.print(packet.remoteIP());
  Serial.print("Message: ");
  Serial.println(message.substring(0, 50) + "...");
  Serial.print("🔍 MULTICAST DETECTION: ");
  Serial.println(isMulticastPacket ? "MULTICAST SERVICE" : "NON-MULTICAST SERVICE");
  
  // Update statistics
  if (isMulticastPacket) {
    client.totalMulticastsReceived++;
  } else if (packet.isBroadcast()) {
    client.totalBroadcastsReceived++;
  } else {
    client.totalUnicastsReceived++;
  }
  
  // Skip processing our own messages
  if (message.indexOf(client.clientId) >= 0) {
    Serial.println("⚠️  Ignoring own service message");
    return;
  }
  
  // Process service announcements
  if (message.startsWith("SERVICE_ANNOUNCE") && isMulticastPacket) {
    Serial.println("📢 Processing multicast service announcement");
    processServiceAnnouncement(message, packet);
    client.lastServiceInfo = message.substring(0, 100);
  }
  
  Serial.println("=== End Client Service Processing ===");
}

void handleClientControlPacket(AsyncUDPPacket packet) {
  // Extract message content
  String message = "";
  for (size_t i = 0; i < packet.length(); i++) {
    message += (char)packet.data()[i];
  }
  message.trim();
  
  // Check multicast control using isMulticast()
  bool isMulticastPacket = packet.isMulticast();
  
  Serial.println("🎛️  === Client Control Packet Received ===");
  Serial.print("From: ");
  Serial.print(packet.remoteIP());
  Serial.print("Command: ");
  Serial.println(message);
  Serial.print("🔍 MULTICAST DETECTION: ");
  Serial.println(isMulticastPacket ? "MULTICAST CONTROL" : "NON-MULTICAST CONTROL");
  
  // Update statistics
  if (isMulticastPacket) {
    client.totalMulticastsReceived++;
  } else if (packet.isBroadcast()) {
    client.totalBroadcastsReceived++;
  } else {
    client.totalUnicastsReceived++;
  }
  
  // Process control commands
  if (isMulticastPacket) {
    Serial.println("🎛️  Processing multicast control command");
    processControlCommand(message);
  }
  
  Serial.println("=== End Client Control Processing ===");
}

void handleClientResponsePacket(AsyncUDPPacket packet) {
  // Extract message content
  String message = "";
  for (size_t i = 0; i < packet.length(); i++) {
    message += (char)packet.data()[i];
  }
  message.trim();
  
  // Check response packet type
  bool isMulticastPacket = packet.isMulticast();
  bool isBroadcastPacket = packet.isBroadcast();
  
  Serial.println("📨 === Client Response Packet Received ===");
  Serial.print("From: ");
  Serial.print(packet.remoteIP());
  Serial.print("Message: ");
  Serial.println(message.substring(0, 50) + "...");
  Serial.print("🔍 MULTICAST DETECTION: ");
  Serial.println(isMulticastPacket ? "MULTICAST RESPONSE" : "NON-MULTICAST RESPONSE");
  Serial.print("Is Broadcast: ");
  Serial.println(isBroadcastPacket ? "YES" : "NO");
  
  // Update statistics
  if (isMulticastPacket) {
    client.totalMulticastsReceived++;
  } else if (isBroadcastPacket) {
    client.totalBroadcastsReceived++;
  } else {
    client.totalUnicastsReceived++;
  }
  
  // Process responses
  if (message.startsWith("SERVICE_INFO")) {
    Serial.println("📋 Processing service information response");
    processServiceInformation(message);
  }
  
  Serial.println("=== End Client Response Processing ===");
}

void processStreamContent(const String& content, const String& streamType) {
  Serial.print("📡 Processing ");
  Serial.print(streamType);
  Serial.println(" stream content");
  
  // Extract stream information
  if (content.indexOf("streamId") >= 0) {
    String streamId = extractJSONValue(content, "streamId");
    String sequence = extractJSONValue(content, "sequence");
    Serial.print("  Stream ID: ");
    Serial.print(streamId);
    Serial.print(", Sequence: ");
    Serial.println(sequence);
  }
  
  // Process content based on type
  if (streamType == "AUDIO" && content.indexOf("audioData") >= 0) {
    String audioData = extractJSONValue(content, "audioData");
    Serial.print("  🎵 Audio Data: ");
    Serial.println(audioData);
  } else if (streamType == "VIDEO" && content.indexOf("videoFrame") >= 0) {
    String videoFrame = extractJSONValue(content, "videoFrame");
    Serial.print("  📹 Video Frame: ");
    Serial.println(videoFrame);
  } else if (streamType == "DATA" && content.indexOf("sensorData") >= 0) {
    Serial.println("  📊 Sensor Data Received");
  }
}

void processServiceAnnouncement(const String& message, AsyncUDPPacket& packet) {
  String serverId = extractJSONValue(message, "serverId");
  String serverName = extractJSONValue(message, "serverName");
  Serial.print("📢 Service from: ");
  Serial.print(serverName);
  Serial.print(" (");
  Serial.print(serverId);
  Serial.println(")");
}

void processControlCommand(const String& message) {
  if (message.startsWith("START_STREAM")) {
    Serial.println("▶️  Received start stream command");
    client.receivingStreams = true;
  } else if (message.startsWith("STOP_STREAM")) {
    Serial.println("⏹️  Received stop stream command");
    client.receivingStreams = false;
  }
}

void processServiceInformation(const String& message) {
  String serverId = extractJSONValue(message, "serverId");
  String capabilities = extractJSONValue(message, "capabilities");
  Serial.print("📋 Service capabilities from ");
  Serial.print(serverId);
  Serial.print(": ");
  Serial.println(capabilities);
}

void sendServiceDiscovery() {
  Serial.println("🔍 === Sending Service Discovery Request ===");
  
  String request = "{";
  request += "\"type\":\"SERVICE_DISCOVERY\",";
  request += "\"clientId\":\"" + client.clientId + "\",";
  request += "\"clientName\":\"" + client.clientName + "\",";
  request += "\"timestamp\":" + String(millis()) + ",";
  request += "\"requestedServices\":\"streaming,discovery,control\"";
  request += "}";
  
  // Send to service discovery multicast group
  size_t sentBytes = client.serviceUDP.writeTo(
    (uint8_t*)request.c_str(), 
    request.length(), 
    SERVICE_MULTICAST_IP, 
    SERVICE_PORT
  );
  
  if (sentBytes > 0) {
    client.totalMulticastsSent++;
    client.lastDiscovery = millis();
    Serial.print("✅ Service discovery request sent to multicast group ");
    Serial.print(SERVICE_MULTICAST_IP);
    Serial.print(" (");
    Serial.print(sentBytes);
    Serial.println(" bytes)");
    Serial.println("🔍 This multicast will be detected by isMulticast() on receiving devices");
  } else {
    Serial.println("❌ Failed to send service discovery request");
  }
}

void sendQualityReport() {
  Serial.println("📊 === Sending Quality Report ===");
  
  String report = "{";
  report += "\"type\":\"QUALITY_REPORT\",";
  report += "\"clientId\":\"" + client.clientId + "\",";
  report += "\"timestamp\":" + String(millis()) + ",";
  report += "\"packetsReceived\":" + String(client.totalStreamPacketsReceived) + ",";
  report += "\"bytesReceived\":" + String(client.totalStreamBytesReceived) + ",";
  report += "\"quality\":\"HIGH\",";
  report += "\"signalStrength\":" + String(WiFi.RSSI()) + ",";
  report += "\"multicastGroups\":" + String(client.joinedGroups.size());
  report += "}";
  
  // Send to control multicast group
  size_t sentBytes = client.controlUDP.writeTo(
    (uint8_t*)report.c_str(), 
    report.length(), 
    CONTROL_MULTICAST_IP, 
    CONTROL_PORT
  );
  
  if (sentBytes > 0) {
    client.totalMulticastsSent++;
    client.lastQualityReport = millis();
    Serial.print("✅ Quality report sent to multicast group ");
    Serial.print(CONTROL_MULTICAST_IP);
    Serial.print(" (");
    Serial.print(sentBytes);
    Serial.println(" bytes)");
    Serial.println("🔍 This multicast will be detected by isMulticast() on receiving devices");
  } else {
    Serial.println("❌ Failed to send quality report");
  }
}

String extractJSONValue(const String& json, const String& key) {
  int keyIndex = json.indexOf("\"" + key + "\":");
  if (keyIndex == -1) return "";
  
  int valueStart = json.indexOf("\"", keyIndex + key.length() + 3) + 1;
  int valueEnd = json.indexOf("\"", valueStart);
  
  if (valueStart > 0 && valueEnd > valueStart) {
    return json.substring(valueStart, valueEnd);
  }
  return "";
}

void printClientStatus() {
  Serial.println("📊 === MULTICAST CLIENT STATUS REPORT ===");
  Serial.print("Client: ");
  Serial.print(client.clientName);
  Serial.print(" (");
  Serial.print(client.clientId);
  Serial.println(")");
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());
  Serial.print("Uptime: ");
  Serial.print((millis() - client.systemStartTime) / 1000);
  Serial.println(" seconds");
  
  Serial.println("=== MULTICAST vs BROADCAST vs UNICAST STATISTICS ===");
  Serial.print("📡 Multicasts Received: ");
  Serial.println(client.totalMulticastsReceived);
  Serial.print("📻 Broadcasts Received: ");
  Serial.println(client.totalBroadcastsReceived);
  Serial.print("📨 Unicasts Received: ");
  Serial.println(client.totalUnicastsReceived);
  Serial.print("📡 Multicasts Sent: ");
  Serial.println(client.totalMulticastsSent);
  Serial.print("📻 Broadcasts Sent: ");
  Serial.println(client.totalBroadcastsSent);
  Serial.print("📨 Unicasts Sent: ");
  Serial.println(client.totalUnicastsSent);
  
  Serial.println("=== MULTICAST GROUP MEMBERSHIPS ===");
  Serial.print("Total Groups Joined: ");
  Serial.println(client.joinedGroups.size());
  for (size_t i = 0; i < client.joinedGroups.size(); i++) {
    Serial.print("  ");
    Serial.print(i + 1);
    Serial.print(". ");
    Serial.println(client.joinedGroups[i]);
  }
  
  Serial.println("=== STREAMING STATISTICS ===");
  Serial.print("Stream Packets Received: ");
  Serial.println(client.totalStreamPacketsReceived);
  Serial.print("Stream Bytes Received: ");
  Serial.print(client.totalStreamBytesReceived);
  Serial.println(" bytes");
  Serial.print("Receiving Streams: ");
  Serial.println(client.receivingStreams ? "YES" : "NO");
  
  Serial.print("WiFi Signal: ");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");
  Serial.print("Free Heap: ");
  Serial.print(ESP.getFreeHeap());
  Serial.println(" bytes");
  Serial.println("=============================================");
}

void loop() {
  unsigned long currentTime = millis();
  
  // Send periodic service discovery requests
  if (currentTime - client.lastDiscovery >= DISCOVERY_INTERVAL) {
    sendServiceDiscovery();
  }
  
  // Send periodic quality reports
  if (currentTime - client.lastQualityReport >= QUALITY_REPORT_INTERVAL) {
    sendQualityReport();
  }
  
  // Print client status periodically
  static unsigned long lastStatusPrint = 0;
  if (currentTime - lastStatusPrint >= STATS_REPORT_INTERVAL) {
    lastStatusPrint = currentTime;
    printClientStatus();
  }
  
  // Monitor WiFi connection
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("⚠️  WiFi connection lost! Attempting reconnection...");
    WiFi.reconnect();
    delay(5000);
  }
  
  delay(100);
}

Expected Output and Testing Results

On the Streaming Server (Main Device), you should see:

  • Detection of multicast packets using isMulticast() method
  • Distinction between multicast service discovery and unicast responses
  • Multicast streaming to different groups (audio, video, data)
  • Statistics showing multicast vs broadcast vs unicast packet counts
  • Multicast service announcements and group management

On the Client Devices, you should see:

  • Successful joining of multiple multicast groups
  • Reception and identification of multicast packets from the server
  • Multicast discovery requests and service announcements
  • Real-time stream reception with quality monitoring
  • Clear differentiation between multicast, broadcast, and unicast communications

Key isMulticast() Testing Points:

  • Multicast Detection: Verify that isMulticast() correctly identifies multicast packets as true
  • Non-Multicast Detection: Confirm that isMulticast() returns false for unicast and broadcast responses
  • Group Communication: Check that devices can join multicast groups and receive group-specific content
  • Stream Classification: Observe how devices handle different multicast streams (audio, video, data)
  • Service Discovery: See how multicast enables efficient service discovery and group management

Simple Multicast Detection Example

This simpler example demonstrates the basic usage of the isMulticast() method for identifying multicast packets in a minimal setup.

 /*
 * Author: Avant Maker
 * Date: June 18, 2025
 * Version: 1.0
 * License: MIT 
 * 
 * Description: 
 * This example shows basic usage of the isBroadcast() method
 * to detect and handle broadcast UDP packets differently from unicast packets.
 * 
 * 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, 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>

const char* ssid = "your_wifi_ssid";
const char* password = "your_wifi_password";

AsyncUDP udp;

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  
  Serial.println("WiFi connected");
  Serial.println(WiFi.localIP());
  
  // Listen on port 8888 for any UDP packets
  if (udp.listen(8888)) {
    Serial.println("UDP Listening on port 8888");
    
    udp.onPacket([](AsyncUDPPacket packet) {
      // Extract message
      String message = "";
      for (size_t i = 0; i < packet.length(); i++) {
        message += (char)packet.data()[i];
      }
      
      // Use isBroadcast() to check packet type
      if (packet.isBroadcast()) {
        Serial.println("📡 BROADCAST packet received:");
        Serial.print("  From: ");
        Serial.println(packet.remoteIP());
        Serial.print("  Message: ");
        Serial.println(message);
        Serial.println("  → This is a broadcast message sent to all devices");
        
        // Send acknowledgment back (unicast response)
        packet.printf("ACK: Broadcast received from %s", WiFi.localIP().toString().c_str());
        
      } else {
        Serial.println("📨 UNICAST packet received:");
        Serial.print("  From: ");
        Serial.println(packet.remoteIP());
        Serial.print("  Message: ");
        Serial.println(message);
        Serial.println("  → This is a direct message sent specifically to this device");
      }
    });
  }
}

void loop() {
  delay(1000);
}

For more ESP32 development resources and tutorials, visit the All About ESP32 Resources Hub on  AvantMaker.com

ESP32 Library Index

ESP32 Arduino Core Library


FAQ

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!

error: Content is protected !!