Network Performance Lab: Calculations and Experiments
This lab belongs to Network Performance Lab: Measurement Setup and Terms
Start With the Decision
Protocol headers can dominate a small sensor message. A calculation and simulator run show where the time and bytes go.
Route Overview
This is part 2 of 2. Review Network Performance Lab: Measurement Setup and Terms for the preceding evidence.
Learning Objectives
- Calculate protocol overhead and transfer time.
- Run performance experiments and compare expected output.
Chapter Roadmap
- Putting Numbers to It
- Try It: Protocol Overhead Calculator
- Checkpoint: Metrics and Evidence
- Test Before Hardware
- Try It: Network Performance Workbench
- Optional Hardware Build
- Checkpoint: Simulate Before Wiring
- Circuit Diagram
- Optional ESP32 Implementation
- Network Simulator Code Archive
- Step-by-Step Instructions
- Expected Output
- Checkpoint: ESP32 Measurement Record
- Try It: Latency Breakdown Explorer
- Challenge Exercises
- Measure Real Wi-Fi Performance
- Add Adaptive Bitrate Simulation
- Implement Congestion Control
- Challenge 4: Multi-Sensor Competition
- Troubleshooting
- IoT Application: Why This Matters
- Bandwidth Does Not Cut Latency
- Checkpoint: Application Decision
- Concept Review
- Match: Metrics to Definitions
- Order Performance Test Steps
- Common Pitfalls
- Measure Under Real Load
- Report Percentiles, Not Averages
- Control Background Traffic
- Label the Diagram
- Code Challenge
- Summary
- Knowledge Check
- Quiz: Network Performance
- What’s Next
- Key Takeaway
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):
Protocol overhead ratio:
Efficiency (useful data / total transmission):
Compare this to MQTT over TCP/IP with minimum headers (2-byte MQTT fixed header + 20-byte TCP + 20-byte IP):
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.
To test try it: protocol overhead calculator, open the diagram in Figure. Bandwidth, Throughput, and Goodput supplies one named condition; Bandwidth supplies the necessary comparison for diagram showing the relationship between bandwidth, throughput, and goodput with typical loss factors.
Locate Bandwidth, Throughput, and Goodput on Figure before checking Bandwidth. The visual’s third anchor, theoretical link, completes diagram showing the relationship between bandwidth, throughput, and goodput with typical loss factors. Carry Bandwidth, Throughput, and Goodput into try it: protocol overhead calculator; use theoretical link as its limiting condition.
Checkpoint: Metrics and Evidence
You now know:
- A 1 Mbps link label is only a ceiling; the chapter examples show useful data can fall to 800 Kbps throughput, 600 Kbps goodput, or even 100 Kbps under overhead and congestion.
- Goodput efficiency is useful payload divided by total transmitted bytes, such as 12 / 29 = 41.4% for the LoRaWAN example.
- Latency, jitter, loss, and throughput are separate measurements, so a faster bandwidth number does not automatically fix a deadline miss.
The terms are now precise enough to test. The next step is to move from definitions into a controllable experiment where one slider at a time changes the result.
Test Before Hardware
Start here before building the ESP32 version. The controls below let you model the same performance ideas directly in the browser: bandwidth, offered load, latency, jitter, packet loss, and goodput. Begin with offered traffic comfortably below link capacity and record utilization, average latency, jitter range, loss, and delivered goodput. Then raise packet rate or packet size until utilization crosses the congestion region, observing which metric changes first. Finally vary base latency and loss independently so propagation, queueing, and delivery failure are not confused. Carry the same predicted thresholds into the hardware run and explain any difference with measured packet timing or implementation overhead.
Build this version only after you have tried the browser workbench. The extra hardware makes latency, congestion, and button-triggered tests visible on real LEDs.
| 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 |
Checkpoint: Simulate Before Wiring
You now know:
- Offered load comes from packet size, packet rate, and link bandwidth, and the workbench makes queue delay visible when utilisation rises past the healthy region.
- The optional circuit uses red, green, yellow, and blue LEDs to make latency and congestion observable instead of hidden in a log.
- The hardware path should start after the browser model, so errors in wiring do not hide the performance idea being tested.
Circuit Diagram
The visual evidence for circuit diagram sits in Figure. Find Network Performance Lab Circuit beside ESP32 before interpreting circuit diagram for network performance lab showing esp32 with led indicators and button input.
Compare Network Performance Lab Circuit with ESP32 inside the visual at Figure. Next find runs packet simulator, which completes the scope of circuit diagram for network performance lab showing esp32 with led indicators and button input. The decision in circuit diagram must preserve that labelled boundary.
Optional ESP32 Implementation
Use the ESP32 version after the browser workbench. Build it as a set of small behaviours so students can test each performance idea separately.
| ESP32 piece | What to implement | How to verify it works |
|---|---|---|
| Bandwidth vs throughput | Count total bytes sent and useful payload bytes | Throughput is lower than raw bandwidth when overhead is included |
| Latency profile | Timestamp send and receive events | Serial output reports RTT and LED color follows the delay range |
| Jitter analysis | Record several RTT samples | The result shows variation, not just one average |
| Congestion test | Increase packet rate or background load | Blue congestion LED turns on and queue delay increases |
| Packet loss simulation | Drop a controlled percentage of packets | Results include retransmissions and goodput reduction |
/*
* =============================================================================
* 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(¤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 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(¤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 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 +9% loss at 100%
}
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) / durationMs = Kbits/s
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...");
}
Step-by-Step Instructions
Step 1: Set Up the Circuit
- Open your preferred ESP32 development environment
- Add an ESP32 DevKit to your workspace or breadboard
- 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
Step 2: Upload and Run
- Start from the ESP32 behaviour map above rather than pasting one large program.
- Implement one performance feature at a time: latency LED, throughput counter, jitter samples, congestion flag, then packet loss.
- Compile and run after each feature so errors stay easy to isolate.
- Open the Serial Monitor at 115200 baud and compare each result with the browser workbench.
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
- Jitter Analysis: Demonstrates timing variation effects
- Congestion Effects: Simulates network overload
- Full Performance Test: Runs complete measurement cycle
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
Expected Output
When running the simulation, the Serial Monitor should report a readable measurement sequence:
| Stage | Serial monitor should show | What the LEDs should do |
|---|---|---|
| Startup | Lab title, GPIO setup, and LED self-test status | Each LED blinks during self-test |
| Network setup | Simulated bandwidth, base latency, queue size, and packet loss | LEDs turn off after setup |
| Ready | Prompt to press the button or wait for automatic cycles | All LEDs idle |
| Demo cycle | The current topic: bandwidth, latency, jitter, congestion, or full test | LED color matches the measured condition |
| Manual test | Packet count, RTT samples, goodput, and packet-loss summary | Blue LED turns on if congestion is detected |
| Result | Performance rating and recommended tuning action | LEDs reset before the next cycle |
Checkpoint: ESP32 Measurement Record
You now know:
- A complete test sends 20 simulated packets, records RTT samples, counts retransmissions, and reports packet loss before grading the run.
- The chapter’s code separates propagation delay, processing delay, queueing delay, and jitter instead of hiding them inside one average.
- Build the ESP32 lab one feature at a time: latency LED, throughput counter, jitter samples, congestion flag, then packet loss.
With a local measurement loop in place, the remaining question is where the delay should be removed. The next explorer connects this same RTT breakdown to edge, fog, and cloud placement.
Challenge Exercises
Modify the code to measure actual network performance instead of simulation:
- Connect the ESP32 to your Wi-Fi network
- Send HTTP requests to a known server (like httpbin.org)
- Measure actual round-trip time using
millis() - 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.
Implement adaptive transmission similar to video streaming:
- Monitor current network conditions (latency, loss rate)
- If conditions degrade, reduce payload size (lower “quality”)
- If conditions improve, increase payload size
- 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.
Add TCP-like congestion control to the simulator:
- Implement slow start: Begin with small transmission rate
- Implement congestion avoidance: Gradually increase rate
- 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.
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.
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 |
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 |
The table gives the application deadlines; the warning below explains why buying more bandwidth may still leave those deadlines missed.
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)
-
Processing delay (router): 50-500 microseconds (fixed)
-
Queuing delay (congestion): 1-100 milliseconds (dominant factor)
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.
Checkpoint: Application Decision
You now know:
- Industrial sensors care about latency under 100 ms, video surveillance needs more than 2 Mbps throughput, and medical devices care about jitter under 20 ms.
- A 100-byte packet saves only 7.2 microseconds when moving from 100 Mbps to 1 Gbps, so queueing and processing usually dominate the IoT delay budget.
- A strong recommendation names the measured bottleneck — loss, jitter, throughput, retransmissions, or queueing — and ties the fix to that evidence.
Concept Review
Common Pitfalls
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.
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.
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.
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
Knowledge Check
What’s Next
| Topic | Chapter | Description |
|---|---|---|
| Packet Simulator Lab | Packet Simulator Lab | Build and inspect packet structure hands-on, calculate checksums, and observe error detection in action |
| TCP Fundamentals | TCP Fundamentals | Understand how TCP congestion control (slow start, AIMD) manages throughput and prevents congestion collapse |
| Transport Protocol Comparison | Transport Protocol Comparison | Compare TCP vs UDP tradeoffs for latency-sensitive vs reliability-sensitive IoT applications |
| MQTT QoS and Sessions | MQTT QoS and Sessions | Apply QoS levels 0, 1, and 2 to prioritize critical sensor data and manage delivery guarantees |
| Network Topology Design | Topologies Fundamentals | Evaluate how mesh, star, and tree topologies affect latency, jitter, and fault tolerance in IoT deployments |
| Edge and Fog Computing | Edge/Fog Introduction | Reduce network latency by processing sensor data closer to the source with edge computing patterns |
Key Takeaway
Performance labs should measure throughput, latency, loss, jitter, retries, and bottlenecks under changed conditions. A single successful transfer does not prove network capacity.
Continue Your Route
This final part closes the route from Putting Numbers to It through Key Takeaway. Return to Network Performance Lab: Measurement Setup and Terms or continue from the networking-core module index.