40  RPL Lab: Network Design

In 60 Seconds

Hands-on lab for designing RPL network topologies: planning DODAG structures with node placement, calculating RANK values based on hop count and objective functions, comparing Storing vs Non-Storing modes for building deployments, and optimizing the trade-off between routing overhead and battery life.

40.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Design DODAG Topologies: Construct RPL network structures with appropriate node placement and parent relationships
  • Calculate RANK Values: Derive RANK assignments for nodes based on hop count and objective functions
  • Evaluate Operating Modes: Justify Storing vs Non-Storing mode selection for different building and deployment scenarios
  • Classify Routing Nodes: Differentiate which devices should serve as RPL routers versus leaf nodes
  • Optimize for Power: Architect RPL networks that balance routing overhead with battery life requirements

40.2 Prerequisites

Required Chapters:

Technical Background:

  • IPv6 addressing basics
  • Tree-based routing concepts
  • Directed Acyclic Graphs (DAGs)

Lab Requirements:

  • Contiki-NG or RIOT OS environment
  • 6LoWPAN capable hardware (or simulator like Cooja)
  • Wireshark with RPL dissector for packet analysis

Estimated Time: 45 minutes

What is this lab? A practical design exercise for RPL (Routing Protocol for Low-Power and Lossy Networks) network planning.

When to use:

  • After studying RPL fundamentals and DODAG construction
  • When you want hands-on routing design experience
  • Before implementing RPL in real networks

Key Concepts to Practice:

Concept Exercise Focus
DODAG Formation Build routing trees
Rank Calculation Understand path costs
Mode Selection Storing vs Non-Storing
Memory Planning Resource requirements

“This lab lets you be the network architect,” said Max the Microcontroller. “You will design a DODAG for a real smart building – placing the root, assigning RANK values, and deciding which mode to use.”

“Start by figuring out where to put the border router,” suggested Sammy the Sensor. “The root should be in a central location where most devices can reach it within a few hops. Putting it in a corner means some devices need many more hops than others.”

“Then calculate RANK values for each node,” added Lila the LED. “Remember, RANK depends on the objective function. With hop count, a two-hop device has RANK 512 if MinHopRankIncrease is 256. With ETX, the same device might have RANK 640 if the link requires 1.5 transmissions on average.”

“Do not forget to compare Storing and Non-Storing modes for your design,” emphasized Bella the Battery. “Calculate how much memory each node would need in Storing mode. If it exceeds your device’s RAM, Non-Storing mode is your only option!”

40.3 Hands-On Lab: RPL Network Design

Time: ~45 min | Level: Advanced | Code: P07.C23.U01

Lab Activity: Designing RPL Network for Smart Building

Objective: Design an RPL network and compare Storing vs Non-Storing modes

Scenario: 3-floor office building

Devices:

  • 1 Border Router (roof, internet connection)
  • 15 mains-powered sensors (temperature, humidity) - can be routers
  • 30 battery-powered sensors (door, motion) - end devices
  • 10 actuators (lights, HVAC) - mains powered

Floor Layout:

Floor 3: BR + 5 mains sensors + 10 battery sensors + 3 actuators
Floor 2: 5 mains sensors + 10 battery sensors + 4 actuators
Floor 1: 5 mains sensors + 10 battery sensors + 3 actuators

40.3.1 Task 1: DODAG Topology Design

Design the DODAG: 1. Place border router (root) 2. Identify routing nodes (mains-powered devices) 3. Draw DODAG showing parent-child relationships 4. Assign RANK values (assume RANK increase = 100 per hop)

Click to see solution

DODAG Design:

DODAG hierarchy diagram for 3-floor smart building showing Border Router at RANK 0 at roof, Floor 3 mains sensors at RANK 100, Floor 2 mains sensors at RANK 200, Floor 1 mains sensors at RANK 300, with battery sensors and actuators one additional hop beyond their floor router
Figure 40.1: DODAG topology for 3-floor smart building with border router and hierarchical node placement

This variant shows the same DODAG topology from a physical building perspective - emphasizing how RANK correlates with distance from the roof-mounted border router across floors.

Physical floor layout of 3-story building with RANK values overlaid: roof BR at RANK 0, Floor 3 mains sensors at RANK 100, Floor 2 mains sensors at RANK 200, Floor 1 mains sensors at RANK 300, all battery sensors one hop beyond their floor router
Figure 40.2: Physical floor layout showing RANK distribution across building floors

Key Insight: RANK increases as you move further from the roof-mounted border router. This physical-to-logical mapping helps design efficient multi-hop routes where mains-powered devices on each floor act as routing nodes for battery-powered sensors.

RANK Distribution (with RANK increase = 100 per hop): - BR: RANK 0 - Floor 3 mains sensors: RANK 100 (1 hop from BR) - Floor 2 mains sensors: RANK 200 (2 hops from BR) - Floor 1 mains sensors: RANK 300 (3 hops from BR) - Battery sensors / actuators: parent RANK + 100 (one additional hop)

Routing Nodes: 15 mains-powered sensors (can route for battery devices)

Total Nodes: 1 BR + 15 mains + 30 battery + 10 actuators = 56 devices

Objective: Simulate RPL DODAG construction with automatic RANK assignment and parent selection.

Paste this code into the Wokwi editor:

#include <Arduino.h>

#define MAX_NODES 12
#define RANK_INCREASE 256  // MinHopRankIncrease (RFC 6550 default)
#define ROOT_RANK 0
#define INFINITE_RANK 0xFFFF

struct RPLNode {
  uint8_t id;
  const char* name;
  uint16_t rank;
  int8_t parentId;      // -1 = no parent
  bool isRouter;
  uint8_t hopCount;
  float etx;            // Expected Transmission Count
};

RPLNode nodes[MAX_NODES] = {
  {0, "Border Router (Roof)",     ROOT_RANK, -1, true,  0, 1.0},
  {1, "Floor3-Router-A",          INFINITE_RANK, -1, true,  0, 0},
  {2, "Floor3-Router-B",          INFINITE_RANK, -1, true,  0, 0},
  {3, "Floor2-Router-A",          INFINITE_RANK, -1, true,  0, 0},
  {4, "Floor2-Router-B",          INFINITE_RANK, -1, true,  0, 0},
  {5, "Floor1-Router-A",          INFINITE_RANK, -1, true,  0, 0},
  {6, "Floor3-TempSensor",        INFINITE_RANK, -1, false, 0, 0},
  {7, "Floor3-MotionSensor",      INFINITE_RANK, -1, false, 0, 0},
  {8, "Floor2-DoorSensor",        INFINITE_RANK, -1, false, 0, 0},
  {9, "Floor2-LightActuator",     INFINITE_RANK, -1, false, 0, 0},
  {10,"Floor1-HumiditySensor",    INFINITE_RANK, -1, false, 0, 0},
  {11,"Floor1-HVACActuator",      INFINITE_RANK, -1, false, 0, 0}
};

// Adjacency: which nodes can hear each other (link quality as ETX)
struct Link { uint8_t from; uint8_t to; float etx; };
Link links[] = {
  {0,1,1.2}, {0,2,1.5}, {1,3,1.3}, {2,3,1.8}, {2,4,1.4},
  {3,5,1.6}, {4,5,1.2}, {1,6,1.1}, {2,7,1.3}, {3,8,1.2},
  {4,9,1.1}, {5,10,1.4}, {5,11,1.2}
};
const int NUM_LINKS = 13;

void buildDODAG();
void printTopology();
void compareStoringModes();

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println("=========================================");
  Serial.println("  RPL DODAG Construction Simulator");
  Serial.println("  Smart Building: 3 Floors, 12 Nodes");
  Serial.println("=========================================\n");

  Serial.println("[DIO] Root broadcasting DODAG Information...\n");
  buildDODAG();
  printTopology();
  compareStoringModes();
}

void buildDODAG() {
  // Iterative DODAG construction (simplified Dijkstra-like)
  bool changed = true;
  int iteration = 0;

  while (changed && iteration < 10) {
    changed = false;
    iteration++;
    Serial.printf("--- Iteration %d ---\n", iteration);

    for (int l = 0; l < NUM_LINKS; l++) {
      uint8_t from = links[l].from;
      uint8_t to = links[l].to;
      float etx = links[l].etx;

      // Check if 'from' can offer better rank to 'to'
      if (nodes[from].rank < INFINITE_RANK) {
        uint16_t offeredRank = nodes[from].rank +
          (uint16_t)(RANK_INCREASE * etx);
        if (offeredRank < nodes[to].rank) {
          Serial.printf("  Node %d (%s): RANK %u -> %u via Node %d\n",
            to, nodes[to].name, nodes[to].rank, offeredRank, from);
          nodes[to].rank = offeredRank;
          nodes[to].parentId = from;
          nodes[to].hopCount = nodes[from].hopCount + 1;
          nodes[to].etx = etx;
          changed = true;
        }
      }
    }
    Serial.println();
  }
  Serial.printf("DODAG converged in %d iterations\n\n", iteration);
}

void printTopology() {
  Serial.println("=== DODAG Topology ===\n");
  Serial.println("ID | Node                    | RANK  | Parent | Hops | ETX");
  Serial.println("---|-------------------------|-------|--------|------|-----");
  for (int i = 0; i < MAX_NODES; i++) {
    Serial.printf("%2d | %-23s | %5u | %6d | %4d | %.1f\n",
      nodes[i].id, nodes[i].name, nodes[i].rank,
      nodes[i].parentId, nodes[i].hopCount, nodes[i].etx);
  }
  Serial.println();
}

void compareStoringModes() {
  Serial.println("=== Storing vs Non-Storing Mode ===\n");

  int totalNodes = MAX_NODES;
  int routingEntrySize = 18;  // IPv6 addr (16 bytes) + next-hop pointer (2 bytes)

  // Storing mode: each router stores routes to descendants
  int storingTotal = 0;
  Serial.println("Storing Mode (distributed routing tables):");
  for (int i = 0; i < MAX_NODES; i++) {
    if (!nodes[i].isRouter && i != 0) continue;
    int children = 0;
    for (int j = 0; j < MAX_NODES; j++) {
      if (nodes[j].parentId == i) children++;
    }
    int memory = children * routingEntrySize;
    storingTotal += memory;
    Serial.printf("  Node %d (%s): %d children x %d bytes = %d bytes\n",
      i, nodes[i].name, children, routingEntrySize, memory);
  }

  // Non-Storing mode: root stores all routes
  int rootMemory = (totalNodes - 1) * routingEntrySize;
  int leafMemory = (totalNodes - 1) * 2;  // Just parent pointer

  Serial.printf("\nNon-Storing Mode (centralized at root):");
  Serial.printf("\n  Root: %d entries x %d bytes = %d bytes\n",
    totalNodes - 1, routingEntrySize, rootMemory);
  Serial.printf("  Other nodes: parent pointer only = 2 bytes each\n");

  Serial.printf("\n  Comparison:");
  Serial.printf("\n    Storing total:     %d bytes (distributed)\n", storingTotal);
  Serial.printf("    Non-Storing total: %d bytes (centralized)\n",
    rootMemory + leafMemory);
  Serial.printf("    Difference: %d bytes\n",
    storingTotal - (rootMemory + leafMemory));
}

void loop() {
  delay(30000);
}

What to Observe:

  1. DODAG construction iterates until convergence, with RANK values decreasing toward the root
  2. Parent selection follows the lowest-RANK path (considering ETX link quality)
  3. Hop count increases with distance from the border router on the roof
  4. Storing vs Non-Storing comparison shows memory trade-offs for the 12-node building

RPL mode selection hinges on memory trade-offs between distributed routing tables (Storing) and centralized state (Non-Storing).

Storing Mode Total Memory: \(M_{\text{storing}} = \sum_{i=1}^{N_{\text{routers}}} (C_i \times S_{\text{entry}})\) where \(C_i\) = children per router, \(S_{\text{entry}}\) = 18 bytes (16-byte IPv6 address + 2-byte next-hop pointer)

Non-Storing Mode Total Memory: \(M_{\text{non-storing}} = (N_{\text{total}} - 1) \times S_{\text{entry}} + (N_{\text{non-root}} \times 2)\) (root stores all routes; all other nodes store only a parent pointer)

Worked example: 3-floor building, 56 devices (1 BR + 15 mains routers + 40 battery/actuators), 18-byte routing entries:

Storing Mode:

  • BR: 55 devices × 18 = 990 bytes
  • Mains routers (avg 3 children): 15 × (3 × 18) = 810 bytes distributed
  • Leaf nodes: 40 × 2 = 80 bytes
  • Total network: 990 + 810 + 80 = 1,880 bytes (distributed)

Non-Storing Mode:

  • BR: 55 × 18 = 990 bytes (centralized)
  • All other nodes: 55 × 2 = 110 bytes
  • Total network: 990 + 110 = 1,100 bytes (mostly at root)

Savings: 1,880 - 1,100 = 780 bytes (41% reduction) with Non-Storing. Trade-off: memory savings vs routing efficiency.

40.3.2 Task 2: Memory Requirements Comparison

Calculate memory requirements for Storing vs Non-Storing:

Assumptions:

  • IPv6 address: 16 bytes
  • Next-hop pointer: 2 bytes
  • Routing entry: 18 bytes total

Calculate:

  1. Memory per node (Storing mode)
  2. Memory at root (Non-Storing mode)
  3. Memory at leaf nodes (both modes)
Click to see solution

Storing Mode:

Routing Table Size per Node:

  • Root (BR): All 55 devices reachable
    • 55 entries x 18 bytes = 990 bytes
  • Mains sensor (typical, 3 children):
    • 3 entries x 18 bytes = 54 bytes
  • Battery sensor/actuator (leaf):
    • 0 routing entries (just parent pointer: 2 bytes) = 2 bytes

Total network memory (Storing):

  • BR: 990 bytes
  • 15 mains sensors x 54 bytes avg = 810 bytes
  • 40 battery/actuators x 2 bytes = 80 bytes
  • Total: ~1,880 bytes distributed across network

Non-Storing Mode:

Routing Table Size:

  • Root (BR): All 55 devices
    • 55 entries x 18 bytes = 990 bytes
  • All other nodes: Just parent pointer
    • 55 nodes x 2 bytes = 110 bytes

Total network memory (Non-Storing):

  • BR: 990 bytes
  • 55 other nodes x 2 bytes = 110 bytes
  • Total: ~1,100 bytes (mostly at root)

Comparison:

Mode Root Memory Node Memory (avg) Total Memory
Storing 990 bytes 2-54 bytes 1,880 bytes
Non-Storing 990 bytes 2 bytes 1,100 bytes

Analysis:

  • Non-Storing saves ~780 bytes network-wide (41% reduction)
  • Mains sensors: Can afford 54 bytes (have 32-128 KB RAM)
  • Battery sensors: Both modes require only 2 bytes (parent pointer)
  • Trade-off: Memory savings vs routing efficiency for P2P traffic

40.3.3 Task 3: Traffic Pattern Analysis

Analyze routing for different traffic patterns:

Scenarios:

  1. Many-to-One: All battery sensors report temperature every 5 minutes to cloud
  2. One-to-Many: Cloud sends firmware update to all sensors
  3. Point-to-Point: Motion sensor (Floor 1) triggers light (Floor 3)

For each scenario, calculate: - Average hop count (Storing vs Non-Storing) - Messages per minute - Network load

Click to see solution

Scenario 1: Many-to-One (Sensor to Cloud)

Route (any battery sensor to BR):

  • Storing: Sensor to Parent (mains) to Parent to … to BR
    • Average: 2-3 hops (battery to mains, mains to BR, possibly intermediate)
  • Non-Storing: Same (upward routing identical)
    • Average: 2-3 hops

Messages:

  • 30 battery sensors x 12 msgs/hour = 360 msgs/hour = 6 msgs/min

Conclusion: Identical performance (many-to-one is RPL’s strength)

Scenario 2: One-to-Many (Cloud to All Sensors)

Route (BR to any sensor):

  • Storing: BR to optimal path to sensor
    • BR has routing table, knows best path
    • Average: 2-3 hops (BR to mains to battery)
  • Non-Storing: BR to mains to battery (same path, but source routed)
    • BR inserts source route header
    • Average: 2-3 hops

Difference: Minimal - Storing: routing table lookup at each hop - Non-Storing: source route in header (extra ~10 bytes overhead per hop)

Messages: 55 devices x 1 update = 55 messages (infrequent)

Conclusion: Non-Storing adds header overhead but same hop count

Scenario 3: Point-to-Point (Floor 1 Sensor to Floor 3 Light)

Route:

Storing Mode:

Point-to-point routing in Storing Mode sequence diagram showing Floor 1 Sensor sending data packet through Mains Router 1, Border Router, Mains Router 2 to Floor 3 Light across 4 hops, with routing table lookup annotations at each hop, demonstrating distributed routing decision process
Figure 40.3: Point-to-point routing in Storing Mode with distributed routing table lookups

Non-Storing Mode:

Point-to-point routing in Non-Storing Mode sequence diagram showing Floor 1 Sensor sending packet to Border Router which builds source route path and forwards through Mains Router 2 to Floor 3 Light across 4 hops, with centralized routing at BR, demonstrating source routing approach
Figure 40.4: Point-to-point routing in Non-Storing Mode with source routing through border router

Conclusion: For this topology, same hop count, but: - Storing: Distributed routing decisions (higher memory per router) - Non-Storing: Centralized at root (source routing overhead in packet header)

If topology were flatter (e.g., many sensors directly under BR):

  • Storing: F1-Sensor to BR to F3-Light (2 hops)
  • Non-Storing: F1-Sensor to BR to F3-Light (2 hops)
  • Same performance

Summary:

Traffic Pattern Storing Advantage Non-Storing Advantage
Many-to-One None (equal) None (equal)
One-to-Many None (equal) Lower node memory
Point-to-Point Can optimize locally Simpler nodes
Recommendation: Non-Storing for this scenario - Battery-powered sensors dominate (low memory) - Many-to-one traffic primary (sensors reporting) - Point-to-point rare (motion to light scenarios uncommon)

Use this framework to systematically choose between Storing and Non-Storing mode based on quantifiable network characteristics.

Decision Factor Choose Storing Mode Choose Non-Storing Mode
Node RAM Routers have > 64 KB RAM Routers have < 32 KB RAM
Traffic Pattern P2P traffic > 20% P2P traffic < 5%
Network Size < 100 nodes (routing tables fit) > 200 nodes (centralized more scalable)
Border Router Capacity Constrained (shared gateway) Dedicated (powerful border router)
Latency Requirement < 100ms P2P (avoid root relay) > 500ms acceptable
Network Topology Flat (2-4 hops, direct P2P) Deep (5-10 hops, rare P2P)
Control Overhead Can tolerate DAO propagation Minimize DAO floods

Quantitative Decision Model:

Step 1: Calculate Memory Requirements

Storing Mode Total Memory:
  M_storing = (Avg_children_per_router × 18 bytes) × Num_routers

Non-Storing Mode Total Memory:
  M_non_storing = ((Total_nodes - 1) × 18 bytes) + (Non_root_nodes × 2 bytes)

Step 2: Calculate P2P Routing Penalty

Non-Storing P2P penalty = 2 × (Avg_hops_to_root)
Example: Sensor A (4 hops) to Sensor B (4 hops)
  Non-Storing: A → Root (4) → B (4) = 8 hops
  Storing: A → Common ancestor (2 hops) → B (2 hops) = 4 hops

Step 3: Apply Decision Rule

IF (M_storing < Node_RAM × 0.3) AND (P2P_traffic > 15%):
  → Choose Storing Mode
ELSE IF (M_non_storing < Border_router_RAM × 0.5) AND (P2P_traffic < 10%):
  → Choose Non-Storing Mode
ELSE:
  → Run 7-day field trial with both modes, compare battery life and PDR

Example Decision - Smart Building Deployment:

Given:

  • 120 sensors (90 battery, 30 mains-powered routers)
  • Average 3 children per router
  • Traffic: 85% upward (sensor data), 10% downward (commands), 5% P2P (motion → light)
  • Node RAM: 256 KB (nRF52840)
  • Border router RAM: 512 MB (Raspberry Pi 4)

Calculate Storing Mode Memory:

M_storing = 30 routers × 3 children × 18 bytes = 1,620 bytes
Per-router: 1,620 / 30 = 54 bytes (0.02% of 256 KB RAM) ✓ Fits easily

Calculate Non-Storing Mode Memory:

M_non_storing = (120 nodes × 18 bytes) + (119 non-root × 2 bytes) = 2,160 + 238 = 2,398 bytes
At border router: 2,398 bytes (0.0005% of 512 MB RAM) ✓ Trivial

Calculate P2P Penalty:

Non-Storing: Motion sensor (4 hops) → Root → Light (3 hops) = 7 hops
Storing: Motion sensor → Common ancestor (2 hops) → Light = 4 hops
P2P traffic: 5% × 120 sensors × 10 msgs/day = 60 P2P msgs/day
Extra hops = 60 × (7-4) = 180 extra hops/day (negligible)

Decision: Storing Mode - Reason: Memory is not constrained, P2P penalty is small but P2P latency requirement is < 200ms (motion → light must be instant) - Trade-off: Slightly higher DAO overhead (acceptable)

Alternative Decision - Parking Sensor Network:

Given:

  • 500 sensors (all battery, 1 mains-powered gateway)
  • Average 1 child per gateway (star topology)
  • Traffic: 99% upward (occupancy status), 1% downward (config)
  • Node RAM: 16 KB (ultra-low-cost MCU)

Decision: Non-Storing Mode - Reason: Nodes lack RAM for routing tables, zero P2P traffic, gateway has abundant resources - Trade-off: P2P not possible (not needed for this application)

Validation Checklist:

After choosing mode, validate decision during pilot deployment:

✓ Routing table memory < 30% node RAM (Storing) or < 50% border router RAM (Non-Storing) ✓ P2P latency meets SLA (if applicable) ✓ DAO message rate < 10 per hour per node (Storing) ✓ Packet delivery rate > 95% for all traffic types ✓ Battery lifetime meets target (measure for 7 days, extrapolate)

If any check fails, reconsider mode choice or adjust network topology.

40.4 Interactive: Memory Trade-off Calculator

Use this calculator to explore how network size and topology affect Storing vs Non-Storing mode memory requirements.

Common Pitfalls

Network design labs that ignore wall attenuation, device placement, and radio interference produce designs that fail in physical deployments. Always apply link budget calculations and path loss models to validate lab-designed topologies.

Lab network designs that meet requirements without explanation of why specific choices were made don’t build transferable skills. Document the reasoning behind RANK configuration, mode selection, and topology decisions.

Lab exercises often use simplified networks where complex features (multiple instances, floating DODAG) aren’t needed but are included to demonstrate completeness. Focus on what the lab scenario actually requires.

40.5 Summary

This hands-on lab demonstrated practical RPL network design skills:

  • DODAG Topology Design: Strategic placement of border router and routing nodes based on network hierarchy and power availability is crucial for efficient RPL networks
  • Memory Trade-offs: Storing mode distributes routing tables (~54 bytes per router) while Non-Storing mode centralizes at root (~990 bytes for 56-node network), impacting scalability differently
  • Traffic Pattern Analysis: Many-to-one traffic shows identical performance in both modes, while point-to-point routing may benefit from Storing mode’s distributed tables

40.5.1 Key Design Principles

  1. Use mains-powered devices as routers - They have sufficient RAM and continuous power
  2. Configure battery sensors as leaf nodes - Minimize routing overhead
  3. Choose Non-Storing for sensor-heavy networks - Lower memory requirements
  4. Consider Storing for P2P-heavy networks - Better routing efficiency

40.6 Concept Relationships

Understanding RPL network design connects to these related concepts:

  • RPL Fundamentals provides the theoretical foundation for DODAG construction and RANK calculation that this lab applies
  • RPL Operation explains the control message flows (DIO, DAO) that enable the parent selection and route building demonstrated here
  • Network Topologies shows how RPL’s tree-based DODAG relates to mesh, star, and other IoT network patterns
  • 6LoWPAN works with RPL to compress IPv6 headers, affecting the packet overhead calculations in Task 3
  • Thread Operation implements RPL as its routing protocol, demonstrating production deployment of these design principles

40.7 See Also

Related Labs and Exercises:

  • RPL Knowledge Check - Scenario-based quizzes applying these design concepts
  • RPL Production Framework - Production-ready implementation patterns
  • Simulations Hub - Interactive DODAG formation visualizers

Reference Material:

  • RFC 6550 (RPL Specification) - Section 8 on Mode of Operation
  • RFC 6552 (OF0) - Hop count objective function used in Task 1
  • RFC 6719 (MRHOF) - ETX-based objective function for lossy links

40.8 What’s Next

Continue to RPL Knowledge Check to test your understanding with scenario-based quizzes, or explore RPL Quiz Questions for detailed concept review with explanations.

Previous: RPL Labs and Quiz Next: RPL Quiz Questions