#include <TinyGPS++.h>
#include <SD.h>
#include <SPI.h> // Required for SD card
#include <HardwareSerial.h> // For Serial2 (GPS)
#include <SoftwareSerial.h> // For GSM
// --- GPS Definitions ---
#define GPS_BAUDRATE 9600 // The default baudrate of NEO-6M is 9600
TinyGPSPlus gps; // the TinyGPS++ object
// --- SD Card Definitions ---
// SD Card uses ESP32's Hardware SPI pins:
// MISO: GPIO12
// MOSI: GPIO13 (Commonly 23, but we're using 15 for SD MOSI here based on original diagram)
// SCK: GPIO14
// CS (Chip Select) can be any GPIO pin. We're using GPIO13.
const int SDCARD_CS_PIN = 13; // GPIO13 for SD Card Chip Select
// --- GSM Definitions ---
// GSM module uses SoftwareSerial:
// RX (GSM) to TX (ESP32) -> ESP32 GPIO4
// TX (GSM) to RX (ESP32) -> ESP32 GPIO2
#define GSM_TX_PIN 4 // Connects to GSM Module's RXD (ESP32 TX to GSM RX)
#define GSM_RX_PIN 2 // Connects to GSM Module's TXD (ESP32 RX to GSM TX)
SoftwareSerial gsmSerial(GSM_RX_PIN, GSM_TX_PIN); // RX, TX
#define GSM_BAUDRATE 9600 // Common baud rate for SIM800L
// --- Push Button Definition (Optional, for triggering SMS) ---
const int BUTTON_PIN = 18; // GPIO18 for the button
// --- Variables for Data ---
char latitudeStr[15];
char longitudeStr[15];
char altitudeStr[15];
char speedStr[10];
char dateTimeStr[30];
// --- Helper function to send SMS ---
void sendSMS(const char* phoneNumber, const char* message) {
Serial.print(F("Sending SMS to "));
Serial.println(phoneNumber);
Serial.print(F("Message: "));
Serial.println(message);
gsmSerial.print("AT+CMGF=1\r\n"); // Set SMS to text mode
delay(1000);
gsmSerial.print("AT+CMGS=\""); // Start sending SMS
gsmSerial.print(phoneNumber);
gsmSerial.print("\"\r\n");
delay(1000);
gsmSerial.print(message); // Send the message content
delay(100);
gsmSerial.print((char)26); // End with Ctrl+Z
delay(3000); // Wait for SMS to send
while (gsmSerial.available()) {
Serial.write(gsmSerial.read()); // Read any response from GSM module
}
Serial.println(F("\nSMS Send Attempt Complete."));
}
// --- Helper function to log data to SD Card ---
void logToSDCard(const char* data) {
File dataFile = SD.open("/gps_log.txt", FILE_APPEND); // Open file in append mode
if (dataFile) {
dataFile.println(data);
dataFile.close();
Serial.println(F("Data logged to SD card."));
} else {
Serial.println(F("Error opening gps_log.txt"));
}
}
void setup() {
Serial.begin(115200); // Higher baud rate for Serial Monitor
Serial.println(F("ESP32 Combined Project Init..."));
// Initialize GPS Serial2
Serial2.begin(GPS_BAUDRATE); // Serial2 for GPS UART
Serial.println(F("GPS Serial2 Initialized."));
// Initialize SD Card
if (!SD.begin(SDCARD_CS_PIN)) {
Serial.println(F("SD Card initialization failed!"));
return;
}
Serial.println(F("SD Card initialized."));
// Initialize GSM SoftwareSerial
gsmSerial.begin(GSM_BAUDRATE);
Serial.println(F("GSM SoftwareSerial Initialized."));
delay(1000); // Give GSM module time to power up
// Initialize button pin
pinMode(BUTTON_PIN, INPUT_PULLUP); // Use internal pull-up resistor
Serial.println(F("System Ready."));
}
void loop() {
// --- GPS Data Reading ---
while (Serial2.available() > 0) {
if (gps.encode(Serial2.read())) {
if (gps.location.isValid()) {
dtostrf(gps.location.lat(), 1, 6, latitudeStr); // Convert float to string
dtostrf(gps.location.lng(), 1, 6, longitudeStr);
Serial.print(F("Latitude: ")); Serial.println(latitudeStr);
Serial.print(F("Longitude: ")); Serial.println(longitudeStr);
if (gps.altitude.isValid()) {
dtostrf(gps.altitude.meters(), 1, 2, altitudeStr);
Serial.print(F("Altitude: ")); Serial.println(altitudeStr);
} else {
strcpy(altitudeStr, "INVALID");
Serial.println(F("Altitude: INVALID"));
}
} else {
strcpy(latitudeStr, "INVALID");
strcpy(longitudeStr, "INVALID");
strcpy(altitudeStr, "INVALID");
Serial.println(F("Location: INVALID"));
}
Serial.print(F("Speed: "));
if (gps.speed.isValid()) {
dtostrf(gps.speed.kmph(), 1, 2, speedStr);
Serial.print(speedStr); Serial.println(F(" km/h"));
} else {
strcpy(speedStr, "INVALID");
Serial.println(F("INVALID"));
}
Serial.print(F("GPS date&time: "));
if (gps.date.isValid() && gps.time.isValid()) {
sprintf(dateTimeStr, "%d-%02d-%02d %02d:%02d:%02d",
gps.date.year(), gps.date.month(), gps.date.day(),
gps.time.hour(), gps.time.minute(), gps.time.second());
Serial.println(dateTimeStr);
} else {
strcpy(dateTimeStr, "INVALID");
Serial.println(F("INVALID"));
}
Serial.println(); // Blank line for readability
// Log GPS data to SD card
char logEntry[200];
sprintf(logEntry, "Lat: %s, Lon: %s, Alt: %s, Speed: %s km/h, Time: %s",
latitudeStr, longitudeStr, altitudeStr, speedStr, dateTimeStr);
logToSDCard(logEntry);
}
}
// --- Check for GPS data after a delay ---
if (millis() > 5000 && gps.charsProcessed() < 10 && gps.location.isValid() == false) {
Serial.println(F("No GPS data received or invalid: check wiring and custom chip files."));
}
// --- Button Check for SMS ---
if (digitalRead(BUTTON_PIN) == LOW) { // Button is pressed (LOW due to INPUT_PULLUP)
Serial.println(F("Button pressed. Initiating SMS..."));
delay(50); // Debounce
while (digitalRead(BUTTON_PIN) == LOW); // Wait for button release
char smsMessage[160];
sprintf(smsMessage, "Current Location: Lat %s, Lon %s. Time: %s", latitudeStr, longitudeStr, dateTimeStr);
// Replace "YOUR_PHONE_NUMBER" with the actual recipient number
sendSMS("YOUR_PHONE_NUMBER", smsMessage);
}
delay(100); // Small delay to prevent busy-waiting
}