/* MuTech Robotics - GP1 Pet Safety Tracker (4G + GPS geofence SMS) Board : ESP32 development board (30-pin) Modem : 4G + GPS module board (Raspberry-Pi-style header) Power : 3S 18650 pack (11.1V) -> HAT 12V terminal, and -> MP1584 buck set to 5.0V -> ESP32 VIN Wiring Battery + -> HAT +12V terminal and buck IN+ Battery - -> HAT GND terminal and buck IN- Buck OUT+ -> ESP32 VIN (set the buck to 5.0V BEFORE connecting!) Buck OUT- -> ESP32 GND ESP32 D32 (TX) -> module header pin 8 (Pi TXD position) ESP32 D33 (RX) <- module header pin 10 (Pi RXD position) Fit the LTE antenna and the GPS antenna to the HAT. Insert a 4G SIM with SMS enabled. CHECK YOUR HAT: this HAT is made for a Raspberry Pi, so the ESP32 takes the Pi's place on pins 8/10. Confirm on your HAT's pin chart that the modem UART is on pins 8/10 at 3.3V logic. If the modem does not answer "AT", it may need its PWR (power-key) pin pulsed - see the HAT notes. How it works AT+QGPS=1 start GNSS AT+QGPSLOC=2 read position (decimal degrees) If the pet is more than SAFE_RADIUS_M from HOME -> one SMS with a Google Maps link. When it comes back inside -> "Back home" SMS, and the alarm re-arms. */ const char* OWNER_PHONE = "+91XXXXXXXXXX"; // your mobile number const double HOME_LAT = 17.448600; // set to your home location const double HOME_LON = 78.390800; const double SAFE_RADIUS_M = 150.0; const unsigned long CHECK_MS = 10000; // read GPS every 10 s HardwareSerial& modem = Serial2; bool outsideAlerted = false; String sendAT(const String& cmd, unsigned long waitMs = 1000) { while (modem.available()) modem.read(); modem.println(cmd); String resp; unsigned long t0 = millis(); while (millis() - t0 < waitMs) { while (modem.available()) resp += (char)modem.read(); if (resp.indexOf("OK") >= 0 || resp.indexOf("ERROR") >= 0) break; } return resp; } bool sendSMS(const String& text) { sendAT("AT+CMGF=1"); modem.print("AT+CMGS=\""); modem.print(OWNER_PHONE); modem.println("\""); unsigned long t0 = millis(); while (millis() - t0 < 3000 && modem.read() != (int)'>') {} modem.print(text); modem.write(26); // Ctrl+Z sends the message String r = ""; t0 = millis(); while (millis() - t0 < 10000) { while (modem.available()) r += (char)modem.read(); if (r.indexOf("OK") >= 0) return true; } return false; } // +QGPSLOC: ,,,,,,,,,, bool readGPS(double& lat, double& lon, int& sats) { String r = sendAT("AT+QGPSLOC=2", 1500); int p = r.indexOf("+QGPSLOC:"); if (p < 0) return false; // no fix yet (CME ERROR 516) String s = r.substring(p + 9); int c1 = s.indexOf(','), c2 = s.indexOf(',', c1 + 1), c3 = s.indexOf(',', c2 + 1); lat = s.substring(c1 + 1, c2).toDouble(); lon = s.substring(c2 + 1, c3).toDouble(); int last = s.lastIndexOf(','); sats = s.substring(last + 1).toInt(); return lat != 0.0 && lon != 0.0; } double distanceM(double lat1, double lon1, double lat2, double lon2) { const double R = 6371000.0; double dLat = radians(lat2 - lat1), dLon = radians(lon2 - lon1); double a = sin(dLat / 2) * sin(dLat / 2) + cos(radians(lat1)) * cos(radians(lat2)) * sin(dLon / 2) * sin(dLon / 2); return 2 * R * atan2(sqrt(a), sqrt(1 - a)); } String mapLink(double lat, double lon) { return "https://maps.google.com/?q=" + String(lat, 6) + "," + String(lon, 6); } void setup() { Serial.begin(115200); modem.begin(115200, SERIAL_8N1, 33, 32); // RX = D33, TX = D32 delay(3000); Serial.println(sendAT("AT")); Serial.println(sendAT("AT+CPIN?")); Serial.println(sendAT("AT+QGPS=1")); // start GNSS (OK, or ERROR 504 if already on) } void loop() { static unsigned long last = 0; if (millis() - last < CHECK_MS) return; last = millis(); double lat, lon; int sats; if (!readGPS(lat, lon, sats)) { Serial.println("Waiting for GPS fix..."); return; } double d = distanceM(HOME_LAT, HOME_LON, lat, lon); Serial.printf("lat %.6f lon %.6f sats %d from home %.0f m\n", lat, lon, sats, d); if (d > SAFE_RADIUS_M && !outsideAlerted) { outsideAlerted = sendSMS("ALERT: Your pet left the SAFE ZONE! " + String(d, 0) + " m from home. " + mapLink(lat, lon)); } else if (d < SAFE_RADIUS_M * 0.8 && outsideAlerted) { // 20% margin stops repeat alerts at the edge sendSMS("Back home: your pet is inside the safe zone again. " + mapLink(lat, lon)); outsideAlerted = false; } }