International Space Station Tracker With Map Visuals
by saurabhs1333 in Circuits > Microcontrollers
19 Views, 0 Favorites, 0 Comments
International Space Station Tracker With Map Visuals
ISS Tracker is an embedded systems project built around the ESP32 that provides real-time tracking of the International Space Station as it orbits Earth at over 27,000 km/h. The device connects to the internet via Wi-Fi, periodically fetching live orbital data from a public API and translating raw coordinates into a meaningful visual representation. The ISS’s current latitude and longitude are plotted on a world map rendered on an OLED display, allowing users to see its position relative to the planet at a glance.
Alongside the graphical view, a 16×2 I²C LCD presents clear textual information such as coordinates, connection status, and system messages, ensuring readability even from a distance. An onboard buzzer adds audible feedback for key events, enhancing interactivity without overcomplicating the design. Beyond tracking the ISS itself, the project serves as a practical demonstration of real-world IoT concepts, including wireless networking, API consumption, data parsing, and multi-display coordination on a resource-constrained microcontroller. The ISS Tracker is intended as both an educational tool and a foundation for further experimentation in embedded networking and space-related visualization.
Supplies
Component List
ESP32 38P Type-C CP2102
- Description: A versatile micro-controller with Wi-Fi and Bluetooth capabilities, used for processing and controlling the circuit.
- Pins: 5V, CMD, SD3, SD2, G13, GND, G12, G14, G27, G26, G25, G23, G32, G35, G34, SN, SP, EN, 3V3, CLK, SD0, SD1, G15, G2, G0, G4, G16, G17, G5, G18, G19, G21, RXD, TXD, G22
OLED Display
- Description: A small, efficient display used for visual output, connected via I2C.
- Pins: GND, VCC, SCL, SDA
LCD 16x2 Attached I2C
- Description: A 16x2 character display module with I2C interface, used for displaying text.
- Pins: GND, VCC, SDA, SCL
Check All the Components
Component List (yeah its the same thing, just check it guys)
ESP32 38P Type-C CP2102
- Description: A versatile microcontroller with Wi-Fi and Bluetooth capabilities, used for processing and controlling the circuit.
- Pins: 5V, CMD, SD3, SD2, G13, GND, G12, G14, G27, G26, G25, G23, G32, G35, G34, SN, SP, EN, 3V3, CLK, SD0, SD1, G15, G2, G0, G4, G16, G17, G5, G18, G19, G21, RXD, TXD, G22
OLED Display
- Description: A small, efficient display used for visual output, connected via I2C.
- Pins: GND, VCC, SCL, SDA
LCD 16x2 Attached I2C
- Description: A 16x2 character display module with I2C interface, used for displaying text.
- Pins: GND, VCC, SDA, SCL
Bread Board or Proto-board
- Description: To make connections. Either directly put it on breadboard or solder on protoboard for more permanent model.
You are good to go...
Connect the Components
Wiring Details (make sure its correct)
ESP32 38P Type-C CP2102
- G21: Connected to SDA of both OLED and LCD 16x2.
- G22: Connected to SCL of both OLED and LCD 16x2.
- 3V3: Connected to VCC of OLED.
- 5V: Connected to VCC of LCD 16x2.
- GND: Connected to GND of both OLED and LCD 16x2.
OLED Display
- SDA: Connected to G21 of ESP32.
- SCL: Connected to G22 of ESP32.
- VCC: Connected to 3V3 of ESP32.
- GND: Connected to GND of ESP32.
LCD 16x2 Attached I2C
- SDA: Connected to G21 of ESP32.
- SCL: Connected to G22 of ESP32.
- VCC: Connected to 5V of ESP32.
- GND: Connected to GND of ESP32
Note: Yeah lcd and oled sda and scl are on same pins cause they get different addresses so, all of this works out.
Soldering is better option ngl.
Upload the Code on the Esp32
CODE:
#include <WiFi.h>
#include <HTTPClient.h>
#include <Arduino_JSON.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <LiquidCrystal_I2C.h>
// ========== CONFIG ==========
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define BUZZER_PIN 15
#define ISS_UPDATE_INTERVAL 5000 // 5 sec (balance between API limits & freshness)
#define DISPLAY_UPDATE_INTERVAL 500 // 500ms for smooth visuals
#define DOT_BLINK_SPEED 600
#define BUZZER_INTERVAL 500
// WiFi Credentials (consider using NVS for flexibility)
const char* SSID = "wifi ssid/name";
const char* PASSWORD = "wifi password";
const char* ISS_API = "http://api.open-notify.org/iss-now.json";
// ========== DISPLAY OBJECTS ==========
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
LiquidCrystal_I2C lcd(0x27, 16, 2);
// ========== WORLD MAP BITMAP ==========
const unsigned char worldMap[] PROGMEM = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x80, 0x80, 0x07, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xbf, 0x4e, 0x00, 0x07, 0xff, 0xff, 0xef, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xfc, 0x7b, 0xbf, 0xc0, 0x0f, 0xff, 0xff, 0xdf, 0xc0, 0x0d, 0xff, 0xff, 0xff, 0xff,
0xff, 0xcf, 0xfe, 0x1b, 0x07, 0xc0, 0x1f, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff,
0xfe, 0x00, 0x00, 0xb0, 0x63, 0xc0, 0xff, 0xfe, 0x03, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1f, 0xff,
0xf8, 0x00, 0x10, 0x01, 0xc3, 0x87, 0xe7, 0xfc, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff,
0xf0, 0x00, 0x00, 0x0f, 0xef, 0x8f, 0xff, 0xf0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff,
0xe0, 0x80, 0x00, 0x3f, 0x3f, 0xdf, 0xff, 0xf0, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3f, 0xff,
0xf7, 0xf0, 0x00, 0x3e, 0x0f, 0xff, 0xff, 0x7d, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x7f, 0xff,
0xff, 0xe0, 0x00, 0x0e, 0x0f, 0xff, 0xff, 0x7f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x3f, 0xff,
0xff, 0xe0, 0x02, 0x08, 0x07, 0xff, 0xfd, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xbf, 0xff,
0xff, 0xe0, 0x00, 0x00, 0x07, 0xff, 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff,
0xff, 0xf0, 0x00, 0x80, 0x77, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff,
0xff, 0xe0, 0x00, 0x40, 0x7f, 0xff, 0xff, 0x80, 0x01, 0x08, 0x04, 0x00, 0x00, 0x07, 0xff, 0xff,
0xff, 0xc0, 0x00, 0x81, 0xff, 0xff, 0xff, 0x8a, 0x0f, 0x18, 0x00, 0x00, 0x00, 0x06, 0xff, 0xff,
0xff, 0x80, 0x00, 0x03, 0xff, 0xff, 0xfc, 0x3d, 0x8d, 0x88, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff,
0xff, 0x80, 0x00, 0x0f, 0xff, 0xff, 0xfc, 0x7e, 0xb0, 0x0c, 0x00, 0x00, 0x01, 0xdf, 0x7f, 0xff,
0xff, 0x80, 0x00, 0x1f, 0xff, 0xff, 0xfc, 0xfd, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x7f, 0xff,
0xff, 0x80, 0x00, 0x1f, 0xff, 0xff, 0xfe, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xff, 0xff,
0xff, 0xc0, 0x00, 0x7f, 0xff, 0xff, 0xf8, 0x01, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x3b, 0xff, 0xff,
0xff, 0xc0, 0x04, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff,
0xff, 0xd0, 0x3e, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff,
0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x01, 0x03, 0x80, 0x00, 0x00, 0x3f, 0xff, 0xff,
0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x80, 0x78, 0x00, 0x00, 0x5f, 0xff, 0xff,
0xff, 0xf8, 0x77, 0x7f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x80, 0x7c, 0x06, 0x01, 0xff, 0xff, 0xff,
0xff, 0xfc, 0x67, 0xef, 0xff, 0xff, 0xc0, 0x00, 0x00, 0xc0, 0x7e, 0x0f, 0x05, 0xff, 0xff, 0xff,
0xff, 0xfe, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x61, 0xfe, 0x1f, 0x03, 0xef, 0xff, 0xff,
0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x23, 0xff, 0x3f, 0xc3, 0xef, 0xff, 0xff,
0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x1f, 0xff, 0x3f, 0xd1, 0xff, 0xff, 0xff,
0xff, 0xff, 0xfb, 0x81, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x03, 0xff, 0xbf, 0xfb, 0xff, 0xff, 0xff,
0xff, 0xff, 0xfd, 0x00, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x03, 0xff, 0xdf, 0xdf, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xf8, 0x60, 0x00, 0x07, 0xff, 0xff, 0xef, 0xdf, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xf8, 0x00, 0x07, 0xff, 0xff, 0xd7, 0x3f, 0xff, 0xff,
0xff, 0xff, 0xfe, 0x00, 0x0f, 0xff, 0xff, 0xf8, 0x00, 0x1f, 0xff, 0xff, 0xce, 0x1f, 0xff, 0xff,
0xff, 0xff, 0xfe, 0x00, 0x07, 0xff, 0xff, 0xf8, 0x02, 0x1f, 0xff, 0xff, 0xe6, 0x3f, 0x7f, 0xff,
0xff, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x00, 0x3f, 0xff, 0xff, 0xf3, 0x2f, 0x87, 0xff,
0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0xff, 0xfb, 0xff, 0xe1, 0xff,
0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0xff, 0xfc, 0x7f, 0xe1, 0xff,
0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff,
0xff, 0xff, 0xff, 0x80, 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0x37, 0xff,
0xff, 0xff, 0xff, 0x80, 0x00, 0x7f, 0xff, 0xfc, 0x00, 0x3b, 0xff, 0xff, 0xff, 0xfa, 0x37, 0xff,
0xff, 0xff, 0xff, 0xe0, 0x00, 0x7f, 0xff, 0xfc, 0x00, 0x63, 0xff, 0xff, 0xff, 0xf0, 0x13, 0xff,
0xff, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xfc, 0x00, 0xe7, 0xff, 0xff, 0xff, 0xe0, 0x03, 0xff,
0xff, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xfe, 0x01, 0xe7, 0xff, 0xff, 0xff, 0x80, 0x01, 0xff,
0xff, 0xff, 0xff, 0xf0, 0x03, 0xff, 0xff, 0xfe, 0x00, 0xe7, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff,
0xff, 0xff, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff,
0xff, 0xff, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xfe, 0x03, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0xff,
0xff, 0xff, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff,
0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xfe, 0x1e, 0x03, 0xff,
0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x07, 0xff,
0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff,
0xff, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb,
0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xef,
0xff, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f,
0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
0xff, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
// ========== GLOBAL STATE ==========
struct ISSData {
float latitude;
float longitude;
unsigned long lastUpdate;
bool isValid;
} issData = {0, 0, 0, false};
unsigned long prevISS = 0;
unsigned long prevDisplay = 0;
unsigned long prevBuzz = 0;
bool dotVisible = true;
bool wifiConnected = false;
// ========== FUNCTION DECLARATIONS ==========
void initHardware();
void showIntro();
void connectWiFi();
void updateISS();
void drawISS(float lat, float lon);
void updateLCD(float lat, float lon);
void handleBuzzer();
// ========== SETUP ==========
void setup() {
Serial.begin(115200);
delay(500);
initHardware();
showIntro();
connectWiFi();
}
// ========== LOOP ==========
void loop() {
unsigned long now = millis();
// Update ISS data every interval
if (now - prevISS >= ISS_UPDATE_INTERVAL) {
prevISS = now;
updateISS();
}
// Update displays only when needed (not every millisecond)
if (now - prevDisplay >= DISPLAY_UPDATE_INTERVAL) {
prevDisplay = now;
if (issData.isValid) {
drawISS(issData.latitude, issData.longitude);
updateLCD(issData.latitude, issData.longitude);
}
}
// Handle buzzer with timing
handleBuzzer();
delay(10); // Prevent watchdog issues
}
// ========== HARDWARE INITIALIZATION ==========
void initHardware() {
lcd.init();
lcd.backlight();
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED init failed!");
while (1);
}
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
}
// ========== INTRO SCREEN ==========
void showIntro() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(15, 10);
display.println("ISS");
display.setCursor(10, 30);
display.println("TRACKER");
display.setTextSize(1);
display.setCursor(25, 55);
display.println("Initializing...");
display.display();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ISS Tracker");
lcd.setCursor(0, 1);
lcd.print("Initializing...");
delay(2000);
}
// ========== WIFI CONNECTION ==========
void connectWiFi() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WiFi: Connecting");
// Disable power saving for more reliable connection
WiFi.setSleep(false);
WiFi.mode(WIFI_STA);
WiFi.disconnect(true); // Turn off WiFi first
delay(500);
Serial.println("\n--- WiFi Debug Info ---");
Serial.printf("SSID: %s\n", SSID);
Serial.printf("Signal Strength: %d\n", WiFi.RSSI());
WiFi.begin(SSID, PASSWORD);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 30) {
delay(400);
Serial.printf("Attempt %d, Status: %d\n", attempts, WiFi.status());
lcd.setCursor(attempts % 16, 1);
lcd.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
wifiConnected = true;
String ip = WiFi.localIP().toString();
Serial.println("\n--- WiFi Connected ---");
Serial.printf("IP: %s\n", ip.c_str());
Serial.printf("RSSI: %d\n", WiFi.RSSI());
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WiFi: OK");
lcd.setCursor(0, 1);
lcd.print(ip.c_str());
delay(2000);
} else {
Serial.println("WiFi connection failed!");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WiFi: Failed");
lcd.setCursor(0, 1);
lcd.print("Retry in 10s");
wifiConnected = false;
delay(10000);
connectWiFi(); // Retry
}
}
// ========== UPDATE ISS POSITION ==========
void updateISS() {
if (WiFi.status() != WL_CONNECTED) {
wifiConnected = false;
issData.isValid = false;
Serial.println("WiFi not connected");
return;
}
HTTPClient http;
http.setTimeout(8000);
http.setConnectTimeout(8000);
// Use HTTP (not HTTPS) - simpler, faster
bool httpBegin = http.begin("http://api.open-notify.org/iss-now.json");
if (!httpBegin) {
Serial.println("HTTP begin failed");
issData.isValid = false;
http.end();
return;
}
// Add headers
http.addHeader("Connection", "close");
http.addHeader("User-Agent", "ESP32-ISS-Tracker");
Serial.println("Sending HTTP request...");
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("HTTP Code: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK || httpCode == 200) {
String payload = http.getString();
Serial.printf("Payload length: %d\n", payload.length());
Serial.printf("Payload: %.100s\n", payload.c_str());
JSONVar obj = JSON.parse(payload);
if (obj.hasOwnProperty("iss_position")) {
const char* latPtr = (const char*)obj["iss_position"]["latitude"];
const char* lonPtr = (const char*)obj["iss_position"]["longitude"];
if (latPtr && lonPtr) {
issData.latitude = atof(latPtr);
issData.longitude = atof(lonPtr);
issData.lastUpdate = millis();
issData.isValid = true;
Serial.printf("✓ ISS: %.2f, %.2f\n", issData.latitude, issData.longitude);
} else {
Serial.println("Latitude/Longitude missing");
issData.isValid = false;
}
}
} else {
Serial.println("JSON parse failed");
issData.isValid = false;
}
} else {
Serial.printf("HTTP Error: %d\n", httpCode);
issData.isValid = false;
}
http.end();
}
// ========== DRAW ISS ON OLED ==========
void drawISS(float lat, float lon) {
display.clearDisplay();
display.drawBitmap(0, 0, worldMap, 128, 64, WHITE);
// Map coordinates to pixel location
int x = map(lon, -180, 180, 0, 128);
int y = map(lat, 90, -90, 0, 64);
// Clamp values to screen bounds
x = constrain(x, 0, 127);
y = constrain(y, 0, 63);
// Blinking effect
static unsigned long lastBlink = 0;
if (millis() - lastBlink > DOT_BLINK_SPEED) {
lastBlink = millis();
dotVisible = !dotVisible;
}
if (dotVisible) {
display.fillCircle(x, y, 2, WHITE);
}
// Display timestamp
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.printf("Update: %lus ago", (millis() - issData.lastUpdate) / 1000);
display.display();
}
// ========== UPDATE LCD DISPLAY ==========
void updateLCD(float lat, float lon) {
static float prevLat = 0;
static float prevLon = 0;
// Only update if values changed significantly (>0.1 degrees)
if (fabs(lat - prevLat) > 0.1 || fabs(lon - prevLon) > 0.1) {
prevLat = lat;
prevLon = lon;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("LAT:");
lcd.print(lat, 2);
lcd.setCursor(0, 1);
lcd.print("LON:");
lcd.print(lon, 2);
}
}
// ========== BUZZER CONTROL ==========
void handleBuzzer() {
static unsigned long lastBuzzToggle = 0;
if (millis() - lastBuzzToggle >= BUZZER_INTERVAL) {
lastBuzzToggle = millis();
static bool buzzing = false;
buzzing = !buzzing;
if (buzzing && issData.isValid) {
tone(BUZZER_PIN, 1200, BUZZER_INTERVAL - 50);
} else {
noTone(BUZZER_PIN);
}
}
}