feat(ui,backlight): calibracion adaptativa de LDR, tipografia antialiasing y texto simplificado

- Calibracion de curva de brillo por LDR: noche (7%), luz ambiente (25%), luz directa/sol (100%)
- Filtrado progresivo IIR sobre LDR para eliminar parpadeo y transiciones bruscas
- Soporte completo de caracteres ASCII y signos en fuente antialiasing Latin_Sans_Bold_26
- Menu de brillo con renderizado suave de titulos y eliminacion de porcentajes/parentesis redundantes
- Optimizacion en renderizado DSEG7 e indicador de unidad de temperatura
This commit is contained in:
Sebastián Nieto 2026-10-14 21:35:22 -03:00
parent 674c044dd1
commit ddc4a109af
7 changed files with 1911 additions and 1220 deletions

File diff suppressed because it is too large Load diff

View file

@ -97,44 +97,38 @@ inline void drawClusterBaseCards(int x_offset = 0) {
uint16_t cCool = isRainbowTheme() ? getRainbowScreenColor(20, 150) : (isYapaTheme() ? getYapaColor(2) : priCol);
uint16_t cVolt = isRainbowTheme() ? getRainbowScreenColor(180, 150) : (isYapaTheme() ? getYapaColor(3) : accCol);
// 1. TACHOMETER CARD (x: 8, y: 8, w: 304, h: 50)
tft.fillRoundRect(8 + x_offset, 8, 304, 50, 5, cardBg);
tft.drawRoundRect(8 + x_offset, 8, 304, 50, 5, border);
// Tachometer background rail on the LEFT (x: 18 to 90, w: 72, h: 14)
tft.fillRoundRect(18 + x_offset, 15, 72, 14, 3, 0x0841);
if (isYapaTheme()) {
int sx = 20 + x_offset;
char buf[2] = {0, 0};
for (int i = 0; i < 7; i++) {
buf[0] = '1' + i;
tft.setTextColor(getYapaColor(i), cardBg);
tft.drawString(buf, sx, 32, 1);
sx += tft.textWidth(buf, 1) + tft.textWidth(" ", 1);
}
} else {
tft.setTextColor(cRpm, cardBg);
tft.drawString("1 2 3 4 5 6 7", 20 + x_offset, 32, 1);
// 1. TACHOMETER CARD (x: 8, y: 8, w: 304, h: 58)
tft.fillRoundRect(8 + x_offset, 8, 304, 58, 5, cardBg);
tft.drawRoundRect(8 + x_offset, 8, 304, 58, 5, border);
tft.fillRoundRect(14 + x_offset, 14, 4, 46, 2, cRpm);
// Tachometer triangle VU-meter inactive background slots (14 steps, ramp h: 5..31 px, x: 24..106)
for (int s = 0; s < 14; s++) {
int barX = 24 + s * 6 + x_offset;
int barH = 5 + s * 2;
int barY = 54 - barH;
tft.fillRect(barX, barY, 4, barH, 0x2145);
}
// "RPM" unit on the far right in Chakra Petch Bold Italic (aligned with foot of digits at y = 55)
drawRpm_AA(tft, 242 + x_offset, 35, cRpm, cardBg, 240, isRainbowTheme());
// "RPM" unit on the far right in Chakra Petch Bold Italic (aligned with foot of digits at y = 59)
drawRpm_AA(tft, 242 + x_offset, 39, cRpm, cardBg, 240, isRainbowTheme());
// 2. SPEEDOMETER CARD (x: 8, y: 62, w: 304, h: 86)
tft.fillRoundRect(8 + x_offset, 62, 304, 86, 5, cardBg);
tft.drawRoundRect(8 + x_offset, 62, 304, 86, 5, border);
// Large anti-aliased "KM/H" (Chakra Petch Bold Italic) at far right (baseline at y = 145)
drawKmH_AA(tft, 198 + x_offset, 117, cSpd, cardBg, 240, isRainbowTheme());
// 2. SPEEDOMETER CARD (x: 8, y: 70, w: 304, h: 100)
tft.fillRoundRect(8 + x_offset, 70, 304, 100, 5, cardBg);
tft.drawRoundRect(8 + x_offset, 70, 304, 100, 5, border);
tft.fillRoundRect(14 + x_offset, 78, 4, 84, 2, cSpd);
// Large anti-aliased "KM/H" (Chakra Petch Bold Italic) at far right (baseline at y = 160)
drawKmH_AA(tft, 198 + x_offset, 132, cSpd, cardBg, 240, isRainbowTheme());
// 3. COOLANT TEMP CARD (x: 8, y: 152, w: 148, h: 84)
tft.fillRoundRect(8 + x_offset, 152, 148, 84, 5, cardBg);
tft.drawRoundRect(8 + x_offset, 152, 148, 84, 5, border);
drawTitRef_AA(tft, 28 + x_offset, 158, cCool, cardBg, 240, isRainbowTheme());
drawDegreeC_AA(tft, 117 + x_offset, 198, cCool, cardBg, 240, isRainbowTheme());
// 3. COOLANT TEMP CARD (x: 8, y: 174, w: 148, h: 62)
tft.fillRoundRect(8 + x_offset, 174, 148, 62, 5, cardBg);
tft.drawRoundRect(8 + x_offset, 174, 148, 62, 5, border);
tft.fillRoundRect(14 + x_offset, 182, 4, 46, 2, cCool);
draw7SegDegreeC(tft, 118 + x_offset, 201, cCool, cardBg, isRainbowTheme());
// 4. BATTERY VOLTAGE CARD (x: 164, y: 152, w: 148, h: 84)
tft.fillRoundRect(164 + x_offset, 152, 148, 84, 5, cardBg);
tft.drawRoundRect(164 + x_offset, 152, 148, 84, 5, border);
drawTitBat_AA(tft, 208 + x_offset, 158, cVolt, cardBg, 240, isRainbowTheme());
drawVolt_AA(tft, 286 + x_offset, 198, cVolt, cardBg, 240, isRainbowTheme());
// 4. BATTERY VOLTAGE CARD (x: 164, y: 174, w: 148, h: 62)
tft.fillRoundRect(164 + x_offset, 174, 148, 62, 5, cardBg);
tft.drawRoundRect(164 + x_offset, 174, 148, 62, 5, border);
tft.fillRoundRect(170 + x_offset, 182, 4, 46, 2, cVolt);
drawVolt_AA(tft, 286 + x_offset, 202, cVolt, cardBg, 240, isRainbowTheme());
}
// Initial direct draw of Page 0
@ -179,21 +173,23 @@ inline void updateClusterScreen(bool forceRedraw) {
if (forceRedraw || abs(curRpm - oldClusterRpm) >= 20 || (curRpm == 0 && oldClusterRpm != 0)) {
oldClusterRpm = curRpm;
// Segmented Tachometer Bar on the LEFT (10 segments between x: 20 and 88)
int activeSegs = map(constrain(curRpm, 0, 7000), 0, 7000, 0, 10);
// Segmented Tachometer Triangle VU-Meter on the LEFT (14 steps from 0 to 7000 RPM, ramp h: 5..31 px, x: 24..106)
int activeSegs = (curRpm > 0) ? map(constrain(curRpm, 0, 7000), 0, 7000, 1, 14) : 0;
if (activeSegs != oldClusterSegs || forceRedraw) {
oldClusterSegs = activeSegs;
for (int s = 0; s < 10; s++) {
int sx = 20 + s * 7;
for (int s = 0; s < 14; s++) {
int barX = 24 + s * 6;
int barH = 5 + s * 2;
int barY = 54 - barH;
uint16_t segCol;
if (s < activeSegs) {
if (s >= 8) segCol = TFT_RED;
else if (s >= 6) segCol = 0xFD60;
else segCol = isRainbowTheme() ? getRainbowScreenColor(sx, 20) : (isYapaTheme() ? getYapaColor(s) : cRpm);
if (s >= 12) segCol = TFT_RED;
else if (s >= 9) segCol = 0xFD60;
else segCol = isRainbowTheme() ? getRainbowScreenColor(barX, barY) : (isYapaTheme() ? getYapaColor(s) : cRpm);
} else {
segCol = 0x0841; // inactive dark track
segCol = 0x2145; // inactive dark track
}
tft.fillRect(sx, 17, 5, 10, segCol);
tft.fillRect(barX, barY, 4, barH, segCol);
}
}
@ -202,17 +198,17 @@ inline void updateClusterScreen(bool forceRedraw) {
if (curRpm > 0) {
uint16_t rpmCol = (curRpm >= 6000) ? TFT_RED : ((curRpm >= 4500) ? 0xFD60 : cRpm);
String rpmStr = String(curRpm);
drawDseg7RightFast44(tft, rpmStr.c_str(), 238, 11, rpmCol, cardBg, 138, rainbowTxt && (rpmCol != TFT_RED));
drawDseg7RightFast44(tft, rpmStr.c_str(), 238, 15, rpmCol, cardBg, 138, rainbowTxt && (rpmCol != TFT_RED));
} else {
drawDseg7RightFast44(tft, "---", 238, 11, cRpm, cardBg, 138, rainbowTxt);
drawDseg7RightFast44(tft, "---", 238, 15, cRpm, cardBg, 138, rainbowTxt);
}
}
// ECO Indicator in Card 1 centered directly above RPM unit (x = 248, y = 14, w = 48, h = 17)
// ECO Indicator in Card 1 centered directly above RPM unit (x = 248, y = 15, w = 48, h = 17)
if (forceRedraw || isEcoOptimal != oldClusterEco) {
oldClusterEco = isEcoOptimal;
if (isEcoOptimal) {
tft.fillRoundRect(248, 14, 48, 17, 3, 0x0202);
tft.fillRoundRect(248, 15, 48, 17, 3, 0x0202);
if (isYapaTheme()) {
char buf[2] = {0, 0};
const char *ecoStr = "ECO";
@ -220,16 +216,16 @@ inline void updateClusterScreen(bool forceRedraw) {
for (int i = 0; i < 3; i++) {
buf[0] = ecoStr[i];
tft.setTextColor(getYapaColor(i), 0x0202);
tft.drawString(buf, ex, 15, 2);
tft.drawString(buf, ex, 16, 2);
ex += tft.textWidth(buf, 2);
}
} else {
tft.setTextColor(TFT_WHITE, 0x0202);
tft.drawString("ECO", 260, 15, 2);
tft.drawString("ECO", 260, 16, 2);
}
tft.drawRoundRect(248, 14, 48, 17, 3, 0x07E0);
tft.drawRoundRect(248, 15, 48, 17, 3, 0x07E0);
} else {
tft.fillRect(248, 14, 48, 17, cardBg);
tft.fillRect(248, 15, 48, 17, cardBg);
}
}
@ -240,33 +236,33 @@ inline void updateClusterScreen(bool forceRedraw) {
int curSpd = (curSpdF >= 0.0f) ? (int)round(curSpdF) : -999;
bool isGps = isClusterSpeedFromGps();
// GPS Satellite icon in Card 2 centered directly above KM/H (where ECO was): x = 243, y = 82, w = 18, h = 18
// GPS Satellite icon in Card 2 centered directly above KM/H: x = 238, y = 88, w = 28, h = 28 (same large 28x28 icon as Satélites GPS)
// Only shows satellite icon if speed comes from GPS; clears when speed is from OBD2
if (forceRedraw || isGps != oldClusterGpsLock) {
oldClusterGpsLock = isGps;
if (isGps) {
drawGpsSatelliteIcon(tft, 243, 82, 0x07FF, 0x07FF, cardBg, 2);
drawLargeGpsSatelliteIcon(tft, 238, 88, TFT_WHITE, 0x07FF, cardBg, 2);
} else {
tft.fillRect(238, 80, 28, 22, cardBg);
tft.fillRect(236, 86, 32, 32, cardBg);
}
}
if (forceRedraw || curSpd != oldClusterSpd) {
oldClusterSpd = curSpd;
// Giant Speed Display using DSEG7 80pt rendered with zero-flicker line buffer (x: 14..194, rightX: 194)
// Giant Speed Display using DSEG7 80pt rendered with zero-flicker line buffer (x: 24..194, rightX: 194)
if (curSpd >= 0) {
String spdStr = String(curSpd);
uint16_t spdCol = isRainbowTheme() ? getRainbowScreenColor(194, 105) : cSpd;
drawDseg7RightFast80(tft, spdStr.c_str(), 194, 65, spdCol, cardBg, 180, isRainbowTheme());
uint16_t spdCol = isRainbowTheme() ? getRainbowScreenColor(194, 120) : cSpd;
drawDseg7RightFast80(tft, spdStr.c_str(), 194, 80, spdCol, cardBg, 168, isRainbowTheme());
} else {
uint16_t spdCol = isRainbowTheme() ? getRainbowScreenColor(194, 105) : cSpd;
drawDseg7RightFast80(tft, "---", 194, 65, spdCol, cardBg, 180, isRainbowTheme());
uint16_t spdCol = isRainbowTheme() ? getRainbowScreenColor(194, 120) : cSpd;
drawDseg7RightFast80(tft, "---", 194, 80, spdCol, cardBg, 168, isRainbowTheme());
}
}
// -------------------------------------------------------------
// 3. COOLANT TEMP (REFRIGERANTE)
// 3. COOLANT TEMP (REFRIGERANTE / AGUA)
// -------------------------------------------------------------
float curCoolF = getClusterCoolantVal();
int curCool = (curCoolF > -900.0f) ? (int)round(curCoolF) : -999;
@ -277,14 +273,20 @@ inline void updateClusterScreen(bool forceRedraw) {
uint16_t coolCol = (curCool >= 105) ? TFT_RED : ((curCool < 70) ? 0x05BF : cCool);
String valStr = String(curCool);
bool rainbowTxt = isRainbowTheme();
drawDseg7RightFast44(tft, valStr.c_str(), 112, 179, coolCol, cardBg, 102, rainbowTxt && (coolCol != TFT_RED));
drawDseg7RightFast44(tft, valStr.c_str(), 114, 183, coolCol, cardBg, 90, rainbowTxt && (coolCol != TFT_RED));
if (coolCol != oldClusterCoolCol || forceRedraw) {
oldClusterCoolCol = coolCol;
drawDegreeC_AA(tft, 117, 198, coolCol, cardBg, 240, rainbowTxt);
tft.fillRoundRect(14, 182, 4, 46, 2, coolCol);
draw7SegDegreeC(tft, 118, 201, coolCol, cardBg, rainbowTxt && (coolCol != TFT_RED));
}
} else {
drawDseg7RightFast44(tft, "---", 112, 179, cCool, cardBg, 102, isRainbowTheme());
drawDseg7RightFast44(tft, "---", 114, 183, cCool, cardBg, 90, isRainbowTheme());
if (cCool != oldClusterCoolCol || forceRedraw) {
oldClusterCoolCol = cCool;
tft.fillRoundRect(14, 182, 4, 46, 2, cCool);
draw7SegDegreeC(tft, 118, 201, cCool, cardBg, isRainbowTheme());
}
}
}
@ -299,14 +301,20 @@ inline void updateClusterScreen(bool forceRedraw) {
uint16_t voltCol = (curVolt < 11.8f || curVolt > 15.0f) ? TFT_RED : ((curVolt >= 13.5f) ? 0x07E0 : cVolt);
String voltStr = String(curVolt, 1);
bool rainbowTxt = isRainbowTheme();
drawDseg7RightFast44(tft, voltStr.c_str(), 282, 179, voltCol, cardBg, 112, rainbowTxt && (voltCol != TFT_RED));
drawDseg7RightFast44(tft, voltStr.c_str(), 282, 183, voltCol, cardBg, 106, rainbowTxt && (voltCol != TFT_RED));
if (voltCol != oldClusterVoltCol || forceRedraw) {
oldClusterVoltCol = voltCol;
drawVolt_AA(tft, 286, 198, voltCol, cardBg, 240, rainbowTxt);
tft.fillRoundRect(170, 182, 4, 46, 2, voltCol);
drawVolt_AA(tft, 286, 202, voltCol, cardBg, 240, rainbowTxt);
}
} else {
drawDseg7RightFast44(tft, "---", 282, 179, cVolt, cardBg, 112, isRainbowTheme());
drawDseg7RightFast44(tft, "---", 282, 183, cVolt, cardBg, 106, isRainbowTheme());
if (cVolt != oldClusterVoltCol || forceRedraw) {
oldClusterVoltCol = cVolt;
tft.fillRoundRect(170, 182, 4, 46, 2, cVolt);
drawVolt_AA(tft, 286, 202, cVolt, cardBg, 240, isRainbowTheme());
}
}
}
}
@ -332,56 +340,43 @@ inline void renderClusterScreenToSprite(TFT_eSprite &s, int x_offset, int y_slic
s.fillCircle(181, 2, 2, 0x5AEB);
}
// 1. TACHOMETER CARD (y: 8 to 58)
// 1. TACHOMETER CARD (y: 8 to 66)
int c0Y = 8 - y_slice;
if (c0Y < slice_h && c0Y + 50 > 0) {
s.fillRoundRect(8, c0Y, 304, 50, 5, cardBg);
s.drawRoundRect(8, c0Y, 304, 50, 5, border);
s.setTextColor(cRpm, cardBg);
// Tachometer background rail on the LEFT
s.fillRoundRect(18, c0Y + 7, 72, 14, 3, 0x0841);
if (isYapaTheme()) {
int sx = 20;
char buf[2] = {0, 0};
for (int i = 0; i < 7; i++) {
buf[0] = '1' + i;
s.setTextColor(getYapaColor(i), cardBg);
s.drawString(buf, sx, c0Y + 24, 1);
sx += s.textWidth(buf, 1) + s.textWidth(" ", 1);
}
} else {
s.setTextColor(cRpm, cardBg);
s.drawString("1 2 3 4 5 6 7", 20, c0Y + 24, 1);
}
if (c0Y < slice_h && c0Y + 58 > 0) {
s.fillRoundRect(8, c0Y, 304, 58, 5, cardBg);
s.drawRoundRect(8, c0Y, 304, 58, 5, border);
s.fillRoundRect(14, c0Y + 6, 4, 46, 2, cRpm);
float curRpmF = getClusterRpmVal();
int curRpm = (int)round(curRpmF);
bool rainbowTxt = isRainbowTheme();
int digitY = c0Y + 3;
int digitY = c0Y + 7;
if (curRpm > 0) {
uint16_t rpmCol = (curRpm >= 6000) ? TFT_RED : ((curRpm >= 4500) ? 0xFD60 : cRpm);
String rpmStr = String(curRpm);
drawDseg7RightString44(s, rpmStr.c_str(), 238, digitY, rpmCol, cardBg, 0, slice_h, rainbowTxt && (rpmCol != TFT_RED));
int activeSegs = map(constrain(curRpm, 0, 7000), 0, 7000, 0, 10);
for (int seg = 0; seg < 10; seg++) {
int sx = 20 + seg * 7;
uint16_t segCol = 0x0841;
if (seg < activeSegs) {
if (seg >= 8) segCol = TFT_RED;
else if (seg >= 6) segCol = 0xFD60;
else segCol = isRainbowTheme() ? getRainbowScreenColor(sx, 20) : (isYapaTheme() ? getYapaColor(seg) : cRpm);
}
s.fillRect(sx, c0Y + 9, 5, 10, segCol);
}
} else {
drawDseg7RightString44(s, "---", 238, digitY, cRpm, cardBg, 0, slice_h, rainbowTxt);
}
drawRpm_AA(s, 242, c0Y + 27, cRpm, cardBg, slice_h, isRainbowTheme());
// Segmented Tachometer Triangle VU-Meter on the LEFT (14 steps, ramp h: 5..31 px, x: 24..106)
int activeSegs = (curRpm > 0) ? map(constrain(curRpm, 0, 7000), 0, 7000, 1, 14) : 0;
for (int seg = 0; seg < 14; seg++) {
int barX = 24 + seg * 6;
int barH = 5 + seg * 2;
int barY = c0Y + 46 - barH;
uint16_t segCol = 0x2145;
if (seg < activeSegs) {
if (seg >= 12) segCol = TFT_RED;
else if (seg >= 9) segCol = 0xFD60;
else segCol = isRainbowTheme() ? getRainbowScreenColor(barX, 8 + 46 - barH) : (isYapaTheme() ? getYapaColor(seg) : cRpm);
}
s.fillRect(barX, barY, 4, barH, segCol);
}
drawRpm_AA(s, 242, c0Y + 31, cRpm, cardBg, slice_h, isRainbowTheme());
if (isEcoOptimal) {
s.fillRoundRect(248, c0Y + 6, 48, 17, 3, 0x0202);
s.fillRoundRect(248, c0Y + 7, 48, 17, 3, 0x0202);
if (isYapaTheme()) {
char buf[2] = {0, 0};
const char *ecoStr = "ECO";
@ -389,32 +384,33 @@ inline void renderClusterScreenToSprite(TFT_eSprite &s, int x_offset, int y_slic
for (int i = 0; i < 3; i++) {
buf[0] = ecoStr[i];
s.setTextColor(getYapaColor(i), 0x0202);
s.drawString(buf, ex, c0Y + 7, 2);
s.drawString(buf, ex, c0Y + 8, 2);
ex += s.textWidth(buf, 2);
}
} else {
s.setTextColor(TFT_WHITE, 0x0202);
s.drawString("ECO", 260, c0Y + 7, 2);
s.drawString("ECO", 260, c0Y + 8, 2);
}
s.drawRoundRect(248, c0Y + 6, 48, 17, 3, 0x07E0);
s.drawRoundRect(248, c0Y + 7, 48, 17, 3, 0x07E0);
}
}
// 2. SPEEDOMETER CARD (y: 62 to 148)
int c1Y = 62 - y_slice;
if (c1Y < slice_h && c1Y + 86 > 0) {
s.fillRoundRect(8, c1Y, 304, 86, 5, cardBg);
s.drawRoundRect(8, c1Y, 304, 86, 5, border);
// 2. SPEEDOMETER CARD (y: 70 to 170)
int c1Y = 70 - y_slice;
if (c1Y < slice_h && c1Y + 100 > 0) {
s.fillRoundRect(8, c1Y, 304, 100, 5, cardBg);
s.drawRoundRect(8, c1Y, 304, 100, 5, border);
s.fillRoundRect(14, c1Y + 8, 4, 84, 2, cSpd);
bool isGps = isClusterSpeedFromGps();
if (isGps) {
drawGpsSatelliteIcon(s, 243, c1Y + 20, 0x07FF, 0x07FF, cardBg, 2);
drawLargeGpsSatelliteIcon(s, 238, c1Y + 18, TFT_WHITE, 0x07FF, cardBg, 2);
}
float curSpdF = getClusterSpeedVal();
int curSpd = (curSpdF >= 0.0f) ? (int)round(curSpdF) : -999;
int digitY = c1Y + 3;
uint16_t spdCol = isRainbowTheme() ? getRainbowScreenColor(240, 105) : cSpd;
int digitY = c1Y + 10;
uint16_t spdCol = isRainbowTheme() ? getRainbowScreenColor(240, 120) : cSpd;
if (curSpd >= 0) {
String spdStr = String(curSpd);
drawDseg7RightString80(s, spdStr.c_str(), 194, digitY, spdCol, cardBg, 0, slice_h, isRainbowTheme());
@ -422,49 +418,45 @@ inline void renderClusterScreenToSprite(TFT_eSprite &s, int x_offset, int y_slic
drawDseg7RightString80(s, "---", 194, digitY, spdCol, cardBg, 0, slice_h, isRainbowTheme());
}
drawKmH_AA(s, 198, c1Y + 55, cSpd, cardBg, slice_h, isRainbowTheme());
drawKmH_AA(s, 198, c1Y + 62, cSpd, cardBg, slice_h, isRainbowTheme());
}
// 3. COOLANT TEMP CARD (y: 152 to 236)
int c2Y = 152 - y_slice;
if (c2Y < slice_h && c2Y + 84 > 0) {
s.fillRoundRect(8, c2Y, 148, 84, 5, cardBg);
s.drawRoundRect(8, c2Y, 148, 84, 5, border);
drawTitRef_AA(s, 28, c2Y + 6, cCool, cardBg, slice_h, isRainbowTheme());
// 3. COOLANT TEMP CARD (y: 174 to 236)
int c2Y = 174 - y_slice;
if (c2Y < slice_h && c2Y + 62 > 0) {
s.fillRoundRect(8, c2Y, 148, 62, 5, cardBg);
s.drawRoundRect(8, c2Y, 148, 62, 5, border);
float curCoolF = getClusterCoolantVal();
int curCool = (curCoolF > -900.0f) ? (int)round(curCoolF) : -999;
uint16_t coolCol = (curCool > -900) ? ((curCool >= 105) ? TFT_RED : ((curCool < 70) ? 0x05BF : cCool)) : cCool;
s.fillRoundRect(14, c2Y + 8, 4, 46, 2, coolCol);
if (curCool > -900) {
uint16_t coolCol = (curCool >= 105) ? TFT_RED : ((curCool < 70) ? 0x05BF : cCool);
String valStr = String(curCool);
bool rainbowTxt = isRainbowTheme();
int rightX = (valStr.length() >= 3) ? 112 : 106;
drawDseg7RightString44(s, valStr.c_str(), rightX, c2Y + 27, coolCol, cardBg, 0, slice_h, rainbowTxt && (coolCol != TFT_RED));
drawDegreeC_AA(s, 117, c2Y + 46, coolCol, cardBg, slice_h, isRainbowTheme());
int rightX = (valStr.length() >= 3) ? 114 : 108;
drawDseg7RightString44(s, valStr.c_str(), rightX, c2Y + 9, coolCol, cardBg, 0, slice_h, rainbowTxt && (coolCol != TFT_RED));
} else {
drawDseg7RightString44(s, "---", 112, c2Y + 27, cCool, cardBg, 0, slice_h, isRainbowTheme());
drawDseg7RightString44(s, "---", 114, c2Y + 9, cCool, cardBg, 0, slice_h, isRainbowTheme());
}
draw7SegDegreeC(s, 118, c2Y + 27, coolCol, cardBg, isRainbowTheme() && (coolCol != TFT_RED));
}
// 4. BATTERY VOLTAGE CARD (y: 152 to 236)
int c3Y = 152 - y_slice;
if (c3Y < slice_h && c3Y + 84 > 0) {
s.fillRoundRect(164, c3Y, 148, 84, 5, cardBg);
s.drawRoundRect(164, c3Y, 148, 84, 5, border);
drawTitBat_AA(s, 208, c3Y + 6, cVolt, cardBg, slice_h, isRainbowTheme());
// 4. BATTERY VOLTAGE CARD (y: 174 to 236)
int c3Y = 174 - y_slice;
if (c3Y < slice_h && c3Y + 62 > 0) {
s.fillRoundRect(164, c3Y, 148, 62, 5, cardBg);
s.drawRoundRect(164, c3Y, 148, 62, 5, border);
float curVolt = getClusterVoltVal();
uint16_t voltCol = (curVolt > 0.0f) ? ((curVolt < 11.8f || curVolt > 15.0f) ? TFT_RED : ((curVolt >= 13.5f) ? 0x07E0 : cVolt)) : cVolt;
s.fillRoundRect(170, c3Y + 8, 4, 46, 2, voltCol);
if (curVolt > 0.0f) {
uint16_t voltCol = (curVolt < 11.8f || curVolt > 15.0f) ? TFT_RED : ((curVolt >= 13.5f) ? 0x07E0 : cVolt);
String voltStr = String(curVolt, 1);
bool rainbowTxt = isRainbowTheme();
drawDseg7RightString44(s, voltStr.c_str(), 282, c3Y + 27, voltCol, cardBg, 0, slice_h, rainbowTxt && (voltCol != TFT_RED));
drawVolt_AA(s, 286, c3Y + 46, voltCol, cardBg, slice_h, isRainbowTheme());
drawDseg7RightString44(s, voltStr.c_str(), 282, c3Y + 9, voltCol, cardBg, 0, slice_h, rainbowTxt && (voltCol != TFT_RED));
} else {
drawDseg7RightString44(s, "---", 282, c3Y + 27, cVolt, cardBg, 0, slice_h, isRainbowTheme());
drawDseg7RightString44(s, "---", 282, c3Y + 9, cVolt, cardBg, 0, slice_h, isRainbowTheme());
}
drawVolt_AA(s, 286, c3Y + 28, voltCol, cardBg, slice_h, isRainbowTheme());
}
}

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,9 @@
#define VOLT_AA_WIDTH 22
#define VOLT_AA_HEIGHT 25
#define TIT_AGUA_WIDTH 44
#define TIT_AGUA_HEIGHT 11
#define TIT_REF_WIDTH 108
#define TIT_REF_HEIGHT 11
@ -148,6 +151,40 @@ static const uint8_t s_titRefAlpha[1188] PROGMEM = {
0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x0A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0xBF, 0xFF, 0x56, 0x00, 0x00, 0x00, 0x00, 0xA6, 0xFF, 0x6F, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x35, 0xFB, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80,
};
static const uint8_t s_titAguaAlpha[484] PROGMEM = {
0x00, 0x00, 0x00, 0x41, 0xFF, 0xEE, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xC3, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xC3, 0x0B, 0x00, 0x00, 0xFF, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xFF, 0xEE, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A,
0xFF, 0xFF, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0xFF, 0xEE, 0xC4, 0xC4, 0xC4, 0xEB, 0xFF,
0xBC, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x9A, 0xFF, 0xFF, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xED, 0xF6, 0xFF, 0xA6, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x2D, 0x00, 0x00, 0x00, 0x26, 0xFF, 0xFC, 0x00, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x05, 0xED, 0xF6, 0xFF,
0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0xFF, 0x91, 0xE4, 0xF4, 0x0A, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7E, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x4D, 0xFF, 0x91, 0xE4, 0xF4, 0x0A, 0x00, 0x00,
0x00, 0x00, 0xA7, 0xFF, 0x40, 0x93, 0xFF, 0x58, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC,
0x00, 0x00, 0x00, 0x00, 0xA7, 0xFF, 0x40, 0x93, 0xFF, 0x58, 0x00, 0x00, 0x00, 0x0B, 0xF4, 0xEC,
0x03, 0x40, 0xFF, 0xB1, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x68, 0xFF, 0xFF, 0xFF,
0xFC, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x0B,
0xF4, 0xEC, 0x03, 0x40, 0xFF, 0xB1, 0x00, 0x00, 0x00, 0x5A, 0xFF, 0xA0, 0x00, 0x03, 0xEA, 0xF9,
0x11, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x4F, 0xC4, 0xC4, 0xFF, 0xFC, 0x00, 0x00, 0xFF,
0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x5A, 0xFF, 0xA0, 0x00, 0x03,
0xEA, 0xF9, 0x11, 0x00, 0x00, 0xB3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0x00, 0x00, 0x00,
0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0xB3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0x00,
0x12, 0xFA, 0xFE, 0xD0, 0xD0, 0xD0, 0xDD, 0xFF, 0xBC, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x28, 0x00,
0x00, 0x00, 0x22, 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFE, 0x28, 0x00, 0x00, 0x00, 0x22, 0xFF, 0xFC,
0x00, 0x00, 0x12, 0xFA, 0xFE, 0xD0, 0xD0, 0xD0, 0xDD, 0xFF, 0xBC, 0x00, 0x66, 0xFF, 0xBA, 0x00,
0x00, 0x00, 0x12, 0xF8, 0xFD, 0x18, 0x00, 0x00, 0xBB, 0xFF, 0xEE, 0xC4, 0xC4, 0xC4, 0xE9, 0xFF,
0xB7, 0x00, 0x00, 0xBB, 0xFF, 0xEE, 0xC4, 0xC4, 0xC4, 0xE9, 0xFF, 0xB7, 0x00, 0x00, 0x66, 0xFF,
0xBA, 0x00, 0x00, 0x00, 0x12, 0xF8, 0xFD, 0x18, 0xBF, 0xFF, 0x56, 0x00, 0x00, 0x00, 0x00, 0xA6,
0xFF, 0x6F, 0x00, 0x00, 0x0A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x09, 0x00, 0x00, 0x0A,
0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x09, 0x00, 0x00, 0xBF, 0xFF, 0x56, 0x00, 0x00, 0x00,
0x00, 0xA6, 0xFF, 0x6F,
};
// 8-bit Anti-Aliased 'titBat' (60x11)
static const uint8_t s_titBatAlpha[660] PROGMEM = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x57, 0x00, 0x00, 0x00, 0x00, 0x41, 0xFF, 0xEE, 0x06, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCC, 0x0F, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xFF, 0xEE, 0x06, 0x00, 0x00, 0x00,
@ -189,6 +226,8 @@ inline void drawKmhAaBitmap(T &dst, int bx, int by, int w, int h, const uint8_t
curCol = (i < 13) ? getYapaColor(0) : getYapaColor(1);
} else if (alpha == s_voltAlpha) {
curCol = getYapaColor(2);
} else if (alpha == s_titAguaAlpha) {
curCol = getYapaColor((i * 4) / 44);
} else if (alpha == s_titRefAlpha) {
curCol = getYapaColor((i * 12) / 108);
} else if (alpha == s_titBatAlpha) {
@ -223,6 +262,11 @@ inline void drawVolt_AA(T &dst, int bx, int by, uint16_t color = 0xC618, uint16_
drawKmhAaBitmap(dst, bx, by, VOLT_AA_WIDTH, VOLT_AA_HEIGHT, s_voltAlpha, color, bgCol, clip_h, rainbow);
}
template <typename T>
inline void drawTitAgua_AA(T &dst, int bx, int by, uint16_t color = 0xBDD7, uint16_t bgCol = 0x10A2, int clip_h = 240, bool rainbow = false) {
drawKmhAaBitmap(dst, bx, by, TIT_AGUA_WIDTH, TIT_AGUA_HEIGHT, s_titAguaAlpha, color, bgCol, clip_h, rainbow);
}
template <typename T>
inline void drawTitRef_AA(T &dst, int bx, int by, uint16_t color = 0xBDD7, uint16_t bgCol = 0x10A2, int clip_h = 240, bool rainbow = false) {
drawKmhAaBitmap(dst, bx, by, TIT_REF_WIDTH, TIT_REF_HEIGHT, s_titRefAlpha, color, bgCol, clip_h, rainbow);

View file

@ -349,7 +349,8 @@ const float blockC[20][4] PROGMEM = {
// Draw an individual solid rectangular block with smooth subpixel anti-aliasing
inline void drawBarBlock(uint8_t i, uint16_t color) {
if (isRainbowTheme() && color != TFT_RED && color != METER_INACTIVE_COLOR && color != TFT_BLACK)
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 maxX = pgm_read_word(&blockMaxX[i]);
@ -479,7 +480,8 @@ const float quadBlockC[20][4] PROGMEM = {
// subpixel anti-aliasing
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)
if (isRainbowTheme() && color != TFT_RED && color != METER_INACTIVE_COLOR &&
color != TFT_BLACK)
color = getRainbowBlockColor(i);
int16_t ox = cell4_x[cell];
int16_t oy = cell4_y[cell];
@ -1110,9 +1112,11 @@ void arcMeter4(uint8_t cell, uint8_t pid, bool redrawHeader = true) {
old_data[cell] = -999;
plotArc4(cell, last_valid_data[cell], warn, min, max, digit, unit);
} else {
// Value placeholder using DSEG7 Modern 34pt smooth font with rainbow gradient
// Value placeholder using DSEG7 Modern 34pt smooth font with rainbow
// gradient
bool rainbowTxt = isRainbowTheme();
drawDseg7RightString34(tft, "---", x + 152, y + 74, getThemeCellColor(cell), TFT_BLACK, 114, 240, rainbowTxt);
drawDseg7RightString34(tft, "---", x + 152, y + 74, getThemeCellColor(cell),
TFT_BLACK, 114, 240, rainbowTxt);
old_data[cell] = -999;
}
if (cell == 1 && isGpsSpeedValid()) {
@ -1192,7 +1196,8 @@ void plotArc4(uint8_t cell, float data, int warn, int min, int max, bool digit,
uint16_t txtCol = (col == TFT_RED) ? TFT_RED : getThemeCellColor(cell);
bool rainbowTxt = isRainbowTheme() && (col != TFT_RED);
drawDseg7RightString34(tft, numStr.c_str(), x + 152, y + 74, txtCol, TFT_BLACK, 114, 240, rainbowTxt);
drawDseg7RightString34(tft, numStr.c_str(), x + 152, y + 74, txtCol,
TFT_BLACK, 114, 240, rainbowTxt);
old_data[cell] = data;
// Draw GPS Satellite Icon & indicator in top-right corner of cell 1
@ -1255,7 +1260,8 @@ void fullScreenMeter(uint8_t cell, uint8_t pid, bool redrawHeader = true) {
}
bool rainbowTxt = isRainbowTheme();
drawDseg7RightString80(tft, "---", 312, 148, getThemeCellColor(cell), TFT_BLACK, 240, 240, rainbowTxt);
drawDseg7RightString80(tft, "---", 312, 148, getThemeCellColor(cell),
TFT_BLACK, 240, 240, rainbowTxt);
old_data[cell] = -999;
}
}
@ -1333,7 +1339,8 @@ void plotFullScreen(uint8_t cell, float data, int warn, int min, int max,
bool rainbowTxt = isRainbowTheme() && (col != TFT_RED);
// Value bottom-right using DSEG7 Modern smooth font with rainbow gradient
drawDseg7RightString80(tft, numStr.c_str(), 312, 148, txtCol, TFT_BLACK, 240, 240, rainbowTxt);
drawDseg7RightString80(tft, numStr.c_str(), 312, 148, txtCol, TFT_BLACK, 240,
240, rainbowTxt);
old_data[cell] = data;
if (cell == 1 && isGpsSpeedValid()) {
@ -1449,7 +1456,8 @@ inline void drawPidIconToSprite(TFT_eSprite &s, int ix, int iy,
// anti-aliasing
inline void drawQuadBarBlockToSprite(TFT_eSprite &s, int16_t qx, int16_t qy,
uint8_t i, uint16_t color, int slice_h) {
if (isRainbowTheme() && color != TFT_RED && color != METER_INACTIVE_COLOR && color != TFT_BLACK)
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 maxX = pgm_read_word(&quadBlockMaxX[i]);
@ -1554,7 +1562,8 @@ inline void renderPage1ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
else if (val >= 100.0f)
accentCol = TFT_YELLOW;
else
accentCol = isRainbowTheme() ? (uint16_t)0x07FF : getThemePrimaryColor();
accentCol =
isRainbowTheme() ? (uint16_t)0x07FF : getThemePrimaryColor();
} else if (cardIdx == 1) {
titleStr = "INTERIOR";
subStr = (dsSensorCount > 0) ? "DS18B20 SENSOR 1 (GPIO 22)"
@ -1613,19 +1622,23 @@ inline void renderPage1ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
s.fillRoundRect(barX, barY, fillW, 8, 3, accentCol);
// Value text using authentic DSEG7 34pt font with rainbow gradient
String numStr =
(val <= -900.0f) ? "---" : String((int)round(val));
String numStr = (val <= -900.0f) ? "---" : String((int)round(val));
uint16_t numCol = accentCol;
bool rainbowTxt = isRainbowTheme() && (accentCol != TFT_RED);
int numY = cardY + 20;
if (numY < slice_h && numY + 40 > 0) {
drawDseg7RightString34(s, numStr.c_str(), cardX + 262, numY, numCol, cardBg, 0, slice_h, rainbowTxt);
drawDseg7RightString34(s, numStr.c_str(), cardX + 262, numY, numCol,
cardBg, 0, slice_h, rainbowTxt);
}
int unitY = cardY + 22;
if (unitY < slice_h && unitY + 26 > 0) {
bool rainbowUnit = isRainbowTheme() && (accentCol != TFT_RED);
uint16_t unitCol = rainbowUnit ? getRainbowScreenColor(cardX + 268, unitY) : (isYapaTheme() && accentCol != TFT_RED ? getYapaColor(numStr.length()) : (accentCol == TFT_RED ? TFT_RED : accentCol));
uint16_t unitCol =
rainbowUnit ? getRainbowScreenColor(cardX + 268, unitY)
: (isYapaTheme() && accentCol != TFT_RED
? getYapaColor(numStr.length())
: (accentCol == TFT_RED ? TFT_RED : accentCol));
draw7SegDegreeC(s, cardX + 268, unitY, unitCol, cardBg, rainbowUnit);
}
}
@ -1647,7 +1660,8 @@ inline void animateBlackTransition(int8_t toPage = -1) {
if (startBrightness < 20)
startBrightness = 20;
// 1. Smoothly fade backlight down to black in ~200ms (quadratic ease for natural eye response)
// 1. Smoothly fade backlight down to black in ~200ms (quadratic ease for
// natural eye response)
const uint8_t fadeSteps = 16;
for (int8_t i = fadeSteps - 1; i >= 0; i--) {
float t = (float)i / (float)fadeSteps;
@ -1657,7 +1671,8 @@ inline void animateBlackTransition(int8_t toPage = -1) {
}
ledcWrite(backlightChannel, 0);
// 2. Render new screen slice-by-slice while backlight is totally off (100% invisible redraw)
// 2. Render new screen slice-by-slice while backlight is totally off (100%
// invisible redraw)
const int slice_h = 40;
const uint8_t numSlices = 240 / slice_h; // exactly 6 slices
for (uint8_t s = 0; s < numSlices; s++) {
@ -1786,13 +1801,13 @@ inline void animateFadeTransition(int8_t fromPage = -1, int8_t toPage = -1) {
if (y_slice == 0) {
uint8_t activeDot = (alpha < 16) ? fromPage : toPage;
sprTrans.fillCircle(139, 2, 2,
(activeDot == 0) ? getThemePrimaryColor() : 0x5AEB);
(activeDot == 0) ? getThemePrimaryColor() : 0x5AEB);
sprTrans.fillCircle(153, 2, 2,
(activeDot == 1) ? getThemePrimaryColor() : 0x5AEB);
(activeDot == 1) ? getThemePrimaryColor() : 0x5AEB);
sprTrans.fillCircle(167, 2, 2,
(activeDot == 2) ? getThemePrimaryColor() : 0x5AEB);
(activeDot == 2) ? getThemePrimaryColor() : 0x5AEB);
sprTrans.fillCircle(181, 2, 2,
(activeDot == 3) ? getThemePrimaryColor() : 0x5AEB);
(activeDot == 3) ? getThemePrimaryColor() : 0x5AEB);
}
sprTrans.pushSprite(0, y_slice);
@ -2505,7 +2520,8 @@ void handleArcAlarmFlash() {
}
}
} else if (currentScreenPage == 0) {
// Cockpit Instrumental: check alarms for buzzer without drawing old quadrants
// Cockpit Instrumental: check alarms for buzzer without drawing old
// quadrants
for (uint8_t cell = 0; cell < 4; cell++) {
if (last_valid_data[cell] > -900.0f) {
uint8_t pidIdx = pidInCell[cell];
@ -2561,6 +2577,41 @@ void updateBacklightFade() {
}
}
// Global filtered LDR reading for stable noise-free brightness
static float s_filteredLdr = -1.0f;
inline int getFilteredLdr() {
int raw = analogRead(LDR_PIN);
if (s_filteredLdr < 0)
s_filteredLdr = (float)raw;
else
s_filteredLdr = s_filteredLdr * 0.82f + (float)raw * 0.18f;
return (int)round(s_filteredLdr);
}
// Curva de brillo automatico calibrada:
// - Oscuridad / noche: 18 PWM (7%)
// - Luz ambiente de habitacion: 64 PWM (25%)
// - Escala progresiva hasta luz directa / sol: 255 PWM (100%)
inline uint8_t calcAutoBacklight(int light) {
if (light >= 350) return 18; // Noche / oscuridad total: 7%
if (light >= 100) {
// Transicion suave de noche (350) a luz ambiente de habitacion (100): 7% a 25%
float t = (350.0f - (float)light) / 250.0f;
int target = (int)round(18.0f + t * (64.0f - 18.0f));
return (uint8_t)constrain(target, 18, 64);
}
if (light <= 0) return 255;
// De luz ambiente (100) a luz muy intensa / sol directo (0): 25% a 100%
float t = (100.0f - (float)light) / 100.0f;
float curve = t * t;
int target = (int)round(64.0f + curve * (255.0f - 64.0f));
return (uint8_t)constrain(target, 64, 255);
}
// auto dim backlight function
void autoDim() {
if (brightnessIndex > 0) {
@ -2569,38 +2620,16 @@ void autoDim() {
if (targetBacklight != manualVal) {
targetBacklight = manualVal;
}
dim = false;
updateBacklightFade();
return;
}
int light = analogRead(LDR_PIN); // read light
if (light > LIGHT_LEVEL) { // low light (dark)
low_count++;
if (low_count > 6) {
low_count = 6;
if (!dim) {
targetBacklight = 15; // smooth fade target: dim
dim = true;
#ifdef SERIAL_DEBUG
Serial.println(F("Backlight -> target dim (15)"));
#endif
}
}
high_count = 0;
} else { // bright light (day)
high_count++;
if (high_count > 6) {
high_count = 6;
if (dim) {
targetBacklight = 255; // smooth fade target: bright
dim = false;
#ifdef SERIAL_DEBUG
Serial.println(F("Backlight -> target bright (255)"));
#endif
}
}
low_count = 0;
}
// AUTO Mode: continuous adaptive smooth scaling from ambient light
int light = getFilteredLdr();
targetBacklight = calcAutoBacklight(light);
dim = (targetBacklight <= 70);
updateBacklightFade();
} // autodim
//---------------------------

View file

@ -321,9 +321,13 @@ inline void drawDseg7RightFast44(T &dst, const char *str, int rightX, int y0,
bool rainbow = false) {
int totalW = getDseg7StringWidth44(str);
int renderW = (minTotalW > totalW) ? minTotalW : totalW;
if (renderW > 160) renderW = 160;
int startX = rightX - renderW;
int textOffsetX = renderW - totalW;
if (rightX <= 120 && startX < 22) {
renderW -= (22 - startX);
startX = 22;
}
if (renderW > 160) renderW = 160;
int textOffsetX = (rightX - totalW) - startX;
uint16_t lineBuf[160];
@ -390,13 +394,13 @@ inline void drawDseg7RightFast80(T &dst, const char *str, int rightX, int y0,
else totalW += 65;
}
int renderW = (minTotalW > totalW) ? minTotalW : totalW;
if (renderW > 184) renderW = 184;
int startX = rightX - renderW;
if (startX < 10) {
renderW -= (10 - startX);
startX = 10;
if (startX < 22) {
renderW -= (22 - startX);
startX = 22;
}
int textOffsetX = renderW - totalW;
if (renderW > 180) renderW = 180;
int textOffsetX = (rightX - totalW) - startX;
uint16_t lineBuf[200];

View file

@ -105,60 +105,60 @@ inline void initTriTemp() {
#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,
// 8-bit anti-aliased alpha mask for digital 7-segment italic "°C" (34x26)
const uint8_t s_dsegUnitAlpha[34 * 26] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x11, 0x12, 0x0E, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 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, 0x00, 0x00, 0x00,
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, 0x00,
0x00, 0x00, 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, 0x00, 0x00,
0x00, 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, 0x00, 0x00, 0x00,
0x00, 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, 0x00, 0x00, 0x00,
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, 0x00, 0x00, 0x00,
0x00, 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, 0x00, 0x00, 0x00, 0x00,
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, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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
@ -166,8 +166,8 @@ inline void draw7SegDegreeC(TFT_eSPI &dst, int bx, int by, uint16_t color = 0xC6
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 rowOffset = j * 34;
for (int i = 0; i < 34; i++) {
int px = bx + i;
if (px < 0 || px >= dst.width()) continue;
uint8_t a = pgm_read_byte(&s_dsegUnitAlpha[rowOffset + i]);
@ -176,7 +176,7 @@ inline void draw7SegDegreeC(TFT_eSPI &dst, int bx, int by, uint16_t color = 0xC6
if (rainbow) {
curCol = getRainbowScreenColor(px, py);
} else if (isYapaTheme()) {
curCol = (i < 12) ? getYapaColor(0) : getYapaColor(1);
curCol = (i < 15) ? getYapaColor(0) : getYapaColor(1);
}
uint16_t pixCol = (a >= 250) ? curCol : fastBlend(a, curCol, bgCol);
dst.drawPixel(px, py, pixCol);
@ -185,6 +185,7 @@ inline void draw7SegDegreeC(TFT_eSPI &dst, int bx, int by, uint16_t color = 0xC6
}
}
// Double-buffered 120x8 progress bar sprite (zero jitter)
static TFT_eSprite sprBar(&tft);
static bool sprBarCreated = false;
@ -420,6 +421,7 @@ inline void updateTriTempCardValue(uint8_t cardIdx, float val, float oldVal) {
tft.fillRoundRect(cardX + 6, cardY + 6, 4, cardH - 12, 2, accentCol);
bool rainbowUnit = isRainbowTheme() && (accentCol != TFT_RED);
uint16_t unitCol = rainbowUnit ? getRainbowScreenColor(cardX + 268, cardY + 22) : (isYapaTheme() && accentCol != TFT_RED ? getYapaColor(3) : (accentCol == TFT_RED ? TFT_RED : 0xC618));
tft.fillRect(cardX + 268, cardY + 22, 34, 26, cardBg);
draw7SegDegreeC(tft, cardX + 268, cardY + 22, unitCol, cardBg, rainbowUnit);
}