feat(ui,theme): tema de color arcoiris con degradado espectral global y desactivacion de led trasero
- Lienzo espectral continuo (320px) visible a traves de letras y numeros - Renderizado de digitos DSEG7 (34pt y 80pt) con degradado continuo pixel a pixel - Preservacion del suavizado anti-aliased subpixel en titulos y fuentes de texto - Integracion del tema Arcoiris en pantallas de instrumentos, tri-temp, reloj y navegacion - Desactivacion permanente del LED RGB trasero de la placa a nivel de hardware y software
This commit is contained in:
parent
cb77100a49
commit
4c5967d3c5
8 changed files with 545 additions and 370 deletions
|
|
@ -1496,7 +1496,7 @@ void themeMenu() {
|
||||||
tft.fillScreen(TFT_BLACK);
|
tft.fillScreen(TFT_BLACK);
|
||||||
|
|
||||||
const char* const THEME_SHORT_NAMES[6] = {
|
const char* const THEME_SHORT_NAMES[6] = {
|
||||||
"Cian", "Verde", "Ambar", "Blanco", "Naranja", "Violeta"
|
"Cian", "Verde", "Arcoiris", "Blanco", "Naranja", "Violeta"
|
||||||
};
|
};
|
||||||
|
|
||||||
auto drawThemeUI = [&]() {
|
auto drawThemeUI = [&]() {
|
||||||
|
|
@ -1514,6 +1514,12 @@ void themeMenu() {
|
||||||
// Left side: Theme Name and Description
|
// Left side: Theme Name and Description
|
||||||
tft.setTextColor(GAUGE_THEMES[themeIndex].primaryColor, 0x1082);
|
tft.setTextColor(GAUGE_THEMES[themeIndex].primaryColor, 0x1082);
|
||||||
tft.drawString(GAUGE_THEMES[themeIndex].name, 20, 40, 4);
|
tft.drawString(GAUGE_THEMES[themeIndex].name, 20, 40, 4);
|
||||||
|
if (themeIndex == 2) {
|
||||||
|
const uint16_t rainbowMini[6] = {0x03FF, 0x07FF, 0x07E0, 0xFFE0, 0xFD20, 0xF800};
|
||||||
|
for (uint8_t b = 0; b < 6; b++) {
|
||||||
|
tft.fillRect(20 + b * 17, 65, 15, 2, rainbowMini[b]);
|
||||||
|
}
|
||||||
|
}
|
||||||
tft.setTextColor(0xAD55, 0x1082);
|
tft.setTextColor(0xAD55, 0x1082);
|
||||||
tft.drawString(GAUGE_THEMES[themeIndex].desc, 20, 69, 2);
|
tft.drawString(GAUGE_THEMES[themeIndex].desc, 20, 69, 2);
|
||||||
|
|
||||||
|
|
@ -1522,7 +1528,14 @@ void themeMenu() {
|
||||||
tft.fillRectVGradient(168, 39, 126, 12, GAUGE_THEMES[themeIndex].bgColor, TFT_BLACK);
|
tft.fillRectVGradient(168, 39, 126, 12, GAUGE_THEMES[themeIndex].bgColor, TFT_BLACK);
|
||||||
tft.fillRect(168, 51, 126, 6, TFT_BLACK);
|
tft.fillRect(168, 51, 126, 6, TFT_BLACK);
|
||||||
tft.fillRect(220, 39, 2, 18, GAUGE_THEMES[themeIndex].divColor); // Sample Crosshair line
|
tft.fillRect(220, 39, 2, 18, GAUGE_THEMES[themeIndex].divColor); // Sample Crosshair line
|
||||||
|
if (themeIndex == 2) {
|
||||||
|
const uint16_t rainbowMini[6] = {0x03FF, 0x07FF, 0x07E0, 0xFFE0, 0xFD20, 0xF800};
|
||||||
|
for (uint8_t b = 0; b < 6; b++) {
|
||||||
|
tft.fillRoundRect(172 + b * 7, 43, 5, 10, 1, rainbowMini[b]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
tft.fillCircle(188, 48, 3, TFT_GREEN); // Sample green gauge block
|
tft.fillCircle(188, 48, 3, TFT_GREEN); // Sample green gauge block
|
||||||
|
}
|
||||||
tft.setTextColor(0xBDD7, TFT_BLACK);
|
tft.setTextColor(0xBDD7, TFT_BLACK);
|
||||||
tft.drawRightString("VIGNETTE", 290, 42, 1);
|
tft.drawRightString("VIGNETTE", 290, 42, 1);
|
||||||
|
|
||||||
|
|
@ -1562,7 +1575,19 @@ void themeMenu() {
|
||||||
int16_t cx = tx + 16;
|
int16_t cx = tx + 16;
|
||||||
int16_t cy = ty + 17;
|
int16_t cy = ty + 17;
|
||||||
tft.drawCircle(cx, cy, 7, isSel ? TFT_WHITE : 0x4208);
|
tft.drawCircle(cx, cy, 7, isSel ? TFT_WHITE : 0x4208);
|
||||||
|
if (i == 2) {
|
||||||
|
// Rainbow theme: color-wheel swatch (hue by angle)
|
||||||
|
for (int8_t dy = -5; dy <= 5; dy++) {
|
||||||
|
for (int8_t dx = -5; dx <= 5; dx++) {
|
||||||
|
if (dx * dx + dy * dy > 25) continue;
|
||||||
|
float ang = atan2f((float)dy, (float)dx) * 57.2958f;
|
||||||
|
if (ang < 0) ang += 360.0f;
|
||||||
|
tft.drawPixel(cx + dx, cy + dy, hueToRgb565((uint16_t)ang));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
tft.fillCircle(cx, cy, 5, GAUGE_THEMES[i].primaryColor);
|
tft.fillCircle(cx, cy, 5, GAUGE_THEMES[i].primaryColor);
|
||||||
|
}
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
tft.setTextColor(isSel ? TFT_WHITE : 0xBDD7, bg);
|
tft.setTextColor(isSel ? TFT_WHITE : 0xBDD7, bg);
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,16 @@
|
||||||
#include "DSEG7_Modern_BoldItalic_34.h"
|
#include "DSEG7_Modern_BoldItalic_34.h"
|
||||||
#include "DSEG7_Modern_BoldItalic_80.h"
|
#include "DSEG7_Modern_BoldItalic_80.h"
|
||||||
#include "Latin_Sans_Bold_26.h"
|
#include "Latin_Sans_Bold_26.h"
|
||||||
|
#include "rainbow_text.h"
|
||||||
#include "gps_module.h"
|
#include "gps_module.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <TFT_eSPI.h>
|
#include <TFT_eSPI.h>
|
||||||
|
|
||||||
extern TFT_eSPI tft;
|
extern TFT_eSPI tft;
|
||||||
extern uint16_t getThemePrimaryColor();
|
extern uint16_t getThemePrimaryColor();
|
||||||
|
extern uint16_t getThemeAccentColor();
|
||||||
extern uint16_t getThemeBgColor();
|
extern uint16_t getThemeBgColor();
|
||||||
|
extern bool isRainbowTheme();
|
||||||
extern int8_t currentScreenPage;
|
extern int8_t currentScreenPage;
|
||||||
extern int8_t fullScreenCell;
|
extern int8_t fullScreenCell;
|
||||||
|
|
||||||
|
|
@ -26,6 +29,60 @@ static uint8_t oldClockDay = 255;
|
||||||
static uint8_t oldClockMonth = 255;
|
static uint8_t oldClockMonth = 255;
|
||||||
static uint16_t oldClockYear = 0;
|
static uint16_t oldClockYear = 0;
|
||||||
|
|
||||||
|
// Smooth pre-rendered title sprites for Day of Week and Year (100% anti-aliased Latin_Sans_Bold_26 matching Page 1)
|
||||||
|
static TFT_eSprite sprDayTitle(&tft);
|
||||||
|
static uint8_t sprDayWday = 255;
|
||||||
|
|
||||||
|
inline void updateDayTitleSprite(uint8_t wday) {
|
||||||
|
if (sprDayWday == wday && sprDayTitle.created()) return;
|
||||||
|
if (!sprDayTitle.created()) {
|
||||||
|
sprDayTitle.createSprite(160, 26);
|
||||||
|
}
|
||||||
|
if (sprDayTitle.created()) {
|
||||||
|
uint16_t cardBg = 0x10A2;
|
||||||
|
sprDayTitle.loadFont(Latin_Sans_Bold_26);
|
||||||
|
sprDayTitle.setTextPadding(0);
|
||||||
|
sprDayTitle.fillSprite(cardBg);
|
||||||
|
sprDayTitle.setTextColor(TFT_WHITE, cardBg);
|
||||||
|
sprDayTitle.drawString(SPANISH_DAYS[wday], 0, 0, 1);
|
||||||
|
sprDayTitle.unloadFont();
|
||||||
|
if (isRainbowTheme()) {
|
||||||
|
applyRainbowToSprite(sprDayTitle, 4 + 116, 125 + 22, cardBg);
|
||||||
|
}
|
||||||
|
sprDayWday = wday;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static TFT_eSprite sprYearTitle(&tft);
|
||||||
|
static uint16_t sprYearVal = 0;
|
||||||
|
static uint16_t sprYearCol = 0;
|
||||||
|
|
||||||
|
inline void updateYearTitleSprite(uint16_t year, uint16_t priCol) {
|
||||||
|
if (sprYearVal == year && sprYearCol == priCol && sprYearTitle.created()) return;
|
||||||
|
if (!sprYearTitle.created()) {
|
||||||
|
sprYearTitle.createSprite(80, 26);
|
||||||
|
}
|
||||||
|
if (sprYearTitle.created()) {
|
||||||
|
uint16_t cardBg = 0x10A2;
|
||||||
|
sprYearTitle.loadFont(Latin_Sans_Bold_26);
|
||||||
|
sprYearTitle.setTextPadding(0);
|
||||||
|
sprYearTitle.fillSprite(cardBg);
|
||||||
|
sprYearTitle.setTextColor(priCol, cardBg);
|
||||||
|
sprYearTitle.drawString(String(year).c_str(), 0, 0, 1);
|
||||||
|
sprYearTitle.unloadFont();
|
||||||
|
if (isRainbowTheme()) {
|
||||||
|
applyRainbowToSprite(sprYearTitle, 4 + 116, 125 + 64, cardBg);
|
||||||
|
}
|
||||||
|
sprYearVal = year;
|
||||||
|
sprYearCol = priCol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void initGpsClockTitles() {
|
||||||
|
updateDayTitleSprite(gpsDayOfWeek % 7);
|
||||||
|
updateYearTitleSprite(gpsYear, isRainbowTheme() ? (uint16_t)0xFFE0 : getThemeAccentColor());
|
||||||
|
}
|
||||||
|
|
||||||
// Draw Full-Width Date Card (Bottom) - Clean, prominent, automotive badge
|
// Draw Full-Width Date Card (Bottom) - Clean, prominent, automotive badge
|
||||||
// layout
|
// layout
|
||||||
inline void drawDateCardContent(int cardX, int cardY, int cardW, int cardH,
|
inline void drawDateCardContent(int cardX, int cardY, int cardW, int cardH,
|
||||||
|
|
@ -33,7 +90,8 @@ inline void drawDateCardContent(int cardX, int cardY, int cardW, int cardH,
|
||||||
if (cardX + cardW <= 0 || cardX >= 320)
|
if (cardX + cardW <= 0 || cardX >= 320)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint16_t priCol = getThemePrimaryColor();
|
uint16_t accCol = isRainbowTheme() ? (uint16_t)0xFD20 : getThemeAccentColor();
|
||||||
|
uint16_t yearCol = isRainbowTheme() ? (uint16_t)0xFFE0 : accCol;
|
||||||
uint16_t cardBorder = 0x2945;
|
uint16_t cardBorder = 0x2945;
|
||||||
uint8_t wday = gpsDayOfWeek % 7;
|
uint8_t wday = gpsDayOfWeek % 7;
|
||||||
uint8_t mIdx = (gpsMonth >= 1 && gpsMonth <= 12) ? (gpsMonth - 1) : 0;
|
uint8_t mIdx = (gpsMonth >= 1 && gpsMonth <= 12) ? (gpsMonth - 1) : 0;
|
||||||
|
|
@ -41,7 +99,7 @@ inline void drawDateCardContent(int cardX, int cardY, int cardW, int cardH,
|
||||||
String dayStr = (gpsDay < 10) ? "0" + String(gpsDay) : String(gpsDay);
|
String dayStr = (gpsDay < 10) ? "0" + String(gpsDay) : String(gpsDay);
|
||||||
|
|
||||||
// Left vertical accent bar
|
// Left vertical accent bar
|
||||||
tft.fillRoundRect(cardX + 6, cardY + 8, 4, cardH - 16, 2, priCol);
|
tft.fillRoundRect(cardX + 6, cardY + 8, 4, cardH - 16, 2, accCol);
|
||||||
|
|
||||||
// --- CALENDAR BADGE (Left side: 80x82 px) ---
|
// --- CALENDAR BADGE (Left side: 80x82 px) ---
|
||||||
int bx = cardX + 20;
|
int bx = cardX + 20;
|
||||||
|
|
@ -53,10 +111,10 @@ inline void drawDateCardContent(int cardX, int cardY, int cardW, int cardH,
|
||||||
tft.fillRoundRect(bx, by, bw, bh, 6, 0x18C3);
|
tft.fillRoundRect(bx, by, bw, bh, 6, 0x18C3);
|
||||||
tft.drawRoundRect(bx, by, bw, bh, 6, cardBorder);
|
tft.drawRoundRect(bx, by, bw, bh, 6, cardBorder);
|
||||||
|
|
||||||
// Badge top header (Month abbreviated in Primary Color)
|
// Badge top header (Month abbreviated in Accent Color)
|
||||||
tft.fillRoundRect(bx, by, bw, 24, 6, priCol);
|
tft.fillRoundRect(bx, by, bw, 24, 6, accCol);
|
||||||
tft.fillRect(bx, by + 18, bw, 6, priCol); // flatten lower header corners
|
tft.fillRect(bx, by + 18, bw, 6, accCol); // flatten lower header corners
|
||||||
tft.setTextColor(TFT_BLACK, priCol);
|
tft.setTextColor(TFT_BLACK, accCol);
|
||||||
tft.drawCentreString(mShort.c_str(), bx + (bw / 2), by + 4, 2);
|
tft.drawCentreString(mShort.c_str(), bx + (bw / 2), by + 4, 2);
|
||||||
|
|
||||||
// Badge body: Big Day Number in DSEG7 34pt
|
// Badge body: Big Day Number in DSEG7 34pt
|
||||||
|
|
@ -69,22 +127,33 @@ inline void drawDateCardContent(int cardX, int cardY, int cardW, int cardH,
|
||||||
// --- DATE DETAILS (Right side) ---
|
// --- DATE DETAILS (Right side) ---
|
||||||
int tx = cardX + 116;
|
int tx = cardX + 116;
|
||||||
|
|
||||||
|
// Day of week in Latin Sans Bold 26 (Smooth antialiased, White, uppercase matching Page 1)
|
||||||
|
updateDayTitleSprite(wday);
|
||||||
|
if (sprDayTitle.created()) {
|
||||||
|
sprDayTitle.pushSprite(tx, cardY + 22);
|
||||||
|
} else {
|
||||||
tft.loadFont(Latin_Sans_Bold_26);
|
tft.loadFont(Latin_Sans_Bold_26);
|
||||||
tft.setTextPadding(0);
|
tft.setTextPadding(0);
|
||||||
|
|
||||||
// Day of week in Latin Sans Bold 26 (Smooth antialiased, White, uppercase)
|
|
||||||
tft.setTextColor(TFT_WHITE, cardBg);
|
tft.setTextColor(TFT_WHITE, cardBg);
|
||||||
tft.drawString(SPANISH_DAYS[wday], tx, cardY + 22, 1);
|
tft.drawString(SPANISH_DAYS[wday], tx, cardY + 22, 1);
|
||||||
|
tft.unloadFont();
|
||||||
|
}
|
||||||
|
|
||||||
// Elegant two-tone horizontal dividing line
|
// Elegant two-tone horizontal dividing line
|
||||||
tft.drawFastHLine(tx, cardY + 54, 180, cardBorder);
|
tft.drawFastHLine(tx, cardY + 54, 180, cardBorder);
|
||||||
tft.drawFastHLine(tx, cardY + 54, 50, priCol);
|
tft.drawFastHLine(tx, cardY + 54, 50, accCol);
|
||||||
|
|
||||||
// Year in Latin Sans Bold 26 (Smooth antialiased, Primary color)
|
// Year in Latin Sans Bold 26 (Smooth antialiased, Accent color)
|
||||||
tft.setTextColor(priCol, cardBg);
|
updateYearTitleSprite(gpsYear, yearCol);
|
||||||
|
if (sprYearTitle.created()) {
|
||||||
|
sprYearTitle.pushSprite(tx, cardY + 64);
|
||||||
|
} else {
|
||||||
|
tft.loadFont(Latin_Sans_Bold_26);
|
||||||
|
tft.setTextPadding(0);
|
||||||
|
tft.setTextColor(yearCol, cardBg);
|
||||||
tft.drawString(String(gpsYear).c_str(), tx, cardY + 64, 1);
|
tft.drawString(String(gpsYear).c_str(), tx, cardY + 64, 1);
|
||||||
|
|
||||||
tft.unloadFont();
|
tft.unloadFont();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authentic DSEG7 antialiased dot alpha matrix (from
|
// Authentic DSEG7 antialiased dot alpha matrix (from
|
||||||
|
|
@ -115,9 +184,10 @@ static const uint8_t DSEG7_DOT_ALPHA[11][13] PROGMEM = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper: Draw authentic 7-segment italic colon dots with true anti-aliasing
|
// Helper: Draw authentic 7-segment italic colon dots with true anti-aliasing
|
||||||
// matching DSEG7 typography
|
// matching DSEG7 typography and optional rainbow gradient
|
||||||
inline void draw7SegDot(TFT_eSPI &dst, int x0, int y0, uint16_t color,
|
template <typename T>
|
||||||
uint16_t bgCol, int clip_h = 240) {
|
inline void draw7SegDot(T &dst, int x0, int y0, uint16_t color,
|
||||||
|
uint16_t bgCol, int clip_h = 240, bool rainbow = false) {
|
||||||
dst.startWrite();
|
dst.startWrite();
|
||||||
for (int dy = 0; dy < 11; dy++) {
|
for (int dy = 0; dy < 11; dy++) {
|
||||||
int curY = y0 + dy;
|
int curY = y0 + dy;
|
||||||
|
|
@ -144,73 +214,32 @@ inline void draw7SegDot(TFT_eSPI &dst, int x0, int y0, uint16_t color,
|
||||||
dst.drawFastHLine(fxs, curY, fl, color);
|
dst.drawFastHLine(fxs, curY, fl, color);
|
||||||
fl = 0;
|
fl = 0;
|
||||||
}
|
}
|
||||||
dst.drawPixel(curX, curY, dst.alphaBlend(a, color, bgCol));
|
uint16_t fg = rainbow ? getRainbowScreenColor(curX, curY) : color;
|
||||||
|
dst.drawPixel(curX, curY, dst.alphaBlend(a, fg, bgCol));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fl) {
|
if (fl) {
|
||||||
dst.drawFastHLine(fxs, curY, fl, color);
|
if (rainbow) {
|
||||||
|
for (int i = 0; i < fl; i++) {
|
||||||
|
dst.drawPixel(fxs + i, curY, getRainbowScreenColor(fxs + i, curY));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
dst.endWrite();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void draw7SegColon(TFT_eSPI &dst, int clkX, int clkY, uint16_t color,
|
|
||||||
uint16_t bgCol = 0x10A2, int clip_h = 240) {
|
|
||||||
// Top dot: centered at clkX + 156.5, font row 19..29 (y = clkY + 30)
|
|
||||||
draw7SegDot(dst, clkX + 151, clkY + 30, color, bgCol, clip_h);
|
|
||||||
// Bottom dot: centered at clkX + 153.5, font row 53..63 (y = clkY + 64)
|
|
||||||
draw7SegDot(dst, clkX + 148, clkY + 64, color, bgCol, clip_h);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render authentic DSEG7 80pt italic digit directly into transition sprite
|
|
||||||
// using raw PROGMEM font bitmap (zero heap allocations, zero font-switching conflicts)
|
|
||||||
inline void drawDseg7Digit80(TFT_eSprite &dst, int x0, int y0, uint8_t digit,
|
|
||||||
uint16_t color, uint16_t bgCol, int clip_h = 240) {
|
|
||||||
if (digit > 9) return;
|
|
||||||
const uint8_t *bmp = &DSEG7_Modern_BoldItalic_80[388 + digit * 5200];
|
|
||||||
dst.startWrite();
|
|
||||||
for (int dy = 0; dy < 80; dy++) {
|
|
||||||
int curY = y0 + dy;
|
|
||||||
if (curY < 0 || curY >= clip_h)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const uint8_t *row = bmp + dy * 65;
|
|
||||||
int fl = 0;
|
|
||||||
int fxs = 0;
|
|
||||||
for (int dx = 0; dx < 65; dx++) {
|
|
||||||
int curX = x0 + dx;
|
|
||||||
if (curX < 0 || curX >= 320) {
|
|
||||||
if (fl) {
|
|
||||||
dst.drawFastHLine(fxs, curY, fl, color);
|
|
||||||
fl = 0;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
uint8_t a = pgm_read_byte(row + dx);
|
|
||||||
if (a == 0) {
|
|
||||||
if (fl) {
|
|
||||||
dst.drawFastHLine(fxs, curY, fl, color);
|
|
||||||
fl = 0;
|
|
||||||
}
|
|
||||||
} else if (a >= 235) {
|
|
||||||
if (fl == 0)
|
|
||||||
fxs = curX;
|
|
||||||
fl++;
|
|
||||||
} else {
|
} else {
|
||||||
if (fl) {
|
|
||||||
dst.drawFastHLine(fxs, curY, fl, color);
|
dst.drawFastHLine(fxs, curY, fl, color);
|
||||||
fl = 0;
|
|
||||||
}
|
}
|
||||||
dst.drawPixel(curX, curY, dst.alphaBlend(a, color, bgCol));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fl) {
|
|
||||||
dst.drawFastHLine(fxs, curY, fl, color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dst.endWrite();
|
dst.endWrite();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void draw7SegColon(T &dst, int clkX, int clkY, uint16_t color,
|
||||||
|
uint16_t bgCol = 0x10A2, int clip_h = 240, bool rainbow = false) {
|
||||||
|
// Top dot: centered at clkX + 151, font row 19..29 (y = clkY + 30)
|
||||||
|
draw7SegDot(dst, clkX + 151, clkY + 30, color, bgCol, clip_h, rainbow);
|
||||||
|
// Bottom dot: centered at clkX + 148, font row 53..63 (y = clkY + 64)
|
||||||
|
draw7SegDot(dst, clkX + 148, clkY + 64, color, bgCol, clip_h, rainbow);
|
||||||
|
}
|
||||||
|
|
||||||
// Synchronize tracking variables without touching TFT display (zero flicker)
|
// Synchronize tracking variables without touching TFT display (zero flicker)
|
||||||
inline void syncGpsClockTrackers() {
|
inline void syncGpsClockTrackers() {
|
||||||
oldClockHour = gpsHour;
|
oldClockHour = gpsHour;
|
||||||
|
|
@ -230,6 +259,7 @@ inline void drawGpsClockScreen(int x_offset = 0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t priCol = getThemePrimaryColor();
|
uint16_t priCol = getThemePrimaryColor();
|
||||||
|
uint16_t clkAccCol = isRainbowTheme() ? (uint16_t)0x07FF : priCol;
|
||||||
uint16_t cardBg = 0x10A2; // Dark graphite navy
|
uint16_t cardBg = 0x10A2; // Dark graphite navy
|
||||||
uint16_t cardBorder = 0x2945; // Subtle edge border
|
uint16_t cardBorder = 0x2945; // Subtle edge border
|
||||||
bool synced = isGpsSynced();
|
bool synced = isGpsSynced();
|
||||||
|
|
@ -251,24 +281,25 @@ inline void drawGpsClockScreen(int x_offset = 0) {
|
||||||
tft.drawRoundRect(clkX, clkY, clkW, clkH, 6, cardBorder);
|
tft.drawRoundRect(clkX, clkY, clkW, clkH, 6, cardBorder);
|
||||||
|
|
||||||
// Left vertical accent bar
|
// Left vertical accent bar
|
||||||
tft.fillRoundRect(clkX + 6, clkY + 8, 4, clkH - 16, 2, priCol);
|
tft.fillRoundRect(clkX + 6, clkY + 8, 4, clkH - 16, 2, clkAccCol);
|
||||||
|
|
||||||
// Digits: Hours HH (Right aligned to clkX + 142) & Minutes MM (Left aligned
|
// Digits: Hours HH & Minutes MM with smooth rainbow gradient
|
||||||
// to clkX + 170)
|
uint8_t h10 = (gpsHour / 10) % 10;
|
||||||
String hStr = (gpsHour < 10) ? "0" + String(gpsHour) : String(gpsHour);
|
uint8_t h1 = gpsHour % 10;
|
||||||
String mStr = (gpsMinute < 10) ? "0" + String(gpsMinute) : String(gpsMinute);
|
uint8_t m10 = (gpsMinute / 10) % 10;
|
||||||
|
uint8_t m1 = gpsMinute % 10;
|
||||||
|
bool rbClk = isRainbowTheme();
|
||||||
|
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_80);
|
drawDseg7Digit80(tft, clkX + 12, clkY + 11, h10, clkAccCol, cardBg, 240, rbClk);
|
||||||
tft.setTextColor(priCol, cardBg);
|
drawDseg7Digit80(tft, clkX + 77, clkY + 11, h1, clkAccCol, cardBg, 240, rbClk);
|
||||||
tft.setTextPadding(0);
|
drawDseg7Digit80(tft, clkX + 170, clkY + 11, m10, clkAccCol, cardBg, 240, rbClk);
|
||||||
tft.drawRightString(hStr.c_str(), clkX + 142, clkY + 11, 1);
|
drawDseg7Digit80(tft, clkX + 235, clkY + 11, m1, clkAccCol, cardBg, 240, rbClk);
|
||||||
tft.drawString(mStr.c_str(), clkX + 170, clkY + 11, 1);
|
|
||||||
tft.unloadFont();
|
|
||||||
|
|
||||||
// Blinking Colon Dots between HH and MM (italic 7-segment style with
|
// Blinking Colon Dots between HH and MM (italic 7-segment style with
|
||||||
// anti-aliasing)
|
// anti-aliasing)
|
||||||
uint16_t colonCol = (gpsSecond % 2 == 0) ? priCol : 0x18C3;
|
bool colOn = (gpsSecond % 2 == 0);
|
||||||
draw7SegColon(tft, clkX, clkY, colonCol, cardBg);
|
uint16_t colonCol = colOn ? clkAccCol : 0x18C3;
|
||||||
|
draw7SegColon(tft, clkX, clkY, colonCol, cardBg, 240, colOn && rbClk);
|
||||||
|
|
||||||
// 3. FULL-WIDTH DATE CARD (y = 125 to 235, height = 110, width = 312, x = 4)
|
// 3. FULL-WIDTH DATE CARD (y = 125 to 235, height = 110, width = 312, x = 4)
|
||||||
int dateX = 4 + x_offset;
|
int dateX = 4 + x_offset;
|
||||||
|
|
@ -289,6 +320,7 @@ inline void updateGpsClockScreen(bool forceRedraw = false) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint16_t priCol = getThemePrimaryColor();
|
uint16_t priCol = getThemePrimaryColor();
|
||||||
|
uint16_t clkAccCol = isRainbowTheme() ? (uint16_t)0x07FF : priCol;
|
||||||
uint16_t cardBg = 0x10A2;
|
uint16_t cardBg = 0x10A2;
|
||||||
int clkX = 4;
|
int clkX = 4;
|
||||||
int clkY = 19;
|
int clkY = 19;
|
||||||
|
|
@ -296,35 +328,29 @@ inline void updateGpsClockScreen(bool forceRedraw = false) {
|
||||||
// 1. Seconds change: update colon dots only (no seconds text)
|
// 1. Seconds change: update colon dots only (no seconds text)
|
||||||
if (forceRedraw || gpsSecond != oldClockSec) {
|
if (forceRedraw || gpsSecond != oldClockSec) {
|
||||||
oldClockSec = gpsSecond;
|
oldClockSec = gpsSecond;
|
||||||
|
bool colOn = (gpsSecond % 2 == 0);
|
||||||
// Colon dots (italic 7-segment style with anti-aliasing)
|
uint16_t colonCol = colOn ? clkAccCol : 0x18C3;
|
||||||
uint16_t colonCol = (gpsSecond % 2 == 0) ? priCol : 0x18C3;
|
draw7SegColon(tft, clkX, clkY, colonCol, cardBg, 240, colOn && isRainbowTheme());
|
||||||
draw7SegColon(tft, clkX, clkY, colonCol, cardBg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Minutes change: update MM digits
|
// 2. Minutes change: update MM digits
|
||||||
if (forceRedraw || gpsMinute != oldClockMin) {
|
if (forceRedraw || gpsMinute != oldClockMin) {
|
||||||
oldClockMin = gpsMinute;
|
oldClockMin = gpsMinute;
|
||||||
tft.fillRect(clkX + 166, clkY + 10, 142, 82, cardBg);
|
tft.fillRect(clkX + 166, clkY + 10, 142, 82, cardBg);
|
||||||
String mStr =
|
uint8_t m10 = (gpsMinute / 10) % 10;
|
||||||
(gpsMinute < 10) ? "0" + String(gpsMinute) : String(gpsMinute);
|
uint8_t m1 = gpsMinute % 10;
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_80);
|
drawDseg7Digit80(tft, clkX + 170, clkY + 11, m10, clkAccCol, cardBg, 240, isRainbowTheme());
|
||||||
tft.setTextColor(priCol, cardBg);
|
drawDseg7Digit80(tft, clkX + 235, clkY + 11, m1, clkAccCol, cardBg, 240, isRainbowTheme());
|
||||||
tft.setTextPadding(0);
|
|
||||||
tft.drawString(mStr.c_str(), clkX + 170, clkY + 11, 1);
|
|
||||||
tft.unloadFont();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Hours change: update HH digits
|
// 3. Hours change: update HH digits
|
||||||
if (forceRedraw || gpsHour != oldClockHour) {
|
if (forceRedraw || gpsHour != oldClockHour) {
|
||||||
oldClockHour = gpsHour;
|
oldClockHour = gpsHour;
|
||||||
tft.fillRect(clkX + 12, clkY + 10, 136, 82, cardBg);
|
tft.fillRect(clkX + 12, clkY + 10, 136, 82, cardBg);
|
||||||
String hStr = (gpsHour < 10) ? "0" + String(gpsHour) : String(gpsHour);
|
uint8_t h10 = (gpsHour / 10) % 10;
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_80);
|
uint8_t h1 = gpsHour % 10;
|
||||||
tft.setTextColor(priCol, cardBg);
|
drawDseg7Digit80(tft, clkX + 12, clkY + 11, h10, clkAccCol, cardBg, 240, isRainbowTheme());
|
||||||
tft.setTextPadding(0);
|
drawDseg7Digit80(tft, clkX + 77, clkY + 11, h1, clkAccCol, cardBg, 240, isRainbowTheme());
|
||||||
tft.drawRightString(hStr.c_str(), clkX + 142, clkY + 11, 1);
|
|
||||||
tft.unloadFont();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Date changed (midnight rollover): redraw full date card
|
// 4. Date changed (midnight rollover): redraw full date card
|
||||||
|
|
@ -347,6 +373,8 @@ inline void updateGpsClockScreen(bool forceRedraw = false) {
|
||||||
inline void renderPage2ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
inline void renderPage2ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
||||||
int slice_h) {
|
int slice_h) {
|
||||||
uint16_t priCol = getThemePrimaryColor();
|
uint16_t priCol = getThemePrimaryColor();
|
||||||
|
uint16_t accCol = getThemeAccentColor();
|
||||||
|
uint16_t clkAccCol = isRainbowTheme() ? (uint16_t)0x07FF : priCol;
|
||||||
uint16_t cardBg = 0x10A2;
|
uint16_t cardBg = 0x10A2;
|
||||||
uint16_t cardBorder = 0x2945;
|
uint16_t cardBorder = 0x2945;
|
||||||
|
|
||||||
|
|
@ -361,17 +389,13 @@ inline void renderPage2ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
||||||
if (clkX + clkW > 0 && clkX < 320 && clkY + clkH > 0 && clkY < slice_h) {
|
if (clkX + clkW > 0 && clkX < 320 && clkY + clkH > 0 && clkY < slice_h) {
|
||||||
s.fillRoundRect(clkX, clkY, clkW, clkH, 6, cardBg);
|
s.fillRoundRect(clkX, clkY, clkW, clkH, 6, cardBg);
|
||||||
s.drawRoundRect(clkX, clkY, clkW, clkH, 6, cardBorder);
|
s.drawRoundRect(clkX, clkY, clkW, clkH, 6, cardBorder);
|
||||||
s.fillRoundRect(clkX + 6, clkY + 8, 4, clkH - 16, 2, priCol);
|
s.fillRoundRect(clkX + 6, clkY + 8, 4, clkH - 16, 2, clkAccCol);
|
||||||
|
|
||||||
// Colon dots during transition (italic 7-segment style with anti-aliasing)
|
// Colon dots during transition (italic 7-segment style with anti-aliasing)
|
||||||
draw7SegColon(s, clkX, clkY, priCol, cardBg, slice_h);
|
draw7SegColon(s, clkX, clkY, clkAccCol, cardBg, slice_h, isRainbowTheme());
|
||||||
|
|
||||||
// Digits: Hours HH (Right aligned to clkX + 142) & Minutes MM (Left aligned
|
// Digits: Hours HH (Right aligned to clkX + 142) & Minutes MM (Left aligned
|
||||||
// to clkX + 170)
|
// to clkX + 170)
|
||||||
String hStr = (gpsHour < 10) ? "0" + String(gpsHour) : String(gpsHour);
|
|
||||||
String mStr =
|
|
||||||
(gpsMinute < 10) ? "0" + String(gpsMinute) : String(gpsMinute);
|
|
||||||
|
|
||||||
int digitY = clkY + 11;
|
int digitY = clkY + 11;
|
||||||
if (digitY + 80 > 0 && digitY < slice_h) {
|
if (digitY + 80 > 0 && digitY < slice_h) {
|
||||||
uint8_t h10 = (gpsHour / 10) % 10;
|
uint8_t h10 = (gpsHour / 10) % 10;
|
||||||
|
|
@ -379,10 +403,10 @@ inline void renderPage2ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
||||||
uint8_t m10 = (gpsMinute / 10) % 10;
|
uint8_t m10 = (gpsMinute / 10) % 10;
|
||||||
uint8_t m1 = gpsMinute % 10;
|
uint8_t m1 = gpsMinute % 10;
|
||||||
|
|
||||||
drawDseg7Digit80(s, clkX + 12, digitY, h10, priCol, cardBg, slice_h);
|
drawDseg7Digit80(s, clkX + 12, digitY, h10, clkAccCol, cardBg, slice_h, isRainbowTheme());
|
||||||
drawDseg7Digit80(s, clkX + 77, digitY, h1, priCol, cardBg, slice_h);
|
drawDseg7Digit80(s, clkX + 77, digitY, h1, clkAccCol, cardBg, slice_h, isRainbowTheme());
|
||||||
drawDseg7Digit80(s, clkX + 170, digitY, m10, priCol, cardBg, slice_h);
|
drawDseg7Digit80(s, clkX + 170, digitY, m10, clkAccCol, cardBg, slice_h, isRainbowTheme());
|
||||||
drawDseg7Digit80(s, clkX + 235, digitY, m1, priCol, cardBg, slice_h);
|
drawDseg7Digit80(s, clkX + 235, digitY, m1, clkAccCol, cardBg, slice_h, isRainbowTheme());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -394,9 +418,11 @@ inline void renderPage2ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
||||||
|
|
||||||
if (dateX + dateW > 0 && dateX < 320 && dateY + dateH > 0 &&
|
if (dateX + dateW > 0 && dateX < 320 && dateY + dateH > 0 &&
|
||||||
dateY < slice_h) {
|
dateY < slice_h) {
|
||||||
|
uint16_t dateAccCol = isRainbowTheme() ? (uint16_t)0xFD20 : accCol;
|
||||||
|
uint16_t yearCol = isRainbowTheme() ? (uint16_t)0xFFE0 : accCol;
|
||||||
s.fillRoundRect(dateX, dateY, dateW, dateH, 6, cardBg);
|
s.fillRoundRect(dateX, dateY, dateW, dateH, 6, cardBg);
|
||||||
s.drawRoundRect(dateX, dateY, dateW, dateH, 6, cardBorder);
|
s.drawRoundRect(dateX, dateY, dateW, dateH, 6, cardBorder);
|
||||||
s.fillRoundRect(dateX + 6, dateY + 8, 4, dateH - 16, 2, priCol);
|
s.fillRoundRect(dateX + 6, dateY + 8, 4, dateH - 16, 2, dateAccCol);
|
||||||
|
|
||||||
// Badge container
|
// Badge container
|
||||||
int bx = dateX + 20;
|
int bx = dateX + 20;
|
||||||
|
|
@ -408,10 +434,10 @@ inline void renderPage2ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
||||||
s.drawRoundRect(bx, by, bw, bh, 6, cardBorder);
|
s.drawRoundRect(bx, by, bw, bh, 6, cardBorder);
|
||||||
|
|
||||||
// Header strip
|
// Header strip
|
||||||
s.fillRoundRect(bx, by, bw, 24, 6, priCol);
|
s.fillRoundRect(bx, by, bw, 24, 6, dateAccCol);
|
||||||
s.fillRect(bx, by + 18, bw, 6, priCol);
|
s.fillRect(bx, by + 18, bw, 6, dateAccCol);
|
||||||
uint8_t mIdx = (gpsMonth >= 1 && gpsMonth <= 12) ? (gpsMonth - 1) : 0;
|
uint8_t mIdx = (gpsMonth >= 1 && gpsMonth <= 12) ? (gpsMonth - 1) : 0;
|
||||||
s.setTextColor(TFT_BLACK, priCol);
|
s.setTextColor(TFT_BLACK, dateAccCol);
|
||||||
s.drawCentreString(SPANISH_MONTHS_SHORT[mIdx], bx + (bw / 2), by + 4, 2);
|
s.drawCentreString(SPANISH_MONTHS_SHORT[mIdx], bx + (bw / 2), by + 4, 2);
|
||||||
|
|
||||||
// Badge day number
|
// Badge day number
|
||||||
|
|
@ -427,20 +453,30 @@ inline void renderPage2ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
||||||
uint8_t wday = gpsDayOfWeek % 7;
|
uint8_t wday = gpsDayOfWeek % 7;
|
||||||
int dwY = dateY + 22;
|
int dwY = dateY + 22;
|
||||||
if (dwY + 26 > 0 && dwY < slice_h) {
|
if (dwY + 26 > 0 && dwY < slice_h) {
|
||||||
|
updateDayTitleSprite(wday);
|
||||||
|
if (sprDayTitle.created()) {
|
||||||
|
sprDayTitle.pushToSprite(&s, tx, dwY);
|
||||||
|
} else {
|
||||||
s.setTextColor(TFT_WHITE, cardBg);
|
s.setTextColor(TFT_WHITE, cardBg);
|
||||||
s.drawString(SPANISH_DAYS[wday], tx, dwY, 4);
|
s.drawString(SPANISH_DAYS[wday], tx, dwY, 4);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int divY = dateY + 54;
|
int divY = dateY + 54;
|
||||||
if (divY > 0 && divY < slice_h) {
|
if (divY > 0 && divY < slice_h) {
|
||||||
s.drawFastHLine(tx, divY, 180, cardBorder);
|
s.drawFastHLine(tx, divY, 180, cardBorder);
|
||||||
s.drawFastHLine(tx, divY, 50, priCol);
|
s.drawFastHLine(tx, divY, 50, dateAccCol);
|
||||||
}
|
}
|
||||||
|
|
||||||
int yrY = dateY + 64;
|
int yrY = dateY + 64;
|
||||||
if (yrY + 26 > 0 && yrY < slice_h) {
|
if (yrY + 26 > 0 && yrY < slice_h) {
|
||||||
s.setTextColor(priCol, cardBg);
|
updateYearTitleSprite(gpsYear, yearCol);
|
||||||
|
if (sprYearTitle.created()) {
|
||||||
|
sprYearTitle.pushToSprite(&s, tx, yrY);
|
||||||
|
} else {
|
||||||
|
s.setTextColor(yearCol, cardBg);
|
||||||
s.drawString(String(gpsYear).c_str(), tx, yrY, 4);
|
s.drawString(String(gpsYear).c_str(), tx, yrY, 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -531,6 +531,7 @@ inline uint32_t getGpsFailedChecksum() { return gps.failedChecksum(); }
|
||||||
inline bool isGpsSerialReceiving() {
|
inline bool isGpsSerialReceiving() {
|
||||||
return (gps.charsProcessed() > 0 && (millis() - lastGpsByteTime < 2500));
|
return (gps.charsProcessed() > 0 && (millis() - lastGpsByteTime < 2500));
|
||||||
}
|
}
|
||||||
|
inline bool isGpsUartLocked() { return gpsUartLocked; }
|
||||||
|
|
||||||
inline const char *getGpsDiagMessage() {
|
inline const char *getGpsDiagMessage() {
|
||||||
if (gpsIsSleeping)
|
if (gpsIsSleeping)
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,13 @@
|
||||||
#include <TFT_eSPI.h>
|
#include <TFT_eSPI.h>
|
||||||
#include "gps_module.h"
|
#include "gps_module.h"
|
||||||
#include "DSEG7_Modern_BoldItalic_34.h"
|
#include "DSEG7_Modern_BoldItalic_34.h"
|
||||||
|
#include "rainbow_text.h"
|
||||||
|
|
||||||
extern TFT_eSPI tft;
|
extern TFT_eSPI tft;
|
||||||
extern uint16_t getThemePrimaryColor();
|
extern uint16_t getThemePrimaryColor();
|
||||||
|
extern uint16_t getThemeAccentColor();
|
||||||
extern uint16_t getThemeBgColor();
|
extern uint16_t getThemeBgColor();
|
||||||
|
extern bool isRainbowTheme();
|
||||||
extern int8_t currentScreenPage;
|
extern int8_t currentScreenPage;
|
||||||
extern int8_t fullScreenCell;
|
extern int8_t fullScreenCell;
|
||||||
|
|
||||||
|
|
@ -27,6 +30,7 @@ static bool lastNavHasCourse = false;
|
||||||
static const char* lastNavCardDir = "";
|
static const char* lastNavCardDir = "";
|
||||||
static uint8_t lastNavCardSats = 255;
|
static uint8_t lastNavCardSats = 255;
|
||||||
static bool lastNavSatsSync = false;
|
static bool lastNavSatsSync = false;
|
||||||
|
static bool lastNavUartLocked = false;
|
||||||
static float lastNavCardHdop = -999.0f;
|
static float lastNavCardHdop = -999.0f;
|
||||||
static char lastNavCardDiag[32] = "";
|
static char lastNavCardDiag[32] = "";
|
||||||
static bool lastNavDiagSynced = false;
|
static bool lastNavDiagSynced = false;
|
||||||
|
|
@ -51,6 +55,7 @@ inline void syncGpsNavTrackers() {
|
||||||
|
|
||||||
lastNavCardSats = gpsSats;
|
lastNavCardSats = gpsSats;
|
||||||
lastNavSatsSync = synced;
|
lastNavSatsSync = synced;
|
||||||
|
lastNavUartLocked = isGpsUartLocked();
|
||||||
oldNavSats = gpsSats;
|
oldNavSats = gpsSats;
|
||||||
|
|
||||||
lastNavCardHdop = gpsHdop;
|
lastNavCardHdop = gpsHdop;
|
||||||
|
|
@ -87,15 +92,16 @@ inline void drawGpsNavScreen(int x_offset = 0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t priCol = getThemePrimaryColor();
|
uint16_t priCol = getThemePrimaryColor();
|
||||||
|
uint16_t accCol = getThemeAccentColor();
|
||||||
|
uint16_t c0Col = isRainbowTheme() ? (uint16_t)0x07FF : priCol;
|
||||||
|
uint16_t c1Col = isRainbowTheme() ? (uint16_t)0x07E0 : accCol;
|
||||||
|
uint16_t c2Col = isRainbowTheme() ? (uint16_t)0xFFE0 : priCol;
|
||||||
|
uint16_t c3Col = isRainbowTheme() ? (uint16_t)0xFD20 : accCol;
|
||||||
uint16_t cardBg = 0x10A2; // Dark graphite navy
|
uint16_t cardBg = 0x10A2; // Dark graphite navy
|
||||||
uint16_t cardBorder = 0x2945; // Subtle edge border
|
uint16_t cardBorder = 0x2945; // Subtle edge border
|
||||||
bool synced = isGpsSynced();
|
bool synced = isGpsSynced();
|
||||||
|
|
||||||
// 1. TOP BAR (y = 0 to 18)
|
// 1. TOP BAR (y = 0 to 18)
|
||||||
// Left: Screen Title
|
|
||||||
tft.setTextColor(0x8410, TFT_BLACK);
|
|
||||||
tft.drawString("TELEMETRIA GPS", 8 + x_offset, 5, 1);
|
|
||||||
|
|
||||||
// Center: 4 Pagination Indicator Dots [○ ○ ○ ●] (Page 4 active)
|
// Center: 4 Pagination Indicator Dots [○ ○ ○ ●] (Page 4 active)
|
||||||
tft.fillCircle(139 + x_offset, 2, 2, 0x5AEB); // Page 1 inactive
|
tft.fillCircle(139 + x_offset, 2, 2, 0x5AEB); // Page 1 inactive
|
||||||
tft.fillCircle(153 + x_offset, 2, 2, 0x5AEB); // Page 2 inactive
|
tft.fillCircle(153 + x_offset, 2, 2, 0x5AEB); // Page 2 inactive
|
||||||
|
|
@ -109,7 +115,7 @@ inline void drawGpsNavScreen(int x_offset = 0) {
|
||||||
int cH1 = 102;
|
int cH1 = 102;
|
||||||
tft.fillRoundRect(c0X, c0Y, cW, cH1, 6, cardBg);
|
tft.fillRoundRect(c0X, c0Y, cW, cH1, 6, cardBg);
|
||||||
tft.drawRoundRect(c0X, c0Y, cW, cH1, 6, cardBorder);
|
tft.drawRoundRect(c0X, c0Y, cW, cH1, 6, cardBorder);
|
||||||
tft.fillRoundRect(c0X + 6, c0Y + 8, 4, cH1 - 16, 2, priCol);
|
tft.fillRoundRect(c0X + 6, c0Y + 8, 4, cH1 - 16, 2, c0Col);
|
||||||
|
|
||||||
tft.setTextColor(0x8410, cardBg);
|
tft.setTextColor(0x8410, cardBg);
|
||||||
tft.drawString("ALTITUD", c0X + 16, c0Y + 8, 2);
|
tft.drawString("ALTITUD", c0X + 16, c0Y + 8, 2);
|
||||||
|
|
@ -118,32 +124,24 @@ inline void drawGpsNavScreen(int x_offset = 0) {
|
||||||
|
|
||||||
bool altValid = isGpsAltitudeValid();
|
bool altValid = isGpsAltitudeValid();
|
||||||
String altStr = altValid ? String((int)round(gpsAltitudeM)) : "---";
|
String altStr = altValid ? String((int)round(gpsAltitudeM)) : "---";
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
drawDseg7RightString34(tft, altStr.c_str(), c0X + 140, c0Y + 28, c0Col, cardBg, 0, 240, isRainbowTheme());
|
||||||
tft.setTextColor(priCol, cardBg);
|
|
||||||
tft.setTextPadding(0);
|
|
||||||
tft.drawRightString(altStr.c_str(), c0X + 140, c0Y + 28, 1);
|
|
||||||
tft.unloadFont();
|
|
||||||
|
|
||||||
// 3. CARD 1 (Top Right): RUMBO / BRÚJULA (x = 164, y = 19, w = 152, h = 102)
|
// 3. CARD 1 (Top Right): RUMBO / BRÚJULA (x = 164, y = 19, w = 152, h = 102)
|
||||||
int c1X = 164 + x_offset;
|
int c1X = 164 + x_offset;
|
||||||
int c1Y = 19;
|
int c1Y = 19;
|
||||||
tft.fillRoundRect(c1X, c1Y, cW, cH1, 6, cardBg);
|
tft.fillRoundRect(c1X, c1Y, cW, cH1, 6, cardBg);
|
||||||
tft.drawRoundRect(c1X, c1Y, cW, cH1, 6, cardBorder);
|
tft.drawRoundRect(c1X, c1Y, cW, cH1, 6, cardBorder);
|
||||||
tft.fillRoundRect(c1X + 6, c1Y + 8, 4, cH1 - 16, 2, priCol);
|
tft.fillRoundRect(c1X + 6, c1Y + 8, 4, cH1 - 16, 2, c1Col);
|
||||||
|
|
||||||
tft.setTextColor(0x8410, cardBg);
|
tft.setTextColor(0x8410, cardBg);
|
||||||
tft.drawString("RUMBO / BRUJULA", c1X + 16, c1Y + 8, 2);
|
tft.drawString("RUMBO / BRUJULA", c1X + 16, c1Y + 8, 2);
|
||||||
|
|
||||||
bool hasCourse = synced && isGpsCourseValid();
|
bool hasCourse = synced && isGpsCourseValid();
|
||||||
String crsStr = hasCourse ? String((int)round(gpsCourseDeg)) : "---";
|
String crsStr = hasCourse ? String((int)round(gpsCourseDeg)) : "---";
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
drawDseg7RightString34(tft, crsStr.c_str(), c1X + 130, c1Y + 28, c1Col, cardBg, 0, 240, isRainbowTheme());
|
||||||
tft.setTextColor(priCol, cardBg);
|
|
||||||
tft.setTextPadding(0);
|
|
||||||
tft.drawRightString(crsStr.c_str(), c1X + 130, c1Y + 28, 1);
|
|
||||||
tft.unloadFont();
|
|
||||||
if (hasCourse) {
|
if (hasCourse) {
|
||||||
tft.drawCircle(c1X + 137, c1Y + 33, 3, priCol);
|
tft.drawCircle(c1X + 137, c1Y + 33, 3, c1Col);
|
||||||
tft.drawCircle(c1X + 137, c1Y + 33, 2, priCol);
|
tft.drawCircle(c1X + 137, c1Y + 33, 2, c1Col);
|
||||||
} else {
|
} else {
|
||||||
tft.fillCircle(c1X + 137, c1Y + 33, 4, cardBg);
|
tft.fillCircle(c1X + 137, c1Y + 33, 4, cardBg);
|
||||||
}
|
}
|
||||||
|
|
@ -158,22 +156,18 @@ inline void drawGpsNavScreen(int x_offset = 0) {
|
||||||
int cH2 = 110;
|
int cH2 = 110;
|
||||||
tft.fillRoundRect(c2X, c2Y, cW, cH2, 6, cardBg);
|
tft.fillRoundRect(c2X, c2Y, cW, cH2, 6, cardBg);
|
||||||
tft.drawRoundRect(c2X, c2Y, cW, cH2, 6, cardBorder);
|
tft.drawRoundRect(c2X, c2Y, cW, cH2, 6, cardBorder);
|
||||||
tft.fillRoundRect(c2X + 6, c2Y + 8, 4, cH2 - 16, 2, priCol);
|
tft.fillRoundRect(c2X + 6, c2Y + 8, 4, cH2 - 16, 2, c2Col);
|
||||||
|
|
||||||
tft.setTextColor(0x8410, cardBg);
|
tft.setTextColor(0x8410, cardBg);
|
||||||
tft.drawString("SATELITES GPS", c2X + 16, c2Y + 8, 2);
|
tft.drawString("SATELITES GPS", c2X + 16, c2Y + 8, 2);
|
||||||
|
|
||||||
// Large Satellite Icon (animated body + waves)
|
// Large Satellite Icon (animated body + waves)
|
||||||
uint8_t waveState = getGpsWaveAnimState();
|
uint8_t waveState = getGpsWaveAnimState();
|
||||||
drawLargeSatelliteIcon(c2X + 16, c2Y + 32, priCol, waveState, cardBg);
|
drawLargeSatelliteIcon(c2X + 16, c2Y + 32, c2Col, waveState, cardBg);
|
||||||
|
|
||||||
// Large Satellites Count (DSEG7 34pt)
|
// Large Satellites Count (DSEG7 34pt): Shows "---" while scanning/probing UART, "0" or count when locked
|
||||||
String satStr = String(gpsSats);
|
String satStr = isGpsUartLocked() ? String(gpsSats) : "---";
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
drawDseg7RightString34(tft, satStr.c_str(), c2X + 140, c2Y + 26, c2Col, cardBg, 0, 240, isRainbowTheme());
|
||||||
tft.setTextColor(priCol, cardBg);
|
|
||||||
tft.setTextPadding(0);
|
|
||||||
tft.drawRightString(satStr.c_str(), c2X + 140, c2Y + 26, 1);
|
|
||||||
tft.unloadFont();
|
|
||||||
|
|
||||||
// Telemetry details: HDOP precision & live diagnostic status
|
// Telemetry details: HDOP precision & live diagnostic status
|
||||||
String hdopStr = "HDOP: " + (synced ? String(gpsHdop, 1) : "---");
|
String hdopStr = "HDOP: " + (synced ? String(gpsHdop, 1) : "---");
|
||||||
|
|
@ -188,7 +182,7 @@ inline void drawGpsNavScreen(int x_offset = 0) {
|
||||||
int c3Y = 125;
|
int c3Y = 125;
|
||||||
tft.fillRoundRect(c3X, c3Y, cW, cH2, 6, cardBg);
|
tft.fillRoundRect(c3X, c3Y, cW, cH2, 6, cardBg);
|
||||||
tft.drawRoundRect(c3X, c3Y, cW, cH2, 6, cardBorder);
|
tft.drawRoundRect(c3X, c3Y, cW, cH2, 6, cardBorder);
|
||||||
tft.fillRoundRect(c3X + 6, c3Y + 8, 4, cH2 - 16, 2, priCol);
|
tft.fillRoundRect(c3X + 6, c3Y + 8, 4, cH2 - 16, 2, c3Col);
|
||||||
|
|
||||||
tft.setTextColor(0x8410, cardBg);
|
tft.setTextColor(0x8410, cardBg);
|
||||||
tft.drawString("COORDENADAS", c3X + 16, c3Y + 8, 2);
|
tft.drawString("COORDENADAS", c3X + 16, c3Y + 8, 2);
|
||||||
|
|
@ -196,13 +190,13 @@ inline void drawGpsNavScreen(int x_offset = 0) {
|
||||||
tft.setTextColor(0x8410, cardBg);
|
tft.setTextColor(0x8410, cardBg);
|
||||||
tft.drawString("LAT", c3X + 16, c3Y + 26, 1);
|
tft.drawString("LAT", c3X + 16, c3Y + 26, 1);
|
||||||
String latStr = gpsLocationValid ? (String(gpsLatitude, 5) + "°") : "--.-----°";
|
String latStr = gpsLocationValid ? (String(gpsLatitude, 5) + "°") : "--.-----°";
|
||||||
tft.setTextColor(priCol, cardBg);
|
tft.setTextColor(c3Col, cardBg);
|
||||||
tft.drawString(latStr.c_str(), c3X + 16, c3Y + 38, 2);
|
tft.drawString(latStr.c_str(), c3X + 16, c3Y + 38, 2);
|
||||||
|
|
||||||
tft.setTextColor(0x8410, cardBg);
|
tft.setTextColor(0x8410, cardBg);
|
||||||
tft.drawString("LON", c3X + 16, c3Y + 64, 1);
|
tft.drawString("LON", c3X + 16, c3Y + 64, 1);
|
||||||
String lonStr = gpsLocationValid ? (String(gpsLongitude, 5) + "°") : "--.-----°";
|
String lonStr = gpsLocationValid ? (String(gpsLongitude, 5) + "°") : "--.-----°";
|
||||||
tft.setTextColor(priCol, cardBg);
|
tft.setTextColor(c3Col, cardBg);
|
||||||
tft.drawString(lonStr.c_str(), c3X + 16, c3Y + 76, 2);
|
tft.drawString(lonStr.c_str(), c3X + 16, c3Y + 76, 2);
|
||||||
|
|
||||||
// Sync state trackers
|
// Sync state trackers
|
||||||
|
|
@ -222,6 +216,11 @@ inline void updateGpsNavScreen(bool forceRedraw = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t priCol = getThemePrimaryColor();
|
uint16_t priCol = getThemePrimaryColor();
|
||||||
|
uint16_t accCol = getThemeAccentColor();
|
||||||
|
uint16_t c0Col = isRainbowTheme() ? (uint16_t)0x07FF : priCol;
|
||||||
|
uint16_t c1Col = isRainbowTheme() ? (uint16_t)0x07E0 : accCol;
|
||||||
|
uint16_t c2Col = isRainbowTheme() ? (uint16_t)0xFFE0 : priCol;
|
||||||
|
uint16_t c3Col = isRainbowTheme() ? (uint16_t)0xFD20 : accCol;
|
||||||
uint16_t cardBg = 0x10A2;
|
uint16_t cardBg = 0x10A2;
|
||||||
bool synced = isGpsSynced();
|
bool synced = isGpsSynced();
|
||||||
|
|
||||||
|
|
@ -235,12 +234,7 @@ inline void updateGpsNavScreen(bool forceRedraw = false) {
|
||||||
int c0X = 4;
|
int c0X = 4;
|
||||||
int c0Y = 19;
|
int c0Y = 19;
|
||||||
String altStr = altValid ? String(curAltRound) : "---";
|
String altStr = altValid ? String(curAltRound) : "---";
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
drawDseg7RightString34(tft, altStr.c_str(), c0X + 140, c0Y + 28, c0Col, cardBg, 126, 240, isRainbowTheme());
|
||||||
tft.setTextColor(priCol, cardBg);
|
|
||||||
tft.setTextPadding(126);
|
|
||||||
tft.drawRightString(altStr.c_str(), c0X + 140, c0Y + 28, 1);
|
|
||||||
tft.setTextPadding(0);
|
|
||||||
tft.unloadFont();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Course / Heading changed: update Card 1
|
// 2. Course / Heading changed: update Card 1
|
||||||
|
|
@ -254,16 +248,11 @@ inline void updateGpsNavScreen(bool forceRedraw = false) {
|
||||||
oldNavCourse = gpsCourseDeg;
|
oldNavCourse = gpsCourseDeg;
|
||||||
lastNavHasCourse = hasCourse;
|
lastNavHasCourse = hasCourse;
|
||||||
String crsStr = hasCourse ? String(curCrsRound) : "---";
|
String crsStr = hasCourse ? String(curCrsRound) : "---";
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
drawDseg7RightString34(tft, crsStr.c_str(), c1X + 130, c1Y + 28, c1Col, cardBg, 85, 240, isRainbowTheme());
|
||||||
tft.setTextColor(priCol, cardBg);
|
|
||||||
tft.setTextPadding(85);
|
|
||||||
tft.drawRightString(crsStr.c_str(), c1X + 130, c1Y + 28, 1);
|
|
||||||
tft.setTextPadding(0);
|
|
||||||
tft.unloadFont();
|
|
||||||
|
|
||||||
if (hasCourse) {
|
if (hasCourse) {
|
||||||
tft.drawCircle(c1X + 137, c1Y + 33, 3, priCol);
|
tft.drawCircle(c1X + 137, c1Y + 33, 3, c1Col);
|
||||||
tft.drawCircle(c1X + 137, c1Y + 33, 2, priCol);
|
tft.drawCircle(c1X + 137, c1Y + 33, 2, c1Col);
|
||||||
} else {
|
} else {
|
||||||
tft.fillCircle(c1X + 137, c1Y + 33, 4, cardBg);
|
tft.fillCircle(c1X + 137, c1Y + 33, 4, cardBg);
|
||||||
}
|
}
|
||||||
|
|
@ -284,17 +273,14 @@ inline void updateGpsNavScreen(bool forceRedraw = false) {
|
||||||
int c2Y = 125;
|
int c2Y = 125;
|
||||||
|
|
||||||
// 3a. Satellites Count (DSEG7 34pt)
|
// 3a. Satellites Count (DSEG7 34pt)
|
||||||
if (forceRedraw || gpsSats != lastNavCardSats || synced != lastNavSatsSync) {
|
bool uartLocked = isGpsUartLocked();
|
||||||
|
if (forceRedraw || gpsSats != lastNavCardSats || synced != lastNavSatsSync || uartLocked != lastNavUartLocked) {
|
||||||
lastNavCardSats = gpsSats;
|
lastNavCardSats = gpsSats;
|
||||||
lastNavSatsSync = synced;
|
lastNavSatsSync = synced;
|
||||||
|
lastNavUartLocked = uartLocked;
|
||||||
oldNavSats = gpsSats;
|
oldNavSats = gpsSats;
|
||||||
String satStr = String(gpsSats);
|
String satStr = uartLocked ? String(gpsSats) : "---";
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
drawDseg7RightString34(tft, satStr.c_str(), c2X + 140, c2Y + 26, c2Col, cardBg, 85, 240, isRainbowTheme());
|
||||||
tft.setTextColor(priCol, cardBg);
|
|
||||||
tft.setTextPadding(85);
|
|
||||||
tft.drawRightString(satStr.c_str(), c2X + 140, c2Y + 26, 1);
|
|
||||||
tft.setTextPadding(0);
|
|
||||||
tft.unloadFont();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3b. HDOP precision
|
// 3b. HDOP precision
|
||||||
|
|
@ -316,7 +302,7 @@ inline void updateGpsNavScreen(bool forceRedraw = false) {
|
||||||
lastNavCardDiag[sizeof(lastNavCardDiag) - 1] = '\0';
|
lastNavCardDiag[sizeof(lastNavCardDiag) - 1] = '\0';
|
||||||
lastNavDiagSynced = synced;
|
lastNavDiagSynced = synced;
|
||||||
tft.setTextPadding(130);
|
tft.setTextPadding(130);
|
||||||
tft.setTextColor(synced ? 0x9CD3 : priCol, cardBg);
|
tft.setTextColor(synced ? 0x9CD3 : c2Col, cardBg);
|
||||||
tft.drawString(curDiag, c2X + 16, c2Y + 90, 1);
|
tft.drawString(curDiag, c2X + 16, c2Y + 90, 1);
|
||||||
tft.setTextPadding(0);
|
tft.setTextPadding(0);
|
||||||
}
|
}
|
||||||
|
|
@ -326,7 +312,7 @@ inline void updateGpsNavScreen(bool forceRedraw = false) {
|
||||||
if (forceRedraw || curWaveState != oldNavWaveState || synced != oldNavSynced) {
|
if (forceRedraw || curWaveState != oldNavWaveState || synced != oldNavSynced) {
|
||||||
oldNavWaveState = curWaveState;
|
oldNavWaveState = curWaveState;
|
||||||
oldNavSynced = synced;
|
oldNavSynced = synced;
|
||||||
drawLargeSatelliteIcon(c2X + 16, c2Y + 32, priCol, curWaveState, cardBg);
|
drawLargeSatelliteIcon(c2X + 16, c2Y + 32, c2Col, curWaveState, cardBg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Coordinates changed: update Card 3 (setTextPadding avoids fillRect flicker)
|
// 4. Coordinates changed: update Card 3 (setTextPadding avoids fillRect flicker)
|
||||||
|
|
@ -337,7 +323,7 @@ inline void updateGpsNavScreen(bool forceRedraw = false) {
|
||||||
int c3Y = 125;
|
int c3Y = 125;
|
||||||
String latStr = gpsLocationValid ? (String(gpsLatitude, 5) + "°") : "--.-----°";
|
String latStr = gpsLocationValid ? (String(gpsLatitude, 5) + "°") : "--.-----°";
|
||||||
tft.setTextPadding(130);
|
tft.setTextPadding(130);
|
||||||
tft.setTextColor(priCol, cardBg);
|
tft.setTextColor(c3Col, cardBg);
|
||||||
tft.drawString(latStr.c_str(), c3X + 16, c3Y + 38, 2);
|
tft.drawString(latStr.c_str(), c3X + 16, c3Y + 38, 2);
|
||||||
|
|
||||||
String lonStr = gpsLocationValid ? (String(gpsLongitude, 5) + "°") : "--.-----°";
|
String lonStr = gpsLocationValid ? (String(gpsLongitude, 5) + "°") : "--.-----°";
|
||||||
|
|
@ -349,16 +335,15 @@ inline void updateGpsNavScreen(bool forceRedraw = false) {
|
||||||
// Render Page 3 into double-buffered sprite slice for slide / fade transitions
|
// Render Page 3 into double-buffered sprite slice for slide / fade transitions
|
||||||
inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int slice_h) {
|
inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int slice_h) {
|
||||||
uint16_t priCol = getThemePrimaryColor();
|
uint16_t priCol = getThemePrimaryColor();
|
||||||
|
uint16_t accCol = getThemeAccentColor();
|
||||||
|
uint16_t c0Col = isRainbowTheme() ? (uint16_t)0x07FF : priCol;
|
||||||
|
uint16_t c1Col = isRainbowTheme() ? (uint16_t)0x07E0 : accCol;
|
||||||
|
uint16_t c2Col = isRainbowTheme() ? (uint16_t)0xFFE0 : priCol;
|
||||||
|
uint16_t c3Col = isRainbowTheme() ? (uint16_t)0xFD20 : accCol;
|
||||||
uint16_t cardBg = 0x10A2;
|
uint16_t cardBg = 0x10A2;
|
||||||
uint16_t cardBorder = 0x2945;
|
uint16_t cardBorder = 0x2945;
|
||||||
bool synced = isGpsSynced();
|
bool synced = isGpsSynced();
|
||||||
|
|
||||||
// 1. TOP BAR in top slice
|
|
||||||
if (y_slice == 0) {
|
|
||||||
s.setTextColor(0x8410, TFT_BLACK);
|
|
||||||
s.drawString("TELEMETRIA GPS", 8 + x_offset, 5, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. CARD 0 (Top Left): ALTITUD (y = 19 to 121)
|
// 2. CARD 0 (Top Left): ALTITUD (y = 19 to 121)
|
||||||
int c0X = 4 + x_offset;
|
int c0X = 4 + x_offset;
|
||||||
int c0Y = 19 - y_slice;
|
int c0Y = 19 - y_slice;
|
||||||
|
|
@ -367,7 +352,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
|
||||||
if (c0X + cW > 0 && c0X < 320 && c0Y + cH1 > 0 && c0Y < slice_h) {
|
if (c0X + cW > 0 && c0X < 320 && c0Y + cH1 > 0 && c0Y < slice_h) {
|
||||||
s.fillRoundRect(c0X, c0Y, cW, cH1, 6, cardBg);
|
s.fillRoundRect(c0X, c0Y, cW, cH1, 6, cardBg);
|
||||||
s.drawRoundRect(c0X, c0Y, cW, cH1, 6, cardBorder);
|
s.drawRoundRect(c0X, c0Y, cW, cH1, 6, cardBorder);
|
||||||
s.fillRoundRect(c0X + 6, c0Y + 8, 4, cH1 - 16, 2, priCol);
|
s.fillRoundRect(c0X + 6, c0Y + 8, 4, cH1 - 16, 2, c0Col);
|
||||||
|
|
||||||
if (c0Y + 24 > 0 && c0Y + 8 < slice_h) {
|
if (c0Y + 24 > 0 && c0Y + 8 < slice_h) {
|
||||||
s.setTextColor(0x8410, cardBg);
|
s.setTextColor(0x8410, cardBg);
|
||||||
|
|
@ -379,11 +364,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
|
||||||
String altStr = altValid ? String((int)round(gpsAltitudeM)) : "---";
|
String altStr = altValid ? String((int)round(gpsAltitudeM)) : "---";
|
||||||
int valY = c0Y + 28;
|
int valY = c0Y + 28;
|
||||||
if (valY < slice_h && valY + 38 > 0) {
|
if (valY < slice_h && valY + 38 > 0) {
|
||||||
s.fontLoaded = true;
|
drawDseg7RightString34(s, altStr.c_str(), c0X + 140, valY, c0Col, cardBg, 0, slice_h, isRainbowTheme());
|
||||||
s.setTextColor(priCol, cardBg);
|
|
||||||
s.setTextPadding(0);
|
|
||||||
s.drawRightString(altStr.c_str(), c0X + 140, valY, 1);
|
|
||||||
s.fontLoaded = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -393,7 +374,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
|
||||||
if (c1X + cW > 0 && c1X < 320 && c1Y + cH1 > 0 && c1Y < slice_h) {
|
if (c1X + cW > 0 && c1X < 320 && c1Y + cH1 > 0 && c1Y < slice_h) {
|
||||||
s.fillRoundRect(c1X, c1Y, cW, cH1, 6, cardBg);
|
s.fillRoundRect(c1X, c1Y, cW, cH1, 6, cardBg);
|
||||||
s.drawRoundRect(c1X, c1Y, cW, cH1, 6, cardBorder);
|
s.drawRoundRect(c1X, c1Y, cW, cH1, 6, cardBorder);
|
||||||
s.fillRoundRect(c1X + 6, c1Y + 8, 4, cH1 - 16, 2, priCol);
|
s.fillRoundRect(c1X + 6, c1Y + 8, 4, cH1 - 16, 2, c1Col);
|
||||||
|
|
||||||
if (c1Y + 24 > 0 && c1Y + 8 < slice_h) {
|
if (c1Y + 24 > 0 && c1Y + 8 < slice_h) {
|
||||||
s.setTextColor(0x8410, cardBg);
|
s.setTextColor(0x8410, cardBg);
|
||||||
|
|
@ -403,16 +384,12 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
|
||||||
String crsStr = hasCourse ? String((int)round(gpsCourseDeg)) : "---";
|
String crsStr = hasCourse ? String((int)round(gpsCourseDeg)) : "---";
|
||||||
int valY = c1Y + 28;
|
int valY = c1Y + 28;
|
||||||
if (valY < slice_h && valY + 38 > 0) {
|
if (valY < slice_h && valY + 38 > 0) {
|
||||||
s.fontLoaded = true;
|
drawDseg7RightString34(s, crsStr.c_str(), c1X + 130, valY, c1Col, cardBg, 0, slice_h, isRainbowTheme());
|
||||||
s.setTextColor(priCol, cardBg);
|
|
||||||
s.setTextPadding(0);
|
|
||||||
s.drawRightString(crsStr.c_str(), c1X + 130, valY, 1);
|
|
||||||
s.fontLoaded = false;
|
|
||||||
if (hasCourse) {
|
if (hasCourse) {
|
||||||
int circY = c1Y + 33;
|
int circY = c1Y + 33;
|
||||||
if (circY + 4 > 0 && circY - 4 < slice_h) {
|
if (circY + 4 > 0 && circY - 4 < slice_h) {
|
||||||
s.drawCircle(c1X + 137, circY, 3, priCol);
|
s.drawCircle(c1X + 137, circY, 3, c1Col);
|
||||||
s.drawCircle(c1X + 137, circY, 2, priCol);
|
s.drawCircle(c1X + 137, circY, 2, c1Col);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -431,7 +408,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
|
||||||
if (c2X + cW > 0 && c2X < 320 && c2Y + cH2 > 0 && c2Y < slice_h) {
|
if (c2X + cW > 0 && c2X < 320 && c2Y + cH2 > 0 && c2Y < slice_h) {
|
||||||
s.fillRoundRect(c2X, c2Y, cW, cH2, 6, cardBg);
|
s.fillRoundRect(c2X, c2Y, cW, cH2, 6, cardBg);
|
||||||
s.drawRoundRect(c2X, c2Y, cW, cH2, 6, cardBorder);
|
s.drawRoundRect(c2X, c2Y, cW, cH2, 6, cardBorder);
|
||||||
s.fillRoundRect(c2X + 6, c2Y + 8, 4, cH2 - 16, 2, priCol);
|
s.fillRoundRect(c2X + 6, c2Y + 8, 4, cH2 - 16, 2, c2Col);
|
||||||
|
|
||||||
if (c2Y + 24 > 0 && c2Y + 8 < slice_h) {
|
if (c2Y + 24 > 0 && c2Y + 8 < slice_h) {
|
||||||
s.setTextColor(0x8410, cardBg);
|
s.setTextColor(0x8410, cardBg);
|
||||||
|
|
@ -440,16 +417,12 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
|
||||||
uint8_t waveState = getGpsWaveAnimState();
|
uint8_t waveState = getGpsWaveAnimState();
|
||||||
int icnY = c2Y + 32;
|
int icnY = c2Y + 32;
|
||||||
if (icnY + 28 > 0 && icnY < slice_h) {
|
if (icnY + 28 > 0 && icnY < slice_h) {
|
||||||
drawLargeSatelliteIconSprite(s, c2X + 16, icnY, priCol, waveState, cardBg);
|
drawLargeSatelliteIconSprite(s, c2X + 16, icnY, c2Col, waveState, cardBg);
|
||||||
}
|
}
|
||||||
int satY = c2Y + 26;
|
int satY = c2Y + 26;
|
||||||
if (satY < slice_h && satY + 38 > 0) {
|
if (satY < slice_h && satY + 38 > 0) {
|
||||||
String satStr = String(gpsSats);
|
String satStr = isGpsUartLocked() ? String(gpsSats) : "---";
|
||||||
s.fontLoaded = true;
|
drawDseg7RightString34(s, satStr.c_str(), c2X + 140, satY, c2Col, cardBg, 0, slice_h, isRainbowTheme());
|
||||||
s.setTextColor(priCol, cardBg);
|
|
||||||
s.setTextPadding(0);
|
|
||||||
s.drawRightString(satStr.c_str(), c2X + 140, satY, 1);
|
|
||||||
s.fontLoaded = false;
|
|
||||||
}
|
}
|
||||||
int hdopY = c2Y + 68;
|
int hdopY = c2Y + 68;
|
||||||
if (hdopY < slice_h && hdopY + 16 > 0) {
|
if (hdopY < slice_h && hdopY + 16 > 0) {
|
||||||
|
|
@ -459,7 +432,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
|
||||||
}
|
}
|
||||||
int qualY = c2Y + 90;
|
int qualY = c2Y + 90;
|
||||||
if (qualY + 10 > 0 && qualY < slice_h) {
|
if (qualY + 10 > 0 && qualY < slice_h) {
|
||||||
s.setTextColor(synced ? 0x9CD3 : priCol, cardBg);
|
s.setTextColor(synced ? 0x9CD3 : c2Col, cardBg);
|
||||||
s.drawString(getGpsDiagMessage(), c2X + 16, qualY, 1);
|
s.drawString(getGpsDiagMessage(), c2X + 16, qualY, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -470,7 +443,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
|
||||||
if (c3X + cW > 0 && c3X < 320 && c3Y + cH2 > 0 && c3Y < slice_h) {
|
if (c3X + cW > 0 && c3X < 320 && c3Y + cH2 > 0 && c3Y < slice_h) {
|
||||||
s.fillRoundRect(c3X, c3Y, cW, cH2, 6, cardBg);
|
s.fillRoundRect(c3X, c3Y, cW, cH2, 6, cardBg);
|
||||||
s.drawRoundRect(c3X, c3Y, cW, cH2, 6, cardBorder);
|
s.drawRoundRect(c3X, c3Y, cW, cH2, 6, cardBorder);
|
||||||
s.fillRoundRect(c3X + 6, c3Y + 8, 4, cH2 - 16, 2, priCol);
|
s.fillRoundRect(c3X + 6, c3Y + 8, 4, cH2 - 16, 2, c3Col);
|
||||||
|
|
||||||
if (c3Y + 24 > 0 && c3Y + 8 < slice_h) {
|
if (c3Y + 24 > 0 && c3Y + 8 < slice_h) {
|
||||||
s.setTextColor(0x8410, cardBg);
|
s.setTextColor(0x8410, cardBg);
|
||||||
|
|
@ -484,7 +457,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
|
||||||
int latValY = c3Y + 38;
|
int latValY = c3Y + 38;
|
||||||
if (latValY < slice_h && latValY + 18 > 0) {
|
if (latValY < slice_h && latValY + 18 > 0) {
|
||||||
String latStr = gpsLocationValid ? (String(gpsLatitude, 5) + "°") : "--.-----°";
|
String latStr = gpsLocationValid ? (String(gpsLatitude, 5) + "°") : "--.-----°";
|
||||||
s.setTextColor(priCol, cardBg);
|
s.setTextColor(c3Col, cardBg);
|
||||||
s.drawString(latStr.c_str(), c3X + 16, latValY, 2);
|
s.drawString(latStr.c_str(), c3X + 16, latValY, 2);
|
||||||
}
|
}
|
||||||
int lonHdrY = c3Y + 64;
|
int lonHdrY = c3Y + 64;
|
||||||
|
|
@ -495,7 +468,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
|
||||||
int lonValY = c3Y + 76;
|
int lonValY = c3Y + 76;
|
||||||
if (lonValY < slice_h && lonValY + 18 > 0) {
|
if (lonValY < slice_h && lonValY + 18 > 0) {
|
||||||
String lonStr = gpsLocationValid ? (String(gpsLongitude, 5) + "°") : "--.-----°";
|
String lonStr = gpsLocationValid ? (String(gpsLongitude, 5) + "°") : "--.-----°";
|
||||||
s.setTextColor(priCol, cardBg);
|
s.setTextColor(c3Col, cardBg);
|
||||||
s.drawString(lonStr.c_str(), c3X + 16, lonValY, 2);
|
s.drawString(lonStr.c_str(), c3X + 16, lonValY, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,12 @@
|
||||||
#include "DSEG7_Modern_BoldItalic_44.h"
|
#include "DSEG7_Modern_BoldItalic_44.h"
|
||||||
#include "DSEG7_Modern_BoldItalic_80.h"
|
#include "DSEG7_Modern_BoldItalic_80.h"
|
||||||
#include "Latin_Sans_Bold_26.h"
|
#include "Latin_Sans_Bold_26.h"
|
||||||
|
#include "rainbow_text.h"
|
||||||
|
|
||||||
extern TFT_eSprite sprTrans;
|
extern TFT_eSprite sprTrans;
|
||||||
extern bool sprTransAllocated;
|
extern bool sprTransAllocated;
|
||||||
|
extern TFT_eSprite sprFadeB;
|
||||||
|
extern bool sprFadeBAllocated;
|
||||||
|
|
||||||
const uint16_t screenWidth = tft.height(); // landscape
|
const uint16_t screenWidth = tft.height(); // landscape
|
||||||
const uint16_t screenHeight = tft.width();
|
const uint16_t screenHeight = tft.width();
|
||||||
|
|
@ -404,6 +407,8 @@ const float blockC[20][4] PROGMEM = {
|
||||||
|
|
||||||
// Draw an individual solid rectangular block with smooth subpixel anti-aliasing
|
// Draw an individual solid rectangular block with smooth subpixel anti-aliasing
|
||||||
inline void drawBarBlock(uint8_t i, uint16_t color) {
|
inline void drawBarBlock(uint8_t i, uint16_t color) {
|
||||||
|
if (isRainbowTheme() && color != TFT_RED && color != METER_INACTIVE_COLOR && color != TFT_BLACK)
|
||||||
|
color = getRainbowBlockColor(i, NUM_BAR_BLOCKS);
|
||||||
int16_t minX = pgm_read_word(&blockMinX[i]);
|
int16_t minX = pgm_read_word(&blockMinX[i]);
|
||||||
int16_t maxX = pgm_read_word(&blockMaxX[i]);
|
int16_t maxX = pgm_read_word(&blockMaxX[i]);
|
||||||
int16_t minY = pgm_read_word(&blockMinY[i]);
|
int16_t minY = pgm_read_word(&blockMinY[i]);
|
||||||
|
|
@ -531,6 +536,9 @@ const float quadBlockC[20][4] PROGMEM = {
|
||||||
// Draw an individual solid rectangular block for 4-quadrant view with smooth
|
// Draw an individual solid rectangular block for 4-quadrant view with smooth
|
||||||
// subpixel anti-aliasing
|
// subpixel anti-aliasing
|
||||||
inline void drawQuadBarBlock(uint8_t cell, uint8_t i, uint16_t color) {
|
inline void drawQuadBarBlock(uint8_t cell, uint8_t i, uint16_t color) {
|
||||||
|
// Rainbow theme: active blocks become a spectral gradient
|
||||||
|
if (isRainbowTheme() && color != TFT_RED && color != METER_INACTIVE_COLOR && color != TFT_BLACK)
|
||||||
|
color = getRainbowBlockColor(i);
|
||||||
int16_t ox = cell4_x[cell];
|
int16_t ox = cell4_x[cell];
|
||||||
int16_t oy = cell4_y[cell];
|
int16_t oy = cell4_y[cell];
|
||||||
int16_t minX = pgm_read_word(&quadBlockMinX[i]);
|
int16_t minX = pgm_read_word(&quadBlockMinX[i]);
|
||||||
|
|
@ -1174,13 +1182,9 @@ void arcMeter4(uint8_t cell, uint8_t pid, bool redrawHeader = true) {
|
||||||
old_data[cell] = -999;
|
old_data[cell] = -999;
|
||||||
plotArc4(cell, last_valid_data[cell], warn, min, max, digit, unit);
|
plotArc4(cell, last_valid_data[cell], warn, min, max, digit, unit);
|
||||||
} else {
|
} else {
|
||||||
// Value placeholder using DSEG7 Modern 34pt smooth font
|
// Value placeholder using DSEG7 Modern 34pt smooth font with rainbow gradient
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
bool rainbowTxt = isRainbowTheme();
|
||||||
tft.setTextColor(getThemePrimaryColor(), TFT_BLACK);
|
drawDseg7RightString34(tft, "---", x + 152, y + 74, getThemeCellColor(cell), TFT_BLACK, 114, 240, rainbowTxt);
|
||||||
tft.setTextPadding(114);
|
|
||||||
tft.drawRightString("---", x + 152, y + 74, 1);
|
|
||||||
tft.setTextPadding(0);
|
|
||||||
tft.unloadFont();
|
|
||||||
old_data[cell] = -999;
|
old_data[cell] = -999;
|
||||||
}
|
}
|
||||||
if (cell == 1 && isGpsSpeedValid()) {
|
if (cell == 1 && isGpsSpeedValid()) {
|
||||||
|
|
@ -1257,14 +1261,10 @@ void plotArc4(uint8_t cell, float data, int warn, int min, int max, bool digit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t txtCol = getThemePrimaryColor();
|
uint16_t txtCol = (col == TFT_RED) ? TFT_RED : getThemeCellColor(cell);
|
||||||
|
bool rainbowTxt = isRainbowTheme() && (col != TFT_RED);
|
||||||
|
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
drawDseg7RightString34(tft, numStr.c_str(), x + 152, y + 74, txtCol, TFT_BLACK, 114, 240, rainbowTxt);
|
||||||
tft.setTextColor(txtCol, TFT_BLACK);
|
|
||||||
tft.setTextPadding(114);
|
|
||||||
tft.drawRightString(numStr.c_str(), x + 152, y + 74, 1);
|
|
||||||
tft.setTextPadding(0);
|
|
||||||
tft.unloadFont();
|
|
||||||
old_data[cell] = data;
|
old_data[cell] = data;
|
||||||
|
|
||||||
// Draw GPS Satellite Icon & indicator in top-right corner of cell 1
|
// Draw GPS Satellite Icon & indicator in top-right corner of cell 1
|
||||||
|
|
@ -1320,12 +1320,8 @@ void fullScreenMeter(uint8_t cell, uint8_t pid, bool redrawHeader = true) {
|
||||||
drawBarBlock(i, METER_INACTIVE_COLOR);
|
drawBarBlock(i, METER_INACTIVE_COLOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_80);
|
bool rainbowTxt = isRainbowTheme();
|
||||||
tft.setTextColor(getThemePrimaryColor(), TFT_BLACK);
|
drawDseg7RightString80(tft, "---", 312, 148, getThemeCellColor(cell), TFT_BLACK, 240, 240, rainbowTxt);
|
||||||
tft.setTextPadding(240);
|
|
||||||
tft.drawRightString("---", 312, 148, 1);
|
|
||||||
tft.setTextPadding(0);
|
|
||||||
tft.unloadFont();
|
|
||||||
old_data[cell] = -999;
|
old_data[cell] = -999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1399,15 +1395,11 @@ void plotFullScreen(uint8_t cell, float data, int warn, int min, int max,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t txtCol = getThemePrimaryColor();
|
uint16_t txtCol = (col == TFT_RED) ? TFT_RED : getThemeCellColor(cell);
|
||||||
|
bool rainbowTxt = isRainbowTheme() && (col != TFT_RED);
|
||||||
|
|
||||||
// Value bottom-right using DSEG7 Modern smooth font
|
// Value bottom-right using DSEG7 Modern smooth font with rainbow gradient
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_80);
|
drawDseg7RightString80(tft, numStr.c_str(), 312, 148, txtCol, TFT_BLACK, 240, 240, rainbowTxt);
|
||||||
tft.setTextColor(txtCol, TFT_BLACK);
|
|
||||||
tft.setTextPadding(240);
|
|
||||||
tft.drawRightString(numStr.c_str(), 312, 148, 1);
|
|
||||||
tft.setTextPadding(0);
|
|
||||||
tft.unloadFont();
|
|
||||||
old_data[cell] = data;
|
old_data[cell] = data;
|
||||||
|
|
||||||
if (cell == 1 && isGpsSpeedValid()) {
|
if (cell == 1 && isGpsSpeedValid()) {
|
||||||
|
|
@ -1442,7 +1434,7 @@ void plotNoData(uint8_t cell) {
|
||||||
drawBarBlock(i, METER_INACTIVE_COLOR);
|
drawBarBlock(i, METER_INACTIVE_COLOR);
|
||||||
}
|
}
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_80);
|
tft.loadFont(DSEG7_Modern_BoldItalic_80);
|
||||||
tft.setTextColor(getThemePrimaryColor(), TFT_BLACK);
|
tft.setTextColor(getThemeCellColor(cell), TFT_BLACK);
|
||||||
tft.setTextPadding(240);
|
tft.setTextPadding(240);
|
||||||
tft.drawRightString("---", 312, 148, 1);
|
tft.drawRightString("---", 312, 148, 1);
|
||||||
tft.setTextPadding(0);
|
tft.setTextPadding(0);
|
||||||
|
|
@ -1457,7 +1449,7 @@ void plotNoData(uint8_t cell) {
|
||||||
drawQuadBarBlock(cell, i, METER_INACTIVE_COLOR);
|
drawQuadBarBlock(cell, i, METER_INACTIVE_COLOR);
|
||||||
}
|
}
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
||||||
tft.setTextColor(getThemePrimaryColor(), TFT_BLACK);
|
tft.setTextColor(getThemeCellColor(cell), TFT_BLACK);
|
||||||
tft.setTextPadding(114);
|
tft.setTextPadding(114);
|
||||||
tft.drawRightString("---", x + 152, y + 74, 1);
|
tft.drawRightString("---", x + 152, y + 74, 1);
|
||||||
tft.setTextPadding(0);
|
tft.setTextPadding(0);
|
||||||
|
|
@ -1529,6 +1521,8 @@ inline void drawPidIconToSprite(TFT_eSprite &s, int ix, int iy,
|
||||||
// anti-aliasing
|
// anti-aliasing
|
||||||
inline void drawQuadBarBlockToSprite(TFT_eSprite &s, int16_t qx, int16_t qy,
|
inline void drawQuadBarBlockToSprite(TFT_eSprite &s, int16_t qx, int16_t qy,
|
||||||
uint8_t i, uint16_t color, int slice_h) {
|
uint8_t i, uint16_t color, int slice_h) {
|
||||||
|
if (isRainbowTheme() && color != TFT_RED && color != METER_INACTIVE_COLOR && color != TFT_BLACK)
|
||||||
|
color = getRainbowBlockColor(i);
|
||||||
int16_t minX = pgm_read_word(&quadBlockMinX[i]);
|
int16_t minX = pgm_read_word(&quadBlockMinX[i]);
|
||||||
int16_t maxX = pgm_read_word(&quadBlockMaxX[i]);
|
int16_t maxX = pgm_read_word(&quadBlockMaxX[i]);
|
||||||
int16_t minY = pgm_read_word(&quadBlockMinY[i]);
|
int16_t minY = pgm_read_word(&quadBlockMinY[i]);
|
||||||
|
|
@ -1640,21 +1634,18 @@ inline void renderPage0ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
||||||
drawQuadBarBlockToSprite(s, qx, qy, i, bCol, slice_h);
|
drawQuadBarBlockToSprite(s, qx, qy, i, bCol, slice_h);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Digital value string using authentic DSEG7 34pt font (pixel-perfect
|
// 4. Digital value string using authentic DSEG7 34pt font with rainbow gradient
|
||||||
// match with resting screen)
|
|
||||||
String numStr = (val <= -900.0f) ? "---" : String(val, digit);
|
String numStr = (val <= -900.0f) ? "---" : String(val, digit);
|
||||||
|
uint16_t txtCol = (activeCol == TFT_RED) ? TFT_RED : getThemeCellColor(c);
|
||||||
|
bool rainbowTxt = isRainbowTheme() && (activeCol != TFT_RED);
|
||||||
int textY = qy + 74;
|
int textY = qy + 74;
|
||||||
if (textY < slice_h && textY + 40 > 0) {
|
if (textY < slice_h && textY + 40 > 0) {
|
||||||
s.fontLoaded = true;
|
drawDseg7RightString34(s, numStr.c_str(), qx + 152, textY, txtCol, TFT_BLACK, 0, slice_h, rainbowTxt);
|
||||||
s.setTextColor(priCol, TFT_BLACK);
|
|
||||||
s.setTextPadding(0);
|
|
||||||
s.drawRightString(numStr.c_str(), qx + 152, textY, 1);
|
|
||||||
s.fontLoaded = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GPS Satellite icon in top right of Quadrant 1 (Velocidad: cell 1)
|
// GPS Satellite icon in top right of Quadrant 1 (Velocidad: cell 1)
|
||||||
if (c == 1 && isGpsSpeedValid() && y_slice == 0) {
|
if (c == 1 && isGpsSpeedValid() && y_slice == 0) {
|
||||||
uint16_t waveCol = priCol;
|
uint16_t waveCol = isRainbowTheme() ? (uint16_t)0x07E0 : priCol;
|
||||||
uint16_t bodyCol = TFT_WHITE;
|
uint16_t bodyCol = TFT_WHITE;
|
||||||
uint16_t bgCol = themeGradQuad[3];
|
uint16_t bgCol = themeGradQuad[3];
|
||||||
drawGpsSatelliteIcon(s, qx + 136, 3, bodyCol, waveCol, bgCol,
|
drawGpsSatelliteIcon(s, qx + 136, 3, bodyCol, waveCol, bgCol,
|
||||||
|
|
@ -1694,8 +1685,8 @@ inline void renderPage0ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
||||||
inline void renderPage1ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
inline void renderPage1ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
||||||
int slice_h) {
|
int slice_h) {
|
||||||
for (uint8_t cardIdx = 0; cardIdx < 3; cardIdx++) {
|
for (uint8_t cardIdx = 0; cardIdx < 3; cardIdx++) {
|
||||||
int cardY = (6 + cardIdx * 77) - y_slice;
|
int cardY = (19 + cardIdx * 74) - y_slice;
|
||||||
int cardH = 73;
|
int cardH = 68;
|
||||||
int cardX = 4 + x_offset;
|
int cardX = 4 + x_offset;
|
||||||
int cardW = 312;
|
int cardW = 312;
|
||||||
|
|
||||||
|
|
@ -1727,6 +1718,8 @@ inline void renderPage1ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
||||||
accentCol = TFT_RED;
|
accentCol = TFT_RED;
|
||||||
else if (val >= 100.0f)
|
else if (val >= 100.0f)
|
||||||
accentCol = TFT_YELLOW;
|
accentCol = TFT_YELLOW;
|
||||||
|
else
|
||||||
|
accentCol = isRainbowTheme() ? (uint16_t)0x07FF : getThemePrimaryColor();
|
||||||
} else if (cardIdx == 1) {
|
} else if (cardIdx == 1) {
|
||||||
titleStr = "INTERIOR";
|
titleStr = "INTERIOR";
|
||||||
subStr = (dsSensorCount > 0) ? "DS18B20 SENSOR 1 (GPIO 22)"
|
subStr = (dsSensorCount > 0) ? "DS18B20 SENSOR 1 (GPIO 22)"
|
||||||
|
|
@ -1734,7 +1727,7 @@ inline void renderPage1ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
||||||
val = tempInterior;
|
val = tempInterior;
|
||||||
minRange = 10.0f;
|
minRange = 10.0f;
|
||||||
maxRange = 40.0f;
|
maxRange = 40.0f;
|
||||||
accentCol = 0x07F3;
|
accentCol = isRainbowTheme() ? (uint16_t)0x07E0 : getThemeAccentColor();
|
||||||
} else {
|
} else {
|
||||||
titleStr = "EXTERIOR";
|
titleStr = "EXTERIOR";
|
||||||
subStr = (dsSensorCount > 1) ? "DS18B20 SENSOR 2 (GPIO 22)"
|
subStr = (dsSensorCount > 1) ? "DS18B20 SENSOR 2 (GPIO 22)"
|
||||||
|
|
@ -1743,26 +1736,26 @@ inline void renderPage1ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
||||||
val = tempExterior;
|
val = tempExterior;
|
||||||
minRange = -10.0f;
|
minRange = -10.0f;
|
||||||
maxRange = 50.0f;
|
maxRange = 50.0f;
|
||||||
accentCol = 0x3D7F;
|
accentCol = isRainbowTheme() ? (uint16_t)0xFD20 : getThemePrimaryColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
s.fillRoundRect(cardX + 6, cardY + 8, 4, cardH - 16, 2, accentCol);
|
s.fillRoundRect(cardX + 6, cardY + 6, 4, cardH - 12, 2, accentCol);
|
||||||
// Smooth anti-aliased title in transition sprite (zero mallocs, 100%
|
// Smooth anti-aliased title in transition sprite (zero mallocs, 100%
|
||||||
// smooth)
|
// smooth)
|
||||||
if (!sprTriTitleReady)
|
if (!sprTriTitleReady)
|
||||||
initTriTempTitles();
|
initTriTempTitles();
|
||||||
if (sprTriTitleReady && sprTriTitle[cardIdx].created()) {
|
if (sprTriTitleReady && sprTriTitle[cardIdx].created()) {
|
||||||
sprTriTitle[cardIdx].pushToSprite(&s, cardX + 16, cardY + 6);
|
sprTriTitle[cardIdx].pushToSprite(&s, cardX + 16, cardY + 5);
|
||||||
} else {
|
} else {
|
||||||
s.setTextColor(TFT_WHITE, cardBg);
|
s.setTextColor(TFT_WHITE, cardBg);
|
||||||
s.drawString(titleStr, cardX + 16, cardY + 6, 4);
|
s.drawString(titleStr, cardX + 16, cardY + 5, 4);
|
||||||
}
|
}
|
||||||
s.setTextColor(0x8410, cardBg);
|
s.setTextColor(0x8410, cardBg);
|
||||||
s.drawString(subStr, cardX + 16, cardY + 34, 1);
|
s.drawString(subStr, cardX + 16, cardY + 32, 1);
|
||||||
|
|
||||||
// Progress bar
|
// Progress bar
|
||||||
int barX = cardX + 16;
|
int barX = cardX + 16;
|
||||||
int barY = cardY + 50;
|
int barY = cardY + 48;
|
||||||
int barW = 120;
|
int barW = 120;
|
||||||
s.fillRoundRect(barX, barY, barW, 8, 3, 0x18C3);
|
s.fillRoundRect(barX, barY, barW, 8, 3, 0x18C3);
|
||||||
float ratio = (val - minRange) / (maxRange - minRange);
|
float ratio = (val - minRange) / (maxRange - minRange);
|
||||||
|
|
@ -1775,22 +1768,18 @@ inline void renderPage1ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
||||||
fillW = 4;
|
fillW = 4;
|
||||||
s.fillRoundRect(barX, barY, fillW, 8, 3, accentCol);
|
s.fillRoundRect(barX, barY, fillW, 8, 3, accentCol);
|
||||||
|
|
||||||
// Value text using authentic DSEG7 34pt font matching drawTriTempCard
|
// Value text using authentic DSEG7 34pt font with rainbow gradient
|
||||||
String numStr =
|
String numStr =
|
||||||
(val <= -900.0f) ? "---" : (isInt ? String((int)val) : String(val, 1));
|
(val <= -900.0f) ? "---" : (isInt ? String((int)val) : String(val, 1));
|
||||||
int numY = cardY + 22;
|
bool rainbowTxt = isRainbowTheme() && (accentCol != TFT_RED);
|
||||||
|
int numY = cardY + 20;
|
||||||
if (numY < slice_h && numY + 40 > 0) {
|
if (numY < slice_h && numY + 40 > 0) {
|
||||||
s.fontLoaded = true;
|
drawDseg7RightString34(s, numStr.c_str(), cardX + 262, numY, accentCol, cardBg, 0, slice_h, rainbowTxt);
|
||||||
s.setTextColor(accentCol, cardBg);
|
|
||||||
s.setTextPadding(0);
|
|
||||||
s.drawRightString(numStr.c_str(), cardX + 262, numY, 1);
|
|
||||||
s.fontLoaded = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int unitY = cardY + 32;
|
int unitY = cardY + 22;
|
||||||
if (unitY < slice_h && unitY + 16 > 0) {
|
if (unitY < slice_h && unitY + 26 > 0) {
|
||||||
s.setTextColor(0xC618, cardBg);
|
draw7SegDegreeC(s, cardX + 268, unitY, 0xC618, cardBg);
|
||||||
s.drawString("`C", cardX + 268, unitY, 2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1935,22 +1924,11 @@ inline void animateFadeTransition(int8_t fromPage = -1, int8_t toPage = -1) {
|
||||||
const int slice_h = 40;
|
const int slice_h = 40;
|
||||||
const uint8_t numSlices = 240 / slice_h; // exactly 6 slices
|
const uint8_t numSlices = 240 / slice_h; // exactly 6 slices
|
||||||
|
|
||||||
if (!sprTransAllocated) {
|
if (!sprTransAllocated || !sprFadeBAllocated) {
|
||||||
initScreen(true);
|
initScreen(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TFT_eSprite sprB(&tft);
|
|
||||||
if (sprB.createSprite(320, slice_h) == nullptr) {
|
|
||||||
animateSlideTransition(true, fromPage, toPage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sprB.setTextFont(1);
|
|
||||||
sprB.setFreeFont(NULL);
|
|
||||||
sprB.loadFont(DSEG7_Modern_BoldItalic_34);
|
|
||||||
sprB.fontLoaded = false;
|
|
||||||
|
|
||||||
const uint8_t FRAMES = 10;
|
const uint8_t FRAMES = 10;
|
||||||
// Perceptual crossfade steps (0 to 32)
|
// Perceptual crossfade steps (0 to 32)
|
||||||
const uint8_t alpha_steps[FRAMES] = {3, 6, 10, 14, 18, 22, 26, 29, 31, 32};
|
const uint8_t alpha_steps[FRAMES] = {3, 6, 10, 14, 18, 22, 26, 29, 31, 32};
|
||||||
|
|
@ -1963,7 +1941,7 @@ inline void animateFadeTransition(int8_t fromPage = -1, int8_t toPage = -1) {
|
||||||
int y_slice = s * slice_h;
|
int y_slice = s * slice_h;
|
||||||
|
|
||||||
sprTrans.fillSprite(TFT_BLACK);
|
sprTrans.fillSprite(TFT_BLACK);
|
||||||
sprB.fillSprite(TFT_BLACK);
|
sprFadeB.fillSprite(TFT_BLACK);
|
||||||
|
|
||||||
if (fromPage == 0)
|
if (fromPage == 0)
|
||||||
renderPage0ToSprite(sprTrans, 0, y_slice, slice_h);
|
renderPage0ToSprite(sprTrans, 0, y_slice, slice_h);
|
||||||
|
|
@ -1975,16 +1953,16 @@ inline void animateFadeTransition(int8_t fromPage = -1, int8_t toPage = -1) {
|
||||||
renderPage3ToSprite(sprTrans, 0, y_slice, slice_h);
|
renderPage3ToSprite(sprTrans, 0, y_slice, slice_h);
|
||||||
|
|
||||||
if (toPage == 0)
|
if (toPage == 0)
|
||||||
renderPage0ToSprite(sprB, 0, y_slice, slice_h);
|
renderPage0ToSprite(sprFadeB, 0, y_slice, slice_h);
|
||||||
else if (toPage == 1)
|
else if (toPage == 1)
|
||||||
renderPage1ToSprite(sprB, 0, y_slice, slice_h);
|
renderPage1ToSprite(sprFadeB, 0, y_slice, slice_h);
|
||||||
else if (toPage == 2)
|
else if (toPage == 2)
|
||||||
renderPage2ToSprite(sprB, 0, y_slice, slice_h);
|
renderPage2ToSprite(sprFadeB, 0, y_slice, slice_h);
|
||||||
else
|
else
|
||||||
renderPage3ToSprite(sprB, 0, y_slice, slice_h);
|
renderPage3ToSprite(sprFadeB, 0, y_slice, slice_h);
|
||||||
|
|
||||||
uint16_t *pA = (uint16_t *)sprTrans.getPointer();
|
uint16_t *pA = (uint16_t *)sprTrans.getPointer();
|
||||||
uint16_t *pB = (uint16_t *)sprB.getPointer();
|
uint16_t *pB = (uint16_t *)sprFadeB.getPointer();
|
||||||
|
|
||||||
if (pA && pB) {
|
if (pA && pB) {
|
||||||
for (int i = 0; i < totalPixels; i++) {
|
for (int i = 0; i < totalPixels; i++) {
|
||||||
|
|
@ -2032,9 +2010,7 @@ inline void animateFadeTransition(int8_t fromPage = -1, int8_t toPage = -1) {
|
||||||
delay(10);
|
delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
sprB.fontLoaded = true;
|
sprFadeB.fontLoaded = false;
|
||||||
sprB.unloadFont();
|
|
||||||
sprB.deleteSprite();
|
|
||||||
sprTrans.fontLoaded = false;
|
sprTrans.fontLoaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2720,7 +2696,7 @@ void handleArcAlarmFlash() {
|
||||||
drawBarBlock(i, METER_INACTIVE_COLOR);
|
drawBarBlock(i, METER_INACTIVE_COLOR);
|
||||||
}
|
}
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_80);
|
tft.loadFont(DSEG7_Modern_BoldItalic_80);
|
||||||
tft.setTextColor(getThemePrimaryColor(), TFT_BLACK);
|
tft.setTextColor(getThemeCellColor(cell), TFT_BLACK);
|
||||||
tft.setTextPadding(240);
|
tft.setTextPadding(240);
|
||||||
tft.drawRightString(numStr.c_str(), 312, 148, 1);
|
tft.drawRightString(numStr.c_str(), 312, 148, 1);
|
||||||
tft.setTextPadding(0);
|
tft.setTextPadding(0);
|
||||||
|
|
@ -2787,7 +2763,7 @@ void handleArcAlarmFlash() {
|
||||||
drawQuadBarBlock(cell, i, METER_INACTIVE_COLOR);
|
drawQuadBarBlock(cell, i, METER_INACTIVE_COLOR);
|
||||||
}
|
}
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
||||||
tft.setTextColor(getThemePrimaryColor(), TFT_BLACK);
|
tft.setTextColor(getThemeCellColor(cell), TFT_BLACK);
|
||||||
tft.setTextPadding(114);
|
tft.setTextPadding(114);
|
||||||
tft.drawRightString(numStr.c_str(), x + 152, y + 74, 1);
|
tft.drawRightString(numStr.c_str(), x + 152, y + 74, 1);
|
||||||
tft.setTextPadding(0);
|
tft.setTextPadding(0);
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,8 @@ Preferences pref; // create preference
|
||||||
TFT_eSPI tft = TFT_eSPI();
|
TFT_eSPI tft = TFT_eSPI();
|
||||||
TFT_eSprite sprTrans(&tft);
|
TFT_eSprite sprTrans(&tft);
|
||||||
bool sprTransAllocated = false;
|
bool sprTransAllocated = false;
|
||||||
|
TFT_eSprite sprFadeB(&tft);
|
||||||
|
bool sprFadeBAllocated = false;
|
||||||
#define TFT_GREY 0x5AEB
|
#define TFT_GREY 0x5AEB
|
||||||
|
|
||||||
// ELM327 PIDs Configuration
|
// ELM327 PIDs Configuration
|
||||||
|
|
@ -129,8 +131,8 @@ const GaugeTheme GAUGE_THEMES[6] = {
|
||||||
180, 255}, // 0: Vivid Electric Cyan
|
180, 255}, // 0: Vivid Electric Cyan
|
||||||
{"Verde Sport", "Cockpit Clasico", 0x07E0, 0x0446, 0x0283, 0x0141, 0x0205,
|
{"Verde Sport", "Cockpit Clasico", 0x07E0, 0x0446, 0x0283, 0x0141, 0x0205,
|
||||||
0, 255, 30}, // 1: Vivid Emerald Green
|
0, 255, 30}, // 1: Vivid Emerald Green
|
||||||
{"Ambar BMW", "Nocturno M-Power", 0xFDE0, 0x93C0, 0x5240, 0x2120, 0x41E0,
|
{"Arcoiris", "Espectro RGB", 0x07FF, 0x07E0, 0x2965, 0x08E7, 0x08A8,
|
||||||
255, 120, 0}, // 2: Vivid M-Power Glowing Amber
|
0, 200, 255}, // 2: Rainbow spectrum (RGB multi-hue blocks + dynamic spectrum)
|
||||||
{"Blanco Puro", "OLED Precision", 0xFFFF, 0x6B6D, 0x4228, 0x18E3, 0x2965,
|
{"Blanco Puro", "OLED Precision", 0xFFFF, 0x6B6D, 0x4228, 0x18E3, 0x2965,
|
||||||
220, 230, 255}, // 3: Pure Crisp White
|
220, 230, 255}, // 3: Pure Crisp White
|
||||||
{"Naranja Sport", "McLaren Papaya", 0xFD20, 0x9260, 0x5160, 0x20A0, 0x4140,
|
{"Naranja Sport", "McLaren Papaya", 0xFD20, 0x9260, 0x5160, 0x20A0, 0x4140,
|
||||||
|
|
@ -141,7 +143,7 @@ const GaugeTheme GAUGE_THEMES[6] = {
|
||||||
|
|
||||||
uint8_t themeIndex = 0; // Default 0: Azul Cian
|
uint8_t themeIndex = 0; // Default 0: Azul Cian
|
||||||
uint8_t transitionType =
|
uint8_t transitionType =
|
||||||
0; // 0: Deslizamiento (Slide), 1: Desvanecimiento (Fade)
|
1; // Default 1: Desvanecimiento (Fade), 0: Deslizamiento (Slide)
|
||||||
|
|
||||||
inline uint16_t getThemePrimaryColor() {
|
inline uint16_t getThemePrimaryColor() {
|
||||||
if (themeIndex >= 6)
|
if (themeIndex >= 6)
|
||||||
|
|
@ -173,33 +175,100 @@ inline uint16_t getThemeBgColor() {
|
||||||
return GAUGE_THEMES[themeIndex].bgColor;
|
return GAUGE_THEMES[themeIndex].bgColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rainbow theme helpers (theme index 2)
|
||||||
|
inline bool isRainbowTheme() { return themeIndex == 2; }
|
||||||
|
|
||||||
|
// Convert hue (0..359, full saturation/value) to RGB565
|
||||||
|
inline uint16_t hueToRgb565(uint16_t hue) {
|
||||||
|
hue %= 360;
|
||||||
|
uint8_t region = hue / 60;
|
||||||
|
uint8_t rem = (uint8_t)(((hue % 60) * 255) / 60);
|
||||||
|
uint8_t r, g, b;
|
||||||
|
switch (region) {
|
||||||
|
case 0: r = 255; g = rem; b = 0; break;
|
||||||
|
case 1: r = 255 - rem; g = 255; b = 0; break;
|
||||||
|
case 2: r = 0; g = 255; b = rem; break;
|
||||||
|
case 3: r = 0; g = 255 - rem; b = 255; break;
|
||||||
|
case 4: r = rem; g = 0; b = 255; break;
|
||||||
|
default: r = 255; g = 0; b = 255 - rem; break;
|
||||||
|
}
|
||||||
|
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calibrated 20-block spectral rainbow: Electric Sky Blue -> Cyan -> Green -> Lime -> Yellow -> Orange -> Vivid Red
|
||||||
|
const uint16_t RAINBOW_SPECTRUM_20[20] PROGMEM = {
|
||||||
|
0x03FF, // 0: Azul Cielo Eléctrico (Vivid Electric Sky Blue)
|
||||||
|
0x04DF, // 1: Azul Azure Brillante (Bright Azure Blue)
|
||||||
|
0x05BF, // 2: Azul Océano (Deep Cyan-Blue)
|
||||||
|
0x07FF, // 3: Cian Eléctrico Puro (Pure Electric Cyan)
|
||||||
|
0x07F6, // 4: Aguamarina Vibrante (Aquamarine)
|
||||||
|
0x07ED, // 5: Turquesa Menta (Mint Turquoise)
|
||||||
|
0x07E6, // 6: Verde Mar Brillante (Bright Sea Green)
|
||||||
|
0x07E0, // 7: Verde Esmeralda Vivo (Vivid Emerald Green)
|
||||||
|
0x37E0, // 8: Verde Primavera (Bright Spring Green)
|
||||||
|
0x7FE0, // 9: Verde Lima Neón (Neon Lime Green)
|
||||||
|
0xAFE0, // 10: Chartreuse Intenso (Vivid Chartreuse)
|
||||||
|
0xDFE0, // 11: Amarillo-Verdoso Eléctrico (Electric Yellow-Green)
|
||||||
|
0xFFE0, // 12: Amarillo Puro Brillante (Pure Brilliant Yellow)
|
||||||
|
0xFEE0, // 13: Amarillo Oro Cálido (Warm Golden Yellow)
|
||||||
|
0xFD80, // 14: Ámbar Radiante (Bright Amber)
|
||||||
|
0xFD00, // 15: Naranja Papaya Vivo (Vivid Papaya Orange)
|
||||||
|
0xFC60, // 16: Naranja Coral Profundo (Deep Coral Orange)
|
||||||
|
0xFA40, // 17: Rojo-Naranja Fuego (Flame Red-Orange)
|
||||||
|
0xF880, // 18: Carmesí Intenso (Bright Crimson)
|
||||||
|
0xF800 // 19: Rojo Puro Brillante (Pure Vivid Red)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Spectral color for gauge block i of n: Electric Blue (low) -> Cyan -> Green -> Yellow -> Orange -> Red (high).
|
||||||
|
inline uint16_t getRainbowBlockColor(uint8_t i, uint8_t n = 20) {
|
||||||
|
if (n == 20 && i < 20) {
|
||||||
|
return pgm_read_word(&RAINBOW_SPECTRUM_20[i]);
|
||||||
|
}
|
||||||
|
if (n < 2)
|
||||||
|
n = 2;
|
||||||
|
if (i >= n)
|
||||||
|
i = n - 1;
|
||||||
|
uint8_t idx = (uint8_t)(((uint32_t)i * 19) / (n - 1));
|
||||||
|
if (idx > 19)
|
||||||
|
idx = 19;
|
||||||
|
return pgm_read_word(&RAINBOW_SPECTRUM_20[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distinct multi-color identity for each quadrant in Rainbow theme
|
||||||
|
inline uint16_t getRainbowCellColor(uint8_t cell) {
|
||||||
|
switch (cell) {
|
||||||
|
case 0: return 0x07FF; // Cuadrante 0 (ECT Refrigerante): Cian Eléctrico
|
||||||
|
case 1: return 0x07E0; // Cuadrante 1 (Velocidad): Verde Esmeralda
|
||||||
|
case 2: return 0xFD20; // Cuadrante 2 (RPM): Naranja Papaya
|
||||||
|
case 3: return 0xFFE0; // Cuadrante 3 (Voltaje): Amarillo Puro
|
||||||
|
default: return 0x07FF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t getThemeCellColor(uint8_t cell) {
|
||||||
|
if (isRainbowTheme()) {
|
||||||
|
return getRainbowCellColor(cell);
|
||||||
|
}
|
||||||
|
return getThemePrimaryColor();
|
||||||
|
}
|
||||||
|
|
||||||
// Control functions for the Rear RGB LED (Active-LOW, Common Anode)
|
// Control functions for the Rear RGB LED (Active-LOW, Common Anode)
|
||||||
|
// Permanentemente apagado a petición del usuario (sin luces traseras en Bluetooth, temas ni boot)
|
||||||
void setRearLedColor(uint8_t r, uint8_t g, uint8_t b) {
|
void setRearLedColor(uint8_t r, uint8_t g, uint8_t b) {
|
||||||
// Ensure pins are attached to LEDC
|
setRearLedOff();
|
||||||
ledcAttachPin(LED_RED_PIN, LED_RED_CH);
|
|
||||||
ledcAttachPin(LED_GREEN_PIN, LED_GREEN_CH);
|
|
||||||
ledcAttachPin(LED_BLUE_PIN, LED_BLUE_CH);
|
|
||||||
// Duty cycle for 8-bit (0..255): 255 = 3.3V (OFF), 0 = 0V (FULL ON)
|
|
||||||
ledcWrite(LED_RED_CH, 255 - r);
|
|
||||||
ledcWrite(LED_GREEN_CH, 255 - g);
|
|
||||||
ledcWrite(LED_BLUE_CH, 255 - b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setRearLedOff() {
|
void setRearLedOff() {
|
||||||
ledcWrite(LED_RED_CH, 255);
|
pinMode(LED_RED_PIN, OUTPUT);
|
||||||
ledcWrite(LED_GREEN_CH, 255);
|
pinMode(LED_GREEN_PIN, OUTPUT);
|
||||||
ledcWrite(LED_BLUE_CH, 255);
|
pinMode(LED_BLUE_PIN, OUTPUT);
|
||||||
// Drive pins directly HIGH as well for guaranteed zero leakage during sleep
|
|
||||||
digitalWrite(LED_RED_PIN, HIGH);
|
digitalWrite(LED_RED_PIN, HIGH);
|
||||||
digitalWrite(LED_GREEN_PIN, HIGH);
|
digitalWrite(LED_GREEN_PIN, HIGH);
|
||||||
digitalWrite(LED_BLUE_PIN, HIGH);
|
digitalWrite(LED_BLUE_PIN, HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateRearLedTheme() {
|
void updateRearLedTheme() {
|
||||||
if (themeIndex >= 6)
|
setRearLedOff();
|
||||||
return;
|
|
||||||
setRearLedColor(GAUGE_THEMES[themeIndex].ledR, GAUGE_THEMES[themeIndex].ledG,
|
|
||||||
GAUGE_THEMES[themeIndex].ledB);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -517,14 +586,13 @@ void setup() {
|
||||||
pinMode(LDR_PIN, ANALOG); // ldr analog input read brightness
|
pinMode(LDR_PIN, ANALOG); // ldr analog input read brightness
|
||||||
pinMode(BUZZER_PIN, OUTPUT); // speaker
|
pinMode(BUZZER_PIN, OUTPUT); // speaker
|
||||||
pinMode(SELECTOR_PIN, INPUT_PULLUP); // button
|
pinMode(SELECTOR_PIN, INPUT_PULLUP); // button
|
||||||
// Rear RGB LED PWM setup (Channels 4, 5, 6 - Timers 2 & 3)
|
// Rear RGB LED: permanentemente apagado (Active-LOW, pull HIGH)
|
||||||
ledcSetup(LED_RED_CH, 5000, 8);
|
pinMode(LED_RED_PIN, OUTPUT);
|
||||||
ledcSetup(LED_GREEN_CH, 5000, 8);
|
pinMode(LED_GREEN_PIN, OUTPUT);
|
||||||
ledcSetup(LED_BLUE_CH, 5000, 8);
|
pinMode(LED_BLUE_PIN, OUTPUT);
|
||||||
ledcAttachPin(LED_RED_PIN, LED_RED_CH);
|
digitalWrite(LED_RED_PIN, HIGH);
|
||||||
ledcAttachPin(LED_GREEN_PIN, LED_GREEN_CH);
|
digitalWrite(LED_GREEN_PIN, HIGH);
|
||||||
ledcAttachPin(LED_BLUE_PIN, LED_BLUE_CH);
|
digitalWrite(LED_BLUE_PIN, HIGH);
|
||||||
setRearLedOff();
|
|
||||||
// pwm setup
|
// pwm setup
|
||||||
ledcSetup(buzzerChannel, 1500, 10); // buzzer 10 bit
|
ledcSetup(buzzerChannel, 1500, 10); // buzzer 10 bit
|
||||||
ledcSetup(backlightChannel, 12000, 8); // backlight 8 bit
|
ledcSetup(backlightChannel, 12000, 8); // backlight 8 bit
|
||||||
|
|
@ -627,9 +695,9 @@ recent_client_addr : {0x00,0x00,0x00,0x00,0x00,0x00} array of bytes[6]
|
||||||
themeIndex = pref.getUChar("theme_idx", 0);
|
themeIndex = pref.getUChar("theme_idx", 0);
|
||||||
if (themeIndex > 5)
|
if (themeIndex > 5)
|
||||||
themeIndex = 0;
|
themeIndex = 0;
|
||||||
transitionType = pref.getUChar("trans_type", 0);
|
transitionType = pref.getUChar("trans_type", 1);
|
||||||
if (transitionType > 1)
|
if (transitionType > 1)
|
||||||
transitionType = 0;
|
transitionType = 1;
|
||||||
currentScreenPage = pref.getChar("scr_page", 0);
|
currentScreenPage = pref.getChar("scr_page", 0);
|
||||||
if (currentScreenPage < 0 || currentScreenPage > 3)
|
if (currentScreenPage < 0 || currentScreenPage > 3)
|
||||||
currentScreenPage = 0;
|
currentScreenPage = 0;
|
||||||
|
|
@ -649,14 +717,23 @@ recent_client_addr : {0x00,0x00,0x00,0x00,0x00,0x00} array of bytes[6]
|
||||||
String(transitionType == 0 ? "Deslizamiento" : "Desvanecimiento"));
|
String(transitionType == 0 ? "Deslizamiento" : "Desvanecimiento"));
|
||||||
delay(40);
|
delay(40);
|
||||||
|
|
||||||
// Initialize persistent transition sprite (pre-allocated once at boot in clean heap)
|
// Initialize persistent transition sprites (pre-allocated once at boot in clean heap)
|
||||||
if (sprTrans.createSprite(320, 40) != nullptr) {
|
if (sprTrans.createSprite(320, 40) != nullptr) {
|
||||||
sprTransAllocated = true;
|
sprTransAllocated = true;
|
||||||
sprTrans.setTextFont(1);
|
sprTrans.setTextFont(1);
|
||||||
sprTrans.setFreeFont(NULL);
|
sprTrans.setFreeFont(NULL);
|
||||||
sprTrans.loadFont(DSEG7_Modern_BoldItalic_34);
|
sprTrans.loadFont(DSEG7_Modern_BoldItalic_34);
|
||||||
sprTrans.fontLoaded = false;
|
sprTrans.fontLoaded = false;
|
||||||
Terminal("[ OK ] UI: Persistent 25KB Transition Engine Online");
|
}
|
||||||
|
if (sprFadeB.createSprite(320, 40) != nullptr) {
|
||||||
|
sprFadeBAllocated = true;
|
||||||
|
sprFadeB.setTextFont(1);
|
||||||
|
sprFadeB.setFreeFont(NULL);
|
||||||
|
sprFadeB.loadFont(DSEG7_Modern_BoldItalic_34);
|
||||||
|
sprFadeB.fontLoaded = false;
|
||||||
|
}
|
||||||
|
if (sprTransAllocated && sprFadeBAllocated) {
|
||||||
|
Terminal("[ OK ] UI: Persistent 50KB Dual Transition Buffers Online");
|
||||||
} else {
|
} else {
|
||||||
Terminal("[ WARN ] UI: Transition buffer alloc failed");
|
Terminal("[ WARN ] UI: Transition buffer alloc failed");
|
||||||
}
|
}
|
||||||
|
|
@ -664,6 +741,7 @@ recent_client_addr : {0x00,0x00,0x00,0x00,0x00,0x00} array of bytes[6]
|
||||||
|
|
||||||
// Initialize 1-Wire DS18B20 digital temperature bus
|
// Initialize 1-Wire DS18B20 digital temperature bus
|
||||||
initTriTemp();
|
initTriTemp();
|
||||||
|
initGpsClockTitles();
|
||||||
if (dsSensorCount > 0) {
|
if (dsSensorCount > 0) {
|
||||||
Terminal("[ OK ] 1-Wire: " + String(dsSensorCount) +
|
Terminal("[ OK ] 1-Wire: " + String(dsSensorCount) +
|
||||||
"x DS18B20 Temp Sensors on GPIO 22");
|
"x DS18B20 Temp Sensors on GPIO 22");
|
||||||
|
|
@ -720,6 +798,8 @@ void loop() {
|
||||||
handleArcAlarmFlash(); // non-blocking arc alarm flash handler (blinks when in
|
handleArcAlarmFlash(); // non-blocking arc alarm flash handler (blinks when in
|
||||||
// TFT_RED)
|
// TFT_RED)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Trigger automatico: Cuando el Bluetooth conecta en segundo plano (Core 0),
|
// Trigger automatico: Cuando el Bluetooth conecta en segundo plano (Core 0),
|
||||||
// pasar a pantalla OBD!
|
// pasar a pantalla OBD!
|
||||||
if (btConnectedTrigger) {
|
if (btConnectedTrigger) {
|
||||||
|
|
|
||||||
BIN
sketch/obd2_gauge/rainbow_text.h
Normal file
BIN
sketch/obd2_gauge/rainbow_text.h
Normal file
Binary file not shown.
|
|
@ -6,13 +6,16 @@
|
||||||
#include <TFT_eSPI.h>
|
#include <TFT_eSPI.h>
|
||||||
#include "DSEG7_Modern_BoldItalic_34.h"
|
#include "DSEG7_Modern_BoldItalic_34.h"
|
||||||
#include "Latin_Sans_Bold_26.h"
|
#include "Latin_Sans_Bold_26.h"
|
||||||
|
#include "rainbow_text.h"
|
||||||
|
|
||||||
// Hardware 1-Wire Pin: Conector CN1 Pin 3 (GPIO 22)
|
// Hardware 1-Wire Pin: Conector CN1 Pin 3 (GPIO 22)
|
||||||
#define ONE_WIRE_BUS_PIN 22
|
#define ONE_WIRE_BUS_PIN 22
|
||||||
|
|
||||||
extern TFT_eSPI tft;
|
extern TFT_eSPI tft;
|
||||||
extern uint16_t getThemePrimaryColor();
|
extern uint16_t getThemePrimaryColor();
|
||||||
|
extern uint16_t getThemeAccentColor();
|
||||||
extern uint16_t getThemeBgColor();
|
extern uint16_t getThemeBgColor();
|
||||||
|
extern bool isRainbowTheme();
|
||||||
extern void touchBeep();
|
extern void touchBeep();
|
||||||
extern String warningValue[5];
|
extern String warningValue[5];
|
||||||
|
|
||||||
|
|
@ -57,6 +60,9 @@ inline void initTriTempTitles() {
|
||||||
sprTriTitle[i].setTextColor(TFT_WHITE, cardBg);
|
sprTriTitle[i].setTextColor(TFT_WHITE, cardBg);
|
||||||
sprTriTitle[i].drawString(titles[i], 0, 0, 1);
|
sprTriTitle[i].drawString(titles[i], 0, 0, 1);
|
||||||
sprTriTitle[i].unloadFont();
|
sprTriTitle[i].unloadFont();
|
||||||
|
if (isRainbowTheme()) {
|
||||||
|
applyRainbowToSprite(sprTriTitle[i], 4 + 16, 19 + i * 74 + 5, cardBg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sprTriTitleReady = true;
|
sprTriTitleReady = true;
|
||||||
|
|
@ -78,7 +84,79 @@ inline void initTriTemp() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 8-bit anti-aliased alpha mask for digital 7-segment italic "°C" (30x26)
|
||||||
|
const uint8_t s_dsegUnitAlpha[30 * 26] PROGMEM = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x11, 0x12, 0x0E, 0x00, 0x01, 0x00,
|
||||||
|
0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x7A, 0xED, 0xEC, 0xED, 0xED, 0xE4, 0x2A, 0x00, 0x02,
|
||||||
|
0x02, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x11, 0x03, 0x00, 0x01,
|
||||||
|
0x00, 0x00, 0x01, 0x02, 0x0C, 0xA2, 0xFF, 0xF2, 0xF3, 0xF4, 0xF1, 0xFF, 0xD2, 0x12, 0x04,
|
||||||
|
0x00, 0x01, 0x97, 0xEE, 0xEC, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEC, 0xEE, 0xC1, 0x13, 0x01,
|
||||||
|
0x00, 0x00, 0x04, 0x00, 0x5C, 0xFF, 0x9F, 0x10, 0x14, 0x10, 0x13, 0xD9, 0xFF, 0x2B, 0x00,
|
||||||
|
0x12, 0xB6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x77, 0x00,
|
||||||
|
0x00, 0x00, 0x04, 0x00, 0x92, 0xFC, 0x63, 0x00, 0x01, 0x00, 0x19, 0xFC, 0xE1, 0x05, 0x31,
|
||||||
|
0xD8, 0xFF, 0xFC, 0xFE, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFB, 0xF9, 0x89, 0x0B, 0x02,
|
||||||
|
0x00, 0x00, 0x01, 0x00, 0xC8, 0xFF, 0x38, 0x05, 0x0B, 0x05, 0x4E, 0xFF, 0xAD, 0x00, 0xD4,
|
||||||
|
0xFF, 0xFC, 0x66, 0x27, 0x2A, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x23, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x01, 0x00, 0x15, 0xF3, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x79, 0xFF, 0x73, 0x09, 0xF8,
|
||||||
|
0xFD, 0xF6, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00,
|
||||||
|
0x00, 0x02, 0x00, 0x2A, 0xFF, 0xDF, 0x7A, 0x7C, 0x7D, 0x7A, 0xDB, 0xFC, 0x28, 0x3B, 0xFF,
|
||||||
|
0xFF, 0xD5, 0x02, 0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x03, 0x00, 0x84, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x46, 0x00, 0x7E, 0xFF,
|
||||||
|
0xFF, 0xA3, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x01, 0x00, 0x00, 0x70, 0x7B, 0x7A, 0x7A, 0x79, 0x22, 0x00, 0x00, 0xAF, 0xFF,
|
||||||
|
0xFF, 0x6E, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0xDF, 0xFF,
|
||||||
|
0xFF, 0x3A, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x27, 0xFB, 0xFF,
|
||||||
|
0xED, 0x11, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x57, 0xFF, 0xFF,
|
||||||
|
0xC5, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x8A, 0xFF, 0xFF,
|
||||||
|
0x91, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xC0, 0xFF, 0xFF,
|
||||||
|
0x5D, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0C, 0xE9, 0xFF, 0xFC,
|
||||||
|
0x2D, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x34, 0xFF, 0xFF, 0xE4,
|
||||||
|
0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x67, 0xFF, 0xFF, 0xB6,
|
||||||
|
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x9C, 0xFF, 0xFF, 0x82,
|
||||||
|
0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xCF, 0xFF, 0xFF, 0x4D,
|
||||||
|
0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, 0xF2, 0xFF, 0xF7, 0x1A,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x4B, 0xFF, 0xFE, 0xD8, 0x12,
|
||||||
|
0x12, 0x12, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x20, 0xE4, 0xFF, 0xF7, 0xED,
|
||||||
|
0xEE, 0xED, 0xED, 0xED, 0xED, 0xED, 0xEB, 0xED, 0x93, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x37, 0xFA, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3D, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x67, 0xF9, 0xF6,
|
||||||
|
0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF7, 0xE9, 0x5B, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x28, 0x2A,
|
||||||
|
0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x29, 0x2A, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Draw 100% anti-aliased digital 7-segment italic unit "°C" matching DSEG7 typography and color
|
||||||
|
inline void draw7SegDegreeC(TFT_eSPI &dst, int bx, int by, uint16_t color = 0xC618, uint16_t bgCol = 0x10A2) {
|
||||||
|
for (int j = 0; j < 26; j++) {
|
||||||
|
int py = by + j;
|
||||||
|
if (py < 0 || py >= dst.height()) continue;
|
||||||
|
int rowOffset = j * 30;
|
||||||
|
for (int i = 0; i < 30; i++) {
|
||||||
|
int px = bx + i;
|
||||||
|
if (px < 0 || px >= dst.width()) continue;
|
||||||
|
uint8_t a = pgm_read_byte(&s_dsegUnitAlpha[rowOffset + i]);
|
||||||
|
if (a > 8) {
|
||||||
|
uint16_t pixCol = (a >= 250) ? color : fastBlend(a, color, bgCol);
|
||||||
|
dst.drawPixel(px, py, pixCol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Double-buffered 120x8 progress bar sprite (zero jitter)
|
// Double-buffered 120x8 progress bar sprite (zero jitter)
|
||||||
static TFT_eSprite sprBar(&tft);
|
static TFT_eSprite sprBar(&tft);
|
||||||
|
|
@ -93,21 +171,33 @@ inline void drawTriTempBar(int barX, int barY, int fillW, uint16_t accentCol) {
|
||||||
if (sprBarCreated) {
|
if (sprBarCreated) {
|
||||||
sprBar.fillRoundRect(0, 0, 120, 8, 3, 0x18C3);
|
sprBar.fillRoundRect(0, 0, 120, 8, 3, 0x18C3);
|
||||||
if (fillW > 0) {
|
if (fillW > 0) {
|
||||||
|
if (isRainbowTheme()) {
|
||||||
|
for (int bx = 0; bx < fillW; bx++) {
|
||||||
|
sprBar.drawFastVLine(bx, 0, 8, getRainbowScreenColor(barX + bx, barY));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
sprBar.fillRoundRect(0, 0, fillW, 8, 3, accentCol);
|
sprBar.fillRoundRect(0, 0, fillW, 8, 3, accentCol);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
sprBar.pushSprite(barX, barY);
|
sprBar.pushSprite(barX, barY);
|
||||||
} else {
|
} else {
|
||||||
tft.fillRoundRect(barX, barY, 120, 8, 3, 0x18C3);
|
tft.fillRoundRect(barX, barY, 120, 8, 3, 0x18C3);
|
||||||
if (fillW > 0) {
|
if (fillW > 0) {
|
||||||
|
if (isRainbowTheme()) {
|
||||||
|
for (int bx = 0; bx < fillW; bx++) {
|
||||||
|
tft.drawFastVLine(barX + bx, barY, 8, getRainbowScreenColor(barX + bx, barY));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
tft.fillRoundRect(barX, barY, fillW, 8, 3, accentCol);
|
tft.fillRoundRect(barX, barY, fillW, 8, 3, accentCol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw a single temperature card with card index (0: Motor, 1: Interior, 2: Exterior)
|
// Draw a single temperature card with card index (0: Motor, 1: Interior, 2: Exterior)
|
||||||
inline void drawTriTempCard(uint8_t cardIdx, float val, int x_offset = 0) {
|
inline void drawTriTempCard(uint8_t cardIdx, float val, int x_offset = 0) {
|
||||||
int cardY = 6 + cardIdx * 77; // y = 6, 83, 160
|
int cardY = 19 + cardIdx * 74; // y = 19, 93, 167 (bounds 19 to 235 matching clock screen)
|
||||||
int cardH = 73;
|
int cardH = 68;
|
||||||
int cardX = 4 + x_offset;
|
int cardX = 4 + x_offset;
|
||||||
int cardW = 312;
|
int cardW = 312;
|
||||||
|
|
||||||
|
|
@ -135,7 +225,7 @@ inline void drawTriTempCard(uint8_t cardIdx, float val, int x_offset = 0) {
|
||||||
isInt = true;
|
isInt = true;
|
||||||
float warnT = warningValue[0].toFloat();
|
float warnT = warningValue[0].toFloat();
|
||||||
if (warnT <= 0) warnT = 110.0f;
|
if (warnT <= 0) warnT = 110.0f;
|
||||||
if (val < (warnT - 10.0f)) accentCol = getThemePrimaryColor();
|
if (val < (warnT - 10.0f)) accentCol = isRainbowTheme() ? (uint16_t)0x07FF : getThemePrimaryColor();
|
||||||
else if (val < warnT) accentCol = TFT_YELLOW;
|
else if (val < warnT) accentCol = TFT_YELLOW;
|
||||||
else accentCol = TFT_RED;
|
else accentCol = TFT_RED;
|
||||||
} else if (cardIdx == 1) {
|
} else if (cardIdx == 1) {
|
||||||
|
|
@ -143,36 +233,36 @@ inline void drawTriTempCard(uint8_t cardIdx, float val, int x_offset = 0) {
|
||||||
subStr = (dsSensorCount > 0) ? "DS18B20 SENSOR 1 (GPIO 22)" : "DS18B20 SENSOR 1";
|
subStr = (dsSensorCount > 0) ? "DS18B20 SENSOR 1 (GPIO 22)" : "DS18B20 SENSOR 1";
|
||||||
minRange = 10.0f;
|
minRange = 10.0f;
|
||||||
maxRange = 40.0f;
|
maxRange = 40.0f;
|
||||||
accentCol = 0x07F3; // Mint Green
|
accentCol = isRainbowTheme() ? (uint16_t)0x07E0 : getThemeAccentColor(); // Green in rainbow
|
||||||
} else {
|
} else {
|
||||||
titleStr = "EXTERIOR";
|
titleStr = "EXTERIOR";
|
||||||
subStr = (dsSensorCount > 1) ? "DS18B20 SENSOR 2 (GPIO 22)" : (dsSensorCount == 1 ? "ESTIMADA (-4.5`C)" : "DS18B20 SENSOR 2");
|
subStr = (dsSensorCount > 1) ? "DS18B20 SENSOR 2 (GPIO 22)" : (dsSensorCount == 1 ? "ESTIMADA (-4.5`C)" : "DS18B20 SENSOR 2");
|
||||||
minRange = -10.0f;
|
minRange = -10.0f;
|
||||||
maxRange = 50.0f;
|
maxRange = 50.0f;
|
||||||
accentCol = 0x3D7F; // Sky Blue
|
accentCol = isRainbowTheme() ? (uint16_t)0xFD20 : getThemePrimaryColor(); // Orange in rainbow
|
||||||
}
|
}
|
||||||
|
|
||||||
// Left vertical accent bar
|
// Left vertical accent bar
|
||||||
tft.fillRoundRect(cardX + 6, cardY + 8, 4, cardH - 16, 2, accentCol);
|
tft.fillRoundRect(cardX + 6, cardY + 6, 4, cardH - 12, 2, accentCol);
|
||||||
|
|
||||||
// Titles (Smooth anti-aliased font)
|
// Titles (Smooth anti-aliased font)
|
||||||
if (!sprTriTitleReady) initTriTempTitles();
|
if (!sprTriTitleReady) initTriTempTitles();
|
||||||
if (sprTriTitleReady && sprTriTitle[cardIdx].created()) {
|
if (sprTriTitleReady && sprTriTitle[cardIdx].created()) {
|
||||||
sprTriTitle[cardIdx].pushSprite(cardX + 16, cardY + 6);
|
sprTriTitle[cardIdx].pushSprite(cardX + 16, cardY + 5);
|
||||||
} else {
|
} else {
|
||||||
tft.loadFont(Latin_Sans_Bold_26);
|
tft.loadFont(Latin_Sans_Bold_26);
|
||||||
tft.setTextPadding(0);
|
tft.setTextPadding(0);
|
||||||
tft.setTextColor(TFT_WHITE, cardBg);
|
tft.setTextColor(TFT_WHITE, cardBg);
|
||||||
tft.drawString(titleStr, cardX + 16, cardY + 6, 1);
|
tft.drawString(titleStr, cardX + 16, cardY + 5, 1);
|
||||||
tft.unloadFont();
|
tft.unloadFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
tft.setTextColor(0x8410, cardBg); // Soft gray
|
tft.setTextColor(0x8410, cardBg); // Soft gray
|
||||||
tft.drawString(subStr, cardX + 16, cardY + 34, 1);
|
tft.drawString(subStr, cardX + 16, cardY + 32, 1);
|
||||||
|
|
||||||
// 3. Horizontal Progress Gauge Bar
|
// 3. Horizontal Progress Gauge Bar
|
||||||
int barX = cardX + 16;
|
int barX = cardX + 16;
|
||||||
int barY = cardY + 50;
|
int barY = cardY + 48;
|
||||||
int barW = 120;
|
int barW = 120;
|
||||||
|
|
||||||
float ratio = (val - minRange) / (maxRange - minRange);
|
float ratio = (val - minRange) / (maxRange - minRange);
|
||||||
|
|
@ -200,24 +290,19 @@ inline void drawTriTempCard(uint8_t cardIdx, float val, int x_offset = 0) {
|
||||||
numStr = String(val, 1);
|
numStr = String(val, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
bool rainbowTxt = isRainbowTheme() && (accentCol != TFT_RED);
|
||||||
tft.setTextColor(accentCol, cardBg);
|
drawDseg7RightString34(tft, numStr.c_str(), cardX + 262, cardY + 20, accentCol, cardBg, 100, 240, rainbowTxt);
|
||||||
tft.setTextPadding(100);
|
|
||||||
tft.drawRightString(numStr.c_str(), cardX + 262, cardY + 22, 1);
|
|
||||||
tft.setTextPadding(0);
|
|
||||||
tft.unloadFont();
|
|
||||||
|
|
||||||
// Unit "°C"
|
// Unit "°C" in authentic digital 7-segment italic typography matching numbers
|
||||||
tft.setTextColor(0xC618, cardBg);
|
draw7SegDegreeC(tft, cardX + 268, cardY + 22, isRainbowTheme() ? getRainbowScreenColor(cardX + 268, cardY + 22) : 0xC618, cardBg);
|
||||||
tft.drawString("`C", cardX + 268, cardY + 32, 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flicker-free dynamic value update for a single temperature card
|
// Flicker-free dynamic value update for a single temperature card
|
||||||
inline void updateTriTempCardValue(uint8_t cardIdx, float val, float oldVal) {
|
inline void updateTriTempCardValue(uint8_t cardIdx, float val, float oldVal) {
|
||||||
if (currentScreenPage != 1 || fullScreenCell != -1) return;
|
if (currentScreenPage != 1 || fullScreenCell != -1) return;
|
||||||
|
|
||||||
int cardY = 6 + cardIdx * 77; // y = 6, 83, 160
|
int cardY = 19 + cardIdx * 74; // y = 19, 93, 167
|
||||||
int cardH = 73;
|
int cardH = 68;
|
||||||
int cardX = 4;
|
int cardX = 4;
|
||||||
uint16_t cardBg = 0x10A2; // Dark graphite navy
|
uint16_t cardBg = 0x10A2; // Dark graphite navy
|
||||||
|
|
||||||
|
|
@ -232,11 +317,11 @@ inline void updateTriTempCardValue(uint8_t cardIdx, float val, float oldVal) {
|
||||||
isInt = true;
|
isInt = true;
|
||||||
float warnT = warningValue[0].toFloat();
|
float warnT = warningValue[0].toFloat();
|
||||||
if (warnT <= 0) warnT = 110.0f;
|
if (warnT <= 0) warnT = 110.0f;
|
||||||
if (val < (warnT - 10.0f)) accentCol = getThemePrimaryColor();
|
if (val < (warnT - 10.0f)) accentCol = isRainbowTheme() ? (uint16_t)0x07FF : getThemePrimaryColor();
|
||||||
else if (val < warnT) accentCol = TFT_YELLOW;
|
else if (val < warnT) accentCol = TFT_YELLOW;
|
||||||
else accentCol = TFT_RED;
|
else accentCol = TFT_RED;
|
||||||
|
|
||||||
if (oldVal < (warnT - 10.0f)) oldAccentCol = getThemePrimaryColor();
|
if (oldVal < (warnT - 10.0f)) oldAccentCol = isRainbowTheme() ? (uint16_t)0x07FF : getThemePrimaryColor();
|
||||||
else if (oldVal < warnT) oldAccentCol = TFT_YELLOW;
|
else if (oldVal < warnT) oldAccentCol = TFT_YELLOW;
|
||||||
else oldAccentCol = TFT_RED;
|
else oldAccentCol = TFT_RED;
|
||||||
|
|
||||||
|
|
@ -244,30 +329,30 @@ inline void updateTriTempCardValue(uint8_t cardIdx, float val, float oldVal) {
|
||||||
if (ignitionOn != oldTriIgnition) {
|
if (ignitionOn != oldTriIgnition) {
|
||||||
oldTriIgnition = ignitionOn;
|
oldTriIgnition = ignitionOn;
|
||||||
const char* subStr = ignitionOn ? "ECU OBD-II (PID 0105)" : "ECU OBD-II (STANDBY)";
|
const char* subStr = ignitionOn ? "ECU OBD-II (PID 0105)" : "ECU OBD-II (STANDBY)";
|
||||||
tft.fillRect(cardX + 16, cardY + 34, 130, 10, cardBg);
|
tft.fillRect(cardX + 16, cardY + 32, 130, 10, cardBg);
|
||||||
tft.setTextColor(0x8410, cardBg);
|
tft.setTextColor(0x8410, cardBg);
|
||||||
tft.drawString(subStr, cardX + 16, cardY + 34, 1);
|
tft.drawString(subStr, cardX + 16, cardY + 32, 1);
|
||||||
}
|
}
|
||||||
} else if (cardIdx == 1) {
|
} else if (cardIdx == 1) {
|
||||||
minRange = 10.0f;
|
minRange = 10.0f;
|
||||||
maxRange = 40.0f;
|
maxRange = 40.0f;
|
||||||
accentCol = 0x07F3; // Mint Green
|
accentCol = isRainbowTheme() ? (uint16_t)0x07E0 : getThemeAccentColor();
|
||||||
oldAccentCol = accentCol;
|
oldAccentCol = accentCol;
|
||||||
} else {
|
} else {
|
||||||
minRange = -10.0f;
|
minRange = -10.0f;
|
||||||
maxRange = 50.0f;
|
maxRange = 50.0f;
|
||||||
accentCol = 0x3D7F; // Sky Blue
|
accentCol = isRainbowTheme() ? (uint16_t)0xFD20 : getThemePrimaryColor();
|
||||||
oldAccentCol = accentCol;
|
oldAccentCol = accentCol;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Accent color changed: update vertical accent bar
|
// 1. Accent color changed: update vertical accent bar
|
||||||
if (accentCol != oldAccentCol) {
|
if (accentCol != oldAccentCol) {
|
||||||
tft.fillRoundRect(cardX + 6, cardY + 8, 4, cardH - 16, 2, accentCol);
|
tft.fillRoundRect(cardX + 6, cardY + 6, 4, cardH - 12, 2, accentCol);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Horizontal Progress Gauge Bar update (only redraw when pixel fill changes or accent changes)
|
// 2. Horizontal Progress Gauge Bar update (only redraw when pixel fill changes or accent changes)
|
||||||
int barX = cardX + 16;
|
int barX = cardX + 16;
|
||||||
int barY = cardY + 50;
|
int barY = cardY + 48;
|
||||||
int barW = 120;
|
int barW = 120;
|
||||||
|
|
||||||
float ratio = (val - minRange) / (maxRange - minRange);
|
float ratio = (val - minRange) / (maxRange - minRange);
|
||||||
|
|
@ -292,12 +377,8 @@ inline void updateTriTempCardValue(uint8_t cardIdx, float val, float oldVal) {
|
||||||
numStr = String(val, 1);
|
numStr = String(val, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
bool rainbowTxt = isRainbowTheme() && (accentCol != TFT_RED);
|
||||||
tft.setTextColor(accentCol, cardBg);
|
drawDseg7RightString34(tft, numStr.c_str(), cardX + 262, cardY + 20, accentCol, cardBg, 100, 240, rainbowTxt);
|
||||||
tft.setTextPadding(100);
|
|
||||||
tft.drawRightString(numStr.c_str(), cardX + 262, cardY + 22, 1);
|
|
||||||
tft.setTextPadding(0);
|
|
||||||
tft.unloadFont();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Synchronize tracking variables without touching TFT display (zero flicker)
|
// Synchronize tracking variables without touching TFT display (zero flicker)
|
||||||
|
|
@ -311,7 +392,11 @@ inline void syncTriTempTrackers() {
|
||||||
float vals[3] = { tempEngineCoolant, tempInterior, tempExterior };
|
float vals[3] = { tempEngineCoolant, tempInterior, tempExterior };
|
||||||
float minR[3] = { 60.0f, 10.0f, -10.0f };
|
float minR[3] = { 60.0f, 10.0f, -10.0f };
|
||||||
float maxR[3] = { 120.0f, 40.0f, 50.0f };
|
float maxR[3] = { 120.0f, 40.0f, 50.0f };
|
||||||
uint16_t defAcc[3] = { getThemePrimaryColor(), 0x07F3, 0x3D7F };
|
uint16_t defAcc[3] = {
|
||||||
|
isRainbowTheme() ? (uint16_t)0x07FF : getThemePrimaryColor(),
|
||||||
|
isRainbowTheme() ? (uint16_t)0x07E0 : getThemeAccentColor(),
|
||||||
|
isRainbowTheme() ? (uint16_t)0xFD20 : getThemePrimaryColor()
|
||||||
|
};
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
float ratio = (vals[i] - minR[i]) / (maxR[i] - minR[i]);
|
float ratio = (vals[i] - minR[i]) / (maxR[i] - minR[i]);
|
||||||
if (ratio < 0.0f) ratio = 0.0f;
|
if (ratio < 0.0f) ratio = 0.0f;
|
||||||
|
|
@ -333,11 +418,10 @@ inline void syncTriTempTrackers() {
|
||||||
|
|
||||||
// Draw full Tri-Temp screen
|
// Draw full Tri-Temp screen
|
||||||
inline void drawTriTempScreen(int x_offset = 0) {
|
inline void drawTriTempScreen(int x_offset = 0) {
|
||||||
if (x_offset != 0) {
|
if (x_offset == 0) {
|
||||||
tft.fillRect(0, 0, x_offset, 240, TFT_BLACK);
|
tft.fillScreen(TFT_BLACK);
|
||||||
} else {
|
} else {
|
||||||
tft.fillRect(0, 0, 320, 6, TFT_BLACK);
|
tft.fillRect(0, 0, x_offset, 240, TFT_BLACK);
|
||||||
tft.fillRect(0, 233, 320, 7, TFT_BLACK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drawTriTempCard(0, tempEngineCoolant, x_offset);
|
drawTriTempCard(0, tempEngineCoolant, x_offset);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue