This ESP32 lab demonstrates why bandwidth, throughput, latency, and jitter are independent metrics. A 1 Mbps link may deliver only 100 Kbps of useful data (goodput) due to protocol overhead and congestion. You will simulate network conditions, measure round-trip time, observe jitter effects on real-time IoT data, and calculate efficiency metrics hands-on.
52.1 Learning Objectives
By completing this lab, you will be able to:
Differentiate bandwidth from throughput: Explain why a 1 Mbps link may only deliver 100 Kbps of actual data due to protocol overhead and contention
Measure and interpret latency: Calculate round-trip time (RTT) and decompose its components (propagation, processing, queuing)
Analyze jitter patterns: Evaluate how latency variation affects real-time IoT applications such as industrial control and sensor fusion
Demonstrate congestion effects: Apply network simulation to compare packet delivery under normal and overloaded traffic conditions
Calculate efficiency metrics: Compute goodput as a fraction of total transmitted data and compare protocol overhead ratios
Time: ~30 min | Difficulty: Intermediate | Unit: P07.C15.U08
For Beginners: Network Performance
Network performance measures how quickly and reliably data moves between devices. Think of it like measuring traffic flow on a road – you care about speed (how fast cars go), throughput (how many cars pass per hour), and delays (how long you wait at intersections). These same ideas apply when IoT sensors send data to the cloud.
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 ESP32 simulation that these metrics are independent and understanding their relationships is critical for IoT system design.
52.2 Key Concepts Explained
Before diving into the lab, review the terminology:
Metric
Definition
IoT Example
Bandwidth
Maximum theoretical data rate (bits/second)
LoRaWAN SF7: 5.47 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-200 ms
Jitter
Variation in latency over time
Video stream: +/-15 ms
Goodput
Application-layer useful data rate
Sensor reading: 100 bytes/min
Overhead
Protocol headers, retransmissions, ACKs
TCP/IP: 40+ bytes per packet
Quick Check: Network Performance Terminology
Putting Numbers to It
For a LoRaWAN sensor transmitting 12-byte payloads, calculate the protocol overhead and efficiency:
LoRaWAN adds a 13-byte MAC header plus a 4-byte Message Integrity Check (MIC):
For small payloads, LoRaWAN’s dedicated IoT protocol is nearly 2x more efficient than MQTT/TCP/IP, despite LoRaWAN’s encryption overhead. This efficiency gap widens for sub-10-byte sensor readings.
Figure 52.1: Diagram showing the relationship between bandwidth, throughput, and goodput with typical loss factors
52.3 Components Needed
Component
Quantity
Purpose
ESP32 DevKit
1
Microcontroller for network simulation
Red LED
1
High latency indicator (>200 ms)
Green LED
1
Low latency indicator (<50 ms)
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
52.4 Wokwi Simulator
Use the embedded simulator below to build and test your network performance measurement circuit. Click “Start Simulation” to begin.
52.5 Circuit Diagram
Figure 52.2: Circuit diagram for network performance lab showing ESP32 with LED indicators and button input
52.6 Complete Code
Copy this code into the Wokwi editor and upload to the ESP32. The simulator covers bandwidth vs throughput measurement, latency profiling, jitter analysis, packet loss simulation, and congestion detection.
Full Network Performance Simulator Code (click to expand)
/* * ============================================================================= * 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 transmissionstruct 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 runstruct TestResults {// Timing metricsfloat avgLatency;float minLatency;float maxLatency;float jitter;// Standard deviation of latency// Throughput metricsfloat theoreticalBandwidthKbps;float achievedThroughputKbps;float goodputKbps;float efficiency;// goodput / bandwidth ratio// Reliability metricsuint32_t packetsSent;uint32_t packetsReceived;uint32_t packetsLost;uint32_t packetsRetransmitted;float packetLossRate;// Congestion indicatorsbool congestionDetected;uint32_t maxQueueDepth;float avgQueueDepth;};// Network state simulationstruct NetworkState {float currentUtilization;// 0.0 to 1.0uint32_t queueDepth;// Current packets in queuebool 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 calculationfloat 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 debouncingif(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();}}}elseif(digitalRead(BUTTON_PIN)== LOW){ buttonPressed =false;}// Run automated demo every 15 seconds if not testingstaticuint32_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(¤tTest,0,sizeof(TestResults)); memset(packetHistory,0,sizeof(packetHistory)); sampleCount =0; currentTest.theoreticalBandwidthKbps = SIMULATED_BANDWIDTH_KBPS; currentTest.minLatency =999999;// Simulate varying network conditions for this testfloat 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 latencyfloat 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 statisticsif(!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 latencyif(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 statisticsfloat 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(¤tTest); 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 packetif(congestionDelay >500) congestionDelay =500;// Cap at 500ms// Simulate queue buildupif(random(100)<30){// 30% chance to increase queue network.queueDepth = min(network.queueDepth +1,(uint32_t)MAX_QUEUE_SIZE);}elseif(network.queueDepth >0&& random(100)<50){ network.queueDepth--;}}return baseLatency + jitter + congestionDelay;}float simulateJitter(){// Random jitter following roughly normal distributionfloat 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 distributionreturn abs(jitter);// Return absolute value}bool simulatePacketLoss(float congestionLevel){float lossProb = PACKET_LOSS_PROBABILITY;// Increase loss probability during congestionif(congestionLevel > CONGESTION_THRESHOLD){ lossProb +=(congestionLevel - CONGESTION_THRESHOLD)*0.3;// Up to +9% loss at 100%}return(random(1000)/1000.0)< lossProb;}// =============================================================================// CALCULATION FUNCTIONS// =============================================================================float calculateThroughput(uint32_t bytesTransmitted,uint32_t durationMs){if(durationMs ==0)return0;// Convert to Kbps: (bytes * 8 bits/byte) / durationMs = Kbits/sreturn(float)(bytesTransmitted *8)/ durationMs;}float calculateJitter(float* samples,int count){if(count <2)return0;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)return0;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 latencyif(latency <50){ digitalWrite(LED_LOW_LATENCY, HIGH);// Green - excellent}elseif(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 barint barLength = min((int)(pkt->latency /25),20);// Scale to max 20 charsfor(int i =0; i <20; i++){ latencyBar[i]=(i < barLength)?'#':'-';} latencyBar[20]='\0';// Determine statusif(pkt->wasLost &&!pkt->wasRetransmitted){ strcpy(status,"LOST");}elseif(pkt->wasRetransmitted){ strcpy(status,"RETX");}elseif(pkt->latency <50){ strcpy(status,"OK ");}elseif(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 lengthsint 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 distributionint 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]++;}constchar* 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 latencyif(currentTest.avgLatency >50) score -=10;if(currentTest.avgLatency >150) score -=15;if(currentTest.avgLatency >300) score -=20;// Deduct for jitterif(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 efficiencyif(currentTest.efficiency <0.7) score -=10;if(currentTest.efficiency <0.5) score -=15; score = max(0, score);char grade;constchar* gradeDesc;if(score >=90){ grade ='A'; gradeDesc ="Excellent - Suitable for real-time IoT";}elseif(score >=80){ grade ='B'; gradeDesc ="Good - Suitable for most IoT apps";}elseif(score >=70){ grade ='C'; gradeDesc ="Acceptable - Monitor and optimize";}elseif(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(){staticint demoStep =0;switch(demoStep){case0: demonstrateBandwidthConcept();break;case1: demonstrateLatencyConcept();break;case2: demonstrateJitterConcept();break;case3: demonstrateCongestionConcept();break;case4: 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...");}
52.7 Step-by-Step Instructions
52.7.1 Step 1: Set Up the Circuit
Open the Wokwi simulator above
Add an ESP32 DevKit to your workspace
Add 4 LEDs (red, green, yellow, blue) to the breadboard
Add 4 x 220 ohm resistors for current limiting
Add 1 push button and 1 x 10K ohm resistor for pull-down
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)
Connect button: 3.3V to one leg, other leg to GPIO 15 with 10K pull-down to GND
Connect all LED cathodes (short legs) to GND
52.7.2 Step 2: Upload and Run
Copy the complete code above
Paste it into the Wokwi code editor (replacing any existing code)
Click the “Start Simulation” button
Open the Serial Monitor (115200 baud)
52.7.3 Step 3: Observe the Demonstrations
The simulator automatically cycles through educational demonstrations:
Bandwidth vs Throughput: Explains why actual data rate differs from link capacity
Latency Concepts: Shows components of round-trip time
On packet loss: Cut rate in half (multiplicative decrease)
Visualize the congestion window (CWND) changes
Hint: Research TCP Reno’s AIMD (Additive Increase Multiplicative Decrease) algorithm.
Challenge 4: Multi-Sensor Competition
Simulate multiple IoT sensors competing for network resources:
Create 5 virtual sensors, each trying to send data
Implement a simple TDMA (time division) scheduler
Compare performance with and without scheduling
Show how fair queuing improves overall throughput
Hint: Use an array of sensor structures and cycle through them with different scheduling algorithms.
52.10 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
52.11 IoT Application: Why This Matters
Understanding these metrics is critical for real IoT deployments:
Application
Key Metric
Threshold
Why
Industrial sensors
Latency
<100 ms
Control loop stability
Video surveillance
Throughput
>2 Mbps
Image quality
Medical devices
Jitter
<20 ms
Consistent readings
Smart meters
Goodput
>90%
Data completeness
Voice assistants
Latency
<150 ms
Natural conversation
Fleet tracking
Packet loss
<1%
Position accuracy
Interactive: Network Conditions Emulator
Interactive: Network QoS Simulator
Common Mistake: Assuming Higher Bandwidth Reduces Latency
The Mistake: “We upgraded from 100 Mbps to 1 Gbps, so our IoT sensor latency should decrease 10x.”
Why It’s Wrong: Bandwidth (bits per second) and latency (time per packet) are independent metrics. Propagation delay (speed of light) and processing delay dominate IoT latency, not transmission time.
The Numbers:
100-byte packet at 100 Mbps: transmission time = 8 microseconds
100-byte packet at 1 Gbps: transmission time = 0.8 microseconds
Savings: 7.2 microseconds
Propagation delay (100 m cable): 500 nanoseconds (fixed)
Total latency: ~10-150 milliseconds, where 7.2 microseconds is negligible
The Fix: To reduce latency, minimize hops, reduce congestion, use UDP instead of TCP, or move processing closer to sensors (edge computing). Bandwidth upgrades help throughput, not latency.
52.12 Concept Review
Match: Metrics to Definitions
Order: Steps in a Network Performance Test
Common Pitfalls
1. Measuring Performance Only Under Light Load
Networks often perform well at 10% capacity but degrade sharply at 70–80% utilisation. Fix: measure performance at multiple load levels (10%, 30%, 50%, 70%, 90%) and identify the saturation point.
2. Reporting Averages Without Percentiles
An average latency of 50 ms looks acceptable, but a 99th-percentile latency of 5 seconds is unacceptable for real-time control. Fix: always report p50, p95, and p99 latency alongside the mean.
3. Not Controlling for Background Traffic During Measurements
Other devices sharing the channel during measurements add uncontrolled variability. Fix: run performance measurements in a controlled environment with no other active devices on the channel, or explicitly measure and subtract background traffic.
🏷️ Label the Diagram
Code Challenge
52.13 Summary
This lab demonstrated the critical network performance metrics through hands-on simulation:
Bandwidth vs Throughput: Maximum capacity differs from actual achieved data rate due to protocol overhead, contention, and retransmissions
Latency Components: Propagation, processing, queuing, and transmission delays combine to produce the round-trip time
Jitter Impact: Variation in latency affects real-time applications more than average latency alone
Congestion Effects: Overloaded links cause exponential latency increases and can trigger congestion collapse
Efficiency Metrics: Goodput measures useful application data as a fraction of total transmitted data