632  Lab: Network Performance Measurement Simulator

632.1 Learning Objectives

By completing this lab, you will be able to:

  • Differentiate bandwidth from throughput: Understand why a 1 Mbps link may only deliver 100 Kbps of actual data
  • Measure and interpret latency: Calculate round-trip time (RTT) and understand its components
  • Analyze jitter patterns: See how latency variation affects real-time IoT applications
  • Observe congestion effects: Watch how competing traffic degrades network performance
  • Calculate efficiency metrics: Determine goodput (useful data) vs. overhead

Time: ~30 min | Difficulty: Intermediate | Unit: P07.C15.U08

NoteHands-On Learning: Network Performance Fundamentals

This interactive lab uses the Wokwi ESP32 simulator to help you understand the critical difference between bandwidth, throughput, latency, and jitter through hands-on experimentation. You will simulate network conditions and measure their impact on IoT data transmission.

632.2 What You Will Learn

Network performance is often misunderstood. Many engineers conflate bandwidth with throughput or assume that higher bandwidth automatically means lower latency. This lab demonstrates through practical simulation that these metrics are independent and understanding their relationships is critical for IoT system design.

632.3 Key Concepts Explained

Before diving into the lab, let’s clarify the terminology:

Metric Definition IoT Example
Bandwidth Maximum theoretical data rate (bits/second) LoRaWAN SF7: 5.5 Kbps max
Throughput Actual measured data rate achieved LoRaWAN real-world: 2-3 Kbps
Latency Time for data to travel from source to destination MQTT publish: 50-200ms
Jitter Variation in latency over time Video stream: +/-15ms
Goodput Application-layer useful data rate Sensor reading: 100 bytes/min
Overhead Protocol headers, retransmissions, ACKs TCP/IP: 40+ bytes per packet

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22', 'secondaryColor': '#16A085', 'tertiaryColor': '#E8F6F3', 'fontSize': '14px'}}}%%
graph TB
    subgraph BW["Bandwidth (Pipe Size)"]
        BW1["1 Mbps Theoretical Maximum"]
    end

    subgraph TP["Throughput (Actual Flow)"]
        TP1["~800 Kbps with overhead"]
    end

    subgraph GP["Goodput (Useful Data)"]
        GP1["~600 Kbps application data"]
    end

    BW --> |"Protocol Overhead<br/>Headers, ACKs"| TP
    TP --> |"Retransmissions<br/>Congestion"| GP

    subgraph Losses["Capacity Lost To"]
        L1["TCP/IP Headers: 40 bytes/packet"]
        L2["Retransmissions: 5-20%"]
        L3["Congestion delays"]
        L4["Error correction"]
    end

    style BW fill:#2C3E50,stroke:#16A085,color:#fff
    style TP fill:#16A085,stroke:#2C3E50,color:#fff
    style GP fill:#E67E22,stroke:#2C3E50,color:#fff
    style Losses fill:#E8F6F3,stroke:#7F8C8D,color:#2C3E50

Figure 632.1: Diagram showing the relationship between bandwidth, throughput, and goodput with typical loss factors

{fig-alt=“Hierarchical diagram showing bandwidth as the maximum theoretical capacity of 1 Mbps, reduced to 800 Kbps throughput due to protocol overhead, then further reduced to 600 Kbps goodput due to retransmissions and congestion, with annotations showing TCP/IP headers consume 40 bytes per packet and retransmissions can reach 5-20 percent”}

632.4 Components Needed

Component Quantity Purpose
ESP32 DevKit 1 Microcontroller for network simulation
Red LED 1 High latency indicator (>200ms)
Green LED 1 Low latency indicator (<50ms)
Yellow LED 1 Medium latency/transmission active
Blue LED 1 Congestion detected indicator
RGB LED (optional) 1 Jitter visualization (color intensity)
220 ohm Resistors 4-5 Current limiting for LEDs
Push Button 1 Trigger network tests
10K ohm Resistor 1 Button pull-down
Breadboard 1 Circuit assembly
Jumper Wires Several Connections

632.5 Wokwi Simulator

Use the embedded simulator below to build and test your network performance measurement circuit. Click “Start Simulation” to begin.

632.6 Circuit Diagram

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22', 'secondaryColor': '#16A085', 'tertiaryColor': '#E8F6F3', 'fontSize': '14px'}}}%%
graph LR
    subgraph ESP32["ESP32 DevKit"]
        GPIO2["GPIO 2"]
        GPIO4["GPIO 4"]
        GPIO5["GPIO 5"]
        GPIO18["GPIO 18"]
        GPIO15["GPIO 15"]
        GND["GND"]
        V33["3.3V"]
    end

    subgraph LEDs["Performance Indicators"]
        RED["Red LED<br/>(High Latency)"]
        GREEN["Green LED<br/>(Low Latency)"]
        YELLOW["Yellow LED<br/>(Transmitting)"]
        BLUE["Blue LED<br/>(Congestion)"]
    end

    subgraph Input["User Input"]
        BTN["Push Button<br/>(Run Test)"]
    end

    GPIO2 -->|"220 ohm"| RED
    GPIO4 -->|"220 ohm"| GREEN
    GPIO5 -->|"220 ohm"| YELLOW
    GPIO18 -->|"220 ohm"| BLUE

    V33 --> BTN
    BTN --> GPIO15
    GPIO15 -->|"10K ohm"| GND

    RED --> GND
    GREEN --> GND
    YELLOW --> GND
    BLUE --> GND

    style ESP32 fill:#2C3E50,stroke:#16A085,color:#fff
    style LEDs fill:#E8F6F3,stroke:#16A085,color:#2C3E50
    style Input fill:#E8F6F3,stroke:#E67E22,color:#2C3E50

Figure 632.2: Circuit diagram for network performance lab showing ESP32 with LED indicators and button input

{fig-alt=“Circuit diagram showing ESP32 DevKit connected to four LEDs for network performance visualization: red LED on GPIO 2 for high latency over 200ms, green LED on GPIO 4 for low latency under 50ms, yellow LED on GPIO 5 for active transmission, and blue LED on GPIO 18 for congestion detection, plus a push button on GPIO 15 with 10K ohm pull-down resistor for triggering network tests”}

632.7 Complete Code

Copy this code into the Wokwi editor and upload to the ESP32:

/*
 * =============================================================================
 * Network Performance Measurement Simulator for ESP32
 * =============================================================================
 *
 * This comprehensive lab demonstrates key network performance concepts:
 *
 * 1. BANDWIDTH vs THROUGHPUT
 *    - Bandwidth: Maximum theoretical capacity (simulated link speed)
 *    - Throughput: Actual achieved data rate (affected by overhead, errors)
 *
 * 2. LATENCY MEASUREMENT
 *    - Base latency: Propagation delay through the network
 *    - Processing latency: Time to encode/decode data
 *    - Queuing latency: Time waiting in buffers
 *    - Total Round-Trip Time (RTT): Complete send-receive cycle
 *
 * 3. JITTER ANALYSIS
 *    - Jitter: Variation in latency between packets
 *    - Critical for real-time applications (voice, video, sensor streams)
 *    - Measured as standard deviation of latency samples
 *
 * 4. CONGESTION EFFECTS
 *    - How competing traffic affects performance
 *    - Queue buildup and increased latency
 *    - Packet loss under heavy load
 *
 * 5. GOODPUT CALCULATION
 *    - Useful application data vs total transmitted data
 *    - Impact of protocol overhead and retransmissions
 *
 * LED Indicators:
 * - Green (GPIO 4):  Low latency (<50ms) - excellent network conditions
 * - Yellow (GPIO 5): Medium latency (50-200ms) / transmission in progress
 * - Red (GPIO 2):    High latency (>200ms) - poor network conditions
 * - Blue (GPIO 18):  Congestion detected - queue backup or packet loss
 *
 * Button (GPIO 15): Press to run a network performance test cycle
 *
 * Author: IoT Learning Platform
 * License: Educational Use
 */

#include <Arduino.h>
#include <math.h>

// =============================================================================
// PIN DEFINITIONS
// =============================================================================

#define LED_HIGH_LATENCY   2    // Red LED - latency > 200ms
#define LED_LOW_LATENCY    4    // Green LED - latency < 50ms
#define LED_TRANSMIT       5    // Yellow LED - medium latency / transmitting
#define LED_CONGESTION     18   // Blue LED - congestion detected
#define BUTTON_PIN         15   // Push button to trigger tests

// =============================================================================
// NETWORK SIMULATION PARAMETERS
// =============================================================================

// Simulated network characteristics (adjustable)
#define SIMULATED_BANDWIDTH_KBPS    1000    // 1 Mbps theoretical bandwidth
#define BASE_LATENCY_MS             25      // Base propagation delay
#define PROCESSING_LATENCY_MS       5       // Encoding/decoding time
#define MAX_QUEUE_SIZE              10      // Packets that can be queued

// Packet parameters
#define PACKET_SIZE_BYTES           100     // Data payload size
#define HEADER_OVERHEAD_BYTES       40      // TCP/IP header overhead
#define MAX_PACKETS_PER_TEST        20      // Packets to send per test

// Congestion simulation
#define CONGESTION_THRESHOLD        0.7     // 70% utilization triggers congestion
#define PACKET_LOSS_PROBABILITY     0.05    // 5% base packet loss rate

// Jitter parameters
#define MAX_JITTER_MS               30      // Maximum random jitter

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

// Statistics for a single packet transmission
struct PacketStats {
    uint32_t sequenceNumber;
    uint32_t sendTime;
    uint32_t receiveTime;
    uint32_t latency;
    bool wasLost;
    bool wasRetransmitted;
    uint16_t payloadSize;
    uint16_t totalSize;  // payload + overhead
};

// Aggregate statistics for a test run
struct TestResults {
    // Timing metrics
    float avgLatency;
    float minLatency;
    float maxLatency;
    float jitter;           // Standard deviation of latency

    // Throughput metrics
    float theoreticalBandwidthKbps;
    float achievedThroughputKbps;
    float goodputKbps;
    float efficiency;       // goodput / bandwidth ratio

    // Reliability metrics
    uint32_t packetsSent;
    uint32_t packetsReceived;
    uint32_t packetsLost;
    uint32_t packetsRetransmitted;
    float packetLossRate;

    // Congestion indicators
    bool congestionDetected;
    uint32_t maxQueueDepth;
    float avgQueueDepth;
};

// Network state simulation
struct NetworkState {
    float currentUtilization;   // 0.0 to 1.0
    uint32_t queueDepth;        // Current packets in queue
    bool isCongested;
    uint32_t backgroundTraffic; // Simulated competing traffic (bytes/sec)
};

// =============================================================================
// GLOBAL VARIABLES
// =============================================================================

PacketStats packetHistory[MAX_PACKETS_PER_TEST];
TestResults currentTest;
NetworkState network;

uint32_t testNumber = 0;
bool buttonPressed = false;
bool testInProgress = false;
uint32_t lastButtonDebounce = 0;

// Latency samples for jitter calculation
float latencySamples[MAX_PACKETS_PER_TEST];
int sampleCount = 0;

// =============================================================================
// FUNCTION PROTOTYPES
// =============================================================================

void initializeHardware();
void initializeNetwork();
void updateLEDs(float latency, bool congested);
void runPerformanceTest();
float simulateLatency(bool congested);
float simulateJitter();
bool simulatePacketLoss(float congestionLevel);
float calculateThroughput(uint32_t bytesTransmitted, uint32_t durationMs);
float calculateJitter(float* samples, int count);
float calculateStandardDeviation(float* samples, int count, float mean);
void printTestHeader(int testNum);
void printPacketDetails(PacketStats* pkt);
void printTestSummary(TestResults* results);
void printBandwidthVsThroughput();
void printLatencyBreakdown();
void printJitterAnalysis();
void printCongestionAnalysis();
void printPerformanceGrade();
void demonstrateBandwidthConcept();
void demonstrateLatencyConcept();
void demonstrateJitterConcept();
void demonstrateCongestionConcept();
void runAutomatedDemo();
void blinkLED(int pin, int times, int delayMs);
void setAllLEDs(bool state);

// =============================================================================
// SETUP
// =============================================================================

void setup() {
    Serial.begin(115200);
    delay(1000);  // Allow serial to initialize

    printWelcomeBanner();
    initializeHardware();
    initializeNetwork();

    Serial.println("\n[READY] Press the button to run a network performance test");
    Serial.println("        Or wait for automatic demonstration cycles\n");
}

// =============================================================================
// MAIN LOOP
// =============================================================================

void loop() {
    // Check for button press with debouncing
    if (digitalRead(BUTTON_PIN) == HIGH && !buttonPressed) {
        if (millis() - lastButtonDebounce > 200) {  // 200ms debounce
            buttonPressed = true;
            lastButtonDebounce = millis();

            if (!testInProgress) {
                Serial.println("\n[BUTTON] Manual test triggered!\n");
                runPerformanceTest();
            }
        }
    } else if (digitalRead(BUTTON_PIN) == LOW) {
        buttonPressed = false;
    }

    // Run automated demo every 15 seconds if not testing
    static uint32_t lastAutoDemo = 0;
    if (!testInProgress && millis() - lastAutoDemo > 15000) {
        lastAutoDemo = millis();
        runAutomatedDemo();
    }

    delay(10);  // Small delay for stability
}

// =============================================================================
// INITIALIZATION FUNCTIONS
// =============================================================================

void printWelcomeBanner() {
    Serial.println("\n");
    Serial.println("+=================================================================+");
    Serial.println("|     NETWORK PERFORMANCE MEASUREMENT SIMULATOR v2.0              |");
    Serial.println("|     ESP32 IoT Learning Lab - Understanding Network Metrics      |");
    Serial.println("+=================================================================+");
    Serial.println("|  This lab teaches:                                              |");
    Serial.println("|  - Bandwidth vs Throughput - Why they're different              |");
    Serial.println("|  - Latency Measurement - RTT and its components                 |");
    Serial.println("|  - Jitter Analysis - Variation affects real-time apps           |");
    Serial.println("|  - Congestion Effects - How traffic overload degrades networks  |");
    Serial.println("|  - Goodput Calculation - Useful data vs total transmitted       |");
    Serial.println("+=================================================================+");
    Serial.println();
}

void initializeHardware() {
    Serial.println("[INIT] Configuring GPIO pins...");

    // Configure LED pins as outputs
    pinMode(LED_HIGH_LATENCY, OUTPUT);
    pinMode(LED_LOW_LATENCY, OUTPUT);
    pinMode(LED_TRANSMIT, OUTPUT);
    pinMode(LED_CONGESTION, OUTPUT);

    // Configure button pin as input
    pinMode(BUTTON_PIN, INPUT);

    // Initial LED test - all off
    setAllLEDs(false);

    // LED self-test sequence
    Serial.println("[INIT] Running LED self-test...");

    Serial.println("       Testing: Green LED (Low Latency)");
    blinkLED(LED_LOW_LATENCY, 2, 200);

    Serial.println("       Testing: Yellow LED (Transmitting)");
    blinkLED(LED_TRANSMIT, 2, 200);

    Serial.println("       Testing: Red LED (High Latency)");
    blinkLED(LED_HIGH_LATENCY, 2, 200);

    Serial.println("       Testing: Blue LED (Congestion)");
    blinkLED(LED_CONGESTION, 2, 200);

    Serial.println("[INIT] Hardware initialization complete!\n");
}

void initializeNetwork() {
    Serial.println("[INIT] Initializing simulated network...");

    network.currentUtilization = 0.1;   // Start with 10% background utilization
    network.queueDepth = 0;
    network.isCongested = false;
    network.backgroundTraffic = SIMULATED_BANDWIDTH_KBPS * 100;  // 10% of bandwidth

    Serial.printf("       Simulated Bandwidth: %d Kbps (%.2f Mbps)\n",
                  SIMULATED_BANDWIDTH_KBPS, SIMULATED_BANDWIDTH_KBPS / 1000.0);
    Serial.printf("       Base Latency: %d ms\n", BASE_LATENCY_MS);
    Serial.printf("       Max Queue Size: %d packets\n", MAX_QUEUE_SIZE);
    Serial.printf("       Base Packet Loss: %.1f%%\n", PACKET_LOSS_PROBABILITY * 100);
    Serial.println("[INIT] Network simulation ready!\n");
}

// =============================================================================
// MAIN TEST FUNCTION
// =============================================================================

void runPerformanceTest() {
    testInProgress = true;
    testNumber++;

    printTestHeader(testNumber);

    // Reset test results
    memset(&currentTest, 0, sizeof(TestResults));
    memset(packetHistory, 0, sizeof(packetHistory));
    sampleCount = 0;

    currentTest.theoreticalBandwidthKbps = SIMULATED_BANDWIDTH_KBPS;
    currentTest.minLatency = 999999;

    // Simulate varying network conditions for this test
    float congestionLevel = (testNumber % 4) * 0.25;  // Cycle through 0%, 25%, 50%, 75%
    network.currentUtilization = 0.2 + congestionLevel * 0.6;  // 20% to 80%
    network.isCongested = (network.currentUtilization > CONGESTION_THRESHOLD);

    Serial.printf("\n[CONFIG] Network Conditions for Test %lu:\n", testNumber);
    Serial.printf("         Utilization Level: %.0f%%\n", network.currentUtilization * 100);
    Serial.printf("         Congestion Status: %s\n", network.isCongested ? "CONGESTED" : "Normal");
    Serial.println();

    // Update congestion LED
    digitalWrite(LED_CONGESTION, network.isCongested);

    uint32_t testStartTime = millis();
    uint32_t totalBytesTransmitted = 0;
    uint32_t totalUsefulBytes = 0;

    // Transmit packets
    Serial.println("+----------------------------------------------------------------+");
    Serial.println("|                    PACKET TRANSMISSION LOG                      |");
    Serial.println("+----------------------------------------------------------------+");

    for (int i = 0; i < MAX_PACKETS_PER_TEST; i++) {
        PacketStats* pkt = &packetHistory[i];
        pkt->sequenceNumber = i + 1;
        pkt->payloadSize = PACKET_SIZE_BYTES;
        pkt->totalSize = PACKET_SIZE_BYTES + HEADER_OVERHEAD_BYTES;

        // Indicate transmission start
        digitalWrite(LED_TRANSMIT, HIGH);

        // Record send time
        pkt->sendTime = millis();

        // Simulate network latency
        float latency = simulateLatency(network.isCongested);
        pkt->latency = (uint32_t)latency;

        // Simulate potential packet loss
        pkt->wasLost = simulatePacketLoss(network.currentUtilization);

        if (pkt->wasLost) {
            // Retransmit the packet
            pkt->wasRetransmitted = true;
            latency += simulateLatency(network.isCongested);  // Add retransmission delay
            pkt->latency = (uint32_t)latency;
            currentTest.packetsRetransmitted++;
        }

        // Simulate the actual delay
        delay((uint32_t)(latency / 10));  // Scaled down for demo (divide by 10)

        pkt->receiveTime = pkt->sendTime + pkt->latency;

        // Update LED based on latency
        updateLEDs(latency, network.isCongested);

        // End transmission indication
        digitalWrite(LED_TRANSMIT, LOW);

        // Update statistics
        if (!pkt->wasLost || pkt->wasRetransmitted) {
            currentTest.packetsReceived++;
            totalUsefulBytes += pkt->payloadSize;
        } else {
            currentTest.packetsLost++;
        }

        totalBytesTransmitted += pkt->totalSize;
        currentTest.packetsSent++;

        // Record latency sample for jitter calculation
        latencySamples[sampleCount++] = latency;

        // Update min/max latency
        if (latency < currentTest.minLatency) currentTest.minLatency = latency;
        if (latency > currentTest.maxLatency) currentTest.maxLatency = latency;

        // Print packet details
        printPacketDetails(pkt);

        // Small delay between packets
        delay(50);
    }

    Serial.println("+----------------------------------------------------------------+");

    uint32_t testDuration = millis() - testStartTime;

    // Calculate final statistics
    float sumLatency = 0;
    for (int i = 0; i < sampleCount; i++) {
        sumLatency += latencySamples[i];
    }
    currentTest.avgLatency = sumLatency / sampleCount;

    // Calculate jitter (standard deviation of latency)
    currentTest.jitter = calculateJitter(latencySamples, sampleCount);

    // Calculate throughput and goodput
    currentTest.achievedThroughputKbps = calculateThroughput(totalBytesTransmitted, testDuration);
    currentTest.goodputKbps = calculateThroughput(totalUsefulBytes, testDuration);
    currentTest.efficiency = currentTest.goodputKbps / currentTest.theoreticalBandwidthKbps;

    // Calculate packet loss rate
    currentTest.packetLossRate = (float)currentTest.packetsLost / currentTest.packetsSent;
    currentTest.congestionDetected = network.isCongested;

    // Print comprehensive summary
    printTestSummary(&currentTest);
    printBandwidthVsThroughput();
    printLatencyBreakdown();
    printJitterAnalysis();
    printCongestionAnalysis();
    printPerformanceGrade();

    // Turn off all LEDs
    setAllLEDs(false);

    Serial.println("\n[COMPLETE] Test finished. Press button for another test or wait for auto-demo.\n");

    testInProgress = false;
}

// =============================================================================
// SIMULATION FUNCTIONS
// =============================================================================

float simulateLatency(bool congested) {
    float baseLatency = BASE_LATENCY_MS + PROCESSING_LATENCY_MS;
    float jitter = simulateJitter();
    float congestionDelay = 0;

    if (congested) {
        // Congestion adds significant delay (exponential growth)
        congestionDelay = pow(2, network.queueDepth) * 5;  // Doubles for each queued packet
        if (congestionDelay > 500) congestionDelay = 500;  // Cap at 500ms

        // Simulate queue buildup
        if (random(100) < 30) {  // 30% chance to increase queue
            network.queueDepth = min(network.queueDepth + 1, (uint32_t)MAX_QUEUE_SIZE);
        } else if (network.queueDepth > 0 && random(100) < 50) {
            network.queueDepth--;
        }
    }

    return baseLatency + jitter + congestionDelay;
}

float simulateJitter() {
    // Random jitter following roughly normal distribution
    float jitter = 0;
    for (int i = 0; i < 3; i++) {
        jitter += random(-MAX_JITTER_MS, MAX_JITTER_MS);
    }
    jitter /= 3;  // Average of 3 random values approximates normal distribution

    return abs(jitter);  // Return absolute value
}

bool simulatePacketLoss(float congestionLevel) {
    float lossProb = PACKET_LOSS_PROBABILITY;

    // Increase loss probability during congestion
    if (congestionLevel > CONGESTION_THRESHOLD) {
        lossProb += (congestionLevel - CONGESTION_THRESHOLD) * 0.3;  // Up to +15% loss
    }

    return (random(1000) / 1000.0) < lossProb;
}

// =============================================================================
// CALCULATION FUNCTIONS
// =============================================================================

float calculateThroughput(uint32_t bytesTransmitted, uint32_t durationMs) {
    if (durationMs == 0) return 0;

    // Convert to Kbps: (bytes * 8 bits/byte * 1000 ms/sec) / (ms * 1000 bits/Kbit)
    return (float)(bytesTransmitted * 8) / durationMs;
}

float calculateJitter(float* samples, int count) {
    if (count < 2) return 0;

    float sum = 0;
    for (int i = 0; i < count; i++) {
        sum += samples[i];
    }
    float mean = sum / count;

    return calculateStandardDeviation(samples, count, mean);
}

float calculateStandardDeviation(float* samples, int count, float mean) {
    if (count < 2) return 0;

    float sumSquaredDiff = 0;
    for (int i = 0; i < count; i++) {
        float diff = samples[i] - mean;
        sumSquaredDiff += diff * diff;
    }

    return sqrt(sumSquaredDiff / (count - 1));
}

// =============================================================================
// LED CONTROL FUNCTIONS
// =============================================================================

void updateLEDs(float latency, bool congested) {
    // Turn off all status LEDs first
    digitalWrite(LED_LOW_LATENCY, LOW);
    digitalWrite(LED_TRANSMIT, LOW);
    digitalWrite(LED_HIGH_LATENCY, LOW);

    // Set appropriate LED based on latency
    if (latency < 50) {
        digitalWrite(LED_LOW_LATENCY, HIGH);   // Green - excellent
    } else if (latency < 200) {
        digitalWrite(LED_TRANSMIT, HIGH);       // Yellow - acceptable
    } else {
        digitalWrite(LED_HIGH_LATENCY, HIGH);   // Red - poor
    }

    // Congestion LED
    digitalWrite(LED_CONGESTION, congested);
}

void blinkLED(int pin, int times, int delayMs) {
    for (int i = 0; i < times; i++) {
        digitalWrite(pin, HIGH);
        delay(delayMs);
        digitalWrite(pin, LOW);
        delay(delayMs);
    }
}

void setAllLEDs(bool state) {
    digitalWrite(LED_LOW_LATENCY, state);
    digitalWrite(LED_TRANSMIT, state);
    digitalWrite(LED_HIGH_LATENCY, state);
    digitalWrite(LED_CONGESTION, state);
}

// =============================================================================
// PRINTING FUNCTIONS
// =============================================================================

void printTestHeader(int testNum) {
    Serial.println("\n+=================================================================+");
    Serial.printf("|                    PERFORMANCE TEST #%d                          |\n", testNum);
    Serial.println("+=================================================================+");
    Serial.println("|  Measuring: Bandwidth, Throughput, Latency, Jitter, Goodput     |");
    Serial.println("+=================================================================+");
}

void printPacketDetails(PacketStats* pkt) {
    char status[20];
    char latencyBar[21];

    // Create visual latency bar
    int barLength = min((int)(pkt->latency / 25), 20);  // Scale to max 20 chars
    for (int i = 0; i < 20; i++) {
        latencyBar[i] = (i < barLength) ? '#' : '-';
    }
    latencyBar[20] = '\0';

    // Determine status
    if (pkt->wasLost && !pkt->wasRetransmitted) {
        strcpy(status, "LOST");
    } else if (pkt->wasRetransmitted) {
        strcpy(status, "RETX");
    } else if (pkt->latency < 50) {
        strcpy(status, "OK  ");
    } else if (pkt->latency < 200) {
        strcpy(status, "SLOW");
    } else {
        strcpy(status, "POOR");
    }

    Serial.printf("| PKT #%02lu | %4lu ms | [%s] | %s | %d+%d bytes |\n",
                  pkt->sequenceNumber,
                  pkt->latency,
                  latencyBar,
                  status,
                  pkt->payloadSize,
                  HEADER_OVERHEAD_BYTES);
}

void printTestSummary(TestResults* results) {
    Serial.println("\n+=================================================================+");
    Serial.println("|                       TEST RESULTS SUMMARY                       |");
    Serial.println("+=================================================================+");

    Serial.println("|                                                                 |");
    Serial.println("|  LATENCY METRICS:                                               |");
    Serial.printf("|    Average Latency:    %7.1f ms                                |\n", results->avgLatency);
    Serial.printf("|    Minimum Latency:    %7.1f ms                                |\n", results->minLatency);
    Serial.printf("|    Maximum Latency:    %7.1f ms                                |\n", results->maxLatency);
    Serial.printf("|    Jitter (StdDev):    %7.1f ms                                |\n", results->jitter);

    Serial.println("|                                                                 |");
    Serial.println("|  THROUGHPUT METRICS:                                            |");
    Serial.printf("|    Theoretical BW:     %7.1f Kbps                              |\n", results->theoreticalBandwidthKbps);
    Serial.printf("|    Achieved Throughput:%7.1f Kbps                              |\n", results->achievedThroughputKbps);
    Serial.printf("|    Goodput (Useful):   %7.1f Kbps                              |\n", results->goodputKbps);
    Serial.printf("|    Efficiency:         %7.1f%%                                 |\n", results->efficiency * 100);

    Serial.println("|                                                                 |");
    Serial.println("|  RELIABILITY METRICS:                                           |");
    Serial.printf("|    Packets Sent:       %7lu                                   |\n", results->packetsSent);
    Serial.printf("|    Packets Received:   %7lu                                   |\n", results->packetsReceived);
    Serial.printf("|    Packets Lost:       %7lu                                   |\n", results->packetsLost);
    Serial.printf("|    Retransmissions:    %7lu                                   |\n", results->packetsRetransmitted);
    Serial.printf("|    Packet Loss Rate:   %7.1f%%                                 |\n", results->packetLossRate * 100);

    Serial.println("|                                                                 |");
    Serial.println("+=================================================================+");
}

void printBandwidthVsThroughput() {
    Serial.println("\n+----------------------------------------------------------------+");
    Serial.println("|           CONCEPT: BANDWIDTH vs THROUGHPUT vs GOODPUT          |");
    Serial.println("+----------------------------------------------------------------+");
    Serial.println("|                                                                |");
    Serial.println("|  Bandwidth (Theoretical Maximum):                              |");
    Serial.printf("|  [##################################################] %4.0f Kbps |\n",
                  currentTest.theoreticalBandwidthKbps);
    Serial.println("|                                                                |");

    // Calculate bar lengths
    int throughputBar = (int)(currentTest.achievedThroughputKbps / currentTest.theoreticalBandwidthKbps * 50);
    int goodputBar = (int)(currentTest.goodputKbps / currentTest.theoreticalBandwidthKbps * 50);

    Serial.println("|  Throughput (Actual Measured):                                 |");
    Serial.print("|  [");
    for (int i = 0; i < 50; i++) Serial.print(i < throughputBar ? "#" : "-");
    Serial.printf("] %4.0f Kbps |\n", currentTest.achievedThroughputKbps);

    Serial.println("|                                                                |");
    Serial.println("|  Goodput (Useful Application Data):                            |");
    Serial.print("|  [");
    for (int i = 0; i < 50; i++) Serial.print(i < goodputBar ? "#" : "-");
    Serial.printf("] %4.0f Kbps |\n", currentTest.goodputKbps);

    Serial.println("|                                                                |");
    Serial.println("|  WHY THE DIFFERENCE?                                           |");
    Serial.printf("|  - Protocol Overhead: %d bytes per %d-byte packet (%.0f%%)         |\n",
                  HEADER_OVERHEAD_BYTES, PACKET_SIZE_BYTES,
                  (float)HEADER_OVERHEAD_BYTES / (PACKET_SIZE_BYTES + HEADER_OVERHEAD_BYTES) * 100);
    Serial.printf("|  - Retransmissions: %lu packets resent                           |\n",
                  currentTest.packetsRetransmitted);
    Serial.printf("|  - Net Efficiency: %.1f%% of bandwidth used for actual data      |\n",
                  currentTest.efficiency * 100);
    Serial.println("|                                                                |");
    Serial.println("+----------------------------------------------------------------+");
}

void printLatencyBreakdown() {
    Serial.println("\n+----------------------------------------------------------------+");
    Serial.println("|                    LATENCY BREAKDOWN                            |");
    Serial.println("+----------------------------------------------------------------+");
    Serial.println("|                                                                |");
    Serial.println("|  Total RTT (Round-Trip Time) Components:                       |");
    Serial.println("|                                                                |");
    Serial.printf("|  +- Propagation Delay:  %3d ms (signal travel time)            |\n", BASE_LATENCY_MS);
    Serial.printf("|  +- Processing Delay:   %3d ms (encode/decode)                 |\n", PROCESSING_LATENCY_MS);
    Serial.printf("|  +- Queuing Delay:      %3.0f ms (buffer waiting)                |\n",
                  currentTest.avgLatency - BASE_LATENCY_MS - PROCESSING_LATENCY_MS - (currentTest.jitter / 2));
    Serial.printf("|  +- Jitter Variation:  +/-%3.0f ms (timing variance)             |\n", currentTest.jitter);
    Serial.println("|  ----------------------------------------------------------    |");
    Serial.printf("|  = Measured Avg RTT:   %3.0f ms                                  |\n", currentTest.avgLatency);
    Serial.println("|                                                                |");
    Serial.println("|  LATENCY RATINGS:                                              |");
    Serial.println("|  - < 50ms:   Excellent (real-time interactive apps)            |");
    Serial.println("|  - 50-150ms: Good (most IoT applications)                      |");
    Serial.println("|  - 150-300ms: Acceptable (sensor monitoring)                   |");
    Serial.println("|  - > 300ms:  Poor (may cause timeouts, retransmissions)        |");
    Serial.println("|                                                                |");
    Serial.printf("|  Your Result: %.0fms = %s                                       |\n",
                  currentTest.avgLatency,
                  currentTest.avgLatency < 50 ? "EXCELLENT" :
                  currentTest.avgLatency < 150 ? "GOOD" :
                  currentTest.avgLatency < 300 ? "ACCEPTABLE" : "POOR");
    Serial.println("|                                                                |");
    Serial.println("+----------------------------------------------------------------+");
}

void printJitterAnalysis() {
    Serial.println("\n+----------------------------------------------------------------+");
    Serial.println("|                      JITTER ANALYSIS                            |");
    Serial.println("+----------------------------------------------------------------+");
    Serial.println("|                                                                |");
    Serial.println("|  What is Jitter?                                               |");
    Serial.println("|  Jitter = Variation in packet latency over time                |");
    Serial.println("|  Measured as standard deviation of latency samples             |");
    Serial.println("|                                                                |");
    Serial.println("|  Latency Distribution:                                         |");
    Serial.println("|                                                                |");

    // Create ASCII histogram of latency distribution
    int histogram[5] = {0, 0, 0, 0, 0};  // 0-50, 50-100, 100-150, 150-200, 200+
    for (int i = 0; i < sampleCount; i++) {
        int bucket = min((int)(latencySamples[i] / 50), 4);
        histogram[bucket]++;
    }

    const char* labels[] = {"  0-50ms", " 50-100ms", "100-150ms", "150-200ms", "200+ms"};
    for (int b = 0; b < 5; b++) {
        int barLen = (histogram[b] * 30) / MAX_PACKETS_PER_TEST;
        Serial.printf("|  %s: ", labels[b]);
        for (int j = 0; j < 30; j++) {
            Serial.print(j < barLen ? "#" : ".");
        }
        Serial.printf(" (%d)     |\n", histogram[b]);
    }

    Serial.println("|                                                                |");
    Serial.printf("|  Jitter (StdDev): %.1f ms                                       |\n", currentTest.jitter);
    Serial.println("|                                                                |");
    Serial.println("|  JITTER IMPACT BY APPLICATION:                                 |");
    Serial.println("|  - VoIP/Video:    < 30ms required  (buffer to smooth)          |");
    Serial.println("|  - Real-time IoT: < 50ms preferred (sensor fusion)             |");
    Serial.println("|  - Monitoring:    < 200ms acceptable (dashboard display)       |");
    Serial.println("|  - Bulk Transfer: Any (not time-sensitive)                     |");
    Serial.println("|                                                                |");
    Serial.printf("|  Your Jitter: %.1fms = %s for IoT applications            |\n",
                  currentTest.jitter,
                  currentTest.jitter < 30 ? "Excellent" :
                  currentTest.jitter < 50 ? "Good" :
                  currentTest.jitter < 100 ? "Acceptable" : "Problematic");
    Serial.println("|                                                                |");
    Serial.println("+----------------------------------------------------------------+");
}

void printCongestionAnalysis() {
    Serial.println("\n+----------------------------------------------------------------+");
    Serial.println("|                   CONGESTION ANALYSIS                           |");
    Serial.println("+----------------------------------------------------------------+");
    Serial.println("|                                                                |");

    if (currentTest.congestionDetected) {
        Serial.println("|  [!] CONGESTION DETECTED                                       |");
        Serial.println("|                                                                |");
        Serial.printf("|  Network Utilization: %.0f%% (threshold: %.0f%%)                  |\n",
                      network.currentUtilization * 100, CONGESTION_THRESHOLD * 100);
        Serial.println("|                                                                |");
        Serial.println("|  Congestion Effects Observed:                                  |");
        Serial.printf("|  - Increased Latency: +%.0fms above baseline                    |\n",
                      currentTest.avgLatency - BASE_LATENCY_MS - PROCESSING_LATENCY_MS);
        Serial.printf("|  - Packet Loss Rate: %.1f%%                                     |\n",
                      currentTest.packetLossRate * 100);
        Serial.printf("|  - Retransmissions Required: %lu                                |\n",
                      currentTest.packetsRetransmitted);
        Serial.println("|                                                                |");
        Serial.println("|  CONGESTION MITIGATION STRATEGIES:                             |");
        Serial.println("|  1. Traffic shaping: Rate-limit non-critical traffic           |");
        Serial.println("|  2. QoS: Prioritize critical IoT sensor data                   |");
        Serial.println("|  3. Load balancing: Distribute across multiple paths           |");
        Serial.println("|  4. Reduce payload: Compress or aggregate sensor readings      |");
    } else {
        Serial.println("|  [OK] NO CONGESTION - Network operating normally               |");
        Serial.println("|                                                                |");
        Serial.printf("|  Network Utilization: %.0f%% (below threshold: %.0f%%)            |\n",
                      network.currentUtilization * 100, CONGESTION_THRESHOLD * 100);
        Serial.println("|                                                                |");
        Serial.println("|  Network Health Indicators:                                    |");
        Serial.println("|  - Queue depth: Minimal                                        |");
        Serial.printf("|  - Packet loss: %.1f%% (within normal range)                    |\n",
                      currentTest.packetLossRate * 100);
        Serial.println("|  - Latency: Stable and predictable                             |");
    }

    Serial.println("|                                                                |");
    Serial.println("+----------------------------------------------------------------+");
}

void printPerformanceGrade() {
    Serial.println("\n+=================================================================+");
    Serial.println("|                    OVERALL PERFORMANCE GRADE                     |");
    Serial.println("+=================================================================+");

    // Calculate overall score (0-100)
    int score = 100;

    // Deduct for latency
    if (currentTest.avgLatency > 50) score -= 10;
    if (currentTest.avgLatency > 150) score -= 15;
    if (currentTest.avgLatency > 300) score -= 20;

    // Deduct for jitter
    if (currentTest.jitter > 30) score -= 10;
    if (currentTest.jitter > 50) score -= 10;
    if (currentTest.jitter > 100) score -= 15;

    // Deduct for packet loss
    score -= (int)(currentTest.packetLossRate * 100);

    // Deduct for low efficiency
    if (currentTest.efficiency < 0.7) score -= 10;
    if (currentTest.efficiency < 0.5) score -= 15;

    score = max(0, score);

    char grade;
    const char* gradeDesc;
    if (score >= 90) { grade = 'A'; gradeDesc = "Excellent - Suitable for real-time IoT"; }
    else if (score >= 80) { grade = 'B'; gradeDesc = "Good - Suitable for most IoT apps"; }
    else if (score >= 70) { grade = 'C'; gradeDesc = "Acceptable - Monitor and optimize"; }
    else if (score >= 60) { grade = 'D'; gradeDesc = "Poor - Needs improvement"; }
    else { grade = 'F'; gradeDesc = "Critical - Not suitable for IoT"; }

    Serial.println("|                                                                 |");
    Serial.printf("|                      SCORE: %d/100                               |\n", score);
    Serial.printf("|                      GRADE: %c                                   |\n", grade);
    Serial.printf("|                                                                 |\n");
    Serial.printf("|  %s               |\n", gradeDesc);
    Serial.println("|                                                                 |");
    Serial.println("|  Scoring Criteria:                                              |");
    Serial.println("|  - Latency < 50ms: +20 points                                   |");
    Serial.println("|  - Jitter < 30ms: +15 points                                    |");
    Serial.println("|  - Packet Loss < 1%: +15 points                                 |");
    Serial.println("|  - Efficiency > 70%: +10 points                                 |");
    Serial.println("|                                                                 |");
    Serial.println("+=================================================================+");
}

// =============================================================================
// AUTOMATED DEMONSTRATION
// =============================================================================

void runAutomatedDemo() {
    static int demoStep = 0;

    switch (demoStep) {
        case 0:
            demonstrateBandwidthConcept();
            break;
        case 1:
            demonstrateLatencyConcept();
            break;
        case 2:
            demonstrateJitterConcept();
            break;
        case 3:
            demonstrateCongestionConcept();
            break;
        case 4:
            runPerformanceTest();
            break;
    }

    demoStep = (demoStep + 1) % 5;
}

void demonstrateBandwidthConcept() {
    Serial.println("\n+=================================================================+");
    Serial.println("|           MINI-LESSON: BANDWIDTH vs THROUGHPUT                  |");
    Serial.println("+=================================================================+");
    Serial.println();
    Serial.println("Think of BANDWIDTH as the SIZE of a water pipe:");
    Serial.println();
    Serial.println("  Bandwidth = Maximum theoretical capacity");
    Serial.println("  +======================================+");
    Serial.println("  |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ->   |  1 Mbps pipe");
    Serial.println("  +======================================+");
    Serial.println();
    Serial.println("Think of THROUGHPUT as how much water ACTUALLY flows:");
    Serial.println();
    Serial.println("  +======================================+");
    Serial.println("  |  ~~~~~~~~~~~[X][X]~~~~~~~~~ ->      |  800 Kbps actual");
    Serial.println("  +======================================+");
    Serial.println("              ^   ^");
    Serial.println("          Overhead & Losses");
    Serial.println();
    Serial.println("WHY THE DIFFERENCE?");
    Serial.println("- Protocol headers consume space (TCP/IP = 40+ bytes/packet)");
    Serial.println("- Retransmissions waste capacity");
    Serial.println("- Congestion causes queuing delays");
    Serial.println("- Physical layer overhead (preambles, gaps)");
    Serial.println();

    // Visual LED demo
    digitalWrite(LED_LOW_LATENCY, HIGH);
    delay(500);
    digitalWrite(LED_TRANSMIT, HIGH);
    delay(500);
    setAllLEDs(false);
}

void demonstrateLatencyConcept() {
    Serial.println("\n+=================================================================+");
    Serial.println("|               MINI-LESSON: UNDERSTANDING LATENCY                |");
    Serial.println("+=================================================================+");
    Serial.println();
    Serial.println("LATENCY = Time for data to travel from source to destination");
    Serial.println();
    Serial.println("  [Sensor] ----------------------------------------> [Cloud]");
    Serial.println("           <--------- RTT (Round-Trip Time) ------->");
    Serial.println();
    Serial.println("Components of Latency:");
    Serial.println();
    Serial.println("  1. Propagation Delay (25ms)");
    Serial.println("     +- Speed of light through fiber/copper");
    Serial.println();
    Serial.println("  2. Processing Delay (5ms)");
    Serial.println("     +- CPU time to encode/decode packets");
    Serial.println();
    Serial.println("  3. Queuing Delay (0-500ms)");
    Serial.println("     +- Time waiting in router buffers");
    Serial.println();
    Serial.println("  4. Transmission Delay");
    Serial.println("     +- Time to push bits onto the wire");
    Serial.println();
    Serial.println("IMPORTANT: Bandwidth does NOT reduce latency!");
    Serial.println("A 10 Gbps link has the same propagation delay as 1 Mbps.");
    Serial.println();

    // Demo: Show latency progression
    Serial.println("Simulating latency levels...");

    digitalWrite(LED_LOW_LATENCY, HIGH);
    Serial.println("  [GREEN]  Low latency (<50ms) - Excellent");
    delay(1000);
    digitalWrite(LED_LOW_LATENCY, LOW);

    digitalWrite(LED_TRANSMIT, HIGH);
    Serial.println("  [YELLOW] Medium latency (50-200ms) - Acceptable");
    delay(1000);
    digitalWrite(LED_TRANSMIT, LOW);

    digitalWrite(LED_HIGH_LATENCY, HIGH);
    Serial.println("  [RED]    High latency (>200ms) - Problematic");
    delay(1000);
    digitalWrite(LED_HIGH_LATENCY, LOW);
}

void demonstrateJitterConcept() {
    Serial.println("\n+=================================================================+");
    Serial.println("|                  MINI-LESSON: WHAT IS JITTER?                   |");
    Serial.println("+=================================================================+");
    Serial.println();
    Serial.println("JITTER = Variation in latency between packets");
    Serial.println();
    Serial.println("Low Jitter (Good - Consistent timing):");
    Serial.println("  Pkt1: 50ms  .....#");
    Serial.println("  Pkt2: 52ms  .....#");
    Serial.println("  Pkt3: 48ms  .....#");
    Serial.println("  Pkt4: 51ms  .....#");
    Serial.println("  Jitter = +/-2ms");
    Serial.println();
    Serial.println("High Jitter (Bad - Unpredictable timing):");
    Serial.println("  Pkt1: 50ms  .....#");
    Serial.println("  Pkt2: 120ms ............#");
    Serial.println("  Pkt3: 30ms  ...#");
    Serial.println("  Pkt4: 200ms ....................#");
    Serial.println("  Jitter = +/-70ms");
    Serial.println();
    Serial.println("WHY JITTER MATTERS:");
    Serial.println("- VoIP calls: High jitter causes choppy audio");
    Serial.println("- Video streaming: Causes buffering/stutter");
    Serial.println("- Sensor fusion: Difficult to correlate readings");
    Serial.println("- Industrial control: Timing-sensitive operations fail");
    Serial.println();

    // Visual jitter demo with LEDs
    Serial.println("Demonstrating jitter with LEDs...");

    // Low jitter - consistent timing
    Serial.println("  Low jitter (consistent blinks):");
    for (int i = 0; i < 5; i++) {
        digitalWrite(LED_LOW_LATENCY, HIGH);
        delay(100);
        digitalWrite(LED_LOW_LATENCY, LOW);
        delay(100);
    }

    delay(500);

    // High jitter - inconsistent timing
    Serial.println("  High jitter (erratic blinks):");
    int jitterDelays[] = {50, 200, 30, 300, 80};
    for (int i = 0; i < 5; i++) {
        digitalWrite(LED_HIGH_LATENCY, HIGH);
        delay(jitterDelays[i]);
        digitalWrite(LED_HIGH_LATENCY, LOW);
        delay(jitterDelays[i]);
    }

    setAllLEDs(false);
}

void demonstrateCongestionConcept() {
    Serial.println("\n+=================================================================+");
    Serial.println("|                MINI-LESSON: NETWORK CONGESTION                  |");
    Serial.println("+=================================================================+");
    Serial.println();
    Serial.println("Congestion occurs when traffic exceeds network capacity");
    Serial.println();
    Serial.println("Normal Operation (30% utilization):");
    Serial.println("  ========================================");
    Serial.println("  ==> ==> ==>    ==>    ==>   ==>       ");
    Serial.println("  ========================================");
    Serial.println("  Packets flow smoothly, low latency");
    Serial.println();
    Serial.println("Congested (90% utilization):");
    Serial.println("  ========================================");
    Serial.println("  ==>==>==>==>==>==>==>==>==>==>==>==>==>");
    Serial.println("  ========================================");
    Serial.println("  ^ Packets queue up, latency increases!");
    Serial.println();
    Serial.println("CONGESTION EFFECTS:");
    Serial.println("- Latency increases exponentially");
    Serial.println("- Packet loss increases (buffers overflow)");
    Serial.println("- Jitter becomes unpredictable");
    Serial.println("- Throughput may actually DECREASE");
    Serial.println();
    Serial.println("TCP CONGESTION COLLAPSE:");
    Serial.println("When packet loss triggers retransmissions,");
    Serial.println("which cause more congestion, which causes");
    Serial.println("more loss... throughput can drop to near zero!");
    Serial.println();

    // Visual demo - congestion building
    Serial.println("Simulating congestion buildup...");

    digitalWrite(LED_LOW_LATENCY, HIGH);
    Serial.println("  Network normal... [GREEN]");
    delay(1000);

    digitalWrite(LED_LOW_LATENCY, LOW);
    digitalWrite(LED_TRANSMIT, HIGH);
    Serial.println("  Traffic increasing... [YELLOW]");
    delay(1000);

    digitalWrite(LED_TRANSMIT, LOW);
    digitalWrite(LED_HIGH_LATENCY, HIGH);
    Serial.println("  Latency rising... [RED]");
    delay(1000);

    digitalWrite(LED_CONGESTION, HIGH);
    Serial.println("  CONGESTION DETECTED! [BLUE]");
    delay(1500);

    setAllLEDs(false);
    Serial.println("  Traffic cleared, network recovering...");
}

632.8 Step-by-Step Instructions

632.8.1 Step 1: Set Up the Circuit

  1. Open the Wokwi simulator above
  2. Add an ESP32 DevKit to your workspace
  3. Add 4 LEDs (red, green, yellow, blue) to the breadboard
  4. Add 4 x 220 ohm resistors for current limiting
  5. Add 1 push button and 1 x 10K ohm resistor for pull-down
  6. Connect each LED through its resistor to the specified GPIO pins:
    • Red LED: GPIO 2 (high latency indicator)
    • Green LED: GPIO 4 (low latency indicator)
    • Yellow LED: GPIO 5 (transmission active)
    • Blue LED: GPIO 18 (congestion detected)
  7. Connect button: 3.3V to one leg, other leg to GPIO 15 with 10K pull-down to GND
  8. Connect all LED cathodes (short legs) to GND

632.8.2 Step 2: Upload and Run

  1. Copy the complete code above
  2. Paste it into the Wokwi code editor (replacing any existing code)
  3. Click the “Start Simulation” button
  4. Open the Serial Monitor (115200 baud)

632.8.3 Step 3: Observe the Demonstrations

The simulator automatically cycles through educational demonstrations:

  1. Bandwidth vs Throughput: Explains why actual data rate differs from link capacity
  2. Latency Concepts: Shows components of round-trip time
  3. Jitter Analysis: Demonstrates timing variation effects
  4. Congestion Effects: Simulates network overload
  5. Full Performance Test: Runs complete measurement cycle

632.8.4 Step 4: Run Manual Tests

Press the button to trigger a full network performance test at any time. Each test:

  • Sends 20 simulated packets
  • Measures latency for each packet
  • Calculates jitter (standard deviation)
  • Simulates varying congestion levels
  • Reports comprehensive statistics

632.9 Expected Output

When running the simulation, your Serial Monitor displays:

+=================================================================+
|     NETWORK PERFORMANCE MEASUREMENT SIMULATOR v2.0              |
|     ESP32 IoT Learning Lab - Understanding Network Metrics      |
+=================================================================+
|  This lab teaches:                                              |
|  - Bandwidth vs Throughput - Why they're different              |
|  - Latency Measurement - RTT and its components                 |
|  - Jitter Analysis - Variation affects real-time apps           |
|  - Congestion Effects - How traffic overload degrades networks  |
|  - Goodput Calculation - Useful data vs total transmitted       |
+=================================================================+

[INIT] Configuring GPIO pins...
[INIT] Running LED self-test...
[INIT] Hardware initialization complete!

[INIT] Initializing simulated network...
       Simulated Bandwidth: 1000 Kbps (1.00 Mbps)
       Base Latency: 25 ms
       Max Queue Size: 10 packets
       Base Packet Loss: 5.0%
[INIT] Network simulation ready!

[READY] Press the button to run a network performance test
        Or wait for automatic demonstration cycles

632.10 Challenge Exercises

TipChallenge 1: Measure Real Wi-Fi Performance

Modify the code to measure actual network performance instead of simulation:

  1. Connect the ESP32 to your Wi-Fi network
  2. Send HTTP requests to a known server (like httpbin.org)
  3. Measure actual round-trip time using millis()
  4. Compare simulated results to real-world measurements

Hint: Use WiFi.h and HTTPClient.h libraries. The key insight is that real networks have much more variable latency than our simulation.

TipChallenge 2: Add Adaptive Bitrate Simulation

Implement adaptive transmission similar to video streaming:

  1. Monitor current network conditions (latency, loss rate)
  2. If conditions degrade, reduce payload size (lower “quality”)
  3. If conditions improve, increase payload size
  4. Display quality level changes on Serial Monitor

Hint: Create quality levels like “High (500 bytes)”, “Medium (200 bytes)”, “Low (50 bytes)” and switch based on measured performance.

TipChallenge 3: Implement Congestion Control

Add TCP-like congestion control to the simulator:

  1. Implement slow start: Begin with small transmission rate
  2. Implement congestion avoidance: Gradually increase rate
  3. On packet loss: Cut rate in half (multiplicative decrease)
  4. Visualize CWND (congestion window) changes

Hint: Research TCP Reno’s AIMD (Additive Increase Multiplicative Decrease) algorithm.

TipChallenge 4: Multi-Sensor Competition

Simulate multiple IoT sensors competing for network resources:

  1. Create 5 virtual sensors, each trying to send data
  2. Implement a simple TDMA (time division) scheduler
  3. Compare performance with and without scheduling
  4. Show how fair queuing improves overall throughput

Hint: Use an array of sensor structures and cycle through them with different scheduling algorithms.

632.11 Key Concepts Reinforced

Concept How It’s Demonstrated
Bandwidth Theoretical 1 Mbps capacity shown as baseline
Throughput Actual measured data rate after overhead and losses
Goodput Useful application data extracted from throughput
Latency RTT measured for each packet with component breakdown
Jitter Standard deviation of latency samples calculated and visualized
Congestion Simulated overload conditions showing exponential latency increase
Packet Loss Random loss simulation with retransmission handling
Protocol Overhead 40-byte headers on 100-byte payloads = 28.6% overhead
QoS Impact Different network conditions produce different performance grades

632.12 Troubleshooting

Problem Solution
LEDs not lighting Check resistor values (220 ohm) and GPIO pin assignments
No Serial output Ensure baud rate is 115200 in Serial Monitor
Button not responding Verify 10K pull-down resistor and button wiring
Simulation freezes Reduce delay times in code; ESP32 may need reset
Results seem random This is intentional - jitter simulation includes randomness

632.13 IoT Application: Why This Matters

Understanding these metrics is critical for real IoT deployments:

Application Key Metric Threshold Why
Industrial sensors Latency <100ms Control loop stability
Video surveillance Throughput >2 Mbps Image quality
Medical devices Jitter <20ms Consistent readings
Smart meters Goodput >90% Data completeness
Voice assistants Latency <150ms Natural conversation
Fleet tracking Packet loss <1% Position accuracy

632.14 Summary

This lab demonstrated the critical network performance metrics through hands-on simulation:

  • Bandwidth vs Throughput: Maximum capacity differs from actual achieved data rate
  • Latency Components: Propagation, processing, queuing, and transmission delays
  • Jitter Impact: Variation in latency affects real-time applications
  • Congestion Effects: Overload causes exponential latency increases
  • Efficiency Metrics: Goodput measures useful data vs overhead

632.15 What’s Next

Continue to the Packet Simulator Lab to learn about packet structure, checksums, and error detection through hands-on experimentation.

632.16 Further Reading

After completing this lab, explore these related topics: