Chapter 06

Microcontroller Integration & Code Examples

Production-grade, copy-pasteable, tested code for the Arduino Uno/Nano (SoftwareSerial + AT+GPSRD), Arduino Mega 2560 (Dual Hardware UART), and ESP32 (Non-blocking location-on-demand tracker).

Example 1: Arduino Uno / Nano (Single-Port with AT+GPSRD)

Demonstrates how to boot the A7, enable GPS via AT+GPS=1, mirror NMEA sentences onto the AT port with AT+GPSRD=1, acquire a lock, stop redirection with AT+GPSRD=0, and transmit the coordinates via SMS.

Arduino Uno (5V)                            AI-Thinker A7 Board
+-------------------+                       +--------------------+
|  Pin 10 (Soft RX) <-----------------------+ U_TXD    (P1 Pin 5)|
|                   |                       |                    |
|  Pin 11 (Soft TX) +----[ 1.0k ]---+-------> U_RXD    (P1 Pin 4)|
|                   |               |       |                    |
|                   |             [2.0k]    |                    |
|                   |               |       |                    |
|  Pin 7  (PWR_KEY) +---------------+-------> PWR_KEY  (P2 Pin 11|
|                   |               |       |                    |
|  GND              +---------------+-------> GND      (P1 Pin 3)|
+-------------------+                       +--------------------+
#include <SoftwareSerial.h>

const byte A7_RX_PIN = 10; // Connect to A7 U_TXD
const byte A7_TX_PIN = 11; // Connect to A7 U_RXD (via 1k/2k divider)
const byte A7_PWR_PIN = 7; // Connect to A7 PWR_KEY

SoftwareSerial A7Serial(A7_RX_PIN, A7_TX_PIN);
const char TARGET_PHONE[] = "+1234567890"; // Target recipient

void setup() {
  Serial.begin(9600);
  A7Serial.begin(9600); // Ensure A7 is set to 9600 baud (see Chapter 3)
  
  Serial.println(F("=== AI-Thinker A7 Uno Tracker Starting ==="));
  powerOnModule();

  sendAT("AT", 1000);
  sendAT("AT+CPIN?", 2000);
  sendAT("AT+CMGF=1", 1000); // Set SMS to Text Mode

  Serial.println(F("[GPS] Turning ON GPS Receiver..."));
  sendAT("AT+GPS=1", 2000);

  acquireFixAndSendSMS();
}

void loop() {
  // Idle after execution
}

void powerOnModule() {
  Serial.println(F("[PWR] Sending Active-HIGH pulse to PWR_KEY..."));
  pinMode(A7_PWR_PIN, OUTPUT);
  digitalWrite(A7_PWR_PIN, HIGH);
  delay(2500); // 2.5 second pulse
  digitalWrite(A7_PWR_PIN, LOW);
  delay(5000); // Allow RTOS and network registration to complete
}

void acquireFixAndSendSMS() {
  Serial.println(F("[GPS] Starting AT+GPSRD stream..."));
  sendAT("AT+GPSRD=1", 2000);

  char nmeaLine[100];
  byte index = 0;
  bool fixAcquired = false;
  char lat[12] = "";
  char lon[12] = "";

  unsigned long startTime = millis();

  while (!fixAcquired && (millis() - startTime < 180000)) { // 3-min timeout
    while (A7Serial.available()) {
      char c = A7Serial.read();
      if (c == '\n') {
        nmeaLine[index] = '\0';
        index = 0;

        if (strstr(nmeaLine, "$GPRMC") != NULL || strstr(nmeaLine, "$GNRMC") != NULL) {
          Serial.print(F("[NMEA] "));
          Serial.println(nmeaLine);

          // Parse: $GPRMC,time,status,lat,NS,lon,EW,...
          char* p = strchr(nmeaLine, ','); 
          if (p) p = strchr(p + 1, ',');   
          if (p && *(p + 1) == 'A') {      // 'A' indicates Valid Fix
            Serial.println(F("[GPS] >>> VALID FIX ACQUIRED! <<<"));
            
            char* latStart = p + 3;
            char* latEnd = strchr(latStart, ',');
            if (latEnd) {
              strncpy(lat, latStart, latEnd - latStart);
              lat[latEnd - latStart] = '\0';
            }

            char* ns = latEnd + 1;
            char* lonStart = ns + 2;
            char* lonEnd = strchr(lonStart, ',');
            if (lonEnd) {
              strncpy(lon, lonStart, lonEnd - lonStart);
              lon[lonEnd - lonStart] = '\0';
            }

            fixAcquired = true;
            break;
          }
        }
      } else if (c != '\r' && index < sizeof(nmeaLine) - 1) {
        nmeaLine[index++] = c;
      }
    }
  }

  // CRITICAL: Stop GPSRD to restore clean AT command interface!
  Serial.println(F("[GPS] Stopping AT+GPSRD stream..."));
  sendAT("AT+GPSRD=0", 2000);
  delay(1000);

  if (fixAcquired) {
    Serial.println(F("[SMS] Transmitting Location SMS..."));
    A7Serial.print(F("AT+CMGS=\""));
    A7Serial.print(TARGET_PHONE);
    A7Serial.println(F("\""));
    delay(1000);

    A7Serial.print(F("A7 Alert: Vehicle Location: Lat: "));
    A7Serial.print(lat);
    A7Serial.print(F(", Lon: "));
    A7Serial.print(lon);
    A7Serial.write(0x1A); // Send Ctrl+Z to finish SMS
    delay(3000);
    Serial.println(F("[SMS] SMS Sent Successfully!"));
  } else {
    Serial.println(F("[GPS] Timed out waiting for fix."));
  }
}

String sendAT(const char* cmd, const int timeout) {
  A7Serial.println(cmd);
  String response = "";
  unsigned long t = millis();
  while (millis() - t < timeout) {
    while (A7Serial.available()) {
      response += (char)A7Serial.read();
    }
  }
  Serial.print(F("CMD: ")); Serial.print(cmd);
  Serial.print(F(" -> RESP: ")); Serial.println(response);
  return response;
}

Example 2: Arduino Mega 2560 (Dual Hardware UART)

Using hardware UARTs allows continuous GPS streaming from GPS_TXD without ever having to send AT+GPSRD.

#define A7_AT_Serial   Serial3 // Mega TX3 (Pin 14), RX3 (Pin 15)
#define A7_GPS_Serial  Serial2 // Mega RX2 (Pin 19) <- A7 GPS_TXD

void setup() {
  Serial.begin(115200);
  A7_AT_Serial.begin(115200);
  A7_GPS_Serial.begin(9600); // GPS_TXD fixed at 9600 baud

  Serial.println(F("=== Mega 2560 Dual-UART A7 Demo Starting ==="));
  delay(1000);

  // Turn ON GPS subsystem
  Serial.println(F("Enabling GPS Subsystem via Serial3..."));
  A7_AT_Serial.println("AT+GPS=1");
  delay(1000);
  
  // NOTE: DO NOT send AT+GPSRD!
  // GPS data will stream automatically out of GPS_TXD into Serial2.
}

void loop() {
  // Read and print GPS stream from Serial2
  if (A7_GPS_Serial.available()) {
    char c = A7_GPS_Serial.read();
    Serial.write(c); // Direct forward to PC monitor
  }

  // Read unsolicited cellular events (incoming SMS, RING) from Serial3
  if (A7_AT_Serial.available()) {
    char c = A7_AT_Serial.read();
    Serial.write(c);
  }

  // Forward PC terminal commands to A7
  if (Serial.available()) {
    char c = Serial.read();
    A7_AT_Serial.write(c);
  }
}

Example 3: ESP32 Non-Blocking Location-On-Demand Tracker

The ESP32 runs at 3.3V, connecting directly to the A7 without level shifters. This non-blocking sketch listens for incoming text messages containing "LOC" and responds with a clickable Google Maps URL.

Looking for the Zero-Conflict Pinout? Prevent ESP32-WROVER PSRAM crashes, eliminate the GPIO 12 flash brownout trap, and keep hardware I2C & SPI buses 100% free for OLEDs and SD cards.
Optimal ESP32 Guide →
#include <HardwareSerial.h>

HardwareSerial A7_AT(2);         // UART2: GPIO 16 (RX), GPIO 17 (TX)
HardwareSerial A7_GPS(1);        // UART1: GPIO 4 (RX), GPIO 5 (TX unused)

const int PIN_PWR_KEY = 23;
String currentLat = "0.0";
String currentLon = "0.0";
bool hasGPSLock = false;

void setup() {
  Serial.begin(115200);
  A7_AT.begin(115200, SERIAL_8N1, 16, 17);
  A7_GPS.begin(9600, SERIAL_8N1, 4, -1);

  Serial.println(F("[ESP32] Booting AI-Thinker A7..."));

  // Pulse PWR_KEY
  pinMode(PIN_PWR_KEY, OUTPUT);
  digitalWrite(PIN_PWR_KEY, HIGH);
  delay(2500);
  digitalWrite(PIN_PWR_KEY, LOW);
  delay(4000);

  // Initialize Cellular & GPS
  A7_AT.println("AT");
  delay(500);
  A7_AT.println("AT+CMGF=1"); // SMS Text mode
  delay(500);
  A7_AT.println("AT+CNMI=2,2,0,0,0"); // Direct SMS display
  delay(500);
  A7_AT.println("AT+GPS=1"); // Turn on GPS
  delay(500);

  Serial.println(F("[ESP32] Tracker Ready. Listening for SMS 'LOC'..."));
}

void loop() {
  processGPSStream();
  processCellularEvents();
}

void processGPSStream() {
  static char buffer[120];
  static byte idx = 0;

  while (A7_GPS.available()) {
    char c = A7_GPS.read();
    if (c == '\n') {
      buffer[idx] = '\0';
      idx = 0;
      
      if (strstr(buffer, "$GPRMC") || strstr(buffer, "$GNRMC")) {
        char* p = strchr(buffer, ',');
        if (p) p = strchr(p + 1, ',');
        if (p && *(p + 1) == 'A') { // Valid Fix
          hasGPSLock = true;
          char* latStart = p + 3;
          char* latEnd = strchr(latStart, ',');
          if (latEnd) {
            currentLat = String(latStart).substring(0, latEnd - latStart);
            char* lonStart = latEnd + 3;
            char* lonEnd = strchr(lonStart, ',');
            if (lonEnd) {
              currentLon = String(lonStart).substring(0, lonEnd - lonStart);
            }
          }
        } else {
          hasGPSLock = false;
        }
      }
    } else if (c != '\r' && idx < sizeof(buffer) - 1) {
      buffer[idx++] = c;
    }
  }
}

void processCellularEvents() {
  static String atBuffer = "";
  while (A7_AT.available()) {
    char c = A7_AT.read();
    atBuffer += c;
    if (c == '\n') {
      atBuffer.trim();
      if (atBuffer.startsWith("+CMT:")) {
        int senderStart = atBuffer.indexOf("\"") + 1;
        int senderEnd = atBuffer.indexOf("\"", senderStart);
        String senderNumber = atBuffer.substring(senderStart, senderEnd);

        String body = A7_AT.readStringUntil('\n');
        body.trim();
        Serial.printf("[SMS] From: %s | Text: %s\n", senderNumber.c_str(), body.c_str());

        if (body.equalsIgnoreCase("LOC")) {
          sendReplySMS(senderNumber);
        }
      }
      atBuffer = "";
    }
  }
}

void sendReplySMS(String recipient) {
  Serial.printf("[SMS] Sending reply to %s...\n", recipient.c_str());
  A7_AT.printf("AT+CMGS=\"%s\"\r\n", recipient.c_str());
  delay(1000);

  if (hasGPSLock) {
    A7_AT.printf("Vehicle Location Lock:\nhttp://maps.google.com/maps?q=%s,%s", 
                 currentLat.c_str(), currentLon.c_str());
  } else {
    A7_AT.print("GPS searching for satellites. Please retry in 1 minute.");
  }
  A7_AT.write(0x1A); // Send Ctrl+Z
}