forked from cifertech/ESP32-DIV
-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathutils.cpp
More file actions
619 lines (494 loc) · 18.9 KB
/
Copy pathutils.cpp
File metadata and controls
619 lines (494 loc) · 18.9 KB
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
#include "utils.h"
#include "shared.h"
#include "icon.h"
#include "Touchscreen.h"
#include <WiFi.h> // For WiFi.RSSI() and WiFi.status()
/*
*
* Notification
*
*/
/*
showNotification("New Message!", "Task Failed Successfully.");
if (notificationVisible && ts.touched()) {
int x, y, z;
TS_Point p = ts.getPoint();
x = ::map(p.x, TS_MINX, TS_MAXX, 0, DISPLAY_WIDTH - 1);
y = ::map(p.y, TS_MAXY, TS_MINY, 0, DISPLAY_HEIGHT - 1);
if (x >= closeButtonX && x <= (closeButtonX + closeButtonSize) &&
y >= closeButtonY && y <= (closeButtonY + closeButtonSize)) {
hideNotification();
}
if (x >= okButtonX && x <= (okButtonX + okButtonWidth) &&
y >= okButtonY && y <= (okButtonY + okButtonHeight)) {
hideNotification();
}
delay(100);
}
*/
bool notificationVisible = true;
int notifX, notifY, notifWidth, notifHeight;
int closeButtonX, closeButtonY, closeButtonSize = 15;
int okButtonX, okButtonY, okButtonWidth = 60, okButtonHeight = 20;
void showNotification(const char* title, const char* message) {
notifWidth = 200;
notifHeight = 80;
notifX = (240 - notifWidth) / 2;
notifY = (320 - notifHeight) / 2;
tft.fillRect(notifX, notifY, notifWidth, notifHeight, LIGHT_GRAY);
tft.fillRect(notifX, notifY, notifWidth, 20, BLUE);
tft.setTextColor(WHITE);
tft.setTextSize(1);
tft.setCursor(notifX + 5, notifY + 5);
tft.print(title);
closeButtonX = notifX + notifWidth - closeButtonSize - 5;
closeButtonY = notifY + 2;
tft.fillRect(closeButtonX, closeButtonY, closeButtonSize, closeButtonSize, RED);
tft.setTextColor(WHITE);
tft.setCursor(closeButtonX + 5, closeButtonY + 4);
tft.print("X");
int messageBoxX = notifX + 5;
int messageBoxY = notifY + 25;
int messageBoxWidth = notifWidth - 10;
int messageBoxHeight = notifHeight - 45;
tft.fillRect(messageBoxX, messageBoxY, messageBoxWidth, messageBoxHeight, WHITE);
tft.setTextColor(BLACK);
printWrappedText(messageBoxX + 2, messageBoxY + 5, messageBoxWidth + 2, message);
okButtonX = notifX + (notifWidth - okButtonWidth) / 2;
okButtonY = notifY + notifHeight - 25;
tft.fillRect(okButtonX, okButtonY, okButtonWidth, okButtonHeight, GRAY);
tft.drawRect(okButtonX, okButtonY, okButtonWidth, okButtonHeight, DARK_GRAY);
tft.drawLine(okButtonX, okButtonY, okButtonX + okButtonWidth, okButtonY, WHITE);
tft.drawLine(okButtonX, okButtonY, okButtonX, okButtonY + okButtonHeight, WHITE);
tft.setTextColor(BLACK);
tft.setCursor(okButtonX + 20, okButtonY + 5);
tft.print("OK");
notificationVisible = true;
}
void hideNotification() {
tft.fillRect(notifX, notifY, notifWidth, notifHeight, BLACK);
notificationVisible = false;
}
void printWrappedText(int x, int y, int maxWidth, const char* text) {
String message = text;
int cursorX = x, cursorY = y;
while (message.length() > 0) {
int lineEnd = message.length();
while (tft.textWidth(message.substring(0, lineEnd)) > maxWidth) {
lineEnd--;
}
if (lineEnd < message.length()) {
int lastSpace = message.substring(0, lineEnd).lastIndexOf(' ');
if (lastSpace > 0) lineEnd = lastSpace;
}
tft.setCursor(cursorX, cursorY);
tft.print(message.substring(0, lineEnd));
message = message.substring(lineEnd);
message.trim();
cursorY += 15;
}
}
/*
*
* StatusBar
*
*/
#ifdef __cplusplus
extern "C" {
#endif
uint8_t temprature_sens_read();
#ifdef __cplusplus
}
#endif
uint8_t temprature_sens_read();
unsigned long lastStatusBarUpdate = 0;
const int STATUS_BAR_UPDATE_INTERVAL = 1000;
float lastBatteryVoltage = 0.0;
float readBatteryVoltage() {
static bool adcInitialized = false;
static unsigned long lastDebugPrint = 0;
// Initialize ADC attenuation on first call (11dB for 0-3.3V range)
if (!adcInitialized) {
analogSetPinAttenuation(36, ADC_11db);
adcInitialized = true;
Serial.println("[BATTERY] ADC initialized on GPIO36 with 11dB attenuation");
}
const int sampleCount = 16; // More samples for better accuracy
uint32_t sum = 0;
for (int i = 0; i < sampleCount; i++) {
sum += analogReadMilliVolts(36); // Use calibrated millivolt reading
delayMicroseconds(500); // Faster sampling
}
float avgMv = (float)sum / sampleCount;
// Voltage divider ratio is 2:1, so multiply by 2 to get actual battery voltage
float voltage = (avgMv / 1000.0) * 2.0;
// DEBUG: Print battery info every 5 seconds to help diagnose issues
if (millis() - lastDebugPrint > 5000) {
Serial.printf("[BATTERY] Raw mV: %.1f, Voltage: %.2fV, Expected range: 3.0-4.2V\n", avgMv, voltage);
if (avgMv < 100) {
Serial.println("[BATTERY] WARNING: Very low reading - check GPIO36 wiring to battery divider!");
}
lastDebugPrint = millis();
}
return voltage;
}
float readInternalTemperature() {
float temperature = ((temprature_sens_read() - 32) / 1.8);
return temperature;
}
// Check if SD card is available
bool isSDCardAvailable() {
return SD.begin();
}
void drawStatusBar(float batteryVoltage, bool forceUpdate) {
static int lastBatteryPercentage = -1;
static int lastWiFiStrength = -1;
static String lastDisplayedTime = "";
// IGNORE the passed voltage - read FRESH every time!
// The caller passes a stale global that's set once at startup
float freshVoltage = readBatteryVoltage();
int batteryPercentage = map(freshVoltage * 100, 300, 420, 0, 100);
batteryPercentage = constrain(batteryPercentage, 0, 100);
// Get REAL WiFi signal strength (not random!)
int wifiStrength = 0;
wifi_mode_t wifiMode = WiFi.getMode();
if (WiFi.status() == WL_CONNECTED) {
int rssi = WiFi.RSSI();
// Map RSSI (-100 to -30 dBm) to percentage (0-100%)
wifiStrength = constrain(map(rssi, -100, -30, 0, 100), 0, 100);
} else if (wifiMode == WIFI_AP || wifiMode == WIFI_AP_STA) {
// In AP mode, show full bars (we ARE the access point)
wifiStrength = 100;
}
// If not connected and not in AP mode, wifiStrength stays 0
float internalTemp = readInternalTemperature();
bool sdAvailable = false;
//bool sdAvailable = isSDCardAvailable();
if (batteryPercentage != lastBatteryPercentage || wifiStrength != lastWiFiStrength || forceUpdate) {
int barHeight = 20; // Status bar height
int x = 7; // Padding for battery icon
int y = 4; // Vertical offset
// **Dark Background with Neon Green Edge**
tft.fillRect(0, 0, tft.width(), barHeight, DARK_GRAY);
//tft.fillRect(0, barHeight - 2, tft.width(), 3, ORANGE);
// **Draw Battery Icon (Hacker/Techy Look)**
tft.drawRoundRect(x, y, 22, 10, 2, SHREDDY_TEAL); // Battery border
tft.fillRect(x + 22, y + 3, 2, 4, SHREDDY_TEAL); // Battery terminal
int batteryLevelWidth = map(batteryPercentage, 0, 100, 0, 20);
uint16_t batteryColor = (batteryPercentage > 20) ? GREEN : RED;
tft.fillRoundRect(x + 2, y + 2, batteryLevelWidth, 6, 1, batteryColor);
// **Display Battery Percentage**
tft.setCursor(x + 30, y + 2);
tft.setTextColor(GREEN, DARK_GRAY);
tft.setTextFont(1);
tft.setTextSize(1);
tft.print(String(batteryPercentage) + "%");
// **Draw Wi-Fi Signal Bars**
int wifiX = 180;
int wifiY = y + 11;
for (int i = 0; i < 4; i++) {
int barHeight = (i + 1) * 3;
int barWidth = 4;
int barX = wifiX + i * 6;
if (wifiStrength > i * 25) {
tft.fillRoundRect(barX, wifiY - barHeight, barWidth, barHeight, 1, GREEN);
} else {
tft.drawRoundRect(barX, wifiY - barHeight, barWidth, barHeight, 1, SHREDDY_TEAL);
}
}
// Temperature indicator
if (internalTemp >= 55) {
tft.drawBitmap(203, y - 3, bitmap_icon_temp, 16, 16, RED); // HOT - danger zone
} else if (internalTemp >= 50) {
tft.drawBitmap(203, y - 3, bitmap_icon_temp, 16, 16, ORANGE); // WARM - caution
} else {
tft.drawBitmap(203, y - 3, bitmap_icon_temp, 16, 16, HALEHOUND_CYAN); // COOL - normal
}
// **Display SD Card Icon (If Available)**
if (sdAvailable) {
tft.drawBitmap(220, y - 3, bitmap_icon_sdcard, 16, 16, GREEN);
} else {
tft.drawBitmap(220, y - 3, bitmap_icon_nullsdcard, 16, 16, RED);
}
// **Bottom Line for Aesthetic (Neon Green)**
//tft.drawLine(0, barHeight - 1, tft.width(), barHeight - 1, ORANGE);
// **Update Last Values**
lastBatteryPercentage = batteryPercentage;
lastWiFiStrength = wifiStrength;
}
}
void updateStatusBar() {
unsigned long currentMillis = millis();
if (currentMillis - lastStatusBarUpdate > STATUS_BAR_UPDATE_INTERVAL) {
float batteryVoltage = readBatteryVoltage();
if (abs(batteryVoltage - lastBatteryVoltage) > 0.05 || lastBatteryVoltage == 0) {
drawStatusBar(batteryVoltage);
lastBatteryVoltage = batteryVoltage;
}
lastStatusBarUpdate = currentMillis;
}
}
/*
*
* Loading
*
*/
void loading(int frameDelay, uint16_t color, int16_t x, int16_t y, int repeats, bool center) {
int16_t bitmapWidth = 100;
int16_t bitmapHeight = 120;
int16_t logoX = x;
int16_t logoY = y;
int16_t screenWidth = tft.width();
int16_t screenHeight = tft.height();
if (center) {
logoX = (screenWidth - bitmapWidth) / 2;
logoY = (screenHeight - bitmapHeight) / 2 - 25; // Move up for text
}
// Array of bitmaps
const unsigned char* bitmaps[] = {
bitmap_icon_skull_loading_1,
bitmap_icon_skull_loading_2,
bitmap_icon_skull_loading_3,
bitmap_icon_skull_loading_4,
bitmap_icon_skull_loading_5,
bitmap_icon_skull_loading_6,
bitmap_icon_skull_loading_7,
bitmap_icon_skull_loading_8,
bitmap_icon_skull_loading_9,
bitmap_icon_skull_loading_10
};
const int numFrames = 10;
// HaleHound colors - alternating magenta and cyan
uint16_t colors[] = {HALEHOUND_MAGENTA, HALEHOUND_CYAN};
for (int r = 0; r < repeats; r++) {
for (int i = 0; i < numFrames; i++) {
uint16_t frameColor = colors[i % 2]; // Alternate colors
// Clear skull area
tft.fillRect(logoX, logoY, bitmapWidth, bitmapHeight + 40, TFT_BLACK);
// Draw skull frame
tft.drawBitmap(logoX, logoY, bitmaps[i], bitmapWidth, bitmapHeight, frameColor);
// Draw HALEHOUND text below skull
if (center) {
tft.setTextFont(4);
tft.setTextSize(1);
tft.setTextColor(frameColor, TFT_BLACK);
const char* text = "HALEHOUND";
int16_t textW = tft.textWidth(text);
int16_t textX = (screenWidth - textW) / 2;
int16_t textY = logoY + bitmapHeight + 10;
tft.setCursor(textX, textY);
tft.print(text);
}
delay(frameDelay);
}
}
}
/*
*
* Display Logo
*
*/
void displayLogo(uint16_t color, int displayTime) {
int16_t screenWidth = tft.width();
int16_t screenHeight = tft.height();
// Clear screen with black
tft.fillScreen(TFT_BLACK);
// Draw full-screen skull stack in muted gray
tft.drawBitmap(0, 0, bitmap_halehound_splash, HALEHOUND_SPLASH_WIDTH, HALEHOUND_SPLASH_HEIGHT, GRAY);
// Draw branding text in HaleHound magenta
tft.setTextColor(HALEHOUND_MAGENTA);
// ESP32-DIV title - larger
tft.setTextFont(4); // Font 4 = 26px, clean and sharp
tft.setTextSize(1);
String title = "ESP32-DIV";
int16_t titleWidth = tft.textWidth(title);
tft.setCursor((screenWidth - titleWidth) / 2, 255);
tft.print(title);
// Version line
tft.setTextFont(2); // Font 2 = 16px
String version = "v2.5.0 - HaleHound Edition";
int16_t versionWidth = tft.textWidth(version);
tft.setCursor((screenWidth - versionWidth) / 2, 285);
tft.print(version);
// Credit line
tft.setTextFont(2);
String credit = "By: JMFH";
int16_t creditWidth = tft.textWidth(credit);
tft.setCursor((screenWidth - creditWidth) / 2, 303);
tft.print(credit);
Serial.println("==========================================");
Serial.println("ESP32-DIV v2.5.0 - HaleHound Edition ");
Serial.println("Developed by: HaleHound (JMFH) ");
Serial.println("Original by: CiferTech ");
Serial.println("GitHub: github.com/JesseCHale ");
Serial.println("==========================================");
delay(displayTime);
}
/*
*
* Terminal
*
*/
namespace Terminal {
#define TEXT_HEIGHT 16
#define BOT_FIXED_AREA 0
#define TOP_FIXED_AREA 86
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 320
#define SCREEN_WIDTH 240
#define SCREENHEIGHT 320
static bool uiDrawn = false;
uint16_t yStart = TOP_FIXED_AREA;
uint16_t yArea = DISPLAY_HEIGHT - TOP_FIXED_AREA - BOT_FIXED_AREA;
uint16_t yDraw = DISPLAY_HEIGHT - BOT_FIXED_AREA - TEXT_HEIGHT;
uint16_t xPos = 0;
byte data = 0;
boolean change_colour = 1;
boolean selected = 1;
boolean terminalActive = true;
int blank[19];
long baudRates[] = {9600, 19200, 38400, 57600, 115200};
byte baudIndex = 0;
void runUI() {
#define STATUS_BAR_Y_OFFSET 20
#define STATUS_BAR_HEIGHT 16
#define ICON_SIZE 16
#define ICON_NUM 3
static int iconX[ICON_NUM] = {210, 170, 10};
static int iconY = STATUS_BAR_Y_OFFSET;
static const unsigned char* icons[ICON_NUM] = {
bitmap_icon_sort_up_plus,
bitmap_icon_power,
bitmap_icon_go_back
};
if (!uiDrawn) {
tft.drawLine(0, 19, 240, 19, SHREDDY_TEAL);
tft.fillRect(0, STATUS_BAR_Y_OFFSET, SCREEN_WIDTH, STATUS_BAR_HEIGHT, DARK_GRAY);
for (int i = 0; i < ICON_NUM; i++) {
if (icons[i] != NULL) {
tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, SHREDDY_TEAL);
}
}
tft.drawLine(0, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT, ORANGE);
uiDrawn = true;
}
static unsigned long lastAnimationTime = 0;
static int animationState = 0;
static int activeIcon = -1;
if (animationState > 0 && millis() - lastAnimationTime >= 150) {
if (animationState == 1) {
tft.drawBitmap(iconX[activeIcon], iconY, icons[activeIcon], ICON_SIZE, ICON_SIZE, SHREDDY_TEAL);
animationState = 2;
switch (activeIcon) {
case 0:
if (terminalActive) {
terminalActive = false;
} else if (!terminalActive) {
baudIndex = (baudIndex + 1) % 5;
Serial.end();
delay(100);
Serial.begin(baudRates[baudIndex]);
tft.fillRect(0, 37, DISPLAY_WIDTH, 16, ORANGE);
tft.setTextColor(SHREDDY_TEAL, SHREDDY_TEAL);
String baudMsg = " Serial Terminal - " + String(baudRates[baudIndex]) + " baud ";
tft.drawCentreString(baudMsg, DISPLAY_WIDTH / 2, 37, 2);
delay(10);
}
break;
case 1:
delay(10);
tft.fillRect(0, 37, DISPLAY_WIDTH, 16, ORANGE);
tft.setTextColor(SHREDDY_TEAL, SHREDDY_TEAL);
tft.drawCentreString(" Serial Terminal Active ", DISPLAY_WIDTH / 2, 37, 2);
terminalActive = true;
break;
case 2:
feature_exit_requested = true;
break;
}
} else if (animationState == 2) {
animationState = 0;
activeIcon = -1;
}
lastAnimationTime = millis();
}
static unsigned long lastTouchCheck = 0;
const unsigned long touchCheckInterval = 50;
if (millis() - lastTouchCheck >= touchCheckInterval) {
if (ts.touched() && feature_active) {
TS_Point p = ts.getPoint();
int x = ::map(p.x, TS_MINX, TS_MAXX, 0, SCREEN_WIDTH - 1);
int y = ::map(p.y, TS_MAXY, TS_MINY, 0, SCREENHEIGHT - 1);
if (y > STATUS_BAR_Y_OFFSET && y < STATUS_BAR_Y_OFFSET + STATUS_BAR_HEIGHT) {
for (int i = 0; i < ICON_NUM; i++) {
if (x > iconX[i] && x < iconX[i] + ICON_SIZE) {
if (icons[i] != NULL && animationState == 0) {
tft.drawBitmap(iconX[i], iconY, icons[i], ICON_SIZE, ICON_SIZE, TFT_BLACK);
animationState = 1;
activeIcon = i;
lastAnimationTime = millis();
}
break;
}
}
}
}
lastTouchCheck = millis();
}
}
void scrollAddress(uint16_t vsp) {
tft.writecommand(ILI9341_VSCRSADD);
tft.writedata(vsp >> 8);
tft.writedata(vsp);
}
int scroll_line() {
int yTemp = yStart;
tft.fillRect(0, yStart, blank[(yStart - TOP_FIXED_AREA) / TEXT_HEIGHT], TEXT_HEIGHT, TFT_BLACK);
yStart += TEXT_HEIGHT;
if (yStart >= DISPLAY_HEIGHT - BOT_FIXED_AREA) yStart = TOP_FIXED_AREA + (yStart - DISPLAY_HEIGHT + BOT_FIXED_AREA);
scrollAddress(yStart);
delay(1);
return yTemp;
}
void setupScrollArea(uint16_t tfa, uint16_t bfa) {
tft.writecommand(ILI9341_VSCRDEF);
tft.writedata(tfa >> 8);
tft.writedata(tfa);
tft.writedata((DISPLAY_HEIGHT - tfa - bfa) >> 8);
tft.writedata(DISPLAY_HEIGHT - tfa - bfa);
tft.writedata(bfa >> 8);
tft.writedata(bfa);
}
void terminalSetup() {
setupTouchscreen();
tft.fillScreen(TFT_BLACK);
tft.fillRect(0, 37, DISPLAY_WIDTH, 16, ORANGE);
tft.setTextColor(SHREDDY_TEAL, SHREDDY_TEAL);
String baudMsg = " Serial Terminal - " + String(baudRates[baudIndex]) + " baud ";
tft.drawCentreString(baudMsg, DISPLAY_WIDTH / 2, 37, 2);
float currentBatteryVoltage = readBatteryVoltage();
drawStatusBar(currentBatteryVoltage, false);
uiDrawn = false;
Serial.begin(baudRates[baudIndex]);
setupScrollArea(TOP_FIXED_AREA, BOT_FIXED_AREA);
for (byte i = 0; i < 19; i++) blank[i] = 0;
}
void terminalLoop() {
runUI();
if (terminalActive) {
byte charCount = 0;
while (Serial.available() && charCount < 10) {
data = Serial.read();
if (data == '\r' || xPos > 231) {
xPos = 0;
yDraw = scroll_line();
}
if (data > 31 && data < 128) {
xPos += tft.drawChar(data, xPos, yDraw, 2);
blank[(18 + (yStart - TOP_FIXED_AREA) / TEXT_HEIGHT) % 19] = xPos;
}
charCount++;
}
}
}
}