1075 LoRaWAN ADR and Duty Cycle Optimization
1075.1 Learning Objectives
By the end of this chapter, you should be able to:
- Understand the Adaptive Data Rate (ADR) algorithm
- Calculate link margins and optimal spreading factors
- Configure ADR for different deployment scenarios
- Understand EU and US duty cycle regulations
- Calculate message budgets under duty cycle constraints
- Troubleshoot ADR convergence problems
1075.2 Expert Deep Dive: Protocol Internals
1075.2.1 Adaptive Data Rate (ADR) Algorithm Implementation
For practitioners implementing LoRaWAN networks, understanding the ADR algorithm internals is crucial for optimal performance.
1075.2.2 ADR Decision Process
The network server calculates optimal spreading factor using these steps:
1. Signal Quality Measurement (Gateway -> Network Server)
For each uplink from device:
- RSSI (Received Signal Strength Indicator): Measured in dBm
- SNR (Signal-to-Noise Ratio): Measured in dB
- Packet count: Last 20 uplinks tracked
2. Link Margin Calculation
Link Margin = SNR - SNR_required(SF)
Required SNR by spreading factor:
SF7: -7.5 dB
SF8: -10 dB
SF9: -12.5 dB
SF10: -15 dB
SF11: -17.5 dB
SF12: -20 dB
Example:
Measured SNR = -8 dB
SF7 required: -7.5 dB
Link Margin = -8 - (-7.5) = -0.5 dB -> SF7 NOT viable
SF8 required: -10 dB
Link Margin = -8 - (-10) = +2 dB -> SF8 viable but marginal
SF9 required: -12.5 dB
Link Margin = -8 - (-12.5) = +4.5 dB -> SF9 safe
3. Optimal SF Selection
def calculate_optimal_sf(snr_measurements):
"""
ADR algorithm to determine optimal spreading factor
Args:
snr_measurements: List of last 20 SNR readings (dB)
Returns:
Optimal SF and TX power
"""
# Use worst-case SNR from last 20 packets (with margin)
snr_min = min(snr_measurements)
# Required SNR per SF (dBm)
sf_requirements = {
7: -7.5, 8: -10, 9: -12.5,
10: -15, 11: -17.5, 12: -20
}
# Target: 10 dB link margin for reliability
target_margin = 10
# Find lowest SF with sufficient margin
for sf in range(7, 13):
required_snr = sf_requirements[sf]
link_margin = snr_min - required_snr
if link_margin >= target_margin:
return sf, calculate_tx_power(link_margin)
# If no SF works with 10 dB margin, use SF12
return 12, max_tx_power
def calculate_tx_power(link_margin):
"""
Reduce TX power if excessive link margin
"""
excess_margin = link_margin - 10 # Keep 10 dB safety
if excess_margin > 0:
# Reduce TX power by excess margin (up to 10 dB max reduction)
power_reduction = min(excess_margin, 10)
return default_tx_power - power_reduction
return default_tx_power4. ADR Command Transmission
Network Server -> Device (via LinkADRReq MAC command):
- New Spreading Factor (SF7-SF12)
- New TX Power (0-15, maps to 2-20 dBm)
- New Channel Mask (enable/disable channels)
- NbRep (number of transmissions for redundancy)
Device acknowledges with LinkADRAns
1075.2.3 Real-World ADR Example
Scenario: Sensor initially joins network at default SF12 (conservative)
| Uplink # | RSSI (dBm) | SNR (dB) | Current SF | Link Margin | Network Action |
|---|---|---|---|---|---|
| 1 | -95 | +5 | SF12 | +25 dB | Collect data |
| 5 | -96 | +4 | SF12 | +24 dB | Collect data |
| 10 | -94 | +6 | SF12 | +26 dB | Collect data |
| 20 | -95 | +5 | SF12 | +25 dB | Command: Switch to SF9 |
| 25 | -96 | +4 | SF9 | +16.5 dB | Excellent |
| 30 | -95 | +5 | SF9 | +17.5 dB | Excellent |
| 40 | -94 | +6 | SF9 | +18.5 dB | Command: Switch to SF7 |
| 45 | -95 | +5 | SF7 | +12.5 dB | Good (10+ dB margin) |
Result: Battery life improves by 23x (SF12 -> SF7)!
1075.2.4 ADR Limitations and Pitfalls
1. Mobile Devices:
Problem: Device moves from gateway A (strong signal) to gateway B (weak signal)
- ADR at A: SF7 (fast, short range)
- Device moves to B: Signal degrades
- Still using SF7 -> messages lost
- Takes 20+ uplinks to readjust -> unacceptable for mobile
Solution: Disable ADR for mobile devices, use SF10-SF12
2. Interferer Appears:
Problem: New Wi-Fi access point near gateway
- Before: SNR +10 dB, SF7 optimal
- After: SNR -5 dB (interference), SF7 fails
- ADR takes 20 uplinks to detect -> 20 lost messages
Solution: Faster ADR response (reduce convergence window) or use SF9-SF10 for critical apps
3. Weather Changes:
Problem: Heavy rain increases path loss by 5-10 dB
- Clear weather: SF7 with +12 dB margin
- Heavy rain: Margin drops to +2 dB -> packet loss spikes
- ADR eventually adjusts to SF9, but 10-20 packets lost
Solution: Use higher default SF in regions with severe weather
1075.2.5 Best Practices for ADR
- Enable ADR for stationary devices (sensors, meters)
- Disable ADR for mobile devices (asset trackers, vehicles)
- Set ADR_ACK_LIMIT = 64 (default) for good convergence
- Monitor failed uplink ratio -> if >5%, ADR not working well
- Manually set SF for critical devices where packet loss unacceptable
1075.2.6 ADR Best Practices by Deployment Type
| Deployment | ADR Setting | Installation Margin | NbTrans | Notes |
|---|---|---|---|---|
| Urban IoT | Enabled | 15 dB | 2 | Interference-resistant |
| Rural Sensors | Enabled | 5 dB | 1 | Battery-optimized |
| Industrial | Enabled | 12 dB | 2 | Balanced reliability |
| Mobile Assets | Disabled | N/A | 1 | Fixed SF9-SF10 |
| Critical Alarms | Enabled | 20 dB | 3 | Maximum reliability |
1075.3 Duty Cycle Regulations and Fair Access Policy
LoRaWAN operates in unlicensed ISM bands, subject to strict duty cycle regulations:
1075.3.1 EU Regulations (ETSI EN 300.220)
Sub-bands and Duty Cycles:
| Frequency (MHz) | Duty Cycle | Max TX Power | Usage |
|---|---|---|---|
| 865.0 - 868.0 | 1% (36 sec/hour) | 25 mW (14 dBm) | G1 channels (uplink) |
| 868.0 - 868.6 | 1% | 25 mW | G channels (uplink) |
| 868.7 - 869.2 | 0.1% (3.6 sec/hour) | 25 mW | G2 channels (uplink) |
| 869.4 - 869.65 | 10% (6 min/hour) | 500 mW (27 dBm) | G3 downlink (gateways) |
Practical Impact:
Example: SF10, 50-byte payload, 370 ms airtime
1% duty cycle (G1 channels):
- Max airtime: 3600 sec/hour x 1% = 36 sec/hour
- Messages allowed: 36000 ms / 370 ms = 97 messages/hour
- Minimum interval: 370 ms x 100 = 37 seconds between messages
0.1% duty cycle (G2 channels):
- Max airtime: 3600 sec/hour x 0.1% = 3.6 sec/hour
- Messages allowed: 3600 ms / 370 ms = 9.7 messages/hour
- Minimum interval: 370 ms x 1000 = 370 seconds (6.2 minutes!)
1075.3.2 US Regulations (FCC Part 15)
Unlicensed ISM Band: 902-928 MHz
| Parameter | Limit | Notes |
|---|---|---|
| Duty Cycle | None (but Fair Access Policy applies) | No explicit duty cycle |
| Max EIRP | 4W (36 dBm) with FHSS | Frequency hopping required |
| Dwell Time | 400 ms per channel | Must hop channels |
| Hop Channels | 50+ channels | LoRaWAN uses 64 or 72 channels |
LoRaWAN Fair Access Policy (US):
Recommendation: Uplink duty cycle 4% or less (even though not regulated)
- Prevents network saturation
- Allows fair sharing of spectrum
- Max ~2 minutes of airtime per hour
1075.3.3 Duty Cycle Violation Consequences
Regulatory: - EU: Fines up to 10,000 EUR+ per violation - FCC: Fines, equipment seizure, spectrum ban - Enforcement: Automatic monitoring systems detect violations
Network: - The Things Network: Device automatically blocked - Private networks: Self-inflicted interference, packet loss - Other users: Your transmissions cause collisions
Technical: - Module firmware enforces duty cycle (cannot bypass) - Attempted transmission queued until duty cycle resets - Confirmed messages may fail if ACK arrives during duty cycle block
1075.3.4 Calculating Message Budget
Example: Smart meter (EU 868 MHz, G1 channel, 1% duty cycle)
def calculate_max_messages(sf, payload_bytes):
"""
Calculate maximum messages per hour under 1% duty cycle
"""
# Airtime table for EU868, BW=125kHz (milliseconds)
airtime_table = {
7: {10: 41, 20: 51, 50: 72},
8: {10: 72, 20: 92, 50: 133},
9: {10: 144, 20: 185, 50: 247},
10: {10: 247, 20: 329, 50: 452},
11: {10: 454, 20: 617, 50: 865},
12: {10: 908, 20: 1233, 50: 1745}
}
# Find closest payload size
closest_payload = min([10, 20, 50], key=lambda x: abs(x - payload_bytes))
airtime_ms = airtime_table[sf][closest_payload]
# 1% duty cycle = 36,000 ms per hour
duty_cycle_budget_ms = 36000
max_messages = duty_cycle_budget_ms / airtime_ms
min_interval_sec = (airtime_ms / 1000) * 100 # 1% = 100x wait
return int(max_messages), min_interval_sec
# Examples:
# SF7, 20 bytes: (705 msg/hr, 5.1 sec interval)
# SF10, 50 bytes: (79 msg/hr, 45.2 sec interval)
# SF12, 50 bytes: (20 msg/hr, 174.5 sec = 2.9 min interval)Key Insight: Higher spreading factors severely limit message frequency under duty cycle constraints. This is another reason why ADR is critical!
1075.4 Understanding Check: Class A Firmware Updates
Scenario: A municipal water utility has deployed 5,000 LoRaWAN smart meters (Class A devices) across the city. Each meter reports usage once per hour. A critical security patch requires a firmware update - the new firmware is 100 KB. The utility needs to update all meters within 2 weeks.
Think about: 1. Why is the Class A receive window architecture a problem for large file transfers? 2. How many downlink messages would each meter need to receive the full firmware? 3. What trade-offs exist between Class A, Class B, and Class C for this scenario?
The Math: - Firmware size: 100 KB = 100,000 bytes - LoRaWAN payload: ~100 bytes per message (average) - Messages needed: 100,000 / 100 = 1,000 downlink messages per meter - At 1 uplink/hour: 1,000 hours = 41 days per device - For 5,000 meters sequentially: Impossible within 2 weeks
| Update Strategy | Receive Windows | Battery Impact | Time to Update 5,000 Meters |
|---|---|---|---|
| Class A | After uplink only (3 sec total) | Lowest | 41 days x sequential = Months |
| Class B | Scheduled + uplink windows | Medium | ~1 week with coordination |
| Class C | Always listening | Highest (mains power needed) | 1-2 days |
Practical Takeaway: Class A is perfect for sensors sending data upward, but firmware updates require either patient sequential updates, Class B coordination, or Class C power availability.
1075.5 Summary
This chapter covered ADR and duty cycle optimization:
- ADR Algorithm: Uses 20 uplinks to calculate optimal SF based on link margin
- Link Margin Target: 10 dB for reliability, adjustable per deployment
- ADR Limitations: Fails with mobile devices, sudden interference, weather changes
- EU Duty Cycle: 1% (36 sec/hour) on main sub-bands
- US Regulations: No duty cycle but 400ms dwell time and frequency hopping
- Message Budget: Higher SF = fewer messages possible under duty cycle
1075.6 Whatβs Next
Continue to Common Pitfalls to learn about common LoRaWAN deployment mistakes and how to avoid them.
Alternative paths: - LoRaWAN Lab - Hands-on simulation exercises - Practice Exercises - Test your knowledge