Packet Simulator: Software (e.g., NS-3, Cooja, OMNeT++) that models network behaviour as discrete packet transmission events without physical hardware
Simulation Time: The simulated clock inside the simulator, which may run faster or slower than wall-clock time depending on model complexity
Radio Model: The simulator’s representation of wireless channel behaviour, including path loss, multipath, and interference
Mobility Model: A mathematical description of how nodes move over time during a simulation (e.g., random waypoint, Gauss-Markov)
Traffic Generator: A simulation component that creates packet flows at specified rates to load the simulated network
Trace File: A log of all simulated events (packet send, receive, drop) used for post-simulation analysis
Confidence Interval: A statistical range within which the true mean of a simulation metric lies with specified probability; computed over multiple independent runs
51.1 In 60 Seconds
This hands-on ESP32 lab simulates network packet transmission using LEDs to visualize packet states (transmitting, received, success, error). You will construct packets with headers, payloads, and checksums, then observe how error detection works in practice by introducing deliberate corruption and watching checksum verification catch the errors.
51.2 Learning Objectives
By completing this lab, you will be able to:
Construct a packet structure: Identify and explain the components of a network packet (header, payload, checksum) and build one in ESP32 C++ code
Demonstrate transmission states: Apply LED indicators to represent distinct stages of packet transmission through a finite state machine
Implement error detection: Calculate and verify checksums for data integrity using the ones’ complement algorithm (RFC 1071)
Analyze packet communication: Apply serial output inspection to identify transmission errors and differentiate between corrupted and valid packets
Calculate protocol overhead: Evaluate the trade-off between error detection strength (checksum vs CRC32) and bandwidth efficiency for constrained IoT links
For Beginners: Network Packet Simulation
A network packet is a small chunk of data that travels across a network, like a postcard traveling through the postal system. Each packet carries an address (header), the message itself (payload), and a verification stamp (checksum) to make sure nothing was damaged in transit. In this lab, you will simulate sending packets between devices, watching LEDs indicate what is happening at each step. It is a hands-on way to understand what normally happens invisibly inside your Wi-Fi network.
51.3 Browser-First Lab: Build and Break a Packet
Use this activity first. It teaches the same packet ideas without requiring an ESP32, breadboard, or external simulator. Change the payload size, packet rate, and corruption setting, then read how the header, payload, checksum, and link utilisation change.
Try It: Packet State Simulator
Show code
viewof packetLabPayloadBytes = Inputs.range([1,32], {value:12,step:1,label:"Payload bytes"})viewof packetLabRate = Inputs.range([1,1000], {value:100,step:1,label:"Packets per second"})viewof packetLabBaud = Inputs.select([9600,19200,57600,115200,230400], {value:115200,label:"Serial link rate (bps)"})viewof packetLabCorrupt = Inputs.toggle({value:false,label:"Corrupt one payload byte"})
Use this after the browser simulator if you want to connect LEDs to real packet states: transmitting, received, valid, and error.
Component
Quantity
Purpose
ESP32 DevKit
1
Microcontroller for packet simulation
Red LED
1
Error indicator (checksum failure)
Green LED
1
Success indicator (valid packet)
Yellow LED
1
Transmission in progress
Blue LED
1
Packet received indicator
220 ohm Resistors
4
Current limiting for LEDs
Breadboard
1
Circuit assembly
Jumper Wires
Several
Connections
51.4 Circuit Diagram
Figure 51.1: Circuit diagram showing ESP32 connections to four LED indicators for packet state visualization
51.5 Packet Overhead and Efficiency
Before diving into the code, consider how packet structure affects transmission efficiency. For a packet with a 4-byte header, a variable-length payload, and a 2-byte checksum, the overhead ratio depends on how much payload you send relative to the fixed header and checksum bytes.
Use the browser activity as the main lab. If you build the optional ESP32 version, implement the same behaviour in small pieces rather than pasting a long program at once.
/* * Network Packet Simulator for ESP32 * * This program demonstrates basic networking concepts: * - Packet structure (header, payload, checksum) * - Transmission state visualization with LEDs * - Error detection using checksum verification * * LED Indicators: * - Red (GPIO 2): Error - checksum verification failed * - Green (GPIO 4): Success - packet transmitted/received correctly * - Yellow (GPIO 5): Transmitting - packet in transit * - Blue (GPIO 18): Received - packet arrival indicator */// LED Pin Definitions#define LED_ERROR 2// Red LED - Error indicator#define LED_SUCCESS 4// Green LED - Success indicator#define LED_TRANSMIT 5// Yellow LED - Transmission in progress#define LED_RECEIVED 18// Blue LED - Packet received// Packet Structure Constants#define HEADER_SIZE 4// Bytes for header (version, type, length, sequence)#define MAX_PAYLOAD 32// Maximum payload size in bytes#define CHECKSUM_SIZE 2// 16-bit checksum// Packet Types#define PKT_DATA 0x01// Data packet#define PKT_ACK 0x02// Acknowledgment packet#define PKT_NACK 0x03// Negative acknowledgment#define PKT_PING 0x04// Ping/heartbeat packet// Protocol Version#define PROTOCOL_VERSION 0x01// Structure representing a network packetstruct Packet {// Header fields (4 bytes)uint8_t version;// Protocol versionuint8_t type;// Packet type (DATA, ACK, NACK, PING)uint8_t length;// Payload lengthuint8_t sequence;// Sequence number for ordering// Payload (variable length, max 32 bytes)uint8_t payload[MAX_PAYLOAD];// Checksum (2 bytes)uint16_t checksum;};// Global variablesuint8_t sequenceNumber =0;bool simulateError =false;// Toggle to simulate transmission errors// Function prototypesvoid initializeLEDs();void setLEDState(int led,bool state);void blinkLED(int led,int times,int delayMs);uint16_t calculateChecksum(Packet* pkt);bool verifyChecksum(Packet* pkt);void createPacket(Packet* pkt,uint8_t type,constchar* data);void transmitPacket(Packet* pkt);void receivePacket(Packet* pkt);void printPacketDetails(Packet* pkt,constchar* label);void demonstratePacketFlow();void setup(){// Initialize serial communication for debugging Serial.begin(115200); delay(1000); Serial.println("\n"); Serial.println("========================================"); Serial.println(" Network Packet Simulator v1.0"); Serial.println(" ESP32 IoT Learning Lab"); Serial.println("========================================\n");// Initialize LED pins initializeLEDs();// Startup LED test sequence Serial.println("Testing LEDs..."); blinkLED(LED_ERROR,2,200); blinkLED(LED_SUCCESS,2,200); blinkLED(LED_TRANSMIT,2,200); blinkLED(LED_RECEIVED,2,200); Serial.println("\nLED Test Complete!"); Serial.println("- Red: Error indicator"); Serial.println("- Green: Success indicator"); Serial.println("- Yellow: Transmission indicator"); Serial.println("- Blue: Received indicator\n"); delay(1000);}void loop(){// Demonstrate packet transmission and reception demonstratePacketFlow();// Wait before next demonstration cycle Serial.println("\n--- Waiting 5 seconds before next cycle ---\n"); delay(5000);}// Initialize all LED pins as outputsvoid initializeLEDs(){ pinMode(LED_ERROR, OUTPUT); pinMode(LED_SUCCESS, OUTPUT); pinMode(LED_TRANSMIT, OUTPUT); pinMode(LED_RECEIVED, OUTPUT);// Turn off all LEDs initially digitalWrite(LED_ERROR, LOW); digitalWrite(LED_SUCCESS, LOW); digitalWrite(LED_TRANSMIT, LOW); digitalWrite(LED_RECEIVED, LOW);}// Set LED state (on/off)void setLEDState(int led,bool state){ digitalWrite(led, state ? HIGH : LOW);}// Blink an LED a specified number of timesvoid blinkLED(int led,int times,int delayMs){for(int i =0; i < times; i++){ digitalWrite(led, HIGH); delay(delayMs); digitalWrite(led, LOW); delay(delayMs);}}// Calculate 16-bit checksum using ones' complement sum// This is a simplified version of the Internet Checksum (RFC 1071)uint16_t calculateChecksum(Packet* pkt){uint32_t sum =0;// Add header bytes sum += pkt->version; sum += pkt->type; sum += pkt->length; sum += pkt->sequence;// Add payload bytesfor(int i =0; i < pkt->length; i++){ sum += pkt->payload[i];}// Fold 32-bit sum into 16-bit (handle overflow)while(sum >>16){ sum =(sum &0xFFFF)+(sum >>16);}// Return one's complementreturn~((uint16_t)sum);}// Verify packet checksum by recomputing and comparingbool verifyChecksum(Packet* pkt){uint16_t stored = pkt->checksum; pkt->checksum =0;uint16_t computed = calculateChecksum(pkt); pkt->checksum = stored;return(stored == computed);}// Create a new packet with specified type and datavoid createPacket(Packet* pkt,uint8_t type,constchar* data){// Clear packet memory memset(pkt,0,sizeof(Packet));// Set header fields pkt->version = PROTOCOL_VERSION; pkt->type = type; pkt->sequence = sequenceNumber++;// Copy payload dataif(data != NULL){ pkt->length = min(strlen(data),(size_t)MAX_PAYLOAD); memcpy(pkt->payload, data, pkt->length);}else{ pkt->length =0;}// Calculate and set checksum pkt->checksum = calculateChecksum(pkt);}// Simulate packet transmissionvoid transmitPacket(Packet* pkt){ Serial.println(">>> TRANSMITTING PACKET >>>");// Turn on transmission LED setLEDState(LED_TRANSMIT,true);// Print packet being sent printPacketDetails(pkt,"TX");// Simulate transmission delay (represents actual network latency) Serial.println(" Transmitting..."); delay(500);// Simulate network effectsif(simulateError){ Serial.println(" [!] Simulating network error - corrupting data");// Corrupt one byte of payload to demonstrate error detectionif(pkt->length >0){ pkt->payload[0]^=0xFF;// Flip all bits of first byte}} delay(500);// Turn off transmission LED setLEDState(LED_TRANSMIT,false); Serial.println(" Transmission complete.\n");}// Simulate packet reception and verificationvoid receivePacket(Packet* pkt){ Serial.println("<<< RECEIVING PACKET <<<");// Turn on received LED setLEDState(LED_RECEIVED,true); delay(300);// Print received packet printPacketDetails(pkt,"RX");// Verify checksum Serial.println(" Verifying checksum..."); delay(300);bool checksumValid = verifyChecksum(pkt);if(checksumValid){ Serial.println(" [OK] Checksum VALID - Packet integrity confirmed!");// Success indication setLEDState(LED_RECEIVED,false); setLEDState(LED_SUCCESS,true); blinkLED(LED_SUCCESS,3,150); setLEDState(LED_SUCCESS,false);}else{ Serial.println(" [ERROR] Checksum INVALID - Packet corrupted!"); Serial.println(" [ERROR] Request retransmission (NACK)");// Error indication setLEDState(LED_RECEIVED,false); setLEDState(LED_ERROR,true); blinkLED(LED_ERROR,5,100); setLEDState(LED_ERROR,false);} Serial.println();}// Print detailed packet informationvoid printPacketDetails(Packet* pkt,constchar* label){ Serial.println(" +------------------------------------------+"); Serial.printf(" | %s Packet Details |\n", label); Serial.println(" +------------------------------------------+");// Header section Serial.println(" | HEADER: |"); Serial.printf(" | Version: 0x%02X |\n", pkt->version); Serial.printf(" | Type: 0x%02X ", pkt->type);// Print packet type nameswitch(pkt->type){case PKT_DATA: Serial.println("(DATA) |");break;case PKT_ACK: Serial.println("(ACK) |");break;case PKT_NACK: Serial.println("(NACK) |");break;case PKT_PING: Serial.println("(PING) |");break;default: Serial.println("(UNKNOWN) |");break;} Serial.printf(" | Length: %d bytes |\n", pkt->length); Serial.printf(" | Sequence: %d |\n", pkt->sequence);// Payload section Serial.println(" | PAYLOAD: |");if(pkt->length >0){ Serial.print(" | Data: \"");for(int i =0; i < pkt->length && i <20; i++){if(pkt->payload[i]>=32&& pkt->payload[i]<127){ Serial.print((char)pkt->payload[i]);}else{ Serial.print('.');}}if(pkt->length >20) Serial.print("..."); Serial.println("\"");// Hex dump of payload Serial.print(" | Hex: ");for(int i =0; i < min(pkt->length,(uint8_t)8); i++){ Serial.printf("%02X ", pkt->payload[i]);}if(pkt->length >8) Serial.print("..."); Serial.println();}else{ Serial.println(" | (empty) |");}// Checksum section Serial.println(" | CHECKSUM: |"); Serial.printf(" | Value: 0x%04X |\n", pkt->checksum); Serial.println(" +------------------------------------------+");}// Main demonstration functionvoid demonstratePacketFlow(){staticint demoStep =0; Packet packet; Serial.println("============================================"); Serial.printf(" DEMONSTRATION CYCLE %d\n", demoStep +1); Serial.println("============================================\n");switch(demoStep){case0:// Demo 1: Simple data packet (no errors) Serial.println("[Demo 1] Sending DATA packet - Normal transmission\n"); simulateError =false; createPacket(&packet, PKT_DATA,"Hello IoT World!"); transmitPacket(&packet); receivePacket(&packet);break;case1:// Demo 2: Sensor reading packet Serial.println("[Demo 2] Sending sensor data packet\n"); simulateError =false; createPacket(&packet, PKT_DATA,"TEMP:25.5C,HUM:65%"); transmitPacket(&packet); receivePacket(&packet);break;case2:// Demo 3: Corrupted packet (simulated error) Serial.println("[Demo 3] Simulating CORRUPTED packet\n"); simulateError =true; createPacket(&packet, PKT_DATA,"Critical Alert!"); transmitPacket(&packet); receivePacket(&packet); simulateError =false;break;case3:// Demo 4: Ping packet Serial.println("[Demo 4] Sending PING packet (heartbeat)\n"); simulateError =false; createPacket(&packet, PKT_PING, NULL); transmitPacket(&packet); receivePacket(&packet);break;case4:// Demo 5: ACK packet Serial.println("[Demo 5] Sending ACK packet\n"); simulateError =false; createPacket(&packet, PKT_ACK,"OK"); transmitPacket(&packet); receivePacket(&packet);break;}// Cycle through demos demoStep =(demoStep +1)%5;}
51.7 Step-by-Step Instructions
51.7.1 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
Connect each LED through its resistor to the specified GPIO pins:
Red LED: GPIO 2
Green LED: GPIO 4
Yellow LED: GPIO 5
Blue LED: GPIO 18
Connect all LED cathodes (short legs) to GND
51.7.2 Step 2: Upload the Code
Start from the behaviour map in the optional ESP32 section.
Implement one piece at a time: packet fields, LED state changes, checksum calculation, then demo cycles.
Compile after each piece so syntax errors are isolated.
Run the browser simulator beside your ESP32 output and compare the packet size, checksum result, and success/error decision.
51.7.3 Step 3: Observe the Output
Watch the Serial Monitor for detailed packet information:
Startup: All LEDs blink in sequence during self-test
Transmission: Yellow LED lights during packet sending
Reception: Blue LED indicates packet arrival
Success: Green LED blinks 3 times for valid checksum
Error: Red LED blinks 5 times for corrupted packets
51.7.4 Step 4: Understand the Packet Structure
Study the Serial Monitor output to identify:
Packet field
Example value
What to learn
Version
0x01
Identifies the packet format so future versions can coexist
Type
DATA
Tells the receiver whether this is data, ACK, NACK, or ping
Length
16 bytes
Tells the receiver how many payload bytes to read
Sequence
0
Helps detect lost, duplicate, or out-of-order packets
Payload
“Hello IoT World!”
The application data carried by the packet
Hex view
48 65 6C 6C 6F …
The same payload represented as bytes
Checksum
0xF8E2
Error-detection value recomputed by the receiver
Quick Check: Packet Header Fields
51.7.5 Step 5: Experiment with Errors
The demo automatically shows a corrupted packet in cycle 3. Observe how:
The checksum verification fails
The red LED indicates an error
A NACK (negative acknowledgment) would be sent in a real system
51.8 Expected Output
When running the simulation, your Serial Monitor should tell the same story as the LEDs:
Stage
Serial monitor should show
LED meaning
Startup
LED test complete and pin role summary
All LEDs blink once during self-test
Packet created
Packet type, payload length, sequence number, and checksum
No LED yet; the packet is being prepared
Transmitting
Packet is being sent
Yellow LED on
Received
Packet arrived and checksum is being verified
Blue LED on briefly
Success
Checksum valid; packet accepted
Green LED blinks
Error cycle
Checksum invalid; retransmission would be requested
Red LED blinks
Next cycle
Wait period before repeating with a new packet type
All LEDs off between cycles
51.9 Challenge Exercises
Challenge 1: Add Packet Acknowledgment
Modify the code to implement a complete ACK/NACK system:
After receiving a valid packet, automatically create and send an ACK packet
After receiving a corrupted packet, send a NACK packet requesting retransmission
Add a retry counter that gives up after 3 failed attempts
Hint: Create a new function sendAcknowledgment(bool success, uint8_t seqNum) that creates and transmits the appropriate response.
Challenge 2: Implement Sequence Number Validation
Network packets can arrive out of order. Add sequence number tracking:
Keep track of the last received sequence number
Detect and report out-of-order packets
Detect and report duplicate packets
Add a purple LED (GPIO 19) that blinks for sequence errors
Hint: Store lastReceivedSequence as a global variable and compare incoming packets against it.
Challenge 3: Create a Two-ESP32 Network
For advanced learners with two ESP32 boards:
Connect two ESP32s via serial (TX1-RX2, RX1-TX2)
One ESP32 acts as sender, one as receiver
Implement actual packet transmission over the serial line
Add a button to trigger manual packet sending
Display received messages on an OLED screen
Hint: Use Serial2.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN) for the second serial port.
51.10 Checksum Overhead Trade-offs
In resource-constrained IoT systems, every byte counts. Adding error detection increases packet size but prevents silent data corruption. The following worked example quantifies this trade-off.
Worked Example: Checksum Overhead for Industrial IoT
Scenario: An industrial IoT sensor transmits 10-byte temperature readings every second over an IEEE 802.15.4 wireless link (250 kbps) with a 1% packet error rate.
Given:
Payload: 10 bytes per packet
Frequency: 1 packet/second = 86,400 packets/day
Error rate: 1% (864 corrupted packets/day)
Options: No checksum vs 2-byte checksum vs 4-byte CRC32
Header without checksum: 4 bytes (version, type, length, sequence)
Analysis:
Option
Overhead Bytes
Total Bytes
Overhead %
Errors Detected
Undetected Errors
No checksum
4
14
28.6%
0
864/day
2-byte checksum
6
16
37.5%
~863/day
~1/day
4-byte CRC32
8
18
44.4%
~864/day
~0
Energy Cost (at 10 mW transmit power, 250 kbps data rate):
Transmission time per byte: 8 bits / 250,000 bps = 0.032 ms/byte
No checksum: 14 bytes x 0.032 ms = 0.448 ms at 10 mW = 0.00124 uWh per packet
With 2-byte checksum: 16 bytes x 0.032 ms = 0.512 ms at 10 mW = 0.00142 uWh (+14.3%)
With CRC32: 18 bytes x 0.032 ms = 0.576 ms at 10 mW = 0.00160 uWh (+28.6%)
Impact Over 1 Year:
Undetected errors (no checksum): 315,360 corrupted readings
Extra energy for 2-byte checksum: ~0.16 mWh/year (negligible)
Trade-off: Spend a fraction of a percent more battery for 99.9% error detection
Decision: A 2-byte checksum is optimal for most IoT sensors – it catches nearly all errors with minimal overhead. CRC32 is only justified for safety-critical applications (medical devices, industrial control systems) where even a single undetected error per day is unacceptable.
How the packet networking concepts in this lab interconnect:
Concept
Relates To
Key Insight
Packet Header
Protocol Metadata
Version, type, length, and sequence fields identify the packet and enable processing
Payload
Application Data
The actual sensor readings, commands, or messages being transmitted
Checksum
Error Detection
Mathematical verification that data was not corrupted during transmission
Sequence Numbers
Ordering and Loss Detection
Track packets across unreliable links, detect gaps and duplicates
ACK/NACK
Reliability
Confirm receipt or request retransmission to ensure delivery
Protocol Overhead
Efficiency
Headers and checksums consume bandwidth – minimize for constrained networks
LED State Machine
Visualization
Physical representation of transmission, reception, success, and error states
51.12 Match the Concepts
51.13 Order the Process
Common Pitfalls
1. Running Only One Simulation Seed
A single simulation run produces one sample from a random process. Fix: run at least 30 independent runs with different random seeds and report the mean and 95% confidence interval.
2. Not Warming Up the Simulation Before Collecting Metrics
Metrics collected during the initialisation period (when routing tables are forming) skew the results. Fix: discard the first 10–20% of simulation time as a warm-up period before recording metrics.
3. Using an Overly Optimistic Radio Model
The default free-space path loss model ignores walls, furniture, and interference. Fix: use a more realistic model (log-distance, Two-Ray Ground, or measured empirical values) and document which model was used.
Label the Diagram
Code Challenge
51.14 Summary
This lab demonstrated essential packet networking concepts through hands-on simulation:
Packet Structure: Header (version, type, length, sequence) + Payload + Checksum forms a complete self-describing data unit
Transmission States: Visual feedback through LEDs shows the packet lifecycle from creation through verification
Error Detection: Checksums catch data corruption during transmission, triggering retransmission requests
Protocol Design: Version fields enable backward compatibility, sequence numbers detect lost packets, and ACK/NACK mechanisms enable reliable communication over unreliable links