Skip to content

Commit 3cbed77

Browse files
committed
feat: Add GPS support (v1.6.00) and new menu system
1 parent 0ff5521 commit 3cbed77

File tree

5 files changed

+185
-26
lines changed

5 files changed

+185
-26
lines changed

platformio.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ lib_deps =
2525
greiman/SdFat @ ^2.2.3
2626
https://github.com/sparkfun/SparkFun_SGP4_Arduino_Library.git
2727
adafruit/Adafruit NeoPixel @ ^1.12.0
28+
mikalhart/TinyGPSPlus @ ^1.0.3

src/config.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include <M5GFX.h>
55

66
// App Version
7-
#define APP_VERSION "v1.5.00"
7+
#define APP_VERSION "v1.6.00"
88

99
// ---------- Colors ----------
1010
#define COL_BG 0x0000 // Black
@@ -14,6 +14,14 @@
1414
#define COL_SAT_PATH 0x07E0 // Green for radar path
1515
#define COL_SAT_NOW 0xF800 // Red for current pos
1616

17+
// ---------- GPS Module (CAP LoRa868) ----------
18+
#define GPS_RX_PIN 15 // ESP32 RX (Receives from GPS TX)
19+
#define GPS_TX_PIN 13 // ESP32 TX (Sends to GPS RX)
20+
#define GPS_BAUD 115200
21+
22+
// Shared Globals
23+
extern bool useGpsModule; // New config flag
24+
1725
// ---------- Geometry ----------
1826
#define FRAME_MARGIN 5
1927
#define TEXT_LEFT 22
@@ -29,7 +37,7 @@
2937
// ----------LEDs------------
3038
#define LED_PIN 21 // M5StampS3 RGB LED
3139
#define LED_COUNT 1
32-
#define LED_BRIGHTNESS 20 // Keep it low (0-255) to save power/eyes
40+
#define LED_BRIGHTNESS 200 // Keep it low (0-255) to save power/eyes
3341

3442
// ---------- Settings ----------
3543
#define ISS_TLE_PATH "/apps/iss_tracker/iss.tle"

src/main.cpp

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
#include <Preferences.h>
66
#include <time.h>
77
#include <Adafruit_NeoPixel.h>
8+
#include <TinyGPS++.h>
89

910
#include "config.h"
1011
#include "orbit.h"
1112
#include "ui.h"
1213
#include "credentials.h"
1314
#include "iss_icon.h"
1415

16+
1517
// --- GLOBALS ---
1618
M5Canvas canvas(&M5Cardputer.Display);
1719
Preferences prefs;
@@ -27,6 +29,10 @@ int tzOffsetHours = -6;
2729
String satName = "";
2830
bool tleParsedOK = false;
2931

32+
TinyGPSPlus gps;
33+
HardwareSerial gpsSerial(1); // Use UART 1
34+
bool useGpsModule = false;
35+
3036
// --- LED State Variables ---
3137
bool wasVisible = false; // Tracks state from previous loop
3238
bool losTimerActive = false; // Are we currently showing the Red LOS light?
@@ -47,6 +53,7 @@ enum Screen {
4753
SCREEN_MENU_WIFI,
4854
SCREEN_MENU_SAT,
4955
SCREEN_MENU_LOC,
56+
SCREEN_GPS_INFO,
5057

5158
SCREEN_COUNT // Keep this for the G0 cycling logic
5259
};
@@ -162,10 +169,16 @@ void setup() {
162169
obsLonDeg = prefs.getDouble("lon", obsLonDeg);
163170
minElevation = prefs.getInt("minEl", DEFAULT_MIN_EL);
164171
tzOffsetHours = prefs.getInt("tzOffset", -6);
172+
useGpsModule = prefs.getBool("useGps", false); // Load preference
165173
prefs.end();
166174

167175
configTime(tzOffsetHours * 3600, 0, "pool.ntp.org");
168176

177+
// Init GPS Serial if enabled
178+
if (useGpsModule) {
179+
gpsSerial.begin(GPS_BAUD, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
180+
}
181+
169182
// Load TLE
170183
String localTle = readFileFromSD(ISS_TLE_PATH);
171184
if (localTle != "ERROR") {
@@ -204,6 +217,23 @@ void setup() {
204217
void loop() {
205218
M5Cardputer.update();
206219

220+
// --- GPS PARSING ---
221+
if (useGpsModule) {
222+
while (gpsSerial.available() > 0) {
223+
gps.encode(gpsSerial.read());
224+
}
225+
if (gps.location.isUpdated()) {
226+
obsLatDeg = gps.location.lat();
227+
obsLonDeg = gps.location.lng();
228+
setupOrbitLocation(obsLatDeg, obsLonDeg); // Update SGP4 instantly
229+
230+
// Optional: Sync system time from GPS if valid
231+
if (gps.time.isValid() && gps.date.isValid() && gps.time.age() < 1000) {
232+
// You could implement setTime() here if WiFi NTP fails!
233+
}
234+
}
235+
}
236+
207237
// --- 1. KEYBOARD INPUT HANDLING ---
208238
if (M5Cardputer.Keyboard.isChange() && M5Cardputer.Keyboard.isPressed()) {
209239
Keyboard_Class::KeysState k = M5Cardputer.Keyboard.keysState();
@@ -220,6 +250,9 @@ void loop() {
220250
currentScreen == SCREEN_MENU_LOC) {
221251
currentScreen = SCREEN_MENU_MAIN; // Submenu -> Main Menu
222252
}
253+
else if (currentScreen == SCREEN_GPS_INFO) { // <--- Add this
254+
currentScreen = SCREEN_MENU_LOC; // Back to Loc Menu
255+
}
223256
else if (currentScreen == SCREEN_MENU_MAIN) {
224257
currentScreen = SCREEN_HOME; // Main Menu -> Home
225258
}
@@ -291,26 +324,58 @@ void loop() {
291324
}
292325
}
293326
// LOCATION MENU
294-
else if (currentScreen == SCREEN_MENU_LOC) {
327+
else if (currentScreen == SCREEN_MENU_LOC) {
295328
for (auto c : k.word) {
329+
// TOGGLE GPS MODE
296330
if (c == '1') {
297-
String l = textInput(String(obsLatDeg), "Lat:");
298-
obsLatDeg = l.toFloat();
331+
useGpsModule = !useGpsModule;
332+
333+
// Save Preference
299334
prefs.begin("iss_cfg", false);
300-
prefs.putDouble("lat", obsLatDeg);
335+
prefs.putBool("useGps", useGpsModule);
301336
prefs.end();
302-
setupOrbitLocation(obsLatDeg, obsLonDeg);
337+
338+
// Start/Stop Serial
339+
if (useGpsModule) {
340+
gpsSerial.begin(GPS_BAUD, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
341+
} else {
342+
// Keeping serial open is fine, or you can gpsSerial.end();
343+
}
303344
needsRedraw = true;
304345
}
305-
if (c == '2') {
306-
String lo = textInput(String(obsLonDeg), "Lon:");
307-
obsLonDeg = lo.toFloat();
308-
prefs.begin("iss_cfg", false);
309-
prefs.putDouble("lon", obsLonDeg);
310-
prefs.end();
311-
setupOrbitLocation(obsLatDeg, obsLonDeg);
346+
347+
// EDIT LAT (Only if Manual)
348+
if (c == '2') {
349+
if (!useGpsModule) {
350+
String l = textInput(String(obsLatDeg), "Lat:");
351+
obsLatDeg = l.toFloat();
352+
prefs.begin("iss_cfg", false);
353+
prefs.putDouble("lat", obsLatDeg);
354+
prefs.end();
355+
setupOrbitLocation(obsLatDeg, obsLonDeg);
356+
}
357+
needsRedraw = true;
358+
}
359+
360+
// EDIT LON (Only if Manual)
361+
if (c == '3') {
362+
if (!useGpsModule) {
363+
String lo = textInput(String(obsLonDeg), "Lon:");
364+
obsLonDeg = lo.toFloat();
365+
prefs.begin("iss_cfg", false);
366+
prefs.putDouble("lon", obsLonDeg);
367+
prefs.end();
368+
setupOrbitLocation(obsLatDeg, obsLonDeg);
369+
}
312370
needsRedraw = true;
313371
}
372+
373+
if (c == '4') {
374+
if (useGpsModule) {
375+
currentScreen = SCREEN_GPS_INFO;
376+
needsRedraw = true;
377+
}
378+
}
314379
}
315380
}
316381
}
@@ -370,14 +435,18 @@ void loop() {
370435
case SCREEN_HOME: drawHomeScreen(canvas); break;
371436
case SCREEN_LIVE: drawLiveScreen(canvas, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); break;
372437
case SCREEN_RADAR: drawRadarScreen(canvas, unixtime); break;
373-
case SCREEN_PASS: drawPassScreen(canvas, unixtime, minElevation); break;
374-
438+
case SCREEN_PASS:
439+
drawPassScreen(canvas, unixtime, minElevation, obsLatDeg, obsLonDeg);
440+
break;
375441
case SCREEN_MENU_MAIN: drawMainMenu(canvas); break;
376442
case SCREEN_MENU_WIFI: drawWifiMenu(canvas, wifiSsid); break;
377443
case SCREEN_MENU_SAT: drawSatMenu(canvas, minElevation); break;
378-
case SCREEN_MENU_LOC: drawLocationMenu(canvas, obsLatDeg, obsLonDeg); break;
379-
444+
case SCREEN_MENU_LOC:
445+
drawLocationMenu(canvas, obsLatDeg, obsLonDeg, useGpsModule, gps.location.isValid(), gps.satellites.value());
446+
break;
447+
case SCREEN_GPS_INFO: drawGpsInfoScreen(canvas, gps); break;
380448
default: break;
449+
381450
}
382451
canvas.pushSprite(0,0);
383452
needsRedraw = false;

src/ui.cpp

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ void drawRadarScreen(M5Canvas &d, unsigned long currentUnix) {
142142
}
143143
}
144144

145-
void drawPassScreen(M5Canvas &d, unsigned long currentUnix, int minEl) {
145+
void drawPassScreen(M5Canvas &d, unsigned long currentUnix, int minEl, double lat, double lon) {
146146
drawFrame(d, "Pass Prediction");
147147
int y = TEXT_TOP + 25;
148148

@@ -153,15 +153,27 @@ void drawPassScreen(M5Canvas &d, unsigned long currentUnix, int minEl) {
153153
static PassDetails nextPass;
154154
static unsigned long lastCalc = 0;
155155
static int lastMinEl = -1;
156+
// ADD CACHING VARIABLES FOR LOCATION
157+
static double lastLat = -999;
158+
static double lastLon = -999;
156159

157-
if (currentUnix - lastCalc > 10000 || nextPass.aosUnix < currentUnix || lastMinEl != minEl) {
160+
// UPDATE THE CHECK CONDITION:
161+
// We now recalc if: Time expired OR Pass passed OR MinEl changed OR Location changed
162+
if (currentUnix - lastCalc > 10000 ||
163+
nextPass.aosUnix < currentUnix ||
164+
lastMinEl != minEl ||
165+
lastLat != lat ||
166+
lastLon != lon) {
167+
158168
d.setCursor(TEXT_LEFT, y);
159169
d.println("Calculating...");
160170
d.pushSprite(0,0);
161171

162172
if (predictNextPass(currentUnix, nextPass, minEl)) {
163173
lastCalc = currentUnix;
164174
lastMinEl = minEl;
175+
lastLat = lat; // Update Cache
176+
lastLon = lon; // Update Cache
165177
drawFrame(d, "Pass Prediction");
166178
} else {
167179
drawFrame(d, "Pass Prediction");
@@ -174,6 +186,7 @@ void drawPassScreen(M5Canvas &d, unsigned long currentUnix, int minEl) {
174186
}
175187
}
176188

189+
// ... (The rest of the drawing code remains exactly the same) ...
177190
time_t rawAos = nextPass.aosUnix;
178191
struct tm * taos = localtime(&rawAos);
179192

@@ -267,14 +280,80 @@ void drawSatMenu(M5Canvas &d, int minEl) {
267280
d.printf("Inc: %.3f Ecc: %.4f", tleIncDeg, tleEcc);
268281
}
269282

270-
void drawLocationMenu(M5Canvas &d, double lat, double lon) {
283+
void drawLocationMenu(M5Canvas &d, double lat, double lon, bool useGps, bool gpsFix, int sats) {
271284
drawFrame(d, "Location Setup");
272285
int y = TEXT_TOP + 20;
286+
287+
// Item 1: Source
288+
d.setCursor(TEXT_LEFT, y);
289+
d.setTextColor(useGps ? COL_SAT_PATH : COL_TEXT);
290+
d.printf("1) Source: [%s]\n", useGps ? "GPS Module" : "MANUAL");
291+
d.setTextColor(COL_TEXT);
292+
y += LINE_SPACING;
293+
294+
// Item 2 & 3: Lat/Lon
295+
d.setCursor(TEXT_LEFT, y);
296+
if (useGps) d.setTextColor(COL_ACCENT); // Dim if GPS active
297+
d.printf("2) Set Lat: %.4f\n", lat);
298+
y += LINE_SPACING;
273299

274300
d.setCursor(TEXT_LEFT, y);
275-
d.printf("1) Set Latitude\n (Cur: %.4f)\n", lat);
276-
y += LINE_SPACING * 2; // Extra space
301+
d.printf("3) Set Lon: %.4f\n", lon);
302+
d.setTextColor(COL_TEXT);
303+
y += LINE_SPACING;
304+
305+
// Item 4: Details Link
306+
d.setCursor(TEXT_LEFT, y);
307+
if (!useGps) d.setTextColor(COL_ACCENT);
308+
d.println("4) GPS Status Info >");
309+
d.setTextColor(COL_TEXT);
310+
311+
// Footer Status
312+
d.setCursor(TEXT_LEFT, d.height() - 25);
313+
if (useGps) {
314+
if (gpsFix) {
315+
d.setTextColor(COL_SAT_PATH); // Green
316+
d.printf("GPS Acquired (%d Sats)", sats);
317+
} else {
318+
d.setTextColor(COL_SAT_NOW); // Red/Orange
319+
d.print("GPS: Searching...");
320+
}
321+
} else {
322+
d.setTextColor(COL_ACCENT);
323+
d.print("Mode: Fixed Location");
324+
}
325+
}
326+
327+
void drawGpsInfoScreen(M5Canvas &d, TinyGPSPlus &gps) {
328+
drawFrame(d, "GPS Details");
329+
int y = TEXT_TOP + 20;
330+
331+
d.setCursor(TEXT_LEFT, y);
332+
d.printf("Sats: %d HDOP: %.1f\n", gps.satellites.value(), gps.hdop.hdop());
333+
y += LINE_SPACING;
334+
335+
d.setCursor(TEXT_LEFT, y);
336+
d.printf("Lat: %.5f\n", gps.location.lat());
337+
y += LINE_SPACING;
338+
339+
d.setCursor(TEXT_LEFT, y);
340+
d.printf("Lon: %.5f\n", gps.location.lng());
341+
y += LINE_SPACING;
342+
343+
d.setCursor(TEXT_LEFT, y);
344+
d.printf("Alt: %.1f m\n", gps.altitude.meters());
345+
y += LINE_SPACING;
346+
347+
d.setCursor(TEXT_LEFT, y);
348+
d.printf("Time: %02d:%02d:%02d UTC\n", gps.time.hour(), gps.time.minute(), gps.time.second());
277349

350+
y += LINE_SPACING;
278351
d.setCursor(TEXT_LEFT, y);
279-
d.printf("2) Set Longitude\n (Cur: %.4f)\n", lon);
352+
if (gps.location.isValid()) {
353+
d.setTextColor(COL_SAT_PATH);
354+
d.print("STATUS: 3D FIX");
355+
} else {
356+
d.setTextColor(COL_SAT_NOW);
357+
d.print("STATUS: NO FIX");
358+
}
280359
}

src/ui.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#pragma once
22
#include <M5GFX.h>
33
#include <WiFi.h>
4+
#include <TinyGPS++.h>
45

56
void drawHomeScreen(M5Canvas &d);
67
void drawLiveScreen(M5Canvas &d, int year, int mon, int day, int hr, int min);
78
void drawRadarScreen(M5Canvas &d, unsigned long currentUnix);
8-
void drawPassScreen(M5Canvas &d, unsigned long currentUnix, int minEl);
9+
void drawPassScreen(M5Canvas &d, unsigned long currentUnix, int minEl, double lat, double lon);
910

1011
void drawMainMenu(M5Canvas &d);
1112
void drawWifiMenu(M5Canvas &d, String storedSsid);
1213
void drawSatMenu(M5Canvas &d, int minEl);
13-
void drawLocationMenu(M5Canvas &d, double lat, double lon);
14+
void drawLocationMenu(M5Canvas &d, double lat, double lon, bool useGps, bool gpsFix, int sats);
15+
void drawGpsInfoScreen(M5Canvas &d, TinyGPSPlus &gps);

0 commit comments

Comments
 (0)