-
Notifications
You must be signed in to change notification settings - Fork 0
/
GPS_CLOCK_triple_BST_WCYD2USB.ino
693 lines (585 loc) · 30.5 KB
/
GPS_CLOCK_triple_BST_WCYD2USB.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
/**************************************************************************
Title: GPS Clock with TFT display
Author: Bruce E. Hall, w8bh.net
Date: 12 May 2022
Hardware: Blue Pill Microcontroller, 2.8" ILI9341 TFT display,
Adafruit "Ultimate GPS" module v3
Software: Arduino IDE version 2.3.2
ESP32 Board 3.0.4
TFT_eSPI library 2.5.43
Time library 1.6.1
TinyGPSPlus library 1.1.0
Timezone library 1.2.4
Legal: Copyright (c) 2022 Bruce E. Hall.
Open Source under the terms of the MIT License.
Description: GPS Clock, accurate to within a few microseconds!
Set your local time zone and daylight saving rules at top
of the sketch under TimeChangeRules: one rule for standard
time and one rule for daylight saving time.
Choose Local/UTC time and 12/24hr format in defines near
top of the sketch. These can be toggled during runtime via
touch presses, if your display module supports touch.
A few notes about the touch control:
This sketch contains 3 'screens' of information.
Screen 0 is time/date/location/grid display;
Screen 1 is a dual time/date display (local & utc)
Screen 2 is latitude/longitude/altitude/speed/bearing.
* To go from screen#0 to #1, touch the location at bottom right
* To go from screen#0 to #2, touch the time
* Touch anywhere on screens #1 and #2 to return to screen 0.
* On Screen #0, touch the timezone to switch between UTC/local.
* On screen #0, touch the AM/PM marker to toggle 12/24 hour modes.
Check out the DEFINES near the top of sketch for some time formatting
options.
Note: I used Imperial units (altitude in feet, speed in miles per hour).
If you, like most of the world, prefers metric units, try using the following:
alt = gps.altitude.meters
speed = gps.speed.kmph (kilometers per hour)
speed = gps.speed.mps (meters per second)
Also, change the year/month/date order in showDate() to suit your preference.
************************************************************************************************
PORTED BY CoolmdXi GITHUB.
SEPTEMBER 2024 Extensive rewrite to use CYD ESP32 2.8" Module
Connect GPS "Tx" line to CYD GPIO 22 (Serial RXD2) CN1 connector
Connect GPS "PPS" line to CYD GPIO 27 CN1 connector
Ground and 3.3V also on CN1 connector
Removed battery and tone sections and altered touch to use CYD screen
This is a rewrite and Port of Bruce Hall's GPS Triple clock, to run on a CYD 2.8" touchscreen(R) board.
It has been designed so that the GPS Board can be connected to CN1 socket and no soldering is required
to the CYD itself.
**************************************************************************/
#include <HardwareSerial.h>//////////////////////////////////////////////
#include <TFT_eSPI.h> // https://github.com/Bodmer/TFT_eSPI
#include <TimeLib.h> // https://github.com/PaulStoffregen/Time
#include <TinyGPS++.h> // https://github.com/mikalhart/TinyGPSPlus
#include <Timezone.h> // https://github.com/JChristensen/Timezone
#include <XPT2046_Touchscreen.h>
#include <Wire.h>
#include <SPI.h>
//TimeChangeRule CST = {"CST ", Second, Sun, Mar, 2, -300};
//TimeChangeRule CDT = {"CDT ", First, Sun, Nov, 2, -360};
//TimeChangeRule* tz;
//Timezone myTZ(CST ,CDT);
//TimeChangeRule EDT = { "EDT ", Second, Sun, Mar, 2, -240 };
//TimeChangeRule EST= { "EST ", First, Sun, Nov, 2, -300 };
//TimeChangeRule *tz;
//Timezone myTZ(EDT, EST);
TimeChangeRule BST = {"BST ", Last, Sun, Mar, 1, 60}; // British Summer Time
TimeChangeRule GMT = {"GMT ", Last, Sun, Oct, 2, 0}; // Standard Time
TimeChangeRule* tz; // pointer to current time change rule
Timezone myTZ(BST ,GMT); // create timezone object with rules above
// Touchscreen pins
#define XPT2046_IRQ 36 // T_IRQ Pins for touch CYD USB and 2USB and CYD 35
#define XPT2046_MOSI 32 // T_DIN ""
#define XPT2046_MISO 39 // T_OUT ""
#define XPT2046_CLK 25 // T_CLK ""
#define XPT2046_CS 33 // T_CS ""
SPIClass touchscreenSPI = SPIClass(VSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define BACKLIGHT 21 // TFT_BACKLIGHT
#define RXD2 22 //RX (UART2) on CYD TX on gps Module
#define GPS_PPS 27 // On CN1 connector CYD_2USB
#define USING_PPS true // true if GPS_PPS line connected; false otherwise.
#define BAUD_RATE 9600 // data rate of GPS module
#define GRID_SQUARE_SIZE 8 // 0 (none), 4 (EM79), 6 (EM79vr), up to 10 char
#define FIRST_SCREEN 0 // 0 = time/locn; 1= dual time; 2= location/elevation/speed
#define SYNC_MARGINAL 3600 // orange status if no sync for 3600s = 1 hour
#define SYNC_LOST 86400 // red status if no sync for 1 day
#define TITLE "GPS TIME" // shown at top of display
#define SHOW_LOCAL_TIME true // show local or UTC time at startup?
#define LOCAL_FORMAT_12HR true // local time format 12hr "11:34" vs 24hr "23:34"
#define UTC_FORMAT_12HR false // UTC time format 12 hr "11:34" vs 24hr "23:34"
#define HOUR_LEADING_ZERO false // "01:00" vs " 1:00"
#define DATE_LEADING_ZERO true // "Feb 07" vs. "Feb 7"
#define DATE_ABOVE_MONTH true // "12 Feb" vs. "Feb 12"
#define TIMECOLOR TFT_CYAN
#define DATECOLOR TFT_YELLOW
#define ZONECOLOR TFT_GREEN
#define LABEL_FGCOLOR TFT_YELLOW
#define LABEL_BGCOLOR TFT_BLUE
#define SCREEN_ORIENTATION 3 // landscape mode, should be '1' or '3' 3 usb Left
#define TOUCH_FLIP_X false // touch: left-right orientation //unused as touch has own rotation
#define TOUCH_FLIP_Y false // touch: up-down orientation
#define TOUCH_ADJUST_X 0 // touch: fine tune x-coordinate
#define TOUCH_ADJUST_Y 0 // touch: fine tune y-coordinate
void newScreen();
void setBrightness(int level);
void updateDisplay();
// ============ GLOBAL VARIABLES =====================================================
TFT_eSPI tft = TFT_eSPI(); // display object
TinyGPSPlus gps; // gps object
HardwareSerial gpsSerial(2);//allow UART2 pins to be redefined
time_t t, oldT = 0; // UTC time, latest & displayed
time_t lt, oldLt = 0; // Local time, latest & displayed
time_t lastSync = 0; // UTC time of last GPS sync
volatile byte pps = 0; // GPS one-pulse-per-second flag
bool use12hrFormat = LOCAL_FORMAT_12HR; // 12-hour vs 24-hour format?
bool useLocalTime = SHOW_LOCAL_TIME; // display local time or UTC?
int screenID = 0; // 0=time, 1=dual time, 2=location
char gridSquare[12] = { 0 }; // holds current grid square string
typedef struct {
int x; // x position (left side of rectangle)
int y; // y position (top of rectangle)
int w; // width, such that right = x+w
int h; // height, such that bottom = y+h
} region;
region rPM = { 240, 100, 70, 40 }; // AM/PM screen region
region rTZ = { 240, 60, 70, 40 }; // Time Zone screen region
region rTime = { 20, 50, 200, 140 }; // Time screen region
region rSeg = { 160, 180, 120, 40 }; // Segment screen region
region rStatus = { 20, 180, 120, 40 }; // Clock status screen region
region rTitle = { 0, 0, 310, 35 }; // Title bar screen region
region rLocn = { 180, 165, 140, 80 }; // Location screen region
region rSpkr = { 225, 0, 40, 40 }; // Location of speaker icon
// ============ GRID SQUARE ROUTINES ================================================
void getGridSquare(char *gs, float lat, float lon, const byte len = 10) {
int lon1, lon2, lon3, lon4, lon5; // GridSquare longitude components
int lat1, lat2, lat3, lat4, lat5; // GridSquare latitude components
float remainder; // temp holder for residuals
gs[0] = 0; // if input invalid, return null
lon += 180; // convert (-180,180) to (0,360)
lat += 90; // convert (-90,90) to (0,180);
if ((lon < 0) || (lon > 360)) return; // confirm good lon value
if ((lat < 0) || (lat > 180)) return; // confirm good lat value
if (len > 10) return; // allow output length 0-10 chars
remainder = lon; // Parsing Longitude coordinates:
lon1 = remainder / 20; // first: divisions of 20 degrees
remainder -= lon1 * 20; // remove 1st coord contribution
lon2 = remainder / 2; // second: divisions of 2 degrees
remainder -= lon2 * 2; // remove 2nd coord contribution
lon3 = remainder * 12; // third: divisions of 5 minutes
remainder -= lon3 / 12.0; // remove 3nd coord contribution
lon4 = remainder * 120; // forth: divisions of 30 seconds
remainder -= lon4 / 120.0; // remove 4th coord contribution
lon5 = remainder * 2880; // fifth: division of 1.25 seconds
remainder = lat; // Parsing Latitude coordinates:
lat1 = remainder / 10; // first: divisions of 10 degrees
remainder -= lat1 * 10; // remove 1st coord contribution
lat2 = remainder; // second: divisions of 1 degrees
remainder -= lat2; // remove 2nd coord contribution
lat3 = remainder * 24; // third: divisions of 2.5 minutes
remainder -= lat3 / 24.0; // remove 3rd coord contribution
lat4 = remainder * 240; // fourth: divisions of 15 seconds
remainder -= lat4 / 240.0; // remove 4th coord contribution
lat5 = remainder * 5760; // fifth: divisions of 0.625 seconds
gs[0] = lon1 + 'A'; // first coord pair are upper case alpha
gs[1] = lat1 + 'A';
gs[2] = lon2 + '0'; // followed by numbers
gs[3] = lat2 + '0';
gs[4] = lon3 + 'a'; // followed by lower case alpha
gs[5] = lat3 + 'a';
gs[6] = lon4 + '0'; // followed by numbers
gs[7] = lat4 + '0';
gs[8] = lon5 + 'A'; // followed by upper case alpha
gs[9] = lat5 + 'A';
gs[len] = 0; // set desired string length (0-10 chars)
}
void showGridSquare(int x, int y) {
const int f = 4; // text font
char gs[12]; // buffer for new grid square
if (!gps.location.isValid()) return; // leave if no fix
float lat = gps.location.lat(); // get latitude
float lon = gps.location.lng(); // and longitude
getGridSquare(gs, lat, lon, GRID_SQUARE_SIZE); // compute current grid square
tft.setTextPadding(tft.textWidth("WWWWWWWWWW", 4)); // width of 10-char display
tft.drawString(gs, x, y, f); // show current grid square
tft.setTextPadding(0);
}
// ============ SCREEN 2 (LAT/LON) DISPLAY ROUTINES ==============================================
void locationScreen() {
tft.fillScreen(TFT_BLACK); // start with empty screen
tft.fillRoundRect(0, 0, 319, 32, 10, LABEL_BGCOLOR); // put title bar at top
tft.drawRoundRect(0, 0, 319, 239, 10, TFT_WHITE); // draw edge around screen
tft.setTextColor(LABEL_FGCOLOR, LABEL_BGCOLOR); // set label colors
tft.drawString("GPS LOCATION", 20, 4, 4); // show title at top
tft.drawString(" Altitude ", 30, 180, 2); // label for clock status
tft.drawString(" Speed/Course ", 160, 180, 2); // label for segment status
tft.setTextColor(DATECOLOR);
tft.drawString("mph", 200, 204, 2); // label for speed units
tft.drawString("deg", 275, 204, 2); // label for bearing units
tft.drawString("ft.", 94, 204, 2); // label for altitude units
}
void showAltitude() {
int x = 90, y = 200, f = 4; // screen position & font
int alt = gps.altitude.feet(); // get altitude in feet
tft.setTextPadding(tft.textWidth("18888", f)); // width of altitude display
tft.drawNumber(alt, x, y, f); // display altitude
}
void showSpeed() {
int x = 196, y = 200, f = 4; // screen position & font
int dir = gps.course.deg(); // get bearing in degrees
int mph = gps.speed.mph(); // get speed in miles/hour
tft.setTextPadding(tft.textWidth("888", f)); // width of speed & bearing
tft.drawNumber(mph, x, y, f); // display speed
x += 76; // x-offset for bearing
if (mph) tft.drawNumber(dir, x, y, f); // display bearing
else tft.drawString("", x, y, f); // but no bearing if speed=0
}
void updateLocationScreen() {
int x = 280, y = 52, f = 7; // screen position & font
if (!gps.location.isValid()) return; // only show valid data
tft.setTextDatum(TR_DATUM); // right-justify the following numbers
showLatLon(x, y, f, 60, 5); // display lat/lon @ 5 decimals
showAltitude(); // show altitude in feet
showSpeed(); // show speed in mph and direction in degrees
tft.setTextDatum(TL_DATUM); // turn off right justification
tft.setTextPadding(0); // turn off text padding
}
// ============ SCREEN 1 (DUAL TIME) DISPLAY ROUTINES ==========================================
void dualScreen() {
tft.fillScreen(TFT_BLACK); // start with empty screen
tft.fillRoundRect(0, 0, 319, 32, 10, LABEL_BGCOLOR); // title bar for local time
tft.fillRoundRect(0, 126, 319, 32, 10, LABEL_BGCOLOR); // title bar for UTC
tft.setTextColor(LABEL_FGCOLOR, LABEL_BGCOLOR); // set label colors
tft.drawRoundRect(0, 0, 319, 110, 10, TFT_WHITE); // draw edge around local time
tft.drawRoundRect(0, 126, 319, 110, 10, TFT_WHITE); // draw edge around UTC
}
void showTimeD(time_t t, bool hr12, int x, int y) {
tft.setTextColor(TIMECOLOR, TFT_BLACK); // set time color
int h = hour(t);
int m = minute(t);
int s = second(t); // get hours, minutes, and seconds
//showAMPM(h); // display AM/PM, if needed
showTimeBasic(h, m, s, hr12, x, y);
}
void showDateD(time_t t, int x, int y) {
const int f = 4, yspacing = 30; // screen font, spacing
const char *months[] = { "JAN", "FEB", "MAR",
"APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT",
"NOV", "DEC" };
if (!lastSync) return; // if no time yet, forget it
tft.setTextColor(DATECOLOR, TFT_BLACK);
int i = 0, m = month(t), d = day(t); // get date components
tft.fillRect(x, y, 50, 60, TFT_BLACK); // erase previous date
if (DATE_ABOVE_MONTH) { // show date on top -----
if ((DATE_LEADING_ZERO) && (d < 10)) // do we need a leading zero?
i = tft.drawNumber(0, x, y, f); // draw leading zero
tft.drawNumber(d, x + i, y, f); // draw date
y += yspacing; // and below it, the month
tft.drawString(months[m - 1], x, y, f); // draw month
} else { // put month on top ----
tft.drawString(months[m - 1], x, y, f); // draw month
y += yspacing; // and below it, the date
if ((DATE_LEADING_ZERO) && (d < 10)) // do we need a leading zero?
x += tft.drawNumber(0, x, y, f); // draw leading zero
tft.drawNumber(d, x, y, f); // draw date
}
}
void showTimeZoneD(int x, int y) {
const int f = 4; // text font
tft.setTextColor(LABEL_FGCOLOR, LABEL_BGCOLOR); // set text colors
tft.setTextPadding(tft.textWidth("WWWWW", 4)); // width of 5-char TZ
if (!useLocalTime)
tft.drawString("UTC", x, y, f); // UTC time
else if (tz != NULL)
tft.drawString(tz->abbrev, x, y, f); // show local time zone
tft.setTextPadding(0);
}
void showTimeDateD(time_t t, time_t oldT, bool hr12, int x, int y) {
showTimeD(t, hr12, x, y); // display time HH:MM:SS
if ((!oldT) || (hour(t) != hour(oldT))) // did hour change?
showTimeZoneD(x, y - 44); // update time zone
if (day(t) != day(oldT)) // did date change?
showDateD(t, x + 250, y); // update date
}
void updateDualScreen() {
useLocalTime = true; // use local timezone
showTimeDateD(lt, oldLt, use12hrFormat, 10, 46); // show new local time
useLocalTime = false; // use UTC timezone
showTimeDateD(t, oldT, UTC_FORMAT_12HR, 10, 172); // show new UTC time
}
// ============ SCREEN 0 (SINGLE TIME) DISPLAY ROUTINES ========================================
void timeScreen() {
tft.fillScreen(TFT_BLACK); // start with empty screen
tft.fillRoundRect(0, 0, 319, 32, 10, LABEL_BGCOLOR); // put title bar at top
tft.drawRoundRect(0, 0, 319, 239, 10, TFT_WHITE); // draw edge around screen
tft.setTextColor(LABEL_FGCOLOR, LABEL_BGCOLOR); // set label colors
tft.drawString(TITLE, 20, 4, 4); // show title at top
tft.drawString(" Grid Square ", 20, 165, 2); // label for grid square
tft.drawString(" Location ", 186, 165, 2); // label for lat/long data
tft.setTextColor(DATECOLOR);
useLocalTime = SHOW_LOCAL_TIME; // start with TZ preference
showDate(t);
showTimeZone();
showLocation(); // grid square & lat/lon
}
void showTimeBasic(int h, int m, int s,
bool hr12, int x, int y) {
const int f = 7; // time font was 7
if (hr12) { // adjust hours for 12 vs 24hr format:
if (h == 0) h = 12; // 00:00 becomes 12:00
if (h > 12) h -= 12; // 13:00 becomes 01:00
}
if (h < 10) { // is hour a single digit?
if ((!hr12) || (HOUR_LEADING_ZERO)) // 24hr format: always use leading 0
x += tft.drawChar('0', x, y, f); // show leading zero for hours
else {
tft.setTextColor(TFT_BLACK, TFT_BLACK); // black on black text
x += tft.drawChar('8', x, y, f); // will erase the old digit
tft.setTextColor(TIMECOLOR, TFT_BLACK);
}
}
x += tft.drawNumber(h, x, y, f); // hours
x += tft.drawChar(':', x, y, f); // show ":"
if (m < 10) x += tft.drawChar('0', x, y, f); // leading zero for minutes
x += tft.drawNumber(m, x, y, f); // show minutes
x += tft.drawChar(':', x, y, f); // show ":"
if (s < 10) x += tft.drawChar('0', x, y, f); // add leading zero for seconds
x += tft.drawNumber(s, x, y, f); // show seconds
}
void showTime(time_t t) {
int x = 20, y = 85; // screen position & font
tft.setTextColor(TIMECOLOR, TFT_BLACK); // set time color
int h = hour(t);
int m = minute(t);
int s = second(t); // get hours, minutes, and seconds
showAMPM(h); // display AM/PM, if needed
showTimeBasic(h, m, s, use12hrFormat, x, y);
}
void showDate(time_t t) {
int x = 20, y = 35, f = 4; // screen position & font
const char *days[] = { "Sun", "Mon", "Tue",
"Wed", "Thu", "Fri", "Sat" };
if (t <= 0) return; // wait for valid date
tft.setTextColor(DATECOLOR, TFT_BLACK);
tft.fillRect(x, y, 265, 26, TFT_BLACK); // erase previous date
x += tft.drawString(days[weekday(t) - 1], x, y, f); // show day of week
x += tft.drawString(", ", x, y, f); // and
x += tft.drawNumber(month(t), x, y, f); // show date as month/day/year
x += tft.drawChar('/', x, y, f);
x += tft.drawNumber(day(t), x, y, f);
x += tft.drawChar('/', x, y, f);
x += tft.drawNumber(year(t), x, y, f);
}
void showAMPM(int hr) {
int x = 250, y = 110, ft = 4; // screen position & font
tft.setTextColor(TIMECOLOR, TFT_BLACK); // use same color as time
if (!use12hrFormat)
tft.fillRect(x, y, 50, 20, TFT_BLACK); // 24hr display, so no AM/PM
else if (hr < 12)
tft.drawString("AM", x, y, ft); // before noon, so draw AM
else
tft.drawString("PM", x, y, ft); // after noon, so draw PM
}
void showTimeZone() {
int x = 250, y = 80, ft = 4; // screen position & font
tft.setTextColor(ZONECOLOR, TFT_BLACK); // zone has its own color
if (!useLocalTime)
tft.drawString("UTC", x, y, ft); // UTC time
else if (tz != NULL)
tft.drawString(tz->abbrev, x, y, ft); // show local time zone
}
void showTimeDate(time_t t, time_t oldT) {
showTime(t); // display time (includes AM/PM)
if ((!oldT) || (hour(t) != hour(oldT))) // did hour change?
showTimeZone(); // update time zone
if (day(t) != day(oldT)) // did date change?
showDate(t); // update date
}
void showSatellites() {
int x = 200, y = 200, w = 50, h = 28, ft = 4; // screen position and size
tft.setTextColor(TFT_YELLOW);
tft.fillRect(x, y, w, h, TFT_BLACK); // erase previous count
tft.drawNumber(satCount(), x, y, ft); // show latest satellite count
}
void showClockStatus() {
const int x = 290, y = 1, w = 28, h = 29, f = 2; // screen position & size
int color;
if (second() % 10) return; // update every 10 seconds
int syncAge = now() - lastSync; // how long has it been since last sync?
if (syncAge < SYNC_MARGINAL) // time is good & in sync
color = TFT_GREEN;
else if (syncAge < SYNC_LOST) // sync is 1-24 hours old
color = TFT_ORANGE;
else color = TFT_RED; // time is stale & should not be trusted
tft.fillRoundRect(x, y, w, h, 10, color); // show clock status as a color
tft.setTextColor(TFT_BLACK, color);
tft.drawNumber(satCount(), x + 8, y + 6, f); // and number of satellites
}
void showLatLon(int x, int y, int f, int spacer, int decimals) {
char c; // cardinal points N,S,E,W
if (!gps.location.isValid()) return; // only show valid data
double lat = gps.location.lat(); // get current latitude
double lon = gps.location.lng(); // get current longitude
tft.setTextColor(TIMECOLOR, TFT_BLACK); // set time color
tft.drawFloat(abs(lat), decimals, x, y, f); // display latitude
tft.drawFloat(abs(lon), decimals, x, y + spacer, f); // display longitude
c = (lat > 0) ? 'N' : 'S'; // neg latiude is S
tft.drawChar(c, x + 6, y, 4); // show N/S for latitude
c = (lon > 0) ? 'E' : 'W'; // neg longitude is W
tft.drawChar(c, x + 6, y + spacer, 4); // show E/W for longitude
}
void showLocation() {
char c;
int x = 280, y = 185, f = 4; // screen position & font
if (!gps.location.isValid()) return; // only show valid data
tft.setTextColor(TIMECOLOR, TFT_BLACK); // set time color
showGridSquare(20, y); // display grid sqare
tft.setTextDatum(TR_DATUM); // right-justify numbers
showLatLon(x, y, f, 20, 4); // display lat/lon to 4 decimals
tft.setTextDatum(TL_DATUM); // back to left justification
}
void updateTimeScreen() {
if (useLocalTime) showTimeDate(lt, oldLt); // either show local time
else showTimeDate(t, oldT); // or UTC time
if (!(second() % 20)) showLocation(); // update locn every 20s
}
// ============ CLOCK & GPS ROUTINES ================================================
time_t localTime() {
if (!lastSync) return 0; // make sure time has been set
else return myTZ.toLocal(now(), &tz); // before converting to local
}
void syncWithGPS() { // set Arduino time from GPS
if (!gps.time.isValid()) return; // continue only if valid data present
if (gps.time.age() > 1000) return; // dont use stale data
int h = gps.time.hour(); // get hour value
int m = gps.time.minute(); // get minute value
int s = gps.time.second(); // get second value
int d = gps.date.day(); // get day
int mo = gps.date.month(); // get month
int y = gps.date.year(); // get year
setTime(h, m, s, d, mo, y); // set the system time
adjustTime(1); // and adjust forward 1 second
lastSync = now(); // remember time of this sync
}
int satCount() { // return # of satellites in view
int sats = 0; // number of satellites
if (gps.satellites.isValid())
sats = gps.satellites.value(); // get # of satellites in view
return sats;
}
void syncCheck() {
if (pps || (!USING_PPS)) syncWithGPS(); // is it time to sync with GPS?
pps = 0; // reset flag, regardless
}
void feedGPS() { // feed GPS into tinyGPS parser
while (gpsSerial.available()) { // look for data from GPS module altered for using UART2
char c = gpsSerial.read(); // read in all available chars
gps.encode(c); // and feed chars to GPS parser
}
}
void ppsHandler() { // 1pps interrupt handler:
pps = 1; // flag that signal was received
}
// ============ TOUCH ROUTINES ===================================================
bool touched() { ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const int threshold = 500; // Define a threshold to filter out light touches
TS_Point p = touchscreen.getPoint();
return p.z > threshold;
}
boolean inRegion(region b, int x, int y) { // true if regsion contains point (x,y)
if ((x < b.x) || (x > (b.x + b.w))) // x coordinate out of bounds?
return false; // if so, leave
if ((y < b.y) || (y > (b.y + b.h))) // y coordinate out of bounds?
return false; // if so, leave
return true; // x & y both in bounds
}
void touchedTime(int x, int y) {
screenID = 1; // user wants dual time
newScreen(); // switch to a new screen
}
void touchedLocation(int x, int y) {
screenID = 2; // user wants to see location data
newScreen(); // switch to a new screen
}
void touchedPM(int x, int y) {
use12hrFormat = !use12hrFormat; // toggle 12/24 hr display
if (useLocalTime) showTime(lt); // show local time
else showTime(t); // or show UTC time
}
void touchedTZ(int x, int y) {
useLocalTime = !useLocalTime; // toggle the local/UTC flag
use12hrFormat = useLocalTime; // local time is 12hr; UTC is 24hr
if (useLocalTime) showTimeDate(lt, 0); // show local time zone
else showTimeDate(t, 0); // or show UTC time zone
}
void touchedTitle(int x, int y) {
}
bool getTouchPoint(uint16_t &x, uint16_t &y) {
x = 0;
y = 0;
TS_Point p = touchscreen.getPoint();
x = map(p.x, 200, 3700, 1, SCREEN_WIDTH); ///////////////////////////////////
y = map(p.y, 240, 3800, 1, SCREEN_HEIGHT); ////////////////////////////////
if ((x > 0) && (y > 0) && (x < 320) && (y < 240)) { // are they valid coordinates?
if (TOUCH_FLIP_X) x = 320 - x; // flip orientations, if necessary
if (TOUCH_FLIP_Y) y = 240 - y;
x += TOUCH_ADJUST_X; // fine tune coordinates
y += TOUCH_ADJUST_Y;
}
return (x > 0) && (y > 0); // if true, point is valid
}
void checkForTouch() {
uint16_t x, y;
if (touched()) { // did user touch the display?
TS_Point p = touchscreen.getPoint();
setBrightness(100); // light up screen
getTouchPoint(x, y); // get touch coordinates
if (screenID) { // if user touched anywhere in dual/locn screens
screenID = 0; // switch to single display
newScreen(); // and show it
} else if (inRegion(rTime, x, y)) // was time touched?
touchedTime(x, y);
else if (inRegion(rLocn, x, y)) // was location touched?
touchedLocation(x, y);
else if (inRegion(rPM, x, y)) // was AM/PM touched?
touchedPM(x, y);
else if (inRegion(rTZ, x, y)) // was timezone touched?
touchedTZ(x, y);
}
delay(100); // touch debouncer // had to lower due to delaying screen update time, if too low touch too fast
}
// ============ MAIN PROGRAM ===================================================
void setBrightness(int level) // level 0 (off) to 100 (full on)
{
ledcWrite(BACKLIGHT, level * 255 / 100);
delay(5);
}
void initScreen() {
tft.init(); // initialize display library
tft.setRotation(SCREEN_ORIENTATION); // portrait screen orientation
pinMode(BACKLIGHT, OUTPUT); // enable backlight control
ledcAttachChannel(BACKLIGHT, 5000, 8, 3); ///////////////////////////
setBrightness(100); // turn backlight on 100% set this to whatever screen brightness you require
}
void setup() {
gpsSerial.begin(9600, SERIAL_8N1, RXD2); // open UART2 connection to GPS
// Start the SPI for the touchscreen and init the touchscreen
touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
touchscreen.begin(touchscreenSPI);
touchscreen.setRotation(SCREEN_ORIENTATION); /// match this to screen rotation setting
initScreen();
screenID = FIRST_SCREEN; // user preference for startup screen
newScreen(); // show title & labels
attachInterrupt(digitalPinToInterrupt(GPS_PPS), ppsHandler, RISING); // enable 1pps GPS time sync
}
void loop() {
feedGPS(); // decode incoming GPS data
syncCheck(); // synchronize time with GPS
updateDisplay(); // keep display current
checkForTouch(); // act on any user touch
}
void newScreen() {
oldLt = oldT = 0; // force time/date redraw
switch (screenID) {
case 1: dualScreen(); break;
case 2: locationScreen(); break;
default: timeScreen(); break;
}
}
void updateDisplay() {
t = now(); // check latest time
if (t != oldT) { // are we in a new second yet?
lt = localTime(); // keep local time current
switch (screenID) {
case 1: updateDualScreen(); break;
case 2: updateLocationScreen(); break;
default: updateTimeScreen(); break;
}
showClockStatus(); // show GPS status & sat count
oldT = t;
oldLt = lt; // remember currently displayed time
}
}