%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22', 'secondaryColor': '#16A085', 'tertiaryColor': '#E8F6F3', 'fontSize': '14px'}}}%%
graph LR
subgraph ESP32["ESP32 DevKit"]
GPIO2["GPIO 2"]
GPIO4["GPIO 4"]
GPIO5["GPIO 5"]
GPIO18["GPIO 18"]
GND["GND"]
end
subgraph LEDs["LED Indicators"]
RED["Red LED<br/>(Error)"]
GREEN["Green LED<br/>(Success)"]
YELLOW["Yellow LED<br/>(Transmitting)"]
BLUE["Blue LED<br/>(Received)"]
end
GPIO2 -->|"220 ohm"| RED
GPIO4 -->|"220 ohm"| GREEN
GPIO5 -->|"220 ohm"| YELLOW
GPIO18 -->|"220 ohm"| BLUE
RED --> GND
GREEN --> GND
YELLOW --> GND
BLUE --> GND
style ESP32 fill:#2C3E50,stroke:#16A085,color:#fff
style LEDs fill:#E8F6F3,stroke:#16A085,color:#2C3E50
style RED fill:#E74C3C,stroke:#C0392B,color:#fff
style GREEN fill:#2ECC71,stroke:#27AE60,color:#fff
style YELLOW fill:#F1C40F,stroke:#F39C12,color:#2C3E50
style BLUE fill:#3498DB,stroke:#2980B9,color:#fff
631 Lab: Network Packet Simulator
631.1 Learning Objectives
By completing this lab, you will be able to:
- Understand packet structure: Identify and explain the components of a network packet (header, payload, checksum)
- Visualize transmission states: Use LED indicators to represent different stages of packet transmission
- Implement error detection: Create and verify checksums for data integrity
- Debug packet communication: Use serial output to inspect packet contents and identify transmission errors
This interactive lab uses the Wokwi ESP32 simulator to help you understand network packet structure, transmission states, and error detection through hands-on experimentation.
631.2 Components Needed
| 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 |
631.3 Wokwi Simulator
Use the embedded simulator below to build and test your packet simulator circuit. Click “Start Simulation” to begin.
631.4 Circuit Diagram
{fig-alt=“Circuit diagram showing ESP32 DevKit connected to four LEDs through 220 ohm resistors: red LED on GPIO 2 for errors, green LED on GPIO 4 for success, yellow LED on GPIO 5 for transmission, and blue LED on GPIO 18 for packet received, all connected to common ground”}
631.5 Complete Code
Copy this code into the Wokwi editor and upload to the ESP32:
/*
* 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 packet
struct Packet {
// Header fields (4 bytes)
uint8_t version; // Protocol version
uint8_t type; // Packet type (DATA, ACK, NACK, PING)
uint8_t length; // Payload length
uint8_t sequence; // Sequence number for ordering
// Payload (variable length, max 32 bytes)
uint8_t payload[MAX_PAYLOAD];
// Checksum (2 bytes)
uint16_t checksum;
};
// Global variables
uint8_t sequenceNumber = 0;
bool simulateError = false; // Toggle to simulate transmission errors
// Function prototypes
void 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, const char* data);
void transmitPacket(Packet* pkt);
void receivePacket(Packet* pkt);
void printPacketDetails(Packet* pkt, const char* 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 outputs
void 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 times
void 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 simple sum method
// This is a simplified version of real checksums (like CRC)
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 bytes
for (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 complement
return ~((uint16_t)sum);
}
// Verify packet checksum
bool verifyChecksum(Packet* pkt) {
uint16_t calculatedChecksum = calculateChecksum(pkt);
// For verification, adding original checksum should give 0xFFFF
// Here we simply compare calculated vs stored
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 data
void createPacket(Packet* pkt, uint8_t type, const char* data) {
// Clear packet memory
memset(pkt, 0, sizeof(Packet));
// Set header fields
pkt->version = PROTOCOL_VERSION;
pkt->type = type;
pkt->sequence = sequenceNumber++;
// Copy payload data
if (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 transmission
void 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 effects
if (simulateError) {
Serial.println(" [!] Simulating network error - corrupting data");
// Corrupt one byte of payload to demonstrate error detection
if (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 verification
void 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 information
void printPacketDetails(Packet* pkt, const char* 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 name
switch(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 function
void demonstratePacketFlow() {
static int demoStep = 0;
Packet packet;
Serial.println("============================================");
Serial.printf(" DEMONSTRATION CYCLE %d\n", demoStep + 1);
Serial.println("============================================\n");
switch (demoStep) {
case 0:
// 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;
case 1:
// 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;
case 2:
// 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;
case 3:
// 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;
case 4:
// 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;
}631.6 Step-by-Step Instructions
631.6.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
- 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
631.6.2 Step 2: Upload the Code
- Copy the complete code above
- Paste it into the Wokwi code editor (replacing any existing code)
- Click the “Start Simulation” button
631.6.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
631.6.4 Step 4: Understand the Packet Structure
Study the Serial Monitor output to identify:
+------------------------------------------+
| TX Packet Details |
+------------------------------------------+
| HEADER: |
| Version: 0x01 | <-- Protocol version
| Type: 0x01 (DATA) | <-- Packet type
| Length: 16 bytes | <-- Payload size
| Sequence: 0 | <-- Order tracking
| PAYLOAD: |
| Data: "Hello IoT World!" | <-- Actual data
| Hex: 48 65 6C 6C 6F 20 49 6F ... | <-- Binary representation
| CHECKSUM: |
| Value: 0xF8E2 | <-- Error detection code
+------------------------------------------+
631.6.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
631.7 Expected Output
When running the simulation, your Serial Monitor should display:
========================================
Network Packet Simulator v1.0
ESP32 IoT Learning Lab
========================================
Testing LEDs...
LED Test Complete!
- Red: Error indicator
- Green: Success indicator
- Yellow: Transmission indicator
- Blue: Received indicator
============================================
DEMONSTRATION CYCLE 1
============================================
[Demo 1] Sending DATA packet - Normal transmission
>>> TRANSMITTING PACKET >>>
+------------------------------------------+
| TX Packet Details |
+------------------------------------------+
| HEADER: |
| Version: 0x01 |
| Type: 0x01 (DATA) |
| Length: 16 bytes |
| Sequence: 0 |
| PAYLOAD: |
| Data: "Hello IoT World!" |
| Hex: 48 65 6C 6C 6F 20 49 6F ... |
| CHECKSUM: |
| Value: 0xF8E2 |
+------------------------------------------+
Transmitting...
Transmission complete.
<<< RECEIVING PACKET <<<
[... packet details ...]
Verifying checksum...
[OK] Checksum VALID - Packet integrity confirmed!
--- Waiting 5 seconds before next cycle ---
631.8 Challenge Exercises
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.
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.
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.
631.9 Key Concepts Reinforced
| Concept | How It’s Demonstrated |
|---|---|
| Packet Header | Version, type, length, sequence fields provide metadata about the payload |
| Payload | The actual data being transmitted (sensor readings, commands, messages) |
| Checksum | Mathematical verification that data was not corrupted during transmission |
| Error Detection | Comparing calculated checksum against transmitted checksum reveals corruption |
| LED State Machine | Visual representation of packet transmission states (idle, transmitting, received, error) |
| Protocol Versioning | Version field allows backward compatibility as protocols evolve |
| Sequence Numbers | Enable detection of lost, duplicate, or out-of-order packets |
631.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 |
| Checksum always fails | Verify checksum calculation includes all header and payload bytes |
| Simulation not starting | Click “Start Simulation” button; check for code syntax errors |
631.11 Summary
This lab demonstrated essential packet networking concepts through hands-on simulation:
- Packet Structure: Header (version, type, length, sequence) + Payload + Checksum
- Transmission States: Visual feedback through LEDs shows packet lifecycle
- Error Detection: Checksums catch data corruption during transmission
- Protocol Design: Version fields, sequence numbers, and ACK/NACK enable reliable communication
631.12 What’s Next
Continue to the Packet Journey Game for an interactive adventure that ties together all networking concepts through gamified learning.
631.13 Further Reading
After completing this lab, explore these related topics:
- TCP/IP Fundamentals: How real network protocols implement similar concepts
- Error Correction: Beyond detection - actually fixing corrupted data
- Network Topologies: How packets route through different network structures