24  LTE-M Interactive Lab

In 60 Seconds

This hands-on lab simulates LTE-M (Cat-M1) concepts on an ESP32, demonstrating the features that distinguish LTE-M from NB-IoT: EPS bearer establishment for data sessions, VoLTE for voice-enabled IoT, seamless cell handover for mobile tracking at highway speeds, and Coverage Enhancement Mode B for challenging RF environments. The lab compares PSM and eDRX power modes and Control Plane vs. User Plane data optimization paths.

24.1 Learning Objectives

By the end of this lab, you should be able to:

  • Trace the LTE-M connection procedure from cell search through EPS bearer establishment
  • Demonstrate VoLTE capability by initiating and observing a simulated voice call on an IoT device
  • Differentiate PSM and eDRX power modes and justify which is appropriate for LTE-M vs NB-IoT use cases
  • Explain how Coverage Enhancement Mode B extends LTE-M coverage using up to 32 repetitions
  • Analyze seamless cell handover events that maintain data sessions at highway speeds up to 160 km/h
  • Classify when to use Control Plane vs User Plane data optimization based on payload size and latency

LTE-M (LTE for Machines) is a cellular technology optimized for IoT devices that need mobility and voice support, like vehicle trackers and personal safety devices. This interactive lab simulates LTE-M communication so you can experiment with connection procedures, power saving modes, and data transmission.

“LTE-M is the cellular technology that can keep up with a moving truck!” said Max the Microcontroller, pointing to a map with dotted lines. “Unlike NB-IoT which drops the connection when you cross cell boundaries, LTE-M performs seamless handovers – switching from one tower to the next without losing your data session.”

Sammy the Sensor was impressed. “So I can track packages while they are being delivered?” Max nodded. “Exactly! And LTE-M even supports VoLTE – voice calls over cellular data. That means a personal safety pendant can make emergency calls AND send GPS coordinates, all on the same connection.”

“The lab simulates the whole connection process,” explained Lila the LED. “First the device searches for a cell tower, then establishes an RRC connection, authenticates with the network, and sets up an EPS bearer for data. You can watch each step happen and understand what is going on under the hood.”

Bella the Battery noted an important detail. “LTE-M supports the same PSM and eDRX power modes as NB-IoT, but it uses more power during active transmission because it has a wider bandwidth of 1.4 MHz versus NB-IoT’s 180 kHz. The trade-off is higher data rates – up to 1 Mbps – and mobility support. Perfect for trackers that wake up briefly, send a GPS fix, and go back to sleep.”

24.2 Prerequisites

Before starting this lab, you should be familiar with:

24.3 Lab Overview

Lab Overview

This interactive lab provides a hands-on simulation of LTE-M (LTE Cat-M1) concepts using an ESP32 microcontroller in Wokwi. While actual LTE-M communication requires specialized cellular modules and carrier connectivity, this simulation demonstrates the key concepts and procedures that differentiate LTE-M from NB-IoT and make it suitable for mobile IoT applications.

What You Will Learn:

  • LTE-M Connection Procedures: Understand the attach sequence including cell search, RRC connection, and EPS bearer establishment
  • VoLTE for IoT: Experience how Voice over LTE enables push-to-talk and emergency calling in IoT devices
  • PSM and eDRX Power Modes: Compare power-saving behavior in LTE-M versus NB-IoT configurations
  • Coverage Enhancement (CE) Mode B: Learn how LTE-M extends coverage to challenging RF environments
  • Mobility and Handover: Observe seamless cell transitions that enable tracking applications at highway speeds
  • Data Transmission Modes: Compare Control Plane and User Plane optimization paths for different payload sizes

Prerequisites: Basic understanding of C/C++ programming, familiarity with cellular IoT concepts from earlier sections, and knowledge of PSM/eDRX timer operation.

24.3.1 Quick Check: LTE-M Fundamentals

24.4 Lab Environment

The simulation below runs on an ESP32 in Wokwi’s browser-based environment. The code simulates LTE-M modem behavior including realistic timing, state machines, handover procedures, and power calculations that highlight LTE-M’s unique capabilities for mobile IoT.

How to Use This Lab
  1. Copy the code from the section below into the Wokwi editor
  2. Click “Start Simulation” to run the LTE-M simulation
  3. Open the Serial Monitor (115200 baud) to see detailed output
  4. Observe the state transitions including handover events as the simulated device moves
  5. Compare with NB-IoT: Note the differences in latency, handover support, and voice capability
  6. Try the challenge exercises to deepen your understanding of LTE-M design trade-offs

24.5 Complete LTE-M Simulation Code

Copy this code into the Wokwi editor to run the simulation:

/*
 * LTE-M (Cat-M1) Fundamentals Lab - ESP32 Simulation
 *
 * This simulation demonstrates key LTE-M concepts that differentiate it from NB-IoT:
 * - Network attach procedure with EPS bearer establishment
 * - Full mobility support with inter-cell handover
 * - VoLTE capability for voice-enabled IoT devices
 * - PSM and eDRX power modes (similar to NB-IoT but with faster wake times)
 * - Coverage Enhancement Mode B for extended coverage
 * - User Plane vs Control Plane data optimization
 * - Lower latency (10-15ms) compared to NB-IoT (1.6-10s)
 *
 * Key LTE-M Advantages Over NB-IoT:
 * - Handover support (mobile devices up to 160 km/h)
 * - Higher data rate (1 Mbps vs 250 kbps)
 * - Lower latency (10-15ms vs 1.6-10s)
 * - VoLTE voice support
 * - Faster power-up and connection establishment
 *
 * Educational Purpose: Understanding LTE-M without actual cellular hardware
 *
 * Author: IoT Class Lab
 * License: MIT
 */

#include <Arduino.h>

// ============================================================================
// LTE-M Configuration Constants
// ============================================================================

// Network Configuration
#define PLMN_MCC "310"              // Mobile Country Code (US)
#define PLMN_MNC "260"              // Mobile Network Code (T-Mobile)
#define BAND_NUMBER 12              // LTE Band 12 (700 MHz) - common for LTE-M in US
#define EARFCN_SERVING 5110         // E-UTRA Absolute Radio Frequency Channel Number
#define EARFCN_NEIGHBOR 5230        // Neighbor cell EARFCN for handover demo

// LTE-M Specific Parameters (vs NB-IoT)
#define BANDWIDTH_MHZ 1.4           // LTE-M uses 1.4 MHz bandwidth (vs 180 kHz NB-IoT)
#define MAX_DATA_RATE_KBPS 1000     // 1 Mbps (vs 250 kbps NB-IoT)
#define LATENCY_MS 15               // Typical latency (vs 1.6-10s NB-IoT)
#define HANDOVER_SUPPORTED true     // Key differentiator from NB-IoT
#define VOLTE_SUPPORTED true        // Voice over LTE for IoT

// PSM Timer Configuration (T3412 - TAU timer, T3324 - Active timer)
// LTE-M typically uses shorter timers than NB-IoT due to mobility requirements
#define T3412_VALUE_HOURS 12        // TAU period (shorter than NB-IoT for mobile tracking)
#define T3324_VALUE_SECONDS 20      // Active timer before PSM (shorter for responsiveness)

// eDRX Configuration
// LTE-M supports eDRX but typically uses shorter cycles for faster response
#define EDRX_CYCLE_SECONDS 10.24    // Shorter than NB-IoT for mobile applications
#define PTW_SECONDS 1.28            // Paging Time Window

// Coverage Enhancement Mode B (LTE-M equivalent to NB-IoT CE levels)
// Mode A: Normal coverage (no repetitions)
// Mode B: Extended coverage (repetitions, but fewer than NB-IoT CE2)
#define CE_MODE_B_ENABLED true
#define MAX_REPETITIONS_MODE_B 32   // LTE-M caps at 32 (vs 128 for NB-IoT CE2)

// Power Consumption Values (typical for LTE-M modules - higher than NB-IoT)
#define POWER_PSM_UA 5              // PSM sleep: 5 µA (similar to NB-IoT)
#define POWER_EDRX_MA 0.8           // eDRX sleep: 0.8 mA (slightly higher than NB-IoT)
#define POWER_IDLE_MA 15            // RRC Idle: 15 mA
#define POWER_RX_MA 80              // Receiving: 80 mA (higher than NB-IoT 46 mA)
#define POWER_TX_23DBM_MA 350       // Transmitting at 23 dBm: 350 mA
#define POWER_TX_20DBM_MA 250       // Transmitting at 20 dBm: 250 mA
#define POWER_HANDOVER_MA 120       // During handover: 120 mA

// VoLTE Parameters
#define VOLTE_CODEC "EVS"           // Enhanced Voice Services codec
#define VOLTE_BANDWIDTH_KBPS 13.2   // AMR-WB equivalent bandwidth
#define VOLTE_LATENCY_MS 100        // Voice latency requirement

// Mobility Parameters
#define MAX_SPEED_KMH 160           // LTE-M supports up to 160 km/h (vs 0 for NB-IoT)
#define HANDOVER_THRESHOLD_DBM -110 // Trigger handover when RSRP drops below
#define HANDOVER_HYSTERESIS_DB 3    // Hysteresis to prevent ping-pong
#define MEASUREMENT_REPORT_INTERVAL_MS 500

// Timing Constants (milliseconds)
#define CELL_SEARCH_TIME_MS 2000    // Faster than NB-IoT
#define RRC_CONNECT_TIME_MS 300     // LTE-M is faster
#define EPS_BEARER_SETUP_MS 200     // EPS bearer establishment
#define AUTHENTICATION_TIME_MS 500
#define HANDOVER_EXECUTION_MS 50    // Seamless handover time
#define TAU_PROCEDURE_TIME_MS 400
#define DATA_TRANSFER_TIME_MS 100   // Lower latency than NB-IoT

// ============================================================================
// Enumerations and Type Definitions
// ============================================================================

// LTE-M Device States (3GPP compliant with mobility extensions)
enum LTEMState {
  STATE_POWER_OFF,
  STATE_CELL_SEARCH,              // Searching for suitable cell
  STATE_CELL_SELECTION,           // S-criterion evaluation
  STATE_CELL_RESELECTION,         // Evaluating better cells (idle mode)
  STATE_RRC_IDLE,                 // Camped on cell, monitoring paging
  STATE_RRC_CONNECTING,           // Establishing RRC connection
  STATE_RRC_CONNECTED,            // Active connection to eNodeB
  STATE_EPS_BEARER_SETUP,         // Setting up default/dedicated bearers
  STATE_AUTHENTICATING,           // AKA (Authentication and Key Agreement)
  STATE_REGISTERED,               // EMM-REGISTERED, ECM-IDLE or CONNECTED
  STATE_DATA_TRANSFER,            // Active data session
  STATE_VOLTE_SETUP,              // Establishing VoLTE call
  STATE_VOLTE_ACTIVE,             // Voice call in progress
  STATE_HANDOVER_PREP,            // Handover preparation (measurement reports)
  STATE_HANDOVER_EXEC,            // Handover execution
  STATE_PSM,                      // Power Saving Mode (deep sleep)
  STATE_EDRX_SLEEP,               // eDRX sleep period
  STATE_EDRX_PAGING               // eDRX paging window
};

// Coverage Enhancement Modes (LTE-M specific)
enum CEMode {
  CE_MODE_A = 0,    // Normal coverage (no repetitions)
  CE_MODE_B = 1     // Extended coverage (up to 32 repetitions)
};

// Data Path Options
enum DataPath {
  PATH_CONTROL_PLANE,     // CP-CIoT: Data over NAS signaling (small payloads)
  PATH_USER_PLANE,        // UP: Traditional data bearer (larger payloads)
  PATH_VOLTE              // VoLTE: Voice over IMS
};

// Network Registration Status
enum RegistrationStatus {
  REG_NOT_REGISTERED = 0,
  REG_REGISTERED_HOME = 1,
  REG_SEARCHING = 2,
  REG_DENIED = 3,
  REG_UNKNOWN = 4,
  REG_REGISTERED_ROAMING = 5
};

// Handover State
enum HandoverState {
  HO_NONE,
  HO_MEASUREMENT,       // Sending measurement reports
  HO_PREPARATION,       // Network preparing target cell
  HO_EXECUTION,         // Switching to target cell
  HO_COMPLETE           // Handover successful
};

// Power Mode Configuration
enum PowerMode {
  POWER_MODE_ALWAYS_ON,         // Maximum responsiveness
  POWER_MODE_DRX,               // Connected mode DRX
  POWER_MODE_EDRX_ONLY,         // eDRX without PSM
  POWER_MODE_PSM_ONLY,          // PSM without eDRX
  POWER_MODE_PSM_EDRX           // Combined for maximum battery
};

// ============================================================================
// Data Structures
// ============================================================================

// Signal Quality Measurements
struct SignalQuality {
  int rsrp;             // Reference Signal Received Power (-140 to -44 dBm)
  int rsrq;             // Reference Signal Received Quality (-20 to -3 dB)
  int sinr;             // Signal to Interference+Noise Ratio (-20 to 30 dB)
  int rssi;             // Received Signal Strength Indicator
  int cellId;           // Physical Cell ID (0-503)
  int earfcn;           // Frequency channel
  int ta;               // Timing Advance (distance indication)
};

// Neighbor Cell Information (for handover)
struct NeighborCell {
  int cellId;
  int earfcn;
  int rsrp;
  int rsrq;
  bool suitable;        // Meets S-criterion
};

// Network Information
struct NetworkInfo {
  char mcc[4];
  char mnc[4];
  int band;
  int tac;              // Tracking Area Code
  bool attached;
  RegistrationStatus regStatus;
  bool epsBearer;       // EPS bearer established
  bool imsRegistered;   // IMS registration for VoLTE
};

// Mobility Statistics
struct MobilityStats {
  int handoverCount;
  int handoverFailures;
  float avgHandoverTime;
  int cellReselections;
  float currentSpeedKmh;
  float distanceTraveledKm;
};

// Power Statistics
struct PowerStats {
  float totalEnergyMah;
  unsigned long psmDurationMs;
  unsigned long edrxDurationMs;
  unsigned long connectedDurationMs;
  unsigned long txDurationMs;
  unsigned long rxDurationMs;
  unsigned long handoverDurationMs;
  int transmissionCount;
  int pageCheckCount;
};

// VoLTE Call Statistics
struct VoLTEStats {
  int callsAttempted;
  int callsSuccessful;
  float avgSetupTimeMs;
  float avgCallDurationSec;
  int handoversDuringCall;
};

// Timer State
struct TimerState {
  unsigned long t3412StartMs;       // TAU timer
  unsigned long t3324StartMs;       // Active timer
  unsigned long edrxCycleStartMs;
  unsigned long measurementReportMs;
  bool t3412Running;
  bool t3324Running;
  bool psmEnabled;
  bool edrxEnabled;
};

// Data Transfer Statistics
struct DataStats {
  int totalBytesSent;
  int totalBytesReceived;
  int messagesSent;
  int messagesReceived;
  float averageLatencyMs;
  int cpPathMessages;     // Control Plane path
  int upPathMessages;     // User Plane path
};

// ============================================================================
// Global Variables
// ============================================================================

LTEMState currentState = STATE_POWER_OFF;
CEMode currentCEMode = CE_MODE_A;
PowerMode powerMode = POWER_MODE_PSM_EDRX;
DataPath currentDataPath = PATH_USER_PLANE;
HandoverState handoverState = HO_NONE;

SignalQuality servingCell;
NeighborCell neighborCells[3];
NetworkInfo networkInfo;
MobilityStats mobilityStats;
PowerStats powerStats;
VoLTEStats volteStats;
TimerState timerState;
DataStats dataStats;

unsigned long stateEntryTime = 0;
unsigned long lastStatusUpdate = 0;
unsigned long simulationStartTime = 0;
unsigned long lastMeasurementReport = 0;

int repetitionCount = 1;
bool pendingDownlink = false;
bool mobilitySimEnabled = true;
bool volteCallActive = false;
int sensorReadingCounter = 0;
float simulatedSpeedKmh = 0;

// ============================================================================
// Function Declarations
// ============================================================================

void initializeModem();
void processStateMachine();
void updatePowerConsumption();
void printStatus();
void printLTEMvsNBIoTComparison();
void simulateMobility();
void evaluateHandoverNeed();
void sendMeasurementReport();
void initiateVoLTECall();
void handleVoLTECall();
void endVoLTECall();
void enterPSM();
void exitPSM();
void handleEDRXCycle();
void sendUplinkData(const char* data, int length, DataPath path);
void printStateTransition(LTEMState from, LTEMState to);
void printPowerModeInfo();
const char* getStateName(LTEMState state);
const char* getCEModeName(CEMode mode);

// ============================================================================
// Setup Function
// ============================================================================

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

  Serial.println();
  Serial.println("======================================================================");
  Serial.println("          LTE-M (Cat-M1) FUNDAMENTALS INTERACTIVE LAB");
  Serial.println("======================================================================");
  Serial.println();
  Serial.println("  This simulation demonstrates LTE-M concepts including:");
  Serial.println("  * Network attach with EPS bearer establishment");
  Serial.println("  * Full mobility support (handover up to 160 km/h)");
  Serial.println("  * VoLTE capability for voice-enabled IoT");
  Serial.println("  * PSM and eDRX power modes (faster than NB-IoT)");
  Serial.println("  * Coverage Enhancement Mode B");
  Serial.println("  * User Plane vs Control Plane data optimization");
  Serial.println();
  Serial.println("======================================================================");
  Serial.println();

  initializeModem();
  simulationStartTime = millis();

  Serial.println("----------------------------------------------------------------------");
  Serial.println(" LTE-M CONFIGURATION");
  Serial.println("----------------------------------------------------------------------");
  Serial.printf(" PLMN: %s-%s | Band: %d | EARFCN: %d\n",
                PLMN_MCC, PLMN_MNC, BAND_NUMBER, EARFCN_SERVING);
  Serial.printf(" Bandwidth: %.1f MHz | Max Rate: %d kbps | Latency: %d ms\n",
                BANDWIDTH_MHZ, MAX_DATA_RATE_KBPS, LATENCY_MS);
  Serial.printf(" T3412 (TAU): %d hours | T3324 (Active): %d seconds\n",
                T3412_VALUE_HOURS, T3324_VALUE_SECONDS);
  Serial.printf(" Handover: %s | VoLTE: %s | Max Speed: %d km/h\n",
                HANDOVER_SUPPORTED ? "ENABLED" : "DISABLED",
                VOLTE_SUPPORTED ? "ENABLED" : "DISABLED",
                MAX_SPEED_KMH);
  Serial.println("----------------------------------------------------------------------");
  Serial.println();

  printLTEMvsNBIoTComparison();
  Serial.println();
  printPowerModeInfo();
  Serial.println();

  Serial.println(">>> Initiating LTE-M network attach procedure...");
  Serial.println();
}

// ============================================================================
// Main Loop
// ============================================================================

void loop() {
  processStateMachine();
  updatePowerConsumption();

  if (mobilitySimEnabled && currentState >= STATE_REGISTERED) {
    simulateMobility();
  }

  if (currentState == STATE_REGISTERED || currentState == STATE_DATA_TRANSFER) {
    evaluateHandoverNeed();
  }

  if (currentState >= STATE_RRC_CONNECTED && currentState <= STATE_DATA_TRANSFER) {
    if (millis() - lastMeasurementReport > MEASUREMENT_REPORT_INTERVAL_MS) {
      sendMeasurementReport();
      lastMeasurementReport = millis();
    }
  }

  if (millis() - lastStatusUpdate > 5000) {
    printStatus();
    lastStatusUpdate = millis();
  }

  // Simulate sensor readings and data transmission
  static unsigned long lastSensorRead = 0;
  if (currentState == STATE_REGISTERED || currentState == STATE_PSM ||
      currentState == STATE_EDRX_SLEEP) {
    if (millis() - lastSensorRead > 25000) {
      sensorReadingCounter++;

      if (sensorReadingCounter % 3 == 0) {
        char largePayload[200];
        snprintf(largePayload, sizeof(largePayload),
                 "{\"id\":%d,\"type\":\"full\",\"lat\":37.7749,\"lon\":-122.4194,"
                 "\"speed\":%.1f,\"heading\":275,\"temp\":23.5,\"batt\":87,"
                 "\"cell\":%d,\"rsrp\":%d}",
                 sensorReadingCounter, simulatedSpeedKmh, servingCell.cellId,
                 servingCell.rsrp);
        sendUplinkData(largePayload, strlen(largePayload), PATH_USER_PLANE);
      } else {
        char smallPayload[50];
        snprintf(smallPayload, sizeof(smallPayload),
                 "{\"id\":%d,\"loc\":1,\"v\":%.0f}",
                 sensorReadingCounter, simulatedSpeedKmh);
        sendUplinkData(smallPayload, strlen(smallPayload), PATH_CONTROL_PLANE);
      }
      lastSensorRead = millis();
    }
  }

  // Simulate occasional VoLTE call
  static unsigned long lastVoLTEDemo = 0;
  if (VOLTE_SUPPORTED && currentState == STATE_REGISTERED) {
    if (millis() - lastVoLTEDemo > 60000 && !volteCallActive) {
      Serial.println();
      Serial.println(">>> VoLTE DEMONSTRATION: Initiating voice call...");
      initiateVoLTECall();
      lastVoLTEDemo = millis();
    }
  }

  if (volteCallActive) {
    handleVoLTECall();
  }

  delay(100);
}

// ============================================================================
// Implementation Functions (abbreviated for space - see full code in original)
// ============================================================================

void initializeModem() {
  servingCell.rsrp = -95;
  servingCell.rsrq = -10;
  servingCell.sinr = 8;
  servingCell.cellId = 142;
  servingCell.earfcn = EARFCN_SERVING;

  neighborCells[0] = {143, EARFCN_NEIGHBOR, -105, -12, true};
  neighborCells[1] = {144, EARFCN_SERVING, -108, -13, true};
  neighborCells[2] = {145, EARFCN_NEIGHBOR, -115, -15, false};

  strcpy(networkInfo.mcc, PLMN_MCC);
  strcpy(networkInfo.mnc, PLMN_MNC);
  networkInfo.band = BAND_NUMBER;
  networkInfo.attached = false;
  networkInfo.regStatus = REG_NOT_REGISTERED;

  memset(&mobilityStats, 0, sizeof(mobilityStats));
  memset(&powerStats, 0, sizeof(powerStats));
  memset(&volteStats, 0, sizeof(volteStats));
  memset(&dataStats, 0, sizeof(dataStats));

  timerState.psmEnabled = true;
  timerState.edrxEnabled = true;

  currentState = STATE_CELL_SEARCH;
  stateEntryTime = millis();

  Serial.println("[MODEM] LTE-M modem initialized");
}

void processStateMachine() {
  unsigned long timeInState = millis() - stateEntryTime;
  LTEMState previousState = currentState;

  switch (currentState) {
    case STATE_CELL_SEARCH:
      if (timeInState >= CELL_SEARCH_TIME_MS) {
        Serial.println("[ATTACH] Cell search complete");
        Serial.printf("         Cell ID: %d | RSRP: %d dBm\n",
                      servingCell.cellId, servingCell.rsrp);
        currentState = STATE_CELL_SELECTION;
        stateEntryTime = millis();
      }
      break;

    case STATE_CELL_SELECTION:
      if (timeInState >= 500) {
        currentCEMode = (servingCell.rsrp < -115) ? CE_MODE_B : CE_MODE_A;
        Serial.printf("[ATTACH] CE Mode %s selected\n", getCEModeName(currentCEMode));
        currentState = STATE_RRC_CONNECTING;
        stateEntryTime = millis();
      }
      break;

    case STATE_RRC_CONNECTING:
      if (timeInState >= RRC_CONNECT_TIME_MS) {
        Serial.println("[ATTACH] RRC Connection established");
        currentState = STATE_EPS_BEARER_SETUP;
        stateEntryTime = millis();
      }
      break;

    case STATE_EPS_BEARER_SETUP:
      if (timeInState >= EPS_BEARER_SETUP_MS) {
        networkInfo.epsBearer = true;
        Serial.println("[ATTACH] EPS Bearer established");
        currentState = STATE_AUTHENTICATING;
        stateEntryTime = millis();
      }
      break;

    case STATE_AUTHENTICATING:
      if (timeInState >= AUTHENTICATION_TIME_MS) {
        networkInfo.attached = true;
        networkInfo.regStatus = REG_REGISTERED_HOME;
        timerState.t3412Running = true;
        timerState.t3412StartMs = millis();

        Serial.println();
        Serial.println("======================================================================");
        Serial.println(" LTE-M ATTACH COMPLETE - DEVICE REGISTERED");
        Serial.printf(" Total attach time: %lu ms\n", millis() - simulationStartTime);
        Serial.println("======================================================================");

        if (VOLTE_SUPPORTED) {
          networkInfo.imsRegistered = true;
          Serial.println("[VoLTE] IMS registration successful");
        }
        currentState = STATE_REGISTERED;
        stateEntryTime = millis();
      }
      break;

    case STATE_REGISTERED:
      if (timerState.psmEnabled && timerState.t3324Running) {
        if (millis() - timerState.t3324StartMs >= T3324_VALUE_SECONDS * 1000) {
          enterPSM();
        }
      }
      if (!timerState.t3324Running) {
        timerState.t3324Running = true;
        timerState.t3324StartMs = millis();
      }
      break;

    case STATE_DATA_TRANSFER:
      if (timeInState >= DATA_TRANSFER_TIME_MS) {
        dataStats.messagesSent++;
        currentState = STATE_REGISTERED;
        stateEntryTime = millis();
        timerState.t3324StartMs = millis();
      }
      break;

    case STATE_HANDOVER_PREP:
      if (timeInState >= 100) {
        currentState = STATE_HANDOVER_EXEC;
        stateEntryTime = millis();
      }
      break;

    case STATE_HANDOVER_EXEC:
      if (timeInState >= HANDOVER_EXECUTION_MS) {
        Serial.println("[HO] HANDOVER COMPLETE!");
        Serial.printf("     Execution time: %d ms (seamless)\n", HANDOVER_EXECUTION_MS);
        servingCell.cellId = neighborCells[0].cellId;
        servingCell.rsrp = neighborCells[0].rsrp + 15;
        mobilityStats.handoverCount++;
        currentState = STATE_REGISTERED;
        stateEntryTime = millis();
      }
      break;

    case STATE_PSM:
      if (timeInState >= 30000) {
        Serial.println("[PSM] Wake event - exiting PSM");
        exitPSM();
      }
      break;

    default:
      break;
  }

  if (previousState != currentState) {
    printStateTransition(previousState, currentState);
  }
}

void simulateMobility() {
  static unsigned long lastUpdate = 0;
  if (millis() - lastUpdate < 1000) return;
  lastUpdate = millis();

  static float targetSpeed = 60;
  static int counter = 0;
  if (++counter >= 10) {
    targetSpeed = random(20, 120);
    counter = 0;
  }

  if (simulatedSpeedKmh < targetSpeed) simulatedSpeedKmh += 2;
  else if (simulatedSpeedKmh > targetSpeed) simulatedSpeedKmh -= 3;
  simulatedSpeedKmh = constrain(simulatedSpeedKmh, 0, MAX_SPEED_KMH);

  mobilityStats.distanceTraveledKm += (simulatedSpeedKmh / 3600.0);
  mobilityStats.currentSpeedKmh = simulatedSpeedKmh;

  static float degradation = 0;
  degradation += (simulatedSpeedKmh / 1000.0);
  if (degradation > 1) {
    servingCell.rsrp -= 1;
    neighborCells[0].rsrp += random(-1, 2);
    degradation = 0;
  }
  servingCell.rsrp = constrain(servingCell.rsrp, -130, -70);
}

void evaluateHandoverNeed() {
  if (handoverState != HO_NONE && handoverState != HO_COMPLETE) return;

  bool needHandover = (neighborCells[0].rsrp > servingCell.rsrp + HANDOVER_HYSTERESIS_DB) ||
                      (servingCell.rsrp < HANDOVER_THRESHOLD_DBM);

  if (needHandover) {
    Serial.println();
    Serial.println("----------------------------------------------------------------------");
    Serial.println(" HANDOVER TRIGGERED - LTE-M Mobility in Action!");
    Serial.printf(" Serving cell: %d dBm | Target cell: %d dBm | Speed: %.1f km/h\n",
                  servingCell.rsrp, neighborCells[0].rsrp, simulatedSpeedKmh);
    Serial.println("----------------------------------------------------------------------");
    currentState = STATE_HANDOVER_PREP;
    stateEntryTime = millis();
  }
}

void sendMeasurementReport() {
  for (int i = 0; i < 3; i++) {
    neighborCells[i].rsrp += random(-2, 3);
    neighborCells[i].rsrp = constrain(neighborCells[i].rsrp, -130, -80);
  }
}

void initiateVoLTECall() {
  if (!VOLTE_SUPPORTED || !networkInfo.imsRegistered) return;

  Serial.println("----------------------------------------------------------------------");
  Serial.println(" VoLTE CALL - LTE-M Unique Feature (NB-IoT cannot do this!)");
  Serial.println("----------------------------------------------------------------------");
  volteStats.callsAttempted++;
  currentState = STATE_VOLTE_SETUP;
  stateEntryTime = millis();
}

void handleVoLTECall() {
  static unsigned long callStart = 0;
  if (currentState == STATE_VOLTE_SETUP && millis() - stateEntryTime >= 800) {
    Serial.println("[VoLTE] Call connected");
    volteStats.callsSuccessful++;
    currentState = STATE_VOLTE_ACTIVE;
    callStart = millis();
    volteCallActive = true;
  }

  if (currentState == STATE_VOLTE_ACTIVE && millis() - callStart >= 5000) {
    endVoLTECall();
    callStart = 0;
  }
}

void endVoLTECall() {
  Serial.println("[VoLTE] Call ended - 5 seconds HD voice");
  volteCallActive = false;
  currentState = STATE_REGISTERED;
  stateEntryTime = millis();
}

void enterPSM() {
  Serial.println("[PSM] Entering Power Saving Mode (5 µA)");
  currentState = STATE_PSM;
  stateEntryTime = millis();
  timerState.t3324Running = false;
}

void exitPSM() {
  powerStats.psmDurationMs += millis() - stateEntryTime;
  currentState = STATE_RRC_CONNECTING;
  stateEntryTime = millis();
}

void handleEDRXCycle() {
  // eDRX cycle handling (simplified)
}

void sendUplinkData(const char* data, int length, DataPath path) {
  if (currentState == STATE_PSM) exitPSM();

  Serial.println();
  Serial.printf("[DATA] Uplink #%d | %d bytes | %s path | %d ms latency\n",
                sensorReadingCounter, length,
                path == PATH_CONTROL_PLANE ? "CP" : "UP", LATENCY_MS);

  dataStats.totalBytesSent += length;
  if (path == PATH_CONTROL_PLANE) dataStats.cpPathMessages++;
  else dataStats.upPathMessages++;

  currentState = STATE_DATA_TRANSFER;
  stateEntryTime = millis();
}

void updatePowerConsumption() {
  static unsigned long lastUpdate = 0;
  unsigned long elapsed = millis() - lastUpdate;
  if (elapsed < 100) return;
  lastUpdate = millis();

  float currentMa;
  switch (currentState) {
    case STATE_PSM: currentMa = POWER_PSM_UA / 1000.0; break;
    case STATE_DATA_TRANSFER: currentMa = POWER_TX_23DBM_MA; break;
    case STATE_HANDOVER_PREP:
    case STATE_HANDOVER_EXEC: currentMa = POWER_HANDOVER_MA; break;
    default: currentMa = POWER_IDLE_MA; break;
  }

  powerStats.totalEnergyMah += currentMa * (elapsed / 3600000.0);
}

void printStatus() {
  unsigned long runTime = millis() - simulationStartTime;

  Serial.println();
  Serial.println("======================================================================");
  Serial.printf(" State: %s | Run time: %lu s | Speed: %.1f km/h\n",
                getStateName(currentState), runTime / 1000, simulatedSpeedKmh);
  Serial.printf(" Signal: RSRP %d dBm | Cell: %d | Handovers: %d\n",
                servingCell.rsrp, servingCell.cellId, mobilityStats.handoverCount);
  Serial.printf(" Data: %d msgs sent | Energy: %.4f mAh\n",
                dataStats.messagesSent, powerStats.totalEnergyMah);
  Serial.println("======================================================================");
}

void printLTEMvsNBIoTComparison() {
  Serial.println("----------------------------------------------------------------------");
  Serial.println(" LTE-M vs NB-IoT COMPARISON");
  Serial.println("----------------------------------------------------------------------");
  Serial.println(" Feature          | LTE-M (Cat-M1)    | NB-IoT (Cat-NB1)");
  Serial.println("------------------|-------------------|-------------------");
  Serial.println(" Data Rate        | 1 Mbps            | 250 kbps");
  Serial.println(" Latency          | 10-15 ms          | 1.6-10 seconds");
  Serial.println(" Mobility         | YES (160 km/h)    | NO");
  Serial.println(" VoLTE            | YES               | NO");
  Serial.println(" MCL              | 156 dB            | 164 dB");
  Serial.println("----------------------------------------------------------------------");
}

void printPowerModeInfo() {
  Serial.println("----------------------------------------------------------------------");
  Serial.println(" POWER MODE: PSM + eDRX Combined");
  Serial.printf(" PSM Sleep: %d µA | eDRX Sleep: %.1f mA | TX: %d mA\n",
                POWER_PSM_UA, POWER_EDRX_MA, POWER_TX_23DBM_MA);
  Serial.println("----------------------------------------------------------------------");
}

void printStateTransition(LTEMState from, LTEMState to) {
  Serial.printf("[STATE] %s -> %s\n", getStateName(from), getStateName(to));
}

const char* getStateName(LTEMState state) {
  switch (state) {
    case STATE_POWER_OFF: return "POWER_OFF";
    case STATE_CELL_SEARCH: return "CELL_SEARCH";
    case STATE_CELL_SELECTION: return "CELL_SELECTION";
    case STATE_RRC_CONNECTING: return "RRC_CONNECTING";
    case STATE_EPS_BEARER_SETUP: return "EPS_BEARER_SETUP";
    case STATE_AUTHENTICATING: return "AUTHENTICATING";
    case STATE_REGISTERED: return "REGISTERED";
    case STATE_DATA_TRANSFER: return "DATA_TRANSFER";
    case STATE_VOLTE_SETUP: return "VOLTE_SETUP";
    case STATE_VOLTE_ACTIVE: return "VOLTE_ACTIVE";
    case STATE_HANDOVER_PREP: return "HANDOVER_PREP";
    case STATE_HANDOVER_EXEC: return "HANDOVER_EXEC";
    case STATE_PSM: return "PSM";
    default: return "UNKNOWN";
  }
}

const char* getCEModeName(CEMode mode) {
  return (mode == CE_MODE_A) ? "A (Normal)" : "B (Extended)";
}

24.6 Challenge Exercises

Challenge 1: Optimize for Vehicle Tracking

Objective: Configure LTE-M parameters for a GPS tracker on delivery trucks.

Requirements:

  • Position updates every 30 seconds while moving
  • Battery life target: 2+ years with 5000 mAh battery
  • Must handle highway speeds (up to 120 km/h)
  • Quick response to geofence alerts (< 30 second latency)

Tasks:

  1. Modify T3324_VALUE_SECONDS and EDRX_CYCLE_SECONDS for optimal balance
  2. Test different scenarios by adjusting simulatedSpeedKmh
  3. Calculate whether PSM is appropriate for this use case

Analysis Questions:

  • Why is eDRX more suitable than PSM for this application?
  • What happens if the device enters PSM while crossing a geofence?
  • How does handover frequency affect battery life at highway speeds?

For a vehicle tracker transmitting 100 bytes every 30 seconds with 5-second active periods, the average current consumption with PSM is:

\[I_{avg} = \frac{(350 \text{ mA} \times 5\text{s}) + (0.005 \text{ mA} \times 25\text{s})}{30\text{s}} = 58.3 \text{ mA}\]

With a 5000 mAh battery, theoretical battery life = \(\frac{5000}{58.3} \approx 86\) hours (≈3.6 days). This assumes PSM sleep at 5 µA and TX at 350 mA (23 dBm). For 2-year target, eDRX (0.8 mA sleep) with solar charging or larger 15000 mAh battery is required.

Challenge 2: Compare LTE-M and NB-IoT Latency

Objective: Quantify the latency advantage of LTE-M for time-sensitive applications.

Scenario: Emergency button device that must alert monitoring center within 2 seconds.

Tasks:

  1. Measure simulated end-to-end latency in LTE-M (observe LATENCY_MS)
  2. Compare with NB-IoT specifications (1.6-10 second latency)
  3. Determine if PSM can be used while meeting the 2-second requirement

Analysis Questions:

  • Why does NB-IoT have 100x higher latency than LTE-M?
  • How does eDRX cycle length affect response time?
  • What power mode would you recommend for emergency buttons?
Challenge 3: VoLTE Emergency Call Simulation

Objective: Understand VoLTE capability for IoT emergency devices.

Scenario: Elevator intercom that must maintain voice quality during building transit.

Tasks:

  1. Observe VoLTE call setup and duration in the simulation
  2. Note handover behavior during active calls
  3. Calculate bandwidth requirements for continuous voice

Analysis Questions:

  • Why can NB-IoT not support VoLTE?
  • How does handover during a call differ from handover during data transfer?
  • What bandwidth does VoLTE require and how does this compare to sensor data?

24.7 Expected Outcomes

Learning Outcomes Checklist

After completing this lab, you should be able to:

24.8 Real-World Considerations

Simulation vs Reality

This simulation demonstrates LTE-M concepts but differs from real deployments:

What this simulation shows accurately:

  • State machine progression and timing relationships
  • Handover trigger conditions and execution flow
  • Power consumption ratios between operating states
  • VoLTE call setup sequence
  • Data path selection logic (CP vs UP)
  • Comparative advantages over NB-IoT

What requires real hardware/network:

  • Actual RF measurements and handover execution
  • Real carrier authentication with USIM
  • True power consumption profiling
  • Network-negotiated PSM/eDRX parameters
  • IMS registration and VoLTE codec negotiation
  • Roaming and inter-carrier handover

For hands-on LTE-M development:

  • Use modules like Quectel BG96, u-blox SARA-R4, Nordic nRF9160, or Sequans Monarch
  • Obtain LTE-M SIM cards from carriers (T-Mobile, AT&T, Verizon in US)
  • Consider global IoT platforms: Hologram, 1NCE, emnify for multi-carrier support
  • Test in real mobility scenarios (vehicle, train) to validate handover behavior

The Trade-Off: LTE-M modules cost $3-5 more than NB-IoT, data plans cost $1-3/month more, and power consumption is 3-5× higher in active mode. When is this extra cost justified?

Cost Comparison (5-Year TCO for 1,000 Devices):

Item NB-IoT LTE-M Difference
Modules (1,000 × cost) $18,000 $23,000 +$5,000
Data plans (60 months) $150,000 $270,000 +$120,000
Batteries (15-year AA vs 7-year AA) $0 (no replacement) $20,000 (replace at year 6) +$20,000
Total 5-Year $168,000 $313,000 +$145,000 (86% more)

LTE-M is worth the 86% premium when:

1. Mobility is Required (Non-Negotiable)

Application Speed Cell Boundary Crossings NB-IoT Result LTE-M Result Verdict
Parking sensor Stationary Never ✓ Works perfectly Overkill NB-IoT
Cattle tracker <5 km/h Rare (every few days) ⚠️ Occasional drops ✓ Works Either OK
Fleet GPS 50-100 km/h Every 5-10 minutes ❌ Drops constantly ✓ Seamless LTE-M required
High-speed rail 200-300 km/h Every 2 minutes ❌ Impossible ⚠️ Borderline (160 km/h max) LTE-M or satellite

ROI Calculation for Fleet Tracking:

  • 500 delivery trucks × $100,000/truck value = $50M fleet
  • GPS tracker prevents 1% theft/year = $500K savings
  • LTE-M cost: 500 × $313 = $156,500 over 5 years
  • ROI: $500K savings / $156K cost = 320% over 5 years

For mobile assets worth >$10K each, LTE-M’s mobility feature pays for itself by preventing a single theft.

2. Latency is Critical (<5 Seconds)

Application Max Tolerable Latency NB-IoT Latency LTE-M Latency Cost of Latency Failure
Daily meter reading Hours 1.6-10 s ✓ 15 ms ✓ $0 (no consequence) → Choose NB-IoT
Geofence alert <30 seconds 10 s ⚠️ borderline 15 ms ✓ $50 per false alarm (police dispatch) → Choose LTE-M
Emergency button <2 seconds 10 s ❌ 15 ms ✓ Life safetyLTE-M mandatory
Industrial valve control <500 ms 10 s ❌ 15 ms ✓ $10K equipment damage per failure → LTE-M required

ROI Calculation for Emergency Buttons:

  • 1,000 personal safety devices × 10-year lifespan
  • Liability of delayed emergency response: Priceless (regulatory requirement)
  • LTE-M cost premium: 1,000 × $145 = $145,000 over 5 years
  • Verdict: Regulatory compliance mandates <2s latency → LTE-M is only option

For life-safety applications, the cost premium is irrelevant compared to liability risk.

3. VoLTE Capability Enables the Use Case

Application Why VoLTE Matters NB-IoT Workaround Cost of Workaround Verdict
Elevator intercom Two-way voice required Add separate GSM module +$30 module + $10/month cellular voice plan LTE-M cheaper ($20 module + $6/month data+voice)
Construction site emergency phone Push-to-talk + GPS Separate radio + GPS +$200 radio + $40/month LTE-M vastly cheaper
Medical alert pendant Voice call + location Separate 2G module (being sunset) Not viable long-term LTE-M only option

ROI Calculation for Elevator Intercom (1,000 Elevators):

  • NB-IoT + GSM: (1,000 × $30 GSM module) + (1,000 × $10/month × 60) = $30K + $600K = $630K
  • LTE-M with VoLTE: (1,000 × $23 module) + (1,000 × $6/month × 60) = $23K + $360K = $383K
  • Savings: $247K by using LTE-M (even though LTE-M “costs more” than NB-IoT)

When VoLTE replaces a separate voice module, LTE-M is often cheaper than NB-IoT + GSM.

4. Moderate Data Volume (10-100 KB/Day)

Application Daily Data NB-IoT Throughput (250 kbps) LTE-M Throughput (1 Mbps) Time Difference
Temperature sensor (4 bytes/hour) 96 bytes/day Transmits in 3 ms Transmits in 0.7 ms Negligible → NB-IoT wins
GPS tracker (hourly, 50 bytes) 1.2 KB/day Transmits in 38 ms Transmits in 9.6 ms Negligible → NB-IoT wins
Fleet telemetry (30s interval, 100 bytes) 288 KB/day Transmits in 9.2 seconds Transmits in 2.3 seconds LTE-M saves 7s/transmission = 3.3 hours/day of radio-on time
Diagnostic logs (hourly, 5 KB) 120 KB/day Transmits in 3.8 seconds Transmits in 0.96 seconds LTE-M saves battery by 3×

Battery Impact Calculation (Fleet Telemetry, 100 Bytes Every 30 Seconds):

Technology Messages/Day TX Time Each Total TX Time/Day Average Current Daily mAh
NB-IoT 2,880 9.2 s 7.4 hours 200 mA 1,480 mAh
LTE-M 2,880 2.3 s 1.8 hours 350 mA (but 4× shorter) 630 mAh

Paradox: Even though LTE-M uses 350 mA (vs NB-IoT 200 mA) during TX, it finishes 4× faster → uses 57% less energy per day for high-frequency moderate-data applications.

When NOT to Pay for LTE-M:

  1. Stationary deployment with infrequent data (<1 KB/day): NB-IoT costs 40% less with no functional difference
  2. Latency tolerant (reports can wait minutes): NB-IoT’s 1.6-10s latency is free savings
  3. Budget-constrained pilot (1,000 devices): NB-IoT saves $145K over 5 years → use savings to deploy 86% more devices

Final Decision Template:

IF mobility_required OR latency <5s OR needs_voice:
    → LTE-M (premium justified by unique capabilities)
ELSE IF data_volume >50 KB/day AND battery_not_critical:
    → LTE-M (throughput advantage saves battery)
ELSE:
    → NB-IoT (lowest TCO for stationary, latency-tolerant sensors)

The $1M Mistake: A company deployed 10,000 NB-IoT parking sensors assuming they were “good enough.” Later added requirement for 5-second geofence alerts when towing trucks arrive. Couldn’t meet 5s with NB-IoT’s 10s latency. Solution: Replace all 10,000 modules with LTE-M = $50K modules + $200K labor = $250K rework. If they’d chosen LTE-M initially, the $150K 5-year premium would have been far cheaper than $250K rework.

Always evaluate your hardest requirement first (mobility, latency, or voice) before optimizing for cost. The cheapest solution that doesn’t meet requirements costs far more long-term.

24.9 Summary

  • LTE-M simulation demonstrates key differentiators from NB-IoT: mobility, latency, VoLTE, and higher data rates
  • Handover support enables tracking applications at highway speeds (up to 160 km/h) with 50-100 ms interruptions
  • VoLTE capability allows voice-enabled IoT devices like emergency buttons and intercoms
  • Power optimization through PSM and eDRX works similarly to NB-IoT but with shorter cycles for faster response
  • Data path selection between Control Plane and User Plane optimizes for payload size and latency requirements

24.10 Knowledge Check

24.11 Concept Relationships

Core cellular IoT architecture connects these key domains:

  • LTE-M builds on the LTE EPC (Evolved Packet Core) introduced in Cellular IoT Overview, using the same eNodeB base stations and MME/S-GW/P-GW components
  • PSM and eDRX power modes from Cellular IoT Power Optimization are configured via the AT+CPSMS and AT+CEDRXS commands demonstrated in this lab
  • VoLTE capability distinguishes LTE-M from NB-IoT - see NB-IoT vs LTE-M Comparison for detailed technology selection criteria
  • Handover support enables mobility tracking - compare with stationary NB-IoT deployments in NB-IoT Applications
  • EPS bearer establishment and User Plane vs Control Plane optimization connect to Cellular IoT Architecture fundamentals

How LTE-M fits in the broader wireless IoT ecosystem: LTE-M operates in licensed cellular spectrum (700-2600 MHz bands) versus unlicensed alternatives like LoRaWAN (868/915 MHz ISM bands) and Wi-Fi (2.4/5 GHz). The mobility and voice features make LTE-M suitable for applications where LoRaWAN cannot provide seamless connectivity during movement.

24.12 See Also

Related cellular IoT technologies:

Technology comparisons:

  • LoRaWAN - Unlicensed LPWAN alternative without handover
  • 5G NR for IoT - Next-generation cellular with mMTC and URLLC modes

Implementation resources:

  • AT Command Reference - Complete AT command guide
  • MQTT over Cellular - Application layer protocol integration

24.13 Try It Yourself

Extend the LTE-M lab with these exercises:

  1. Optimize PSM for fleet tracking: Modify T3412 and T3324 timers to support 30-second GPS updates while maintaining 5+ year battery backup. Measure the energy impact using the power calculation formulas from the simulation.

  2. Simulate VoLTE emergency call: Add a function to simulate a 30-second VoLTE call during handover. Calculate the total energy consumption and verify that the call maintains connectivity during cell transition.

  3. Compare data paths: Implement both Control Plane (small payload) and User Plane (large payload) transmission in the lab code. Measure the difference in signaling overhead and latency.

  4. Add geofence alert: Create a geofence boundary in the simulation (e.g., speed exceeds 100 km/h). Trigger an immediate eDRX wake-up to send an alert, demonstrating the latency advantage of eDRX over PSM.

  5. Handover failure recovery: Simulate a failed handover scenario (target cell unavailable). Implement retry logic and measure the impact on data transmission reliability.

Expected outcomes:

  • Understanding the trade-offs between PSM (deep sleep, long latency) and eDRX (light sleep, short latency)
  • Practical experience with LTE-M state transitions and power consumption modeling
  • Insight into when LTE-M’s higher cost ($3-5/month data plan premium) is justified by mobility and voice features

Common Pitfalls

LTE-M lab experiments that do not enable PSM measure active-mode power consumption (5–50 mA) but miss the sleep-mode power profile that determines actual battery life. A lab result of 10 mA average current is meaningless without PSM enabled. Always configure PSM parameters (AT+CPSMS=1,,,,) and verify power consumption across a full duty cycle: active transmission + PSM sleep. Measure with a current probe or Nordic PPK2 for accurate energy-per-transmission data.

Default LTE-M connection parameters from modem vendors prioritize connectivity reliability over power efficiency. Default T3324 (Active Timer) may be 30 seconds; for a sensor transmitting once per minute, this keeps the radio active unnecessarily for 30 seconds per cycle, consuming 100× more energy than needed. Tune T3412 (PSM periodic TAU timer) and T3324 (Active Timer) to the minimum values that meet application latency requirements.

LTE-M lab tests conducted in a fixed location do not validate handover behavior for mobile asset tracking applications. Moving assets (vehicles, pallets, containers) experience cell handovers that briefly interrupt connectivity. Test by moving the device between base station coverage zones while streaming data. Measure handover latency, packet loss during handover, and reconnection time after coverage gaps to validate the application’s reconnection and buffering logic.

Lab implementations that use plain TCP or UDP without TLS “just for testing” often reach production without proper TLS implementation added back. LTE-M lab exercises must include TLS 1.3 setup (mbedTLS or WolfSSL on embedded platforms) with proper certificate validation, not just encryption. Test certificate expiry handling, CA certificate rotation, and mutual TLS (mTLS) authentication if the production system requires it.

24.14 What’s Next

Topic Chapter Why It Helps
NB-IoT comparison NB-IoT Labs and Implementation Compare stationary NB-IoT sensor simulation with this LTE-M mobility lab
Technology selection NB-IoT vs LTE-M Comparison Apply lab observations to formal decision criteria and cost analysis
Deployment planning Cellular IoT Deployment Planning Translate lab concepts into real coverage analysis and carrier selection
Global connectivity eSIM and Global Deployment Extend LTE-M deployments across carriers and regions with eSIM
LPWAN alternatives LoRaWAN Overview Contrast licensed cellular IoT with unlicensed LoRaWAN for non-mobile use cases