674 IoT Protocol Review: Hands-On Labs
674.1 Learning Objectives
By the end of this chapter, you will be able to:
- Analyze Protocol Overhead: Calculate header sizes and efficiency for different protocol stacks
- Build Packet Analyzers: Implement ESP32 tools to inspect protocol structures
- Debug Protocol Issues: Use low-level analysis to diagnose communication problems
- Optimize Message Size: Design efficient payloads minimizing protocol overhead
674.2 Prerequisites
Required Chapters: - Protocol Review: Visual Summaries - Protocol stack architecture - IoT Protocols Fundamentals - Core concepts
Technical Background: - Protocol stack concepts - C/C++ programming for ESP32 - Understanding of bytes and bits
Hardware Required: - ESP32 development board - USB cable - Computer with Arduino IDE
Estimated Time: 25 minutes
What is protocol overhead? Protocol overhead is the βwrapperβ that protocols add to your actual data. Like shipping a small item in a big box with lots of packing material, protocols add headers that can be much larger than your actual payload.
Why does this matter? For IoT devices: - Battery life: More bytes = more radio time = shorter battery life - Network capacity: Less overhead = more devices per gateway - Cost: Less airtime = lower cellular/network costs
Example: Sending a 4-byte temperature reading (23.5C) might require: - 66 bytes total with uncompressed IPv6 - 26 bytes total with 6LoWPAN compression - Thatβs 40 bytes saved per message!
674.3 Lab 1: ESP32 Protocol Packet Analyzer
Objective: Analyze IoT protocol packets to understand overhead and compare different protocol stacks.
674.3.1 Code
#include <Wi-Fi.h>
// Simulated protocol headers
struct ProtocolHeaders {
// Data Link Layer
uint8_t mac_dst[6]; // 6 bytes
uint8_t mac_src[6]; // 6 bytes
uint16_t mac_type; // 2 bytes
// Total Ethernet: 14 bytes (without FCS)
// Network Layer (IPv6 simplified)
uint8_t ip_version; // 4 bits + traffic class/flow
uint16_t ip_payload_len; // 2 bytes
uint8_t ip_next_header; // 1 byte
uint8_ip_hop_limit; // 1 byte
uint8_t ip_src[16]; // 16 bytes
uint8_t ip_dst[16]; // 16 bytes
// Total IPv6: 40 bytes
// Transport Layer (UDP)
uint16_t udp_src_port; // 2 bytes
uint16_t udp_dst_port; // 2 bytes
uint16_t udp_length; // 2 bytes
uint16_t udp_checksum; // 2 bytes
// Total UDP: 8 bytes
// Application Layer (CoAP minimal)
uint8_t coap_version_type_tkl; // 1 byte
uint8_t coap_code; // 1 byte
uint16_t coap_message_id; // 2 bytes
// Total CoAP: 4 bytes minimum
// Payload
uint8_t payload[4]; // 4 bytes actual data
};
void setup() {
Serial.begin(115200);
delay(2000);
Serial.println("\nββββββββββββββββββββββββββββββββββββββββββ");
Serial.println("β IoT Protocol Packet Analyzer β");
Serial.println("β ESP32 Protocol Overhead Inspector β");
Serial.println("ββββββββββββββββββββββββββββββββββββββββββ\n");
// Analyze different protocol stacks
analyzeProtocolStacks();
// Demonstrate 6LoWPAN compression benefit
demonstrate6LoWPANCompression();
// Show payload efficiency
analyzePayloadEfficiency();
}
void loop() {
// Analysis runs once
}
void analyzeProtocolStacks() {
Serial.println("βββββββββββββββββββββββββββββββββββββββββββββββββββ");
Serial.println("PROTOCOL STACK OVERHEAD ANALYSIS (4-byte payload)");
Serial.println("βββββββββββββββββββββββββββββββββββββββββββββββββββ\n");
// Stack 1: CoAP/UDP/IPv6/Ethernet (uncompressed)
printStackAnalysis(
"CoAP/UDP/IPv6/Ethernet",
14, // Ethernet
40, // IPv6
8, // UDP
4, // CoAP
4 // Payload
);
// Stack 2: CoAP/UDP/IPv6/802.15.4 with 6LoWPAN
printStackAnalysis(
"CoAP/UDP/IPv6/802.15.4 (6LoWPAN)",
8, // 802.15.4 compressed
6, // IPv6 compressed
4, // UDP compressed
4, // CoAP
4 // Payload
);
// Stack 3: MQTT/TCP/IPv6/Ethernet
printStackAnalysis(
"MQTT/TCP/IPv6/Ethernet",
14, // Ethernet
40, // IPv6
20, // TCP minimum
2, // MQTT minimum
4 // Payload
);
// Stack 4: HTTP/TCP/IPv4/Ethernet
printStackAnalysis(
"HTTP/TCP/IPv4/Ethernet",
14, // Ethernet
20, // IPv4
20, // TCP
50, // HTTP minimum
4 // Payload
);
}
void printStackAnalysis(const char* name, int datalink, int network,
int transport, int application, int payload) {
int total_overhead = datalink + network + transport + application;
int total_packet = total_overhead + payload;
float efficiency = (float)payload / total_packet * 100.0;
Serial.println("βββββββββββββββββββββββββββββββββββββββββββββββββ");
Serial.printf("Stack: %s\n", name);
Serial.println("βββββββββββββββββββββββββββββββββββββββββββββββββ");
Serial.printf("Data Link: %3d bytes\n", datalink);
Serial.printf("Network: %3d bytes\n", network);
Serial.printf("Transport: %3d bytes\n", transport);
Serial.printf("Application: %3d bytes\n", application);
Serial.println(" βββββββββ");
Serial.printf("Total Overhead: %3d bytes\n", total_overhead);
Serial.printf("Payload: %3d bytes\n", payload);
Serial.println(" βββββββββ");
Serial.printf("TOTAL PACKET: %3d bytes\n\n", total_packet);
Serial.printf("Payload Efficiency: %.2f%%\n", efficiency);
Serial.printf("Overhead Ratio: %.2fΓ payload\n\n", (float)total_overhead/payload);
}
void demonstrate6LoWPANCompression() {
Serial.println("\nβββββββββββββββββββββββββββββββββββββββββββββββββββ");
Serial.println("6LoWPAN COMPRESSION BENEFIT");
Serial.println("βββββββββββββββββββββββββββββββββββββββββββββββββββ\n");
Serial.println("IPv6 Header Compression:");
Serial.println(" Uncompressed IPv6: 40 bytes");
Serial.println(" - Version (4 bits)");
Serial.println(" - Traffic Class (8 bits)");
Serial.println(" - Flow Label (20 bits)");
Serial.println(" - Payload Length (16 bits)");
Serial.println(" - Next Header (8 bits)");
Serial.println(" - Hop Limit (8 bits)");
Serial.println(" - Source Address (128 bits = 16 bytes)");
Serial.println(" - Destination Address (128 bits = 16 bytes)");
Serial.println();
Serial.println(" 6LoWPAN Compressed: 6 bytes (85% reduction!)");
Serial.println(" - Context-based compression");
Serial.println(" - Link-local addresses derived from MAC");
Serial.println(" - Omit hop limit (use default)");
Serial.println(" - Omit flow label (not needed)");
Serial.println();
Serial.println("Total Savings:");
Serial.println(" Before: 40 + 25 = 65 bytes (IPv6 + 802.15.4)");
Serial.println(" After: 6 + 8 = 14 bytes (compressed)");
Serial.println(" Savings: 51 bytes = 78% reduction");
Serial.println();
}
void analyzePayloadEfficiency() {
Serial.println("βββββββββββββββββββββββββββββββββββββββββββββββββββ");
Serial.println("PAYLOAD SIZE IMPACT ON EFFICIENCY");
Serial.println("βββββββββββββββββββββββββββββββββββββββββββββββββββ\n");
Serial.println("CoAP/UDP/IPv6/802.15.4 (6LoWPAN compressed):");
Serial.println("Fixed overhead: 22 bytes\n");
Serial.println("Payload Total Packet Efficiency");
Serial.println("(bytes) (bytes) (%)");
Serial.println("ββββββββββββββββββββββββββββββββββ");
int overhead = 22;
int payloads[] = {4, 8, 16, 32, 64, 105}; // 105 = max for 127-byte packet
for (int i = 0; i < 6; i++) {
int payload = payloads[i];
int total = overhead + payload;
float efficiency = (float)payload / total * 100.0;
char line[50];
sprintf(line, "%4d %4d %.1f%%", payload, total, efficiency);
Serial.println(line);
}
Serial.println("\nβ Key Insight: Larger payloads dramatically improve efficiency!");
Serial.println(" Aggregate multiple sensor readings when possible.");
}674.3.2 Expected Serial Output
ββββββββββββββββββββββββββββββββββββββββββ
β IoT Protocol Packet Analyzer β
β ESP32 Protocol Overhead Inspector β
ββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββ
PROTOCOL STACK OVERHEAD ANALYSIS (4-byte payload)
βββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββ
Stack: CoAP/UDP/IPv6/Ethernet
βββββββββββββββββββββββββββββββββββββββββββββββββ
Data Link: 14 bytes
Network: 40 bytes
Transport: 8 bytes
Application: 4 bytes
βββββββββ
Total Overhead: 66 bytes
Payload: 4 bytes
βββββββββ
TOTAL PACKET: 70 bytes
Payload Efficiency: 5.71%
Overhead Ratio: 16.50Γ payload
[... similar for other stacks ...]
βββββββββββββββββββββββββββββββββββββββββββββββββββ
6LoWPAN COMPRESSION BENEFIT
βββββββββββββββββββββββββββββββββββββββββββββββββββ
IPv6 Header Compression:
Uncompressed IPv6: 40 bytes
- Version (4 bits)
- Traffic Class (8 bits)
- Flow Label (20 bits)
- Payload Length (16 bits)
- Next Header (8 bits)
- Hop Limit (8 bits)
- Source Address (128 bits = 16 bytes)
- Destination Address (128 bits = 16 bytes)
6LoWPAN Compressed: 6 bytes (85% reduction!)
- Context-based compression
- Link-local addresses derived from MAC
- Omit hop limit (use default)
- Omit flow label (not needed)
Total Savings:
Before: 40 + 25 = 65 bytes (IPv6 + 802.15.4)
After: 6 + 8 = 14 bytes (compressed)
Savings: 51 bytes = 78% reduction
βββββββββββββββββββββββββββββββββββββββββββββββββββ
PAYLOAD SIZE IMPACT ON EFFICIENCY
βββββββββββββββββββββββββββββββββββββββββββββββββββ
CoAP/UDP/IPv6/802.15.4 (6LoWPAN compressed):
Fixed overhead: 22 bytes
Payload Total Packet Efficiency
(bytes) (bytes) (%)
ββββββββββββββββββββββββββββββββββ
4 26 15.4%
8 30 26.7%
16 38 42.1%
32 54 59.3%
64 86 74.4%
105 127 82.7%
β Key Insight: Larger payloads dramatically improve efficiency!
Aggregate multiple sensor readings when possible.
674.3.3 Lab Analysis
6LoWPAN compression is essential: Reduces IPv6 overhead from 40 bytes to 6 bytes (85% reduction)
Protocol stack matters more than application header: MQTTβs 2-byte header is smaller than CoAPβs 4-byte header, but MQTT requires TCP (20 bytes) vs UDP (8 bytes)
Payload aggregation improves efficiency:
- 4-byte payload: 15.4% efficiency
- 64-byte payload: 74.4% efficiency
- Consider batching sensor readings!
HTTP is unsuitable for constrained devices: 104+ bytes of overhead for tiny payloads
674.4 Lab 2: Overhead Calculator Tool
Objective: Create a reusable calculator to compare protocol efficiency.
674.4.1 Python Implementation
#!/usr/bin/env python3
"""
IoT Protocol Overhead Calculator
Compare different protocol stacks for efficiency analysis
"""
# Protocol header sizes (bytes)
HEADERS = {
# Data Link Layer
'ethernet': 14,
'802.15.4': 25,
'802.15.4_compressed': 8,
'ble': 10,
'lorawan': 13,
# Network Layer
'ipv4': 20,
'ipv6': 40,
'6lowpan': 6, # Compressed IPv6
# Transport Layer
'tcp': 20,
'udp': 8,
'udp_compressed': 4, # With 6LoWPAN NHC
# Application Layer
'http': 50, # Minimum
'mqtt': 2, # Minimum fixed header
'coap': 4, # Base header
'amqp': 8, # Frame header
}
# Common protocol stacks
STACKS = {
'http_tcp_ipv4_eth': ['ethernet', 'ipv4', 'tcp', 'http'],
'mqtt_tcp_ipv6_wifi': ['ethernet', 'ipv6', 'tcp', 'mqtt'],
'mqtt_tcp_6lowpan': ['802.15.4_compressed', '6lowpan', 'tcp', 'mqtt'],
'coap_udp_ipv6_eth': ['ethernet', 'ipv6', 'udp', 'coap'],
'coap_udp_6lowpan': ['802.15.4_compressed', '6lowpan', 'udp_compressed', 'coap'],
'lorawan_mac': ['lorawan'], # LoRaWAN uses MAC-layer addressing
}
def calculate_overhead(stack_name):
"""Calculate total overhead for a protocol stack."""
if stack_name not in STACKS:
raise ValueError(f"Unknown stack: {stack_name}")
stack = STACKS[stack_name]
total = sum(HEADERS[layer] for layer in stack)
return total, stack
def compare_stacks(payload_size):
"""Compare all stacks for a given payload size."""
print(f"\n{'='*60}")
print(f"PROTOCOL STACK COMPARISON (Payload: {payload_size} bytes)")
print(f"{'='*60}\n")
results = []
for name in STACKS:
overhead, layers = calculate_overhead(name)
total = overhead + payload_size
efficiency = (payload_size / total) * 100
results.append((name, overhead, total, efficiency, layers))
# Sort by efficiency (descending)
results.sort(key=lambda x: x[3], reverse=True)
print(f"{'Stack':<25} {'Overhead':>10} {'Total':>8} {'Efficiency':>12}")
print("-" * 60)
for name, overhead, total, eff, layers in results:
print(f"{name:<25} {overhead:>10}B {total:>7}B {eff:>11.1f}%")
# Best and worst
best = results[0]
worst = results[-1]
print(f"\nβ Best: {best[0]} ({best[3]:.1f}% efficiency)")
print(f"β Worst: {worst[0]} ({worst[3]:.1f}% efficiency)")
print(f" Difference: {worst[2] - best[2]} bytes, {worst[3]/best[3]:.1f}x less efficient")
return results
def battery_life_estimate(stack_name, payload_size, tx_interval_sec,
battery_mah=2000, tx_current_ma=20,
data_rate_bps=250000):
"""Estimate battery life for a protocol stack."""
overhead, _ = calculate_overhead(stack_name)
total_bytes = overhead + payload_size
# Calculate transmission time per message
tx_time_sec = (total_bytes * 8) / data_rate_bps
# Messages per day
messages_per_day = 86400 / tx_interval_sec
# Daily active time
daily_active_sec = messages_per_day * tx_time_sec
# Daily energy consumption (mAh)
daily_mah = (tx_current_ma * daily_active_sec) / 3600
# Add sleep current (5 uA typical)
sleep_mah = (0.005 * (86400 - daily_active_sec)) / 3600
total_daily_mah = daily_mah + sleep_mah
# Battery life in days
battery_days = battery_mah / total_daily_mah
battery_years = battery_days / 365
return {
'total_bytes': total_bytes,
'tx_time_ms': tx_time_sec * 1000,
'daily_messages': messages_per_day,
'daily_mah': total_daily_mah,
'battery_days': battery_days,
'battery_years': battery_years
}
# Example usage
if __name__ == "__main__":
# Compare stacks for different payload sizes
for payload in [4, 16, 64]:
compare_stacks(payload)
# Battery life comparison
print(f"\n{'='*60}")
print("BATTERY LIFE COMPARISON (10-minute intervals, 4-byte payload)")
print(f"{'='*60}\n")
stacks_to_compare = ['coap_udp_6lowpan', 'mqtt_tcp_6lowpan', 'http_tcp_ipv4_eth']
for stack in stacks_to_compare:
result = battery_life_estimate(stack, 4, 600) # 10 minutes
print(f"{stack}:")
print(f" Packet: {result['total_bytes']} bytes, TX time: {result['tx_time_ms']:.2f}ms")
print(f" Daily energy: {result['daily_mah']:.4f} mAh")
print(f" Battery life: {result['battery_years']:.1f} years\n")674.4.2 Expected Output
============================================================
PROTOCOL STACK COMPARISON (Payload: 4 bytes)
============================================================
Stack Overhead Total Efficiency
------------------------------------------------------------
lorawan_mac 13B 17B 23.5%
coap_udp_6lowpan 22B 26B 15.4%
mqtt_tcp_6lowpan 36B 40B 10.0%
coap_udp_ipv6_eth 66B 70B 5.7%
mqtt_tcp_ipv6_wifi 76B 80B 5.0%
http_tcp_ipv4_eth 104B 108B 3.7%
β Best: lorawan_mac (23.5% efficiency)
β Worst: http_tcp_ipv4_eth (3.7% efficiency)
Difference: 91 bytes, 6.4x less efficient
============================================================
BATTERY LIFE COMPARISON (10-minute intervals, 4-byte payload)
============================================================
coap_udp_6lowpan:
Packet: 26 bytes, TX time: 0.83ms
Daily energy: 0.0012 mAh
Battery life: 45.7 years
mqtt_tcp_6lowpan:
Packet: 40 bytes, TX time: 1.28ms
Daily energy: 0.0018 mAh
Battery life: 30.1 years
http_tcp_ipv4_eth:
Packet: 108 bytes, TX time: 3.46ms
Daily energy: 0.0050 mAh
Battery life: 11.0 years
674.5 Lab Extension: Real-Time Packet Analysis
For real protocol analysis, use Wireshark with these filters:
CoAP Traffic:
coap && ip.dst == 192.168.1.100
MQTT Traffic:
mqtt && tcp.port == 1883
6LoWPAN over 802.15.4:
wpan && 6lowpan
Key Fields to Analyze: - Frame length (total bytes on wire) - Protocol header sizes at each layer - Payload extraction and efficiency calculation
674.6 Summary
ESP32 Packet Analyzer (Lab 1): - Demonstrated header sizes for common protocol stacks - 6LoWPAN compression: 40-byte IPv6 header to 6 bytes (85% reduction) - Protocol efficiency varies from 3.7% (HTTP) to 82.7% (max CoAP payload)
Overhead Calculator (Lab 2): - LoRaWAN most efficient for small payloads (23.5% at 4 bytes) - CoAP/UDP/6LoWPAN best for IP-based constrained networks - HTTP unsuitable for battery-powered IoT devices
Battery Life Impact: - Protocol choice can mean 4x difference in battery life - For frequent transmissions, use CoAP/UDP over MQTT/TCP - Aggregate payloads when possible to improve efficiency
Optimization Strategies: 1. Use 6LoWPAN for 802.15.4 networks (mandatory for efficiency) 2. Prefer UDP over TCP for constrained devices 3. Batch sensor readings to maximize payload efficiency 4. Consider LoRaWAN MAC-layer for LPWAN (no IP overhead)
674.7 Whatβs Next
Continue with the protocol review series:
- Protocol Review: Assessment: Test your knowledge with comprehensive quizzes
For protocol deep dives: - MQTT: Publish-subscribe messaging - CoAP Architecture: RESTful protocol for IoT - 6LoWPAN Fundamentals: IPv6 compression details