pdf-icon

Arduino Quick Start

2. Devices & Examples

6. Applications

Module COMX LTE Arduino Tutorial

1. Preparation

2. Notes

Pin compatibility
Since different hosts use different pin mappings, please refer to the Pin Compatibility Table in the product documentation before use, and modify the example code according to your actual wiring.

3. Example Program

  • In this tutorial, the main controller used is CoreS3-SE. With Module COMX LTE and a wired headset with microphone, you can make voice calls. Before use, please set the DIP switch positions as shown below.

3.1 DIP Switch Settings

Module COMX LTE communicates via UART. Please modify the pin definitions in the program according to your actual circuit connection. After connecting the devices, the corresponding UART IO are G1 (RX) and G7 (TX), as shown below:

Note
1. If the DIP switch COM RST is set to ON, the module will be connected to the host reset pin. When the host resets, the module will also reset. In this case, the module initialization time will be longer. Configure it as needed.
2. If you are using a Core series host, you can set the DIP switch 25 OUT to ON to use the host's built-in speaker as the call audio output (Core series only).

3.2 Headset Selection

Module COMX LTE integrates a 3.5mm TRRS headset jack, allowing you to connect a wired headset with microphone for call audio input/output. This jack supports 17mm plug length. If you use a 14mm plug, the headset will not work properly. Please use a male-to-female adapter like the one shown below:

3.3 Code

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
#include <M5Unified.h>

// Call-related status variables (volatile for interrupt-safe access)
volatile bool call_connected = false;
volatile bool call_ended     = false;
volatile int  call_result    = 0;   // Call result code: 0=Unknown, 1=Connected, 2=Unanswered, 3=Busy

bool  network_ready = false;
bool  calling       = false;

// Read all available data from LTE module serial port and update call status
// Parses Unsolicited Result Codes (URC) from LTE module to detect call state changes
void readLTEURC()
{
    static String buf;

    while (Serial2.available() > 0) {
        char c = (char)Serial2.read();
        buf += c;
    }

    if (buf.length() == 0) return;

    // Determine call status by matching URC keywords
    if (buf.indexOf("VOICE CALL: BEGIN") != -1) {
        call_connected = true;
        call_result    = 1;
        Serial.println("[COMX LTE URC] CALL CONNECTED (VOICE CALL: BEGIN)");
    }
    if (buf.indexOf("VOICE CALL: END") != -1) {
        call_ended = true;
        Serial.println("[COMX LTE URC] CALL ENDED (VOICE CALL: END)");
    }
    if (buf.indexOf("NO CARRIER") != -1) {
        call_ended = true;
        if (!call_connected) {
            call_result = 2;
        }
        Serial.println("[COMX LTE URC] CALL ENDED (NO CARRIER)");
    }
    if (buf.indexOf("BUSY") != -1) {
        call_ended  = true;
        call_result = 3;
        Serial.println("[COMX LTE URC] CALL BUSY");
    }

    // Print raw OK/ERROR responses (dialing uses sendATandWait's timeout handling)
    if (buf.indexOf("OK") != -1 || buf.indexOf("ERROR") != -1) {
        Serial.print("[COMX LTE RSP RAW] ");
        Serial.println(buf);
    }

    // Clear after processing to prevent overflow
    buf = "";
}

// Send AT command to LTE module and wait for OK/ERROR response with timeout
// @param cmd: AT command string (must include \r\n for line ending)
// @param resp: Reference to store full response from module
// @param timeout_ms: Maximum wait time in milliseconds
// @return: true = "OK" received, false = "ERROR" received or timeout
bool sendATandWait(const String& cmd, String& resp, uint32_t timeout_ms)
{
    resp = "";
    Serial.print("[COMX LTE TX] ");
    Serial.print(cmd);

    Serial2.print(cmd);// Transmit AT command to LTE module

    uint32_t start = millis();
    // Wait for response until timeout expires
    while (millis() - start < timeout_ms) {
        while (Serial2.available() > 0) {
            char c = (char)Serial2.read();
            resp += c;

            // Check for termination conditions (OK/ERROR)
            if (resp.indexOf("OK") != -1) {
                Serial.print("[COMX LTE RSP] ");
                Serial.println(resp);
                return true;
            }
            if (resp.indexOf("ERROR") != -1) {
                Serial.print("[COMX LTE RSP] ");
                Serial.println(resp);
                return false;
            }
        }
        delay(10);
    }

    Serial.print("[COMX LTE RSP TIMEOUT] ");
    Serial.println(resp);
    return false;
}

// Wait for LTE module initialization completion
// Verifies basic AT command responsiveness (OK response to "AT" command)
// @param timeout_ms: Maximum wait time for module to respond
// @return: true = module ready (AT port responsive), false = timeout
bool waitForModuleReady(uint32_t timeout_ms)
{
    uint32_t start = millis();
    Serial.println("[COMX LTE] Waiting for module ready...");
    while (millis() - start < timeout_ms) {
        readLTEURC();                 // 吃掉启动URC/残留输出
        String resp;
        if (sendATandWait("AT\r\n", resp, 1000)) {
            Serial.println("[COMX LTE] Module ready.");
            return true;              // 回复OK,说明AT口已就绪
        }
        delay(200);
    }
    Serial.println("[COMX LTE] Module not ready (timeout).");
    return false;
}

// Wait for SIM7600G network readiness (GPRS attach + LTE network registration)
// Verifies both CGATT (attach) and CEREG (registration) statuses
// @param timeout_ms: Maximum wait time for network registration
// @return: true = network registered, false = timeout
bool waitForNetworkReady(uint32_t timeout_ms)
{
    uint32_t start = millis();
    Serial.println("[COMX LTE] Waiting for network...");

    while (millis() - start < timeout_ms) {
        String resp;

        // 1) Query GPRS attach status: AT+CGATT?
        // Expected response: +CGATT: 1 (attached) / +CGATT: 0 (detached)
        if (sendATandWait("AT+CGATT?\r\n", resp, 2000)) {
            if (resp.indexOf("+CGATT: 1") == -1) {
                Serial.println("[COMX LTE] Not attached, retry...");
                delay(1000);
                continue;
            }
        } else {
            Serial.println("[COMX LTE] CGATT? error, retry...");
            delay(1000);
            continue;
        }

        // 2) Query LTE network registration status: AT+CEREG?
        // Expected responses: 
        // +CEREG: 0,1 = Registered (home network)
        // +CEREG: 0,5 = Registered (roaming network)
        resp = "";
        if (sendATandWait("AT+CEREG?\r\n", resp, 2000)) {
            if (resp.indexOf("+CEREG:") != -1 &&
                (resp.indexOf(",1") != -1 || resp.indexOf(",5") != -1)) {
                Serial.println("[COMX LTE] Network registered.");
                return true;
            } else {
                Serial.println("[COMX LTE] Not registered, retry...");
            }
        } else {
            Serial.println("[COMX LTE] CEREG? error, retry...");
        }

        delay(100);
    }

    Serial.println("[COMX LTE] Network not ready (timeout).");
    return false;
}

void setup()
{
    M5.begin();
    Serial.begin(115200);
    Serial.println("\r\n[COMX LTE] Start");
    Serial2.begin(115200, SERIAL_8N1, 1, 7); // Initialize LTE module serial port RX=1,TX=7

    M5.Display.setFont(&fonts::FreeMonoBold9pt7b);
    M5.Display.setTextDatum(middle_center);
    M5.Display.clear();
    M5.Display.drawString("Module Initializing...", 160, 120);

    delay(500);

    // Initialize LTE module first, then check network status
    if (!waitForModuleReady(25000)) {// Module initialization typically takes up to 20s
        network_ready = false;
    } else {
        network_ready = waitForNetworkReady(30000);
    }

    M5.Display.clear();
    if (network_ready) {
        Serial.println("[COMX LTE] Touch screen to dial/stop");
        M5.Display.drawString("Touch screen to dial", 160, 80);
    } else {
        Serial.println("[COMX LTE] Network NOT ready, please reset.");
        M5.Display.drawString("Network not ready", 160, 120);
    }
}

void loop()
{
    M5.update();
    auto touchDetail = M5.Touch.getDetail();

    // Continuously read URC messages from LTE module
    readLTEURC();

    // Block dialing if network is not fully registered
    if (!network_ready) {
        delay(50);
        return;
    }

    // Handle touch input for dial/hangup control
    if (!calling && touchDetail.wasPressed()) {
        calling          = true;
        call_connected = false;
        call_ended     = false;
        call_result    = 0;

        Serial.println("[COMX LTE] Dialing...");
        M5.Display.clear();
        M5.Display.drawString("Calling......", 160, 120);
        M5.Display.drawString("Touch screen to hang up", 160, 80);

        String resp;
        // ATD command format: ATD<phone_number>
        bool ok = sendATandWait("ATDXXXXXXXXXXX;\r\n", resp, 10000);

        Serial.print("[DIAL RESULT] ");
        Serial.println(ok ? "OK" : "ERROR/TIMEOUT");
    }
    else if (calling && touchDetail.wasPressed()) {
        Serial.println("[COMX LTE] Hangup");
        String resp;
        sendATandWait("AT+CHUP\r\n", resp, 5000);

        calling     = false;
        call_ended  = true; 
        M5.Display.clear();
        M5.Display.drawString("Call ended", 160, 120);
        M5.Display.drawString("Touch screen to dial", 160, 80);
    }

    // Update display based on real-time call status
    if (calling) {
        if (call_connected) {
            M5.Display.clear();
            M5.Display.drawString("Speaking......", 160, 120);
            M5.Display.drawString("Touch screen to hang up", 160, 80);
        }
        if (call_ended) {
            calling = false;
            M5.Display.clear();
            M5.Display.drawString("Call ended", 160, 120);
            M5.Display.drawString("Touch screen to dial", 160, 80);
        }
    }

    delay(20);
}

4. Compile & Upload

  • 1. Enter download mode: Press and hold the Reset button on CoreS3-SE (about 2 seconds) until the internal green LED lights up, then release it. The device will enter download mode and wait for flashing.
Note
Some devices must enter download mode before flashing, and the steps may vary depending on the host controller. For details, refer to the device flashing tutorial list at the bottom of Getting Started with Arduino IDE.
  • Select the device port, click the compile and upload button in the upper left corner of Arduino IDE, and wait for the program to finish compiling and uploading to the device.

5. Voice Call

  • After power-on, the Module COMX LTE module will initialize automatically and attempt to register to the network. Once connected, touch the host screen to dial. During a call, touch the host screen again to hang up. The current call status will be displayed on the screen.

  • Serial monitor output example:
[COMX LTE] Start
[COMX LTE] Waiting for module ready...
[COMX LTE TX] AT
[COMX LTE RSP] AT

OK
[COMX LTE] Module ready.
[COMX LTE] Waiting for network...
[COMX LTE TX] AT+CGATT?
[COMX LTE RSP] 
AT+CGATT?

+CGATT: 1

OK
[COMX LTE TX] AT+CEREG?
[COMX LTE RSP] 
AT+CEREG?

+CEREG: 0,1

OK
[COMX LTE] Network registered.
[COMX LTE] Touch screen to dial/stop
[COMX LTE] Dialing...
[COMX LTE TX] ATDXXXXXXXXXX;
[COMX LTE RSP] ATDXXXXXXXXXX;

OK
[DIAL RESULT] OK
[COMX LTE URC] CALL CONNECTED (VOICE CALL: BEGIN)
[COMX LTE] Hangup
[COMX LTE TX] AT+CHUP
[COMX LTE RSP] AT+CHUP

VOICE CALL: END: 000008

OK
On This Page