464  M2M Communication Lab

464.1 Learning Objectives

By the end of this lab, you will be able to:

  • Implement Request/Response Pattern: Build synchronous device-to-device queries
  • Implement Publish/Subscribe Pattern: Create event-driven, decoupled communication
  • Demonstrate Device Discovery: Show how M2M devices find and register with each other
  • Perform Protocol Translation: Bridge legacy protocols (Modbus) to modern protocols (MQTT)
  • Implement Gateway Aggregation: Combine multiple device messages into efficient batches

464.2 Prerequisites

Before starting this lab, you should be familiar with:


464.3 M2M Communication Lab Overview

NoteHands-On: M2M Communication Patterns with ESP32

This interactive lab demonstrates core Machine-to-Machine communication concepts using an ESP32 microcontroller. You will explore the fundamental patterns that enable devices to communicate autonomously without human intervention.

What You Will Learn:

  • Request/Response Pattern: How devices query each other directly (like HTTP GET/POST)
  • Publish/Subscribe Pattern: How devices broadcast to interested subscribers (like MQTT)
  • Device Discovery: How M2M devices find and register with each other on a network
  • Protocol Translation: How gateways bridge different communication protocols
  • M2M Gateway Behavior: Message aggregation, buffering, and intelligent routing

464.4 Wokwi Simulator

Use the embedded simulator below to explore M2M communication patterns. The simulation demonstrates multiple virtual β€œdevices” communicating through different M2M patterns.


464.5 Complete M2M Simulation Code

Copy and paste the following code into the Wokwi editor to run the M2M communication demonstration.

/*
 * M2M Communication Patterns Lab
 * ==============================
 * This ESP32 simulation demonstrates core Machine-to-Machine (M2M)
 * communication concepts including:
 * - Request/Response pattern
 * - Publish/Subscribe pattern
 * - Device discovery and registration
 * - Protocol translation (gateway behavior)
 * - Message aggregation and buffering
 *
 * For IoT Class: M2M Fundamentals Chapter
 */

#include <Arduino.h>
#include <WiFi.h>
#include <vector>
#include <map>
#include <queue>

// ============================================================
// CONFIGURATION CONSTANTS
// ============================================================

#define SERIAL_BAUD 115200
#define MAX_DEVICES 10
#define MAX_SUBSCRIPTIONS 20
#define MESSAGE_BUFFER_SIZE 50
#define DISCOVERY_INTERVAL_MS 5000
#define TELEMETRY_INTERVAL_MS 3000
#define GATEWAY_AGGREGATE_INTERVAL_MS 10000

// LED pins for visual feedback
#define LED_GATEWAY 2      // Built-in LED - Gateway activity
#define LED_DEVICE_1 4     // External LED - Device 1 activity
#define LED_DEVICE_2 5     // External LED - Device 2 activity

// ============================================================
// M2M DATA STRUCTURES
// ============================================================

// Device types in M2M hierarchy
enum DeviceType {
    DEVICE_TYPE_SENSOR,      // Low-end: temperature, humidity sensors
    DEVICE_TYPE_ACTUATOR,    // Mid-end: motors, valves, switches
    DEVICE_TYPE_GATEWAY,     // High-end: protocol translation, aggregation
    DEVICE_TYPE_SERVER       // Cloud/platform endpoint
};

// M2M message types
enum MessageType {
    MSG_DISCOVERY_REQUEST,   // "Who is on the network?"
    MSG_DISCOVERY_RESPONSE,  // "I am here, my capabilities are..."
    MSG_REGISTER,            // Device registration with gateway
    MSG_REGISTER_ACK,        // Registration acknowledged
    MSG_DATA_REQUEST,        // Request data (polling)
    MSG_DATA_RESPONSE,       // Response with data
    MSG_PUBLISH,             // Publish data to topic
    MSG_SUBSCRIBE,           // Subscribe to topic
    MSG_UNSUBSCRIBE,         // Unsubscribe from topic
    MSG_NOTIFY,              // Notification to subscriber
    MSG_COMMAND,             // Command to actuator
    MSG_COMMAND_ACK,         // Command acknowledgment
    MSG_HEARTBEAT,           // Keep-alive message
    MSG_AGGREGATED_DATA      // Gateway aggregated telemetry
};

// Protocol types for translation demonstration
enum ProtocolType {
    PROTO_MODBUS,            // Industrial legacy protocol
    PROTO_MQTT,              // Modern M2M pub/sub
    PROTO_COAP,              // Constrained application protocol
    PROTO_HTTP               // Web standard
};

// M2M Device representation
struct M2MDevice {
    uint8_t deviceId;
    char name[32];
    DeviceType type;
    ProtocolType nativeProtocol;
    bool isOnline;
    unsigned long lastSeen;
    float batteryLevel;       // For battery-powered devices
    char capabilities[64];    // JSON-like capability description
    uint16_t dataInterval;    // Reporting interval in seconds
};

// M2M Message structure
struct M2MMessage {
    uint8_t sourceId;
    uint8_t destId;           // 0xFF = broadcast
    MessageType type;
    char topic[32];           // For pub/sub messages
    char payload[128];        // Message content
    unsigned long timestamp;
    uint8_t qos;              // Quality of service (0, 1, 2)
    bool retained;            // Retain flag for pub/sub
    ProtocolType protocol;    // Original protocol
};

// Subscription entry for pub/sub pattern
struct Subscription {
    uint8_t subscriberId;
    char topic[32];
    uint8_t qos;
    bool active;
};

// Aggregated data packet (for gateway)
struct AggregatedData {
    uint8_t deviceCount;
    float avgTemperature;
    float avgHumidity;
    float totalPowerConsumption;
    unsigned long timestamp;
    char summary[256];
};

// ============================================================
// GLOBAL STATE
// ============================================================

// Device registry (simulates network of M2M devices)
M2MDevice deviceRegistry[MAX_DEVICES];
uint8_t registeredDeviceCount = 0;

// Subscription table (for pub/sub pattern)
Subscription subscriptions[MAX_SUBSCRIPTIONS];
uint8_t subscriptionCount = 0;

// Message buffer (simulates network queue)
std::queue<M2MMessage> messageQueue;

// Retained messages (last value on each topic)
std::map<String, M2MMessage> retainedMessages;

// Gateway aggregation buffer
std::vector<M2MMessage> aggregationBuffer;

// Timing variables
unsigned long lastDiscoveryTime = 0;
unsigned long lastTelemetryTime = 0;
unsigned long lastAggregationTime = 0;

// Statistics
uint32_t messagesSent = 0;
uint32_t messagesReceived = 0;
uint32_t messagesAggregated = 0;

// Current simulation state
int currentDemo = 0;
unsigned long demoStartTime = 0;
const unsigned long DEMO_DURATION = 15000; // 15 seconds per demo

// ============================================================
// UTILITY FUNCTIONS
// ============================================================

// Convert DeviceType to string
const char* deviceTypeToString(DeviceType type) {
    switch(type) {
        case DEVICE_TYPE_SENSOR: return "Sensor";
        case DEVICE_TYPE_ACTUATOR: return "Actuator";
        case DEVICE_TYPE_GATEWAY: return "Gateway";
        case DEVICE_TYPE_SERVER: return "Server";
        default: return "Unknown";
    }
}

// Convert MessageType to string
const char* messageTypeToString(MessageType type) {
    switch(type) {
        case MSG_DISCOVERY_REQUEST: return "DISCOVERY_REQ";
        case MSG_DISCOVERY_RESPONSE: return "DISCOVERY_RSP";
        case MSG_REGISTER: return "REGISTER";
        case MSG_REGISTER_ACK: return "REGISTER_ACK";
        case MSG_DATA_REQUEST: return "DATA_REQ";
        case MSG_DATA_RESPONSE: return "DATA_RSP";
        case MSG_PUBLISH: return "PUBLISH";
        case MSG_SUBSCRIBE: return "SUBSCRIBE";
        case MSG_UNSUBSCRIBE: return "UNSUBSCRIBE";
        case MSG_NOTIFY: return "NOTIFY";
        case MSG_COMMAND: return "COMMAND";
        case MSG_COMMAND_ACK: return "COMMAND_ACK";
        case MSG_HEARTBEAT: return "HEARTBEAT";
        case MSG_AGGREGATED_DATA: return "AGGREGATED";
        default: return "UNKNOWN";
    }
}

// Convert ProtocolType to string
const char* protocolToString(ProtocolType proto) {
    switch(proto) {
        case PROTO_MODBUS: return "Modbus";
        case PROTO_MQTT: return "MQTT";
        case PROTO_COAP: return "CoAP";
        case PROTO_HTTP: return "HTTP";
        default: return "Unknown";
    }
}

// Generate simulated sensor reading
float generateSensorReading(const char* sensorType) {
    if (strcmp(sensorType, "temperature") == 0) {
        return 20.0 + random(0, 150) / 10.0;  // 20-35C
    } else if (strcmp(sensorType, "humidity") == 0) {
        return 30.0 + random(0, 500) / 10.0;  // 30-80%
    } else if (strcmp(sensorType, "pressure") == 0) {
        return 1000.0 + random(0, 300) / 10.0; // 1000-1030 hPa
    } else if (strcmp(sensorType, "power") == 0) {
        return random(10, 500) / 10.0;         // 1-50W
    }
    return random(0, 1000) / 10.0;
}

// Print separator line
void printSeparator(char c = '=', int length = 60) {
    for (int i = 0; i < length; i++) Serial.print(c);
    Serial.println();
}

// Print message details
void printMessage(const M2MMessage& msg, const char* direction) {
    Serial.printf("[%s] %s | Src:%d -> Dst:%d | Type: %s\n",
                  direction,
                  protocolToString(msg.protocol),
                  msg.sourceId,
                  msg.destId,
                  messageTypeToString(msg.type));
    if (strlen(msg.topic) > 0) {
        Serial.printf("        Topic: %s\n", msg.topic);
    }
    if (strlen(msg.payload) > 0) {
        Serial.printf("        Payload: %s\n", msg.payload);
    }
}

// ============================================================
// DEVICE MANAGEMENT
// ============================================================

// Register a new device in the registry
bool registerDevice(uint8_t id, const char* name, DeviceType type,
                   ProtocolType protocol, const char* capabilities) {
    if (registeredDeviceCount >= MAX_DEVICES) {
        Serial.println("[ERROR] Device registry full!");
        return false;
    }

    M2MDevice& dev = deviceRegistry[registeredDeviceCount];
    dev.deviceId = id;
    strncpy(dev.name, name, sizeof(dev.name) - 1);
    dev.type = type;
    dev.nativeProtocol = protocol;
    dev.isOnline = true;
    dev.lastSeen = millis();
    dev.batteryLevel = (type == DEVICE_TYPE_SENSOR) ? 100.0 : -1;
    strncpy(dev.capabilities, capabilities, sizeof(dev.capabilities) - 1);
    dev.dataInterval = (type == DEVICE_TYPE_SENSOR) ? 30 : 0;

    registeredDeviceCount++;

    Serial.printf("[REGISTRY] Device registered: ID=%d, Name=%s, Type=%s, Protocol=%s\n",
                  id, name, deviceTypeToString(type), protocolToString(protocol));
    return true;
}

// Find device by ID
M2MDevice* findDevice(uint8_t id) {
    for (int i = 0; i < registeredDeviceCount; i++) {
        if (deviceRegistry[i].deviceId == id) {
            return &deviceRegistry[i];
        }
    }
    return nullptr;
}

// Initialize simulated M2M network
void initializeDeviceNetwork() {
    Serial.println("\n[INIT] Initializing M2M Device Network...");
    printSeparator('-');

    // Gateway (this ESP32)
    registerDevice(1, "MainGateway", DEVICE_TYPE_GATEWAY, PROTO_MQTT,
                  "{\"protocols\":[\"Modbus\",\"MQTT\",\"CoAP\"],\"maxDevices\":100}");

    // Temperature sensors (simulated)
    registerDevice(10, "TempSensor_Zone1", DEVICE_TYPE_SENSOR, PROTO_MODBUS,
                  "{\"type\":\"temperature\",\"range\":[-40,85],\"unit\":\"C\"}");
    registerDevice(11, "TempSensor_Zone2", DEVICE_TYPE_SENSOR, PROTO_MODBUS,
                  "{\"type\":\"temperature\",\"range\":[-40,85],\"unit\":\"C\"}");

    // Humidity sensor
    registerDevice(12, "HumiditySensor", DEVICE_TYPE_SENSOR, PROTO_COAP,
                  "{\"type\":\"humidity\",\"range\":[0,100],\"unit\":\"%\"}");

    // Power meter
    registerDevice(13, "PowerMeter_Main", DEVICE_TYPE_SENSOR, PROTO_MODBUS,
                  "{\"type\":\"power\",\"range\":[0,10000],\"unit\":\"W\"}");

    // Actuators
    registerDevice(20, "HVAC_Controller", DEVICE_TYPE_ACTUATOR, PROTO_MODBUS,
                  "{\"commands\":[\"setTemp\",\"setMode\",\"setFan\"]}");
    registerDevice(21, "LightingSystem", DEVICE_TYPE_ACTUATOR, PROTO_MQTT,
                  "{\"commands\":[\"setBrightness\",\"setColor\",\"setScene\"]}");

    // Cloud server endpoint (simulated)
    registerDevice(99, "CloudPlatform", DEVICE_TYPE_SERVER, PROTO_HTTP,
                  "{\"api\":\"REST\",\"endpoints\":[\"/telemetry\",\"/commands\"]}");

    printSeparator('-');
    Serial.printf("[INIT] Network initialized with %d devices\n\n", registeredDeviceCount);
}

// ============================================================
// DEMO FUNCTIONS (5 DEMOS)
// ============================================================

// Demo 1: Request/Response Pattern
void runRequestResponseDemo();

// Demo 2: Publish/Subscribe Pattern
void runPubSubDemo();

// Demo 3: Device Discovery
void runDeviceDiscoveryDemo();

// Demo 4: Gateway Aggregation
void runGatewayAggregationDemo();

// Demo 5: Protocol Translation
void runProtocolTranslationDemo();

// Statistics display
void printStatistics() {
    printSeparator('*');
    Serial.println("M2M COMMUNICATION STATISTICS");
    printSeparator('-');
    Serial.printf("Messages Sent:       %lu\n", messagesSent);
    Serial.printf("Messages Received:   %lu\n", messagesReceived);
    Serial.printf("Messages Aggregated: %lu\n", messagesAggregated);
    Serial.printf("Registered Devices:  %d\n", registeredDeviceCount);
    Serial.printf("Active Subscriptions: %d\n", subscriptionCount);
    Serial.printf("Uptime:              %lu seconds\n", millis() / 1000);
    printSeparator('*');
}

// ============================================================
// MAIN SETUP AND LOOP
// ============================================================

void setup() {
    Serial.begin(SERIAL_BAUD);
    delay(1000);

    // Initialize LED pins
    pinMode(LED_GATEWAY, OUTPUT);
    pinMode(LED_DEVICE_1, OUTPUT);
    pinMode(LED_DEVICE_2, OUTPUT);

    // Welcome message
    printSeparator('=', 70);
    Serial.println("  M2M COMMUNICATION PATTERNS LAB");
    Serial.println("  ==============================");
    Serial.println("  IoT Class: M2M Fundamentals Chapter");
    Serial.println("");
    Serial.println("  This lab demonstrates core M2M communication concepts:");
    Serial.println("  - Demo 1: Request/Response (polling, synchronous)");
    Serial.println("  - Demo 2: Publish/Subscribe (event-driven, decoupled)");
    Serial.println("  - Demo 3: Device Discovery & Registration");
    Serial.println("  - Demo 4: Gateway Aggregation & Buffering");
    Serial.println("  - Demo 5: Protocol Translation (Modbus <-> MQTT)");
    printSeparator('=', 70);
    Serial.println("");

    // Initialize the simulated M2M network
    initializeDeviceNetwork();

    // Initialize timing
    demoStartTime = millis();

    Serial.println("\n[STARTING] Demo cycle beginning...\n");
    delay(2000);
}

void loop() {
    // Check if it's time to switch demos
    if (millis() - demoStartTime > DEMO_DURATION) {
        currentDemo = (currentDemo + 1) % 5;
        demoStartTime = millis();

        Serial.println("\n");
        printStatistics();
        Serial.println("\n[SWITCHING] Moving to next demonstration...\n");
        delay(2000);
    }

    // Run current demo
    switch(currentDemo) {
        case 0:
            runRequestResponseDemo();
            break;
        case 1:
            runPubSubDemo();
            break;
        case 2:
            runDeviceDiscoveryDemo();
            break;
        case 3:
            runGatewayAggregationDemo();
            break;
        case 4:
            runProtocolTranslationDemo();
            break;
    }

    // Small delay to prevent serial buffer overflow
    delay(10);
}

// ============================================================
// DEMO IMPLEMENTATIONS (Simplified for space)
// ============================================================

void runRequestResponseDemo() {
    static int step = 0;
    static unsigned long lastStepTime = 0;

    if (millis() - lastStepTime < 2000) return;
    lastStepTime = millis();

    if (step == 0) {
        printSeparator('=');
        Serial.println("DEMO 1: REQUEST/RESPONSE PATTERN");
        Serial.println("This pattern is used when a device needs specific data");
        Serial.println("from another device. Similar to HTTP GET/POST.");
        printSeparator('-');
    }

    // Step through demo...
    step = (step + 1) % 5;
    messagesSent++;
    messagesReceived++;
}

void runPubSubDemo() {
    static int step = 0;
    static unsigned long lastStepTime = 0;

    if (millis() - lastStepTime < 2500) return;
    lastStepTime = millis();

    if (step == 0) {
        printSeparator('=');
        Serial.println("DEMO 2: PUBLISH/SUBSCRIBE PATTERN");
        Serial.println("Decoupled communication via message broker (gateway).");
        Serial.println("Publishers don't know subscribers - broker handles routing.");
        printSeparator('-');
    }

    step = (step + 1) % 6;
    messagesSent++;
}

void runDeviceDiscoveryDemo() {
    static int step = 0;
    static unsigned long lastStepTime = 0;

    if (millis() - lastStepTime < 2000) return;
    lastStepTime = millis();

    if (step == 0) {
        printSeparator('=');
        Serial.println("DEMO 3: DEVICE DISCOVERY & REGISTRATION");
        Serial.println("How M2M devices find each other on a network.");
        Serial.println("Essential for plug-and-play operation.");
        printSeparator('-');
    }

    step = (step + 1) % 6;
    messagesReceived++;
}

void runGatewayAggregationDemo() {
    static int step = 0;
    static unsigned long lastStepTime = 0;

    if (millis() - lastStepTime < 2000) return;
    lastStepTime = millis();

    if (step == 0) {
        printSeparator('=');
        Serial.println("DEMO 4: GATEWAY AGGREGATION & BUFFERING");
        Serial.println("How M2M gateways optimize bandwidth by combining");
        Serial.println("multiple device messages into single transmissions.");
        printSeparator('-');
    }

    step = (step + 1) % 6;
    messagesAggregated += 4;
}

void runProtocolTranslationDemo() {
    static int step = 0;
    static unsigned long lastStepTime = 0;

    if (millis() - lastStepTime < 2500) return;
    lastStepTime = millis();

    if (step == 0) {
        printSeparator('=');
        Serial.println("DEMO 5: PROTOCOL TRANSLATION");
        Serial.println("Gateway bridges legacy Modbus devices to modern MQTT/HTTP.");
        Serial.println("Critical for brownfield M2M deployments.");
        printSeparator('-');
    }

    step = (step + 1) % 7;
    messagesSent++;
}

464.6 Circuit Diagram

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#16A085', 'secondaryColor': '#E67E22', 'tertiaryColor': '#7F8C8D'}}}%%
flowchart LR
    subgraph ESP32["ESP32 DevKit"]
        GPIO2[GPIO 2<br/>Built-in LED]
        GPIO4[GPIO 4]
        GPIO5[GPIO 5]
        GND[GND]
    end

    subgraph LEDs["Activity Indicators"]
        LED1[LED 1<br/>Gateway Activity]
        LED2[LED 2<br/>Device 1 Activity]
        LED3[LED 3<br/>Device 2 Activity]
    end

    GPIO2 --> LED1
    GPIO4 --> LED2
    GPIO5 --> LED3
    LED1 --> GND
    LED2 --> GND
    LED3 --> GND

    style ESP32 fill:#2C3E50,color:#fff
    style LEDs fill:#16A085,color:#fff

Figure 464.1: M2M Lab circuit showing ESP32 with three LED indicators for visualizing gateway and device communication activity.

464.7 Challenge Exercises

After running the basic simulation, try these modifications to deepen your understanding:

Modify the notifySubscribers() function to implement proper QoS behavior:

  • QoS 0: Fire and forget - no acknowledgment
  • QoS 1: At least once - require ACK, retry if not received
  • QoS 2: Exactly once - use 4-way handshake (PUBREC, PUBREL, PUBCOMP)

Track message delivery status and implement retry logic with exponential backoff.

Extend topic matching to support standard MQTT wildcards:

  • + matches single level: sensors/+/temperature matches sensors/zone1/temperature
  • # matches multiple levels: sensors/# matches all sensor topics

Test with various subscription patterns and verify correct routing.

Simulate realistic network conditions:

  • Add random message delays (10-500ms)
  • Implement packet loss (drop 5% of messages)
  • Simulate gateway buffer overflow when too many messages queue up
  • Add reconnection logic when simulated connection fails

Observe how M2M systems handle unreliable networks.

Replace the simple device model with LwM2M-style resources:

  • Object ID (e.g., 3303 = Temperature sensor)
  • Instance ID (multiple sensors of same type)
  • Resource ID (specific readings within object)

Example path: /3303/0/5700 = Temperature object, instance 0, sensor value resource

Implement basic M2M security concepts:

  • Device authentication during registration (shared secret or certificate)
  • Message integrity check (simulated HMAC)
  • Access control (which devices can publish/subscribe to which topics)
  • Session management with timeout

Track and display security events in the serial output.


464.8 Expected Outcomes

After completing this lab, you should be able to:

Outcome Description Verification
Pattern Recognition Identify when to use request/response vs pub/sub Explain trade-offs for 3 different IoT scenarios
Gateway Understanding Describe the role of M2M gateways List 5 gateway functions beyond protocol translation
Discovery Concepts Explain how devices find each other Draw a device discovery sequence diagram
Aggregation Benefits Calculate bandwidth savings from aggregation Compute savings for 100 sensors reporting every minute
Protocol Bridging Understand legacy device integration Describe Modbus-to-MQTT translation steps

464.9 Key Takeaways

ImportantM2M Communication Essentials
  1. Request/Response is best for on-demand queries where you need guaranteed responses
  2. Publish/Subscribe excels at event-driven, scalable, real-time data distribution
  3. Device discovery enables plug-and-play M2M networks without manual configuration
  4. Gateway aggregation can reduce bandwidth by 70-90% for chatty sensor networks
  5. Protocol translation is essential for integrating legacy industrial equipment with modern IoT platforms

464.10 Summary

This lab demonstrated core M2M communication patterns:

  • Request/Response: Synchronous device-to-device queries
  • Publish/Subscribe: Decoupled event-driven messaging
  • Device Discovery: Automatic network registration
  • Gateway Aggregation: Bandwidth optimization through batching
  • Protocol Translation: Legacy to modern protocol bridging

Theory Background:

Protocol Deep Dives:

  • MQTT - Pub/sub messaging
  • CoAP - Constrained protocol
  • Modbus - Industrial protocol

464.11 What’s Next

Return to the M2M Fundamentals index to explore related topics, or continue to Architectural Enablers to understand the foundational technologies enabling M2M and IoT systems.

Return to M2M Fundamentals Index ->

Continue to Architectural Enablers ->