feat(config,ui): menu de umbrales de alarmas y tipografia DSEG7 80pt en transicion de reloj
- Submenu de alarmas con opciones separadas de 'Seteo de Activacion' y 'Seteo de Umbrales' - Pantalla tactil interactiva para ajuste de umbrales (Temp, RPM, Voltaje, Vel) con persistencia en NVS - Vinculacion del umbral de temperatura de refrigerante en tri-temp con el valor configurado - Renderizado nativo de DSEG7 80pt desde PROGMEM en renderPage2ToSprite para coincidencia exacta de tipografia en transicion de reloj sin interferir con fuentes de otras pantallas
This commit is contained in:
parent
d124d013ab
commit
4937cf3b42
3 changed files with 360 additions and 22 deletions
|
|
@ -15,6 +15,9 @@ extern bool wokeFromSleep;
|
|||
extern uint8_t transitionType;
|
||||
extern volatile bool systemSleeping;
|
||||
extern TaskHandle_t btTaskHandle;
|
||||
extern String warningValue[5];
|
||||
extern bool alarmEnabled[5];
|
||||
extern const String pidConfig[5][9];
|
||||
|
||||
// Control de Inactividad de 10 segundos en Menu de Configuracion y Submenus
|
||||
static uint32_t lastMenuActivityTime = 0;
|
||||
|
|
@ -216,12 +219,173 @@ void warningMenu() {
|
|||
}
|
||||
// 2. Bottom button [ SALIR ]
|
||||
else if (ty >= 180 && tx >= 20 && tx <= 300) {
|
||||
animateExitButtonPress();
|
||||
break;
|
||||
}
|
||||
} else if (digitalRead(SELECTOR_PIN) == LOW) {
|
||||
resetMenuActivity();
|
||||
delay(200);
|
||||
break;
|
||||
}
|
||||
delay(25);
|
||||
}
|
||||
|
||||
// Save alarm enablement on any exit
|
||||
pref.begin("setting", false);
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
String almKey = "alm_" + String(i);
|
||||
pref.putBool(almKey.c_str(), alarmEnabled[i]);
|
||||
}
|
||||
pref.end();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Sub-screen: Seteo de Umbrales de Alarma
|
||||
// -------------------------------------------------------------
|
||||
void thresholdMenu() {
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
// Helper to step a parameter threshold
|
||||
auto stepThreshold = [&](uint8_t row, bool increment) {
|
||||
if (row == 0) { // Temperatura ECT: 70 to 125, step 1 °C
|
||||
int v = warningValue[0].toInt();
|
||||
v += increment ? 1 : -1;
|
||||
if (v < 70) v = 70;
|
||||
if (v > 125) v = 125;
|
||||
warningValue[0] = String(v);
|
||||
} else if (row == 1) { // RPM: 2000 to 6500, step 100 rpm
|
||||
int v = warningValue[1].toInt();
|
||||
v += increment ? 100 : -100;
|
||||
if (v < 2000) v = 2000;
|
||||
if (v > 6500) v = 6500;
|
||||
warningValue[1] = String(v);
|
||||
} else if (row == 2) { // Voltaje: 13.0 to 16.0, step 0.1 V
|
||||
float v = warningValue[2].toFloat();
|
||||
v += increment ? 0.1f : -0.1f;
|
||||
if (v < 13.0f) v = 13.0f;
|
||||
if (v > 16.0f) v = 16.0f;
|
||||
v = roundf(v * 10.0f) / 10.0f;
|
||||
warningValue[2] = String(v, 1);
|
||||
} else if (row == 3) { // Velocidad: 60 to 180, step 5 km/h
|
||||
int v = warningValue[3].toInt();
|
||||
v += increment ? 5 : -5;
|
||||
if (v < 60) v = 60;
|
||||
if (v > 180) v = 180;
|
||||
warningValue[3] = String(v);
|
||||
}
|
||||
};
|
||||
|
||||
// Draw single row value box (fast partial redraw for smooth hold-to-repeat)
|
||||
auto drawRowValue = [&](uint8_t i) {
|
||||
int y = 10 + i * 44;
|
||||
int valX = 176;
|
||||
int valW = 80;
|
||||
int valH = 30;
|
||||
tft.fillRoundRect(valX, y + 4, valW, valH, 4, 0x0821);
|
||||
tft.drawRoundRect(valX, y + 4, valW, valH, 4, 0x2945);
|
||||
|
||||
String dispVal = "";
|
||||
if (i == 0) {
|
||||
dispVal = warningValue[0] + " `C";
|
||||
} else if (i == 1) {
|
||||
dispVal = warningValue[1];
|
||||
} else if (i == 2) {
|
||||
dispVal = String(warningValue[2].toFloat(), 1) + " V";
|
||||
} else if (i == 3) {
|
||||
dispVal = warningValue[3] + " km";
|
||||
}
|
||||
|
||||
tft.setTextColor(getThemePrimaryColor(), 0x0821);
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
tft.drawString(dispVal, valX + valW / 2, y + 19, 4);
|
||||
tft.setTextDatum(TL_DATUM); // restore default
|
||||
};
|
||||
|
||||
auto drawAllThresholdRows = [&]() {
|
||||
uint16_t onCol = getThemePrimaryColor();
|
||||
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
int y = 10 + i * 44;
|
||||
// Card background
|
||||
tft.fillRoundRect(8, y, 304, 38, 5, getThemeDarkColor());
|
||||
tft.drawRoundRect(8, y, 304, 38, 5, getThemeAccentColor());
|
||||
tft.fillRoundRect(10, y + 4, 3, 30, 1, onCol);
|
||||
|
||||
// Parameter label
|
||||
const char* const thrNames[4] = { "Temp.", "RPM", "Voltaje", "Veloc." };
|
||||
tft.setTextColor(TFT_WHITE, getThemeDarkColor());
|
||||
tft.drawString(thrNames[i], 18, y + 7, 4);
|
||||
|
||||
// Decrement button [-] (x: 132, w: 38, h: 30)
|
||||
tft.fillRoundRect(132, y + 4, 38, 30, 4, getThemeAccentColor());
|
||||
tft.drawRoundRect(132, y + 4, 38, 30, 4, onCol);
|
||||
tft.setTextColor(TFT_WHITE, getThemeAccentColor());
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
tft.drawString("-", 132 + 19, y + 19, 4);
|
||||
|
||||
// Value Box (x: 176, w: 80, h: 30)
|
||||
drawRowValue(i);
|
||||
|
||||
// Increment button [+] (x: 262, w: 38, h: 30)
|
||||
tft.fillRoundRect(262, y + 4, 38, 30, 4, getThemeAccentColor());
|
||||
tft.drawRoundRect(262, y + 4, 38, 30, 4, onCol);
|
||||
tft.setTextColor(TFT_WHITE, getThemeAccentColor());
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
tft.drawString("+", 262 + 19, y + 19, 4);
|
||||
tft.setTextDatum(TL_DATUM); // restore default
|
||||
}
|
||||
};
|
||||
|
||||
drawAllThresholdRows();
|
||||
drawExitButton(false);
|
||||
waitForTouchRelease();
|
||||
|
||||
while (true) {
|
||||
if (checkMenuTimeout()) break;
|
||||
autoDim();
|
||||
uint16_t tx = 0, ty = 0;
|
||||
|
||||
if (getTouch(&tx, &ty)) {
|
||||
resetMenuActivity();
|
||||
|
||||
// 1. Direct Touch on [-] or [+] buttons (y: 6 to 184)
|
||||
if (ty >= 6 && ty < 184) {
|
||||
int8_t row = (ty - 10) / 44;
|
||||
if (row >= 0 && row < 4) {
|
||||
bool isDec = (tx >= 122 && tx < 174);
|
||||
bool isInc = (tx >= 256 && tx <= 308);
|
||||
|
||||
if (isDec || isInc) {
|
||||
touchBeep();
|
||||
stepThreshold(row, isInc);
|
||||
drawRowValue(row);
|
||||
|
||||
// Button press highlight
|
||||
int btnX = isDec ? 132 : 262;
|
||||
int btnY = 10 + row * 44 + 4;
|
||||
tft.drawRoundRect(btnX, btnY, 38, 30, 4, TFT_WHITE);
|
||||
|
||||
// Repeat handling if user keeps pressing
|
||||
uint32_t pressStart = millis();
|
||||
uint32_t lastRepeat = millis();
|
||||
while (getTouch(&tx, &ty)) {
|
||||
if (millis() - pressStart > 350 && millis() - lastRepeat > 90) {
|
||||
lastRepeat = millis();
|
||||
stepThreshold(row, isInc);
|
||||
drawRowValue(row);
|
||||
touchBeep();
|
||||
}
|
||||
delay(15);
|
||||
yield();
|
||||
}
|
||||
|
||||
// Un-highlight button
|
||||
tft.drawRoundRect(btnX, btnY, 38, 30, 4, getThemePrimaryColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. Bottom button [ SALIR ]
|
||||
else if (ty >= 180 && tx >= 20 && tx <= 300) {
|
||||
animateExitButtonPress();
|
||||
break;
|
||||
}
|
||||
|
|
@ -232,6 +396,120 @@ void warningMenu() {
|
|||
}
|
||||
delay(25);
|
||||
}
|
||||
|
||||
// Save threshold values to persistent storage on any exit
|
||||
pref.begin("setting", false);
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
pref.putString(pidConfig[i][0].c_str(), warningValue[i]);
|
||||
}
|
||||
pref.end();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Sub-screen: Menu Principal de Alarmas (Activacion y Umbrales)
|
||||
// -------------------------------------------------------------
|
||||
void alarmCategoryMenu() {
|
||||
uint8_t choice = 255;
|
||||
|
||||
auto drawCategoryUI = [&](uint8_t sel) {
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
// Card 0: Activacion de Alarmas (y: 20 to 88, h: 68)
|
||||
uint16_t bg0 = (sel == 0) ? getThemeAccentColor() : getThemeDarkColor();
|
||||
uint16_t border0 = (sel == 0) ? getThemePrimaryColor() : getThemeAccentColor();
|
||||
tft.fillRoundRect(10, 20, 300, 68, 5, bg0);
|
||||
tft.drawRoundRect(10, 20, 300, 68, 5, border0);
|
||||
if (sel == 0) {
|
||||
tft.drawRoundRect(11, 21, 298, 66, 4, TFT_WHITE);
|
||||
} else {
|
||||
tft.fillRoundRect(13, 26, 4, 56, 2, getThemePrimaryColor());
|
||||
}
|
||||
tft.setTextColor(TFT_WHITE, bg0);
|
||||
tft.drawString("Seteo de Activacion", 24, 28, 4);
|
||||
|
||||
uint8_t activeCount = 0;
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
if (alarmEnabled[i]) activeCount++;
|
||||
}
|
||||
String actInfo = String(activeCount) + " de 4 alarmas activas (ON/OFF)";
|
||||
tft.setTextColor((sel == 0) ? TFT_YELLOW : 0xBDD7, bg0);
|
||||
tft.drawString(actInfo, 24, 58, 2);
|
||||
tft.fillTriangle(296, 54, 290, 48, 290, 60, (sel == 0) ? TFT_WHITE : getThemePrimaryColor());
|
||||
|
||||
// Card 1: Seteo de Umbrales (y: 102 to 170, h: 68)
|
||||
uint16_t bg1 = (sel == 1) ? getThemeAccentColor() : getThemeDarkColor();
|
||||
uint16_t border1 = (sel == 1) ? getThemePrimaryColor() : getThemeAccentColor();
|
||||
tft.fillRoundRect(10, 102, 300, 68, 5, bg1);
|
||||
tft.drawRoundRect(10, 102, 300, 68, 5, border1);
|
||||
if (sel == 1) {
|
||||
tft.drawRoundRect(11, 103, 298, 66, 4, TFT_WHITE);
|
||||
} else {
|
||||
tft.fillRoundRect(13, 108, 4, 56, 2, getThemePrimaryColor());
|
||||
}
|
||||
tft.setTextColor(TFT_WHITE, bg1);
|
||||
tft.drawString("Seteo de Umbrales", 24, 110, 4);
|
||||
String thrInfo = "T: " + warningValue[0] + "`C | RPM: " + warningValue[1] + " | V: " + warningValue[2] + "V | Vel: " + warningValue[3] + "k/h";
|
||||
tft.setTextColor((sel == 1) ? TFT_YELLOW : 0xBDD7, bg1);
|
||||
tft.drawString(thrInfo, 24, 140, 2);
|
||||
tft.fillTriangle(296, 136, 290, 130, 290, 142, (sel == 1) ? TFT_WHITE : getThemePrimaryColor());
|
||||
|
||||
// Bottom action button: [ SALIR ] (x: 80, y: 186, w: 160, h: 42)
|
||||
drawExitButton(false);
|
||||
if (sel == 2) {
|
||||
tft.drawRoundRect(80, 186, 160, 42, 5, getThemePrimaryColor());
|
||||
tft.drawRoundRect(81, 187, 158, 40, 4, TFT_WHITE);
|
||||
}
|
||||
};
|
||||
|
||||
drawCategoryUI(choice);
|
||||
waitForTouchRelease();
|
||||
|
||||
while (true) {
|
||||
if (checkMenuTimeout()) break;
|
||||
autoDim();
|
||||
int8_t executeSelect = -1;
|
||||
uint16_t tx = 0, ty = 0;
|
||||
|
||||
if (getTouch(&tx, &ty)) {
|
||||
resetMenuActivity();
|
||||
if (ty >= 16 && ty < 96) {
|
||||
executeSelect = 0;
|
||||
} else if (ty >= 96 && ty < 176) {
|
||||
executeSelect = 1;
|
||||
} else if (ty >= 178 && ty <= 240 && tx >= 20 && tx <= 300) {
|
||||
executeSelect = 2;
|
||||
}
|
||||
|
||||
if (executeSelect >= 0) {
|
||||
choice = executeSelect;
|
||||
if (executeSelect == 2) {
|
||||
animateExitButtonPress();
|
||||
break;
|
||||
} else {
|
||||
drawCategoryUI(choice);
|
||||
touchBeep();
|
||||
delay(120);
|
||||
waitForTouchRelease();
|
||||
|
||||
if (executeSelect == 0) {
|
||||
warningMenu(); // Seteo de Activacion ON/OFF
|
||||
} else if (executeSelect == 1) {
|
||||
thresholdMenu(); // Seteo de Umbrales
|
||||
}
|
||||
|
||||
if (menuExitedDueToTimeout) break;
|
||||
choice = 255;
|
||||
drawCategoryUI(choice);
|
||||
waitForTouchRelease();
|
||||
}
|
||||
}
|
||||
} else if (digitalRead(SELECTOR_PIN) == LOW) {
|
||||
resetMenuActivity();
|
||||
delay(200);
|
||||
break;
|
||||
}
|
||||
delay(25);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
|
|
@ -1892,9 +2170,9 @@ void configMenu() {
|
|||
if (executeSelect >= 0) {
|
||||
select = executeSelect;
|
||||
|
||||
// Option 0: Alarmas (ON/OFF)
|
||||
// Option 0: Alarmas (Menu de Activacion y Umbrales)
|
||||
if (select == 0) {
|
||||
warningMenu();
|
||||
alarmCategoryMenu();
|
||||
if (menuExitedDueToTimeout) break;
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
drawConfigHeader();
|
||||
|
|
|
|||
|
|
@ -162,6 +162,55 @@ inline void draw7SegColon(TFT_eSPI &dst, int clkX, int clkY, uint16_t color,
|
|||
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 {
|
||||
if (fl) {
|
||||
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();
|
||||
}
|
||||
|
||||
// Synchronize tracking variables without touching TFT display (zero flicker)
|
||||
inline void syncGpsClockTrackers() {
|
||||
oldClockHour = gpsHour;
|
||||
|
|
@ -324,12 +373,16 @@ inline void renderPage2ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
|||
(gpsMinute < 10) ? "0" + String(gpsMinute) : String(gpsMinute);
|
||||
|
||||
int digitY = clkY + 11;
|
||||
if (digitY + 75 > 0 && digitY < slice_h) {
|
||||
s.setTextWrap(false, false);
|
||||
s.setTextColor(priCol, cardBg);
|
||||
s.setTextPadding(0);
|
||||
s.drawRightString(hStr.c_str(), clkX + 142, digitY, 8);
|
||||
s.drawString(mStr.c_str(), clkX + 170, digitY, 8);
|
||||
if (digitY + 80 > 0 && digitY < slice_h) {
|
||||
uint8_t h10 = (gpsHour / 10) % 10;
|
||||
uint8_t h1 = gpsHour % 10;
|
||||
uint8_t m10 = (gpsMinute / 10) % 10;
|
||||
uint8_t m1 = gpsMinute % 10;
|
||||
|
||||
drawDseg7Digit80(s, clkX + 12, digitY, h10, priCol, cardBg, slice_h);
|
||||
drawDseg7Digit80(s, clkX + 77, digitY, h1, priCol, cardBg, slice_h);
|
||||
drawDseg7Digit80(s, clkX + 170, digitY, m10, priCol, cardBg, slice_h);
|
||||
drawDseg7Digit80(s, clkX + 235, digitY, m1, priCol, cardBg, slice_h);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ extern TFT_eSPI tft;
|
|||
extern uint16_t getThemePrimaryColor();
|
||||
extern uint16_t getThemeBgColor();
|
||||
extern void touchBeep();
|
||||
extern String warningValue[5];
|
||||
|
||||
// Global states for multi-page system
|
||||
extern int8_t fullScreenCell;
|
||||
|
|
@ -209,8 +210,10 @@ inline void drawTriTempCard(uint8_t cardIdx, float val, int x_offset = 0) {
|
|||
minRange = 60.0f;
|
||||
maxRange = 120.0f;
|
||||
isInt = true;
|
||||
if (val < 100.0f) accentCol = getThemePrimaryColor();
|
||||
else if (val < 110.0f) accentCol = TFT_YELLOW;
|
||||
float warnT = warningValue[0].toFloat();
|
||||
if (warnT <= 0) warnT = 110.0f;
|
||||
if (val < (warnT - 10.0f)) accentCol = getThemePrimaryColor();
|
||||
else if (val < warnT) accentCol = TFT_YELLOW;
|
||||
else accentCol = TFT_RED;
|
||||
} else if (cardIdx == 1) {
|
||||
titleStr = "INTERIOR";
|
||||
|
|
@ -307,12 +310,14 @@ inline void updateTriTempCardValue(uint8_t cardIdx, float val, float oldVal) {
|
|||
minRange = 60.0f;
|
||||
maxRange = 120.0f;
|
||||
isInt = true;
|
||||
if (val < 100.0f) accentCol = getThemePrimaryColor();
|
||||
else if (val < 110.0f) accentCol = TFT_YELLOW;
|
||||
float warnT = warningValue[0].toFloat();
|
||||
if (warnT <= 0) warnT = 110.0f;
|
||||
if (val < (warnT - 10.0f)) accentCol = getThemePrimaryColor();
|
||||
else if (val < warnT) accentCol = TFT_YELLOW;
|
||||
else accentCol = TFT_RED;
|
||||
|
||||
if (oldVal < 100.0f) oldAccentCol = getThemePrimaryColor();
|
||||
else if (oldVal < 110.0f) oldAccentCol = TFT_YELLOW;
|
||||
if (oldVal < (warnT - 10.0f)) oldAccentCol = getThemePrimaryColor();
|
||||
else if (oldVal < warnT) oldAccentCol = TFT_YELLOW;
|
||||
else oldAccentCol = TFT_RED;
|
||||
|
||||
// Subtitle update if ignition state changed
|
||||
|
|
@ -398,8 +403,10 @@ inline void syncTriTempTrackers() {
|
|||
|
||||
uint16_t acc = defAcc[i];
|
||||
if (i == 0) {
|
||||
if (vals[i] >= 110.0f) acc = TFT_RED;
|
||||
else if (vals[i] >= 100.0f) acc = TFT_YELLOW;
|
||||
float warnT = warningValue[0].toFloat();
|
||||
if (warnT <= 0) warnT = 110.0f;
|
||||
if (vals[i] >= warnT) acc = TFT_RED;
|
||||
else if (vals[i] >= (warnT - 10.0f)) acc = TFT_YELLOW;
|
||||
}
|
||||
oldTriAccentCol[i] = acc;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue