diff --git a/dop.png b/dop.png
new file mode 100644
index 0000000..3da17dc
Binary files /dev/null and b/dop.png differ
diff --git a/emulator/obd2_emulator.py b/emulator/obd2_emulator.py
index 12bd5b1..512726b 100755
--- a/emulator/obd2_emulator.py
+++ b/emulator/obd2_emulator.py
@@ -469,8 +469,8 @@ def run_emulator():
sim_thread = threading.Thread(target=simulation_worker, daemon=True)
sim_thread.start()
- # Start GPS simulation thread if port exists or --gps requested
- if not args.no_gps and (args.gps or os.path.exists(args.gps_port)):
+ # Start GPS simulation thread ONLY if --gps explicitly requested
+ if args.gps and not args.no_gps:
gps_thread = threading.Thread(
target=gps_simulation_worker,
args=(args.gps_port, args.gps_baud),
diff --git a/gauge_design.png b/gauge_design.png
new file mode 100644
index 0000000..99048e1
Binary files /dev/null and b/gauge_design.png differ
diff --git a/gps_icon.png b/gps_icon.png
new file mode 100644
index 0000000..ae41669
Binary files /dev/null and b/gps_icon.png differ
diff --git a/gps_icon.svg b/gps_icon.svg
index 4cf5a89..ca95662 100644
--- a/gps_icon.svg
+++ b/gps_icon.svg
@@ -21,24 +21,24 @@
x="-25.60" y="-14.63"
width="51.20" height="29.26"
rx="7.31" ry="7.31"
- transform="translate(100.00, 88.00) rotate(-34.0) translate(61.44, 0)"/>
+ transform="translate(100.00, 88.00) rotate(-39.5) translate(61.44, 0)"/>
+ transform="translate(100.00, 88.00) rotate(-39.5) translate(-61.44, 0)"/>
diff --git a/gps_icon.xcf b/gps_icon.xcf
new file mode 100644
index 0000000..11dc114
Binary files /dev/null and b/gps_icon.xcf differ
diff --git a/sketch/obd2_gauge/bluetooth.h b/sketch/obd2_gauge/bluetooth.h
index f784a7a..c267dcf 100644
--- a/sketch/obd2_gauge/bluetooth.h
+++ b/sketch/obd2_gauge/bluetooth.h
@@ -76,7 +76,8 @@ void scanBTdevice() {//scan bluetooth device
bool isMatch = (dName == "OBDII") ||
(dName.indexOf("OBD") != -1) ||
(dName.indexOf("ELM") != -1) ||
- (dName == "DEBIAN") ||
+ (dName.indexOf("DEBIAN") != -1) ||
+ (dAddr == "6c:62:6d:14:08:fd") ||
(dAddr == "e8:48:b8:c8:20:00");
if (isMatch) {//match name.
diff --git a/sketch/obd2_gauge/cluster_screen.h b/sketch/obd2_gauge/cluster_screen.h
index 0051165..8e8aa64 100644
--- a/sketch/obd2_gauge/cluster_screen.h
+++ b/sketch/obd2_gauge/cluster_screen.h
@@ -6,6 +6,7 @@
#include "DSEG7_Modern_BoldItalic_34.h"
#include "DSEG7_Modern_BoldItalic_44.h"
#include "rainbow_text.h"
+#include "kmh_aa_data.h"
extern TFT_eSPI tft;
extern uint16_t getThemePrimaryColor();
@@ -18,6 +19,7 @@ extern int8_t fullScreenCell;
extern float currentRpm;
extern float last_valid_data[];
extern float tempEngineCoolant;
+extern bool isEcoOptimal;
extern void drawPidIcon(int x, int y, const String &pidStr);
extern void drawPidIconToSprite(TFT_eSprite &s, int ix, int iy, const String &pidStr, int y_slice, int slice_h, int global_y);
extern void draw7SegDegreeC(TFT_eSPI &dst, int bx, int by, uint16_t color, uint16_t bgCol, bool rainbow);
@@ -32,7 +34,7 @@ inline float getClusterRpmVal() {
inline float getClusterSpeedVal() {
if (isGpsSpeedValid()) return gpsSpeedKmph;
if (last_valid_data[1] >= 0.0f) return last_valid_data[1];
- return 0.0f;
+ return -999.0f;
}
inline bool isClusterSpeedFromGps() {
@@ -56,29 +58,34 @@ static int oldClusterSpd = -999;
static int oldClusterCoolant = -999;
static float oldClusterVolt = -999.0f;
static bool oldClusterGpsLock = false;
+static bool oldClusterEco = false;
+static uint16_t oldClusterCoolCol = 0;
+static uint16_t oldClusterVoltCol = 0;
+static int oldClusterSegs = -1;
static uint32_t lastClusterUpdate = 0;
inline void syncClusterTrackers() {
oldClusterRpm = (int)round(getClusterRpmVal());
- oldClusterSpd = (int)round(getClusterSpeedVal());
+ float s = getClusterSpeedVal();
+ oldClusterSpd = (s >= 0.0f) ? (int)round(s) : -999;
float c = getClusterCoolantVal();
oldClusterCoolant = (c > -900.0f) ? (int)round(c) : -999;
oldClusterVolt = getClusterVoltVal();
oldClusterGpsLock = isClusterSpeedFromGps();
+ oldClusterEco = isEcoOptimal;
lastClusterUpdate = millis();
}
-// Helper: draw 5 top pagination dots (page 4 active)
+// Helper: draw 4 top pagination dots (page 0 active)
inline void drawClusterDots(int x_offset = 0) {
- uint16_t actCol = isRainbowTheme() ? getRainbowScreenColor(188, 2) : (isYapaTheme() ? getYapaColor(4) : getThemePrimaryColor());
- tft.fillCircle(132 + x_offset, 2, 2, 0x5AEB);
- tft.fillCircle(146 + x_offset, 2, 2, 0x5AEB);
- tft.fillCircle(160 + x_offset, 2, 2, 0x5AEB);
- tft.fillCircle(174 + x_offset, 2, 2, 0x5AEB);
- tft.fillCircle(188 + x_offset, 2, 2, actCol);
+ uint16_t actCol = isRainbowTheme() ? getRainbowScreenColor(139, 2) : (isYapaTheme() ? getYapaColor(0) : getThemePrimaryColor());
+ tft.fillCircle(139 + x_offset, 2, 2, actCol);
+ tft.fillCircle(153 + x_offset, 2, 2, 0x5AEB);
+ tft.fillCircle(167 + x_offset, 2, 2, 0x5AEB);
+ tft.fillCircle(181 + x_offset, 2, 2, 0x5AEB);
}
-// Draw static framing of all 4 cards
+// Draw static framing of all 4 cards (units and titles drawn ONCE without flicker)
inline void drawClusterBaseCards(int x_offset = 0) {
uint16_t cardBg = 0x10A2;
uint16_t border = 0x2945;
@@ -90,54 +97,47 @@ 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: 10, w: 304, h: 42)
- tft.fillRoundRect(8 + x_offset, 10, 304, 42, 5, cardBg);
- tft.drawRoundRect(8 + x_offset, 10, 304, 42, 5, border);
- tft.fillRoundRect(12 + x_offset, 14, 3, 34, 1, cRpm);
- drawPidIcon(18 + x_offset, 14, "010C");
- tft.setTextColor(0x8410, cardBg);
- tft.drawString("RPM", 38 + x_offset, 16, 2);
- // Tachometer background track (x: 76 to 216, w: 140, h: 12)
- tft.fillRoundRect(76 + x_offset, 16, 140, 12, 3, 0x0841);
- // RPM ticks
- tft.drawString("1 2 3 4 5 6 7", 86 + x_offset, 31, 1);
+ // 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);
+ }
+ // "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());
- // 2. SPEEDOMETER CARD (x: 8, y: 56, w: 304, h: 76)
- tft.fillRoundRect(8 + x_offset, 56, 304, 76, 5, cardBg);
- tft.drawRoundRect(8 + x_offset, 56, 304, 76, 5, border);
- tft.fillRoundRect(12 + x_offset, 60, 3, 68, 1, cSpd);
- drawPidIcon(18 + x_offset, 60, "010D");
- tft.setTextColor(0x8410, cardBg);
- tft.drawString("VELOCIDAD", 38 + x_offset, 62, 2);
- // Background speed glow rail
- tft.fillRoundRect(18 + x_offset, 124, 284, 3, 1, 0x0841);
+ // 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());
- // 3. COOLANT TEMP CARD (x: 8, y: 136, w: 148, h: 98)
- tft.fillRoundRect(8 + x_offset, 136, 148, 98, 5, cardBg);
- tft.drawRoundRect(8 + x_offset, 136, 148, 98, 5, border);
- tft.fillRoundRect(12 + x_offset, 140, 3, 90, 1, cCool);
- drawPidIcon(18 + x_offset, 140, "0105");
- tft.setTextColor(0x8410, cardBg);
- tft.drawString("REFRIG.", 38 + x_offset, 142, 2);
- // Thermal bar rail (x: 18, y: 196, w: 128, h: 8)
- tft.fillRoundRect(18 + x_offset, 196, 128, 8, 3, 0x0841);
- tft.drawString("40`C", 18 + x_offset, 210, 1);
- tft.drawString("120`C", 112 + x_offset, 210, 1);
+ // 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());
- // 4. BATTERY VOLTAGE CARD (x: 164, y: 136, w: 148, h: 98)
- tft.fillRoundRect(164 + x_offset, 136, 148, 98, 5, cardBg);
- tft.drawRoundRect(164 + x_offset, 136, 148, 98, 5, border);
- tft.fillRoundRect(168 + x_offset, 140, 3, 90, 1, cVolt);
- drawPidIcon(174 + x_offset, 140, "ATRV");
- tft.setTextColor(0x8410, cardBg);
- tft.drawString("BATERIA", 194 + x_offset, 142, 2);
- // Voltage bar rail (x: 174, y: 196, w: 128, h: 8)
- tft.fillRoundRect(174 + x_offset, 196, 128, 8, 3, 0x0841);
- tft.drawString("10V", 174 + x_offset, 210, 1);
- tft.drawString("16V", 272 + x_offset, 210, 1);
+ // 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());
}
-// Initial direct draw of Page 4
+// Initial direct draw of Page 0
inline void drawClusterScreen(int x_offset = 0) {
tft.fillScreen(TFT_BLACK);
drawClusterDots(x_offset);
@@ -149,14 +149,18 @@ inline void drawClusterScreen(int x_offset = 0) {
oldClusterCoolant = -999;
oldClusterVolt = -999.0f;
oldClusterGpsLock = false;
+ oldClusterEco = !isEcoOptimal;
+ oldClusterCoolCol = 0;
+ oldClusterVoltCol = 0;
+ oldClusterSegs = -1;
void updateClusterScreen(bool forceRedraw = false);
updateClusterScreen(true);
}
-// Live zero-flicker non-blocking update of Page 4
+// Live zero-flicker non-blocking update of Page 0 (Cockpit Instrumental)
inline void updateClusterScreen(bool forceRedraw) {
- if (currentScreenPage != 4 || fullScreenCell != -1) return;
+ if (currentScreenPage != 0 || fullScreenCell != -1) return;
uint16_t cardBg = 0x10A2;
uint16_t priCol = getThemePrimaryColor();
@@ -175,33 +179,57 @@ inline void updateClusterScreen(bool forceRedraw) {
if (forceRedraw || abs(curRpm - oldClusterRpm) >= 20 || (curRpm == 0 && oldClusterRpm != 0)) {
oldClusterRpm = curRpm;
- // Erase numeric text area
- tft.fillRect(222, 14, 84, 26, cardBg);
- tft.setTextPadding(0);
- if (curRpm > 0) {
- uint16_t rpmCol = (curRpm >= 6000) ? TFT_RED : ((curRpm >= 4500) ? 0xFD60 : cRpm);
- tft.setTextColor(rpmCol, cardBg);
- String rpmStr = String(curRpm);
- tft.drawRightString(rpmStr, 302, 16, 4);
- } else {
- tft.setTextColor(0x632C, cardBg);
- tft.drawRightString("---", 302, 16, 4);
+ // Segmented Tachometer Bar on the LEFT (10 segments between x: 20 and 88)
+ int activeSegs = map(constrain(curRpm, 0, 7000), 0, 7000, 0, 10);
+ if (activeSegs != oldClusterSegs || forceRedraw) {
+ oldClusterSegs = activeSegs;
+ for (int s = 0; s < 10; s++) {
+ int sx = 20 + s * 7;
+ 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);
+ } else {
+ segCol = 0x0841; // inactive dark track
+ }
+ tft.fillRect(sx, 17, 5, 10, segCol);
+ }
}
- // Segmented Tachometer Bar (12 segments between x: 78 and 214)
- // 0 to 7000 RPM -> ~583 RPM per segment
- int activeSegs = map(constrain(curRpm, 0, 7000), 0, 7000, 0, 12);
- for (int s = 0; s < 12; s++) {
- int sx = 78 + s * 11;
- uint16_t segCol;
- if (s < activeSegs) {
- if (s >= 10) segCol = TFT_RED;
- else if (s >= 8) segCol = 0xFD60;
- else segCol = isRainbowTheme() ? getRainbowScreenColor(sx, 20) : cRpm;
+ // RPM Numeric Value (44pt) rendered with zero-flicker line buffer (x: 100..238, rightX: 238)
+ bool rainbowTxt = isRainbowTheme();
+ 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));
+ } else {
+ drawDseg7RightFast44(tft, "---", 238, 11, cRpm, cardBg, 138, rainbowTxt);
+ }
+ }
+
+ // ECO Indicator in Card 1 centered directly above RPM unit (x = 248, y = 14, w = 48, h = 17)
+ if (forceRedraw || isEcoOptimal != oldClusterEco) {
+ oldClusterEco = isEcoOptimal;
+ if (isEcoOptimal) {
+ tft.fillRoundRect(248, 14, 48, 17, 3, 0x0202);
+ if (isYapaTheme()) {
+ char buf[2] = {0, 0};
+ const char *ecoStr = "ECO";
+ int ex = 260;
+ for (int i = 0; i < 3; i++) {
+ buf[0] = ecoStr[i];
+ tft.setTextColor(getYapaColor(i), 0x0202);
+ tft.drawString(buf, ex, 15, 2);
+ ex += tft.textWidth(buf, 2);
+ }
} else {
- segCol = 0x0841; // inactive dark track
+ tft.setTextColor(TFT_WHITE, 0x0202);
+ tft.drawString("ECO", 260, 15, 2);
}
- tft.fillRect(sx, 18, 9, 8, segCol);
+ tft.drawRoundRect(248, 14, 48, 17, 3, 0x07E0);
+ } else {
+ tft.fillRect(248, 14, 48, 17, cardBg);
}
}
@@ -209,50 +237,31 @@ inline void updateClusterScreen(bool forceRedraw) {
// 2. SPEEDOMETER (VELOCIDAD)
// -------------------------------------------------------------
float curSpdF = getClusterSpeedVal();
- int curSpd = (int)round(curSpdF);
+ int curSpd = (curSpdF >= 0.0f) ? (int)round(curSpdF) : -999;
bool isGps = isClusterSpeedFromGps();
- if (forceRedraw || curSpd != oldClusterSpd || isGps != oldClusterGpsLock) {
- oldClusterSpd = curSpd;
+ // GPS Satellite icon in Card 2 centered directly above KM/H (where ECO was): x = 243, y = 82, w = 18, h = 18
+ // Only shows satellite icon if speed comes from GPS; clears when speed is from OBD2
+ if (forceRedraw || isGps != oldClusterGpsLock) {
oldClusterGpsLock = isGps;
-
- // Source Pill (GPS / OBD)
- tft.fillRect(116, 61, 46, 14, cardBg);
if (isGps) {
- tft.fillRoundRect(116, 61, 42, 14, 3, 0x0254);
- tft.setTextColor(0x07FF, 0x0254);
- tft.drawString("GPS", 125, 63, 1);
+ drawGpsSatelliteIcon(tft, 243, 82, 0x07FF, 0x07FF, cardBg, 2);
} else {
- tft.fillRoundRect(116, 61, 42, 14, 3, 0x18C3);
- tft.setTextColor(0x8410, 0x18C3);
- tft.drawString("OBD", 125, 63, 1);
+ tft.fillRect(238, 80, 28, 22, cardBg);
}
+ }
- // Giant Speed Display
- tft.fillRect(166, 60, 138, 56, cardBg);
- String spdStr = String(curSpd);
+ if (forceRedraw || curSpd != oldClusterSpd) {
+ oldClusterSpd = curSpd;
- tft.loadFont(DSEG7_Modern_BoldItalic_44);
- uint16_t spdCol = isRainbowTheme() ? getRainbowScreenColor(210, 80) : cSpd;
- tft.setTextColor(spdCol, cardBg);
- tft.drawRightString(spdStr, 248, 64, 1);
- tft.unloadFont();
-
- // Unit "km/h"
- tft.setTextColor(0xBDD7, cardBg);
- tft.drawString("km/h", 254, 88, 2);
-
- // Dynamic Bottom Speed Rail (w = 284)
- int fillSpdW = map(constrain(curSpd, 0, 200), 0, 200, 0, 284);
- tft.fillRect(18, 124, 284, 3, 0x0841);
- if (fillSpdW > 0) {
- if (isRainbowTheme()) {
- for (int bx = 0; bx < fillSpdW; bx++) {
- tft.drawFastVLine(18 + bx, 124, 3, getRainbowScreenColor(18 + bx, 124));
- }
- } else {
- tft.fillRect(18, 124, fillSpdW, 3, cSpd);
- }
+ // Giant Speed Display using DSEG7 80pt rendered with zero-flicker line buffer (x: 14..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());
+ } else {
+ uint16_t spdCol = isRainbowTheme() ? getRainbowScreenColor(194, 105) : cSpd;
+ drawDseg7RightFast80(tft, "---", 194, 65, spdCol, cardBg, 180, isRainbowTheme());
}
}
@@ -264,27 +273,18 @@ inline void updateClusterScreen(bool forceRedraw) {
if (forceRedraw || curCool != oldClusterCoolant) {
oldClusterCoolant = curCool;
- tft.fillRect(20, 158, 130, 36, cardBg);
if (curCool > -900) {
uint16_t coolCol = (curCool >= 105) ? TFT_RED : ((curCool < 70) ? 0x05BF : cCool);
- tft.setTextColor(coolCol, cardBg);
String valStr = String(curCool);
- tft.loadFont(DSEG7_Modern_BoldItalic_34);
- tft.drawRightString(valStr, 102, 160, 1);
- tft.unloadFont();
+ bool rainbowTxt = isRainbowTheme();
+ drawDseg7RightFast44(tft, valStr.c_str(), 112, 179, coolCol, cardBg, 102, rainbowTxt && (coolCol != TFT_RED));
- draw7SegDegreeC(tft, 106, 164, coolCol, cardBg, isRainbowTheme());
-
- // Progress bar (40 to 120 °C)
- int barW = map(constrain(curCool, 40, 120), 40, 120, 0, 128);
- tft.fillRect(18, 196, 128, 8, 0x0841);
- if (barW > 0) {
- tft.fillRoundRect(18, 196, barW, 8, 2, coolCol);
+ if (coolCol != oldClusterCoolCol || forceRedraw) {
+ oldClusterCoolCol = coolCol;
+ drawDegreeC_AA(tft, 117, 198, coolCol, cardBg, 240, rainbowTxt);
}
} else {
- tft.setTextColor(0x632C, cardBg);
- tft.drawString("---", 60, 164, 4);
- tft.fillRect(18, 196, 128, 8, 0x0841);
+ drawDseg7RightFast44(tft, "---", 112, 179, cCool, cardBg, 102, isRainbowTheme());
}
}
@@ -295,35 +295,24 @@ inline void updateClusterScreen(bool forceRedraw) {
if (forceRedraw || abs(curVolt - oldClusterVolt) >= 0.08f) {
oldClusterVolt = curVolt;
- tft.fillRect(174, 158, 130, 36, cardBg);
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();
+ drawDseg7RightFast44(tft, voltStr.c_str(), 282, 179, voltCol, cardBg, 112, rainbowTxt && (voltCol != TFT_RED));
- tft.loadFont(DSEG7_Modern_BoldItalic_34);
- tft.setTextColor(voltCol, cardBg);
- tft.drawRightString(voltStr, 268, 160, 1);
- tft.unloadFont();
-
- tft.setTextColor(0xBDD7, cardBg);
- tft.drawString("V", 274, 172, 2);
-
- // Voltage bar (10.0 to 16.0 V)
- int vBarW = map(constrain((int)(curVolt * 10), 100, 160), 100, 160, 0, 128);
- tft.fillRect(174, 196, 128, 8, 0x0841);
- if (vBarW > 0) {
- tft.fillRoundRect(174, 196, vBarW, 8, 2, voltCol);
+ if (voltCol != oldClusterVoltCol || forceRedraw) {
+ oldClusterVoltCol = voltCol;
+ drawVolt_AA(tft, 286, 198, voltCol, cardBg, 240, rainbowTxt);
}
} else {
- tft.setTextColor(0x632C, cardBg);
- tft.drawString("---", 216, 164, 4);
- tft.fillRect(174, 196, 128, 8, 0x0841);
+ drawDseg7RightFast44(tft, "---", 282, 179, cVolt, cardBg, 112, isRainbowTheme());
}
}
}
-// Render Page 4 slice-by-slice into sprite (for double-buffered transitions)
-inline void renderPage4ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int slice_h) {
+// Render Cluster Screen (Page 0) slice-by-slice into sprite (for double-buffered transitions)
+inline void renderClusterScreenToSprite(TFT_eSprite &s, int x_offset, int y_slice, int slice_h) {
uint16_t cardBg = 0x10A2;
uint16_t border = 0x2945;
uint16_t priCol = getThemePrimaryColor();
@@ -334,157 +323,148 @@ inline void renderPage4ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
uint16_t cCool = isRainbowTheme() ? getRainbowScreenColor(20, 150) : (isYapaTheme() ? getYapaColor(2) : priCol);
uint16_t cVolt = isRainbowTheme() ? getRainbowScreenColor(180, 150) : (isYapaTheme() ? getYapaColor(3) : accCol);
- // Pagination dots on top slice (page 4 active)
+ // Pagination dots on top slice (page 0 active of 4 pages)
if (y_slice == 0) {
- uint16_t actCol = isRainbowTheme() ? getRainbowScreenColor(188, 2) : (isYapaTheme() ? getYapaColor(4) : priCol);
- s.fillCircle(132, 2, 2, 0x5AEB);
- s.fillCircle(146, 2, 2, 0x5AEB);
- s.fillCircle(160, 2, 2, 0x5AEB);
- s.fillCircle(174, 2, 2, 0x5AEB);
- s.fillCircle(188, 2, 2, actCol);
+ uint16_t actCol = isRainbowTheme() ? getRainbowScreenColor(139, 2) : (isYapaTheme() ? getYapaColor(0) : priCol);
+ s.fillCircle(139, 2, 2, actCol);
+ s.fillCircle(153, 2, 2, 0x5AEB);
+ s.fillCircle(167, 2, 2, 0x5AEB);
+ s.fillCircle(181, 2, 2, 0x5AEB);
}
- // 1. TACHOMETER CARD (y: 10 to 52)
- int c0Y = 10 - y_slice;
- if (c0Y < slice_h && c0Y + 42 > 0) {
- s.fillRoundRect(8, c0Y, 304, 42, 5, cardBg);
- s.drawRoundRect(8, c0Y, 304, 42, 5, border);
- s.fillRoundRect(12, c0Y + 4, 3, 34, 1, cRpm);
- drawPidIconToSprite(s, 18, c0Y + 4, "010C", y_slice, slice_h, 10);
- s.setTextColor(0x8410, cardBg);
- s.drawString("RPM", 38, c0Y + 6, 2);
-
- // Tachometer background rail
- s.fillRoundRect(76, c0Y + 6, 140, 12, 3, 0x0841);
- s.drawString("1 2 3 4 5 6 7", 86, c0Y + 21, 1);
+ // 1. TACHOMETER CARD (y: 8 to 58)
+ 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);
+ }
float curRpmF = getClusterRpmVal();
int curRpm = (int)round(curRpmF);
+ bool rainbowTxt = isRainbowTheme();
+ int digitY = c0Y + 3;
if (curRpm > 0) {
uint16_t rpmCol = (curRpm >= 6000) ? TFT_RED : ((curRpm >= 4500) ? 0xFD60 : cRpm);
- s.setTextColor(rpmCol, cardBg);
- s.drawRightString(String(curRpm), 302, c0Y + 6, 4);
+ 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, 12);
- for (int seg = 0; seg < 12; seg++) {
- int sx = 78 + seg * 11;
+ 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 >= 10) segCol = TFT_RED;
- else if (seg >= 8) segCol = 0xFD60;
- else segCol = isRainbowTheme() ? getRainbowScreenColor(sx, 20) : cRpm;
+ 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 + 8, 9, 8, segCol);
+ s.fillRect(sx, c0Y + 9, 5, 10, segCol);
}
} else {
- s.setTextColor(0x632C, cardBg);
- s.drawRightString("---", 302, c0Y + 6, 4);
+ drawDseg7RightString44(s, "---", 238, digitY, cRpm, cardBg, 0, slice_h, rainbowTxt);
+ }
+
+ drawRpm_AA(s, 242, c0Y + 27, cRpm, cardBg, slice_h, isRainbowTheme());
+
+ if (isEcoOptimal) {
+ s.fillRoundRect(248, c0Y + 6, 48, 17, 3, 0x0202);
+ if (isYapaTheme()) {
+ char buf[2] = {0, 0};
+ const char *ecoStr = "ECO";
+ int ex = 260;
+ for (int i = 0; i < 3; i++) {
+ buf[0] = ecoStr[i];
+ s.setTextColor(getYapaColor(i), 0x0202);
+ s.drawString(buf, ex, c0Y + 7, 2);
+ ex += s.textWidth(buf, 2);
+ }
+ } else {
+ s.setTextColor(TFT_WHITE, 0x0202);
+ s.drawString("ECO", 260, c0Y + 7, 2);
+ }
+ s.drawRoundRect(248, c0Y + 6, 48, 17, 3, 0x07E0);
}
}
- // 2. SPEEDOMETER CARD (y: 56 to 132)
- int c1Y = 56 - y_slice;
- if (c1Y < slice_h && c1Y + 76 > 0) {
- s.fillRoundRect(8, c1Y, 304, 76, 5, cardBg);
- s.drawRoundRect(8, c1Y, 304, 76, 5, border);
- s.fillRoundRect(12, c1Y + 4, 3, 68, 1, cSpd);
- drawPidIconToSprite(s, 18, c1Y + 4, "010D", y_slice, slice_h, 56);
- s.setTextColor(0x8410, cardBg);
- s.drawString("VELOCIDAD", 38, c1Y + 6, 2);
+ // 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);
bool isGps = isClusterSpeedFromGps();
if (isGps) {
- s.fillRoundRect(116, c1Y + 5, 42, 14, 3, 0x0254);
- s.setTextColor(0x07FF, 0x0254);
- s.drawString("GPS", 125, c1Y + 7, 1);
+ drawGpsSatelliteIcon(s, 243, c1Y + 20, 0x07FF, 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;
+ if (curSpd >= 0) {
+ String spdStr = String(curSpd);
+ drawDseg7RightString80(s, spdStr.c_str(), 194, digitY, spdCol, cardBg, 0, slice_h, isRainbowTheme());
} else {
- s.fillRoundRect(116, c1Y + 5, 42, 14, 3, 0x18C3);
- s.setTextColor(0x8410, 0x18C3);
- s.drawString("OBD", 125, c1Y + 7, 1);
+ drawDseg7RightString80(s, "---", 194, digitY, spdCol, cardBg, 0, slice_h, isRainbowTheme());
}
- int curSpd = (int)round(getClusterSpeedVal());
- String spdStr = String(curSpd);
- s.loadFont(DSEG7_Modern_BoldItalic_44);
- uint16_t spdCol = isRainbowTheme() ? getRainbowScreenColor(210, 80) : cSpd;
- s.setTextColor(spdCol, cardBg);
- s.drawRightString(spdStr, 248, c1Y + 8, 1);
- s.unloadFont();
-
- s.setTextColor(0xBDD7, cardBg);
- s.drawString("km/h", 254, c1Y + 32, 2);
-
- int fillSpdW = map(constrain(curSpd, 0, 200), 0, 200, 0, 284);
- s.fillRect(18, c1Y + 68, 284, 3, 0x0841);
- if (fillSpdW > 0) {
- s.fillRect(18, c1Y + 68, fillSpdW, 3, cSpd);
- }
+ drawKmH_AA(s, 198, c1Y + 55, cSpd, cardBg, slice_h, isRainbowTheme());
}
- // 3. COOLANT TEMP CARD (y: 136 to 234)
- int c2Y = 136 - y_slice;
- if (c2Y < slice_h && c2Y + 98 > 0) {
- s.fillRoundRect(8, c2Y, 148, 98, 5, cardBg);
- s.drawRoundRect(8, c2Y, 148, 98, 5, border);
- s.fillRoundRect(12, c2Y + 4, 3, 90, 1, cCool);
- drawPidIconToSprite(s, 18, c2Y + 4, "0105", y_slice, slice_h, 136);
- s.setTextColor(0x8410, cardBg);
- s.drawString("REFRIG.", 38, c2Y + 6, 2);
-
- s.fillRect(18, c2Y + 60, 128, 8, 0x0841);
- s.drawString("40`C", 18, c2Y + 74, 1);
- s.drawString("120`C", 112, c2Y + 74, 1);
+ // 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());
float curCoolF = getClusterCoolantVal();
int curCool = (curCoolF > -900.0f) ? (int)round(curCoolF) : -999;
if (curCool > -900) {
uint16_t coolCol = (curCool >= 105) ? TFT_RED : ((curCool < 70) ? 0x05BF : cCool);
- s.loadFont(DSEG7_Modern_BoldItalic_34);
- s.setTextColor(coolCol, cardBg);
- s.drawRightString(String(curCool), 102, c2Y + 24, 1);
- s.unloadFont();
+ 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));
- int barW = map(constrain(curCool, 40, 120), 40, 120, 0, 128);
- if (barW > 0) {
- s.fillRoundRect(18, c2Y + 60, barW, 8, 2, coolCol);
- }
+ drawDegreeC_AA(s, 117, c2Y + 46, coolCol, cardBg, slice_h, isRainbowTheme());
} else {
- s.setTextColor(0x632C, cardBg);
- s.drawString("---", 60, c2Y + 28, 4);
+ drawDseg7RightString44(s, "---", 112, c2Y + 27, cCool, cardBg, 0, slice_h, isRainbowTheme());
}
}
- // 4. BATTERY VOLTAGE CARD (y: 136 to 234)
- int c3Y = 136 - y_slice;
- if (c3Y < slice_h && c3Y + 98 > 0) {
- s.fillRoundRect(164, c3Y, 148, 98, 5, cardBg);
- s.drawRoundRect(164, c3Y, 148, 98, 5, border);
- s.fillRoundRect(168, c3Y + 4, 3, 90, 1, cVolt);
- drawPidIconToSprite(s, 174, c3Y + 4, "ATRV", y_slice, slice_h, 136);
- s.setTextColor(0x8410, cardBg);
- s.drawString("BATERIA", 194, c3Y + 6, 2);
-
- s.fillRect(174, c3Y + 60, 128, 8, 0x0841);
- s.drawString("10V", 174, c3Y + 74, 1);
- s.drawString("16V", 272, c3Y + 74, 1);
+ // 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());
float curVolt = getClusterVoltVal();
if (curVolt > 0.0f) {
uint16_t voltCol = (curVolt < 11.8f || curVolt > 15.0f) ? TFT_RED : ((curVolt >= 13.5f) ? 0x07E0 : cVolt);
- s.loadFont(DSEG7_Modern_BoldItalic_34);
- s.setTextColor(voltCol, cardBg);
- s.drawRightString(String(curVolt, 1), 268, c3Y + 24, 1);
- s.unloadFont();
- s.setTextColor(0xBDD7, cardBg);
- s.drawString("V", 274, c3Y + 36, 2);
+ 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));
- int vBarW = map(constrain((int)(curVolt * 10), 100, 160), 100, 160, 0, 128);
- if (vBarW > 0) {
- s.fillRoundRect(174, c3Y + 60, vBarW, 8, 2, voltCol);
- }
+ drawVolt_AA(s, 286, c3Y + 46, voltCol, cardBg, slice_h, isRainbowTheme());
} else {
- s.setTextColor(0x632C, cardBg);
- s.drawString("---", 216, c3Y + 28, 4);
+ drawDseg7RightString44(s, "---", 282, c3Y + 27, cVolt, cardBg, 0, slice_h, isRainbowTheme());
}
}
}
+
diff --git a/sketch/obd2_gauge/coords_font.h b/sketch/obd2_gauge/coords_font.h
index 8ef9c53..28d4db0 100644
--- a/sketch/obd2_gauge/coords_font.h
+++ b/sketch/obd2_gauge/coords_font.h
@@ -609,6 +609,45 @@ inline void drawGpsNavSubword(T &dst, const char *str, int x, int y, uint16_t co
}
}
+// Calculate exact pixel width of anti-aliased Subword string
+inline int getNavSubwordWidth(const char *str) {
+ int totalW = 0;
+ for (int i = 0; str[i] != '\0'; i++) {
+ char c = str[i];
+ int gIdx = -1;
+ if (c >= 'A' && c <= 'Z') {
+ gIdx = c - 'A';
+ } else if (c >= 'a' && c <= 'z') {
+ gIdx = c - 'a';
+ } else if (c >= '0' && c <= '9') {
+ gIdx = 26 + (c - '0');
+ } else if (c == ':') {
+ gIdx = 36;
+ } else if (c == '.') {
+ gIdx = 37;
+ } else if (c == '-') {
+ gIdx = 38;
+ } else if ((uint8_t)c == 0xC2 && (uint8_t)str[i + 1] == 0xB0) {
+ gIdx = 39;
+ i++;
+ } else if ((uint8_t)c == 0xB0 || c == '`') {
+ gIdx = 39;
+ } else if (c == '/') {
+ gIdx = 40;
+ } else if (c == ' ') {
+ gIdx = 41;
+ }
+
+ if (gIdx >= 0) {
+ CoordsGlyphAA g;
+ memcpy_P(&g, &s_navSubGlyphs[gIdx], sizeof(CoordsGlyphAA));
+ totalW += g.xAdv;
+ }
+ }
+ return totalW;
+}
+
+
// 3. Draw 100% anti-aliased Coordinate Value (e.g. "-34.60371°" or "--.-----°")
template
inline void drawCoordsAAValue(T &dst, const char *str, int x, int y, uint16_t color, uint16_t bgCol, int clip_h = 240) {
diff --git a/sketch/obd2_gauge/gps_clock.h b/sketch/obd2_gauge/gps_clock.h
index ad52a26..75e6d13 100644
--- a/sketch/obd2_gauge/gps_clock.h
+++ b/sketch/obd2_gauge/gps_clock.h
@@ -348,12 +348,11 @@ inline void drawGpsClockScreen(int x_offset = 0) {
uint16_t cardBorder = 0x2945; // Subtle edge border
bool synced = isGpsSynced();
- // Center: 5 Pagination Indicator Dots [○ ○ ● ○ ○] (Page 2 of 5 active)
- tft.fillCircle(132 + x_offset, 2, 2, 0x5AEB); // Page 0 inactive
- tft.fillCircle(146 + x_offset, 2, 2, 0x5AEB); // Page 1 inactive
- tft.fillCircle(160 + x_offset, 2, 2, priCol); // Page 2 active
- tft.fillCircle(174 + x_offset, 2, 2, 0x5AEB); // Page 3 inactive
- tft.fillCircle(188 + x_offset, 2, 2, 0x5AEB); // Page 4 inactive
+ // Center: 4 Pagination Indicator Dots [○ ○ ● ○] (Page 2 of 4 active)
+ tft.fillCircle(139 + x_offset, 2, 2, 0x5AEB); // Page 0 inactive
+ tft.fillCircle(153 + x_offset, 2, 2, 0x5AEB); // Page 1 inactive
+ tft.fillCircle(167 + x_offset, 2, 2, priCol); // Page 2 active
+ tft.fillCircle(181 + x_offset, 2, 2, 0x5AEB); // Page 3 inactive
// 2. MAIN CLOCK CARD (y = 19 to 121, height = 102, width = 312, x = 4)
int clkX = 4 + x_offset;
diff --git a/sketch/obd2_gauge/gps_icon_aa_data.h b/sketch/obd2_gauge/gps_icon_aa_data.h
index c68f13b..5654864 100644
--- a/sketch/obd2_gauge/gps_icon_aa_data.h
+++ b/sketch/obd2_gauge/gps_icon_aa_data.h
@@ -1,160 +1,155 @@
-// Generated from accurate vector model matching gps_icon.jpg / gps_icon.svg
+// Generated from accurate vector model matching gps_icon.png / gps_icon.svg
// 8-bit Subpixel Antialiased Alpha Maps (0..255)
#pragma once
#include
static const uint8_t GPS_BODY_AA_18[324] PROGMEM = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 201, 255, 99, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 48, 26, 54, 255, 255, 250, 106, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 171, 255, 238, 115, 216, 211, 55, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 77, 255, 250, 255, 223, 33, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 32, 96, 255, 251, 255, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 106, 240, 183, 165, 255, 243, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 172, 255, 255, 213, 0, 45, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 160, 255, 147, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 28, 27, 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2, 2, 4, 0, 56, 231, 231, 22, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 64, 252, 255, 209, 24, 1, 1, 0, 0, 0,
+ 0, 0, 0, 2, 0, 45, 184, 182, 102, 233, 174, 10, 0, 1, 0, 0, 0, 0,
+ 0, 0, 1, 4, 7, 209, 255, 255, 212, 34, 1, 0, 1, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 233, 254, 252, 250, 13, 0, 2, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 76, 155, 128, 249, 255, 130, 1, 3, 0, 0, 0, 0, 0, 0, 0,
+ 2, 5, 107, 249, 255, 101, 36, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 23, 255, 255, 153, 17, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 0, 91, 118, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
-
static const uint8_t GPS_WAVE1_AA_18[324] PROGMEM = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 18, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 185, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 199, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 254, 137, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 44, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 51, 193, 255, 151, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 29, 227, 225, 229, 255, 255, 152, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 23, 199, 234, 226, 167, 66, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 12, 201, 107, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 32, 255, 162, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 1, 6, 0, 77, 253, 111, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 207, 252, 33, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 12, 11, 61, 198, 255, 125, 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 174, 248, 242, 255, 255, 135, 0, 3, 1, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 104, 191, 190, 141, 51, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3, 2, 2, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
-
static const uint8_t GPS_WAVE2_AA_18[324] PROGMEM = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 96, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 84, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 239, 82, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 243, 53, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 229, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 245, 137, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 210, 232, 42, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 182, 248, 112, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 201, 255, 142, 0, 0, 0, 0,
- 0, 0, 0, 0, 88, 124, 88, 96, 142, 198, 254, 242, 128, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 234, 255, 255, 255, 255, 244, 151, 57, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 48, 94, 111, 103, 73, 25, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 12, 15, 0, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 187, 210, 2, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 219, 241, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 7, 238, 212, 0, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 51, 255, 158, 0, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 162, 255, 69, 0, 3,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 6, 0, 73, 255, 201, 3, 2, 0,
+ 0, 0, 0, 0, 2, 1, 3, 3, 1, 0, 0, 69, 242, 248, 48, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 142, 255, 253, 81, 0, 3, 0, 0,
+ 0, 0, 3, 0, 79, 205, 175, 176, 211, 255, 255, 210, 60, 0, 3, 0, 0, 0,
+ 0, 0, 3, 0, 87, 239, 255, 255, 239, 184, 93, 6, 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 25, 25, 9, 0, 0, 0, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 3, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
-
static const uint8_t GPS_BODY_AA_28[784] PROGMEM = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 213, 255, 255, 66, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 244, 255, 201, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 49, 29, 0, 124, 255, 249, 251, 255, 220, 70, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 120, 235, 255, 242, 135, 38, 241, 255, 255, 155, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 94, 255, 255, 254, 255, 255, 106, 87, 210, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 200, 253, 251, 255, 252, 252, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 218, 253, 253, 255, 254, 254, 238, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 115, 40, 149, 255, 247, 252, 248, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 54, 201, 255, 213, 33, 216, 255, 255, 255, 237, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 126, 250, 255, 243, 255, 133, 16, 130, 169, 137, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 155, 255, 251, 249, 255, 229, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 110, 255, 255, 255, 169, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 181, 234, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 32, 58, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 62, 231, 255, 65, 0, 3, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 4, 3, 0, 98, 249, 255, 254, 234, 8, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 126, 255, 251, 253, 255, 127, 3, 2, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 40, 114, 112, 26, 132, 255, 255, 246, 91, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 3, 0, 84, 246, 255, 255, 234, 53, 171, 225, 55, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 15, 234, 255, 249, 249, 255, 208, 5, 13, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 4, 3, 54, 255, 253, 255, 254, 253, 249, 28, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0, 0, 18, 252, 252, 251, 250, 253, 232, 13, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 4, 0, 21, 173, 87, 117, 255, 255, 255, 255, 111, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 51, 219, 255, 248, 65, 97, 187, 180, 89, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 1, 84, 243, 255, 245, 255, 136, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 0, 24, 255, 254, 251, 255, 143, 5, 5, 3, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 3, 145, 255, 251, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 134, 69, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
-
static const uint8_t GPS_WAVE1_AA_28[784] PROGMEM = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 166, 81, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 221, 255, 245, 20, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 252, 244, 26, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 249, 213, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 249, 243, 158, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 236, 239, 106, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 240, 230, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 250, 233, 231, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 79, 76, 51, 68, 116, 193, 249, 232, 255, 139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 119, 255, 255, 248, 246, 255, 255, 242, 224, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 142, 255, 255, 255, 249, 255, 229, 149, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 123, 149, 157, 140, 103, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 176, 241, 83, 0, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 22, 245, 255, 162, 0, 4, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 32, 245, 254, 144, 0, 4, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 83, 255, 255, 105, 0, 4, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 0, 192, 254, 251, 38, 0, 2, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 4, 4, 4, 0, 0, 117, 255, 255, 177, 0, 3, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 131, 255, 253, 247, 40, 1, 2, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 0, 42, 117, 91, 95, 141, 219, 255, 255, 255, 86, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 2, 219, 255, 255, 255, 255, 255, 255, 230, 78, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 1, 165, 255, 255, 255, 252, 218, 135, 24, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 50, 75, 73, 46, 7, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
-
static const uint8_t GPS_WAVE2_AA_28[784] PROGMEM = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 199, 227, 71, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 255, 179, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 248, 252, 171, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 225, 232, 133, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 228, 225, 106, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 224, 226, 68, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 193, 238, 179, 21, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 243, 227, 119, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 180, 228, 189, 49, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 221, 236, 154, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 243, 233, 192, 34, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, 240, 243, 241, 88, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 167, 228, 232, 242, 134, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 74, 144, 225, 244, 229, 235, 121, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 84, 218, 199, 159, 175, 166, 177, 221, 238, 247, 243, 253, 183, 77, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 214, 255, 255, 255, 255, 255, 244, 237, 247, 248, 186, 126, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 107, 238, 242, 231, 255, 245, 224, 196, 175, 102, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 21, 43, 63, 80, 70, 47, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 30, 88, 13, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 203, 255, 157, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 11, 230, 252, 197, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 15, 230, 255, 185, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 36, 248, 255, 164, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 83, 255, 255, 121, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 164, 255, 255, 58, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 29, 243, 254, 218, 6, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 162, 255, 255, 117, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 89, 255, 253, 228, 14, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 72, 248, 250, 255, 80, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 2, 4, 4, 3, 3, 4, 4, 0, 0, 0, 107, 250, 250, 255, 138, 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 67, 186, 255, 253, 255, 153, 0, 3, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2, 0, 57, 125, 90, 71, 73, 96, 143, 207, 255, 255, 255, 254, 124, 0, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0, 12, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 196, 62, 0, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 6, 191, 255, 255, 255, 255, 255, 255, 232, 171, 77, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 8, 63, 97, 115, 113, 93, 59, 17, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 3, 4, 4, 4, 4, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
diff --git a/sketch/obd2_gauge/gps_module.h b/sketch/obd2_gauge/gps_module.h
index b1b0072..f1c3657 100644
--- a/sketch/obd2_gauge/gps_module.h
+++ b/sketch/obd2_gauge/gps_module.h
@@ -149,7 +149,7 @@ extern Preferences pref;
static const uint32_t CANDIDATE_BAUDS[4] = {38400, 115200, 9600, 57600};
static const char *const CANDIDATE_LABELS[4] = {
- "BUSCANDO: 38400", "BUSCANDO: 115K", "BUSCANDO: 9600", "BUSCANDO: 57.6K"};
+ "BUSCANDO: 38.4 KBPS", "BUSCANDO: 115.2 KBPS", "BUSCANDO: 9.6 KBPS", "BUSCANDO: 57.6 KBPS"};
static uint32_t activeGpsBaud = 38400;
static bool gpsUartLocked = false;
@@ -305,7 +305,17 @@ inline void processGPS() {
gpsUartLocked = true;
activeGpsBaud = CANDIDATE_BAUDS[gpsBaudScanIdx];
pref.putUInt("gps_baud", activeGpsBaud); // Guardar velocidad exacta en NVRAM!
- snprintf(gpsDiagLabel, sizeof(gpsDiagLabel), "FIJADO: %lu BPS", (unsigned long)activeGpsBaud);
+ if (activeGpsBaud == 115200) {
+ snprintf(gpsDiagLabel, sizeof(gpsDiagLabel), "FIJADO: 115.2 KBPS");
+ } else if (activeGpsBaud == 57600) {
+ snprintf(gpsDiagLabel, sizeof(gpsDiagLabel), "FIJADO: 57.6 KBPS");
+ } else if (activeGpsBaud == 38400) {
+ snprintf(gpsDiagLabel, sizeof(gpsDiagLabel), "FIJADO: 38.4 KBPS");
+ } else if (activeGpsBaud == 9600) {
+ snprintf(gpsDiagLabel, sizeof(gpsDiagLabel), "FIJADO: 9.6 KBPS");
+ } else {
+ snprintf(gpsDiagLabel, sizeof(gpsDiagLabel), "FIJADO: %lu BPS", (unsigned long)activeGpsBaud);
+ }
configureGpsModule();
}
}
@@ -533,6 +543,26 @@ inline bool isGpsSerialReceiving() {
}
inline bool isGpsUartLocked() { return gpsUartLocked; }
+// DOP Precision Rating matching standard DOP table (dop.png)
+inline const char *getGpsDopRating() {
+ if (gpsHdop < 1.0f) return "IDEAL";
+ if (gpsHdop < 2.0f) return "EXCELENTE";
+ if (gpsHdop < 5.0f) return "BUENA";
+ if (gpsHdop < 10.0f) return "MODERADA";
+ if (gpsHdop <= 20.0f) return "REGULAR";
+ return "POBRE";
+}
+
+inline uint16_t getGpsDopColor() {
+ if (!isGpsSynced()) return 0x9CD3;
+ if (gpsHdop < 1.0f) return 0x07FF; // Cyan (Ideal)
+ if (gpsHdop < 2.0f) return 0x07E0; // Verde (Excelente)
+ if (gpsHdop < 5.0f) return 0xFFE0; // Amarillo (Buena)
+ if (gpsHdop < 10.0f) return 0xFD20; // Naranja (Moderada)
+ if (gpsHdop <= 20.0f) return 0xFA60; // Naranja oscuro (Regular)
+ return TFT_RED; // Rojo (Pobre)
+}
+
inline const char *getGpsDiagMessage() {
if (gpsIsSleeping)
return "MODO REPOSO";
@@ -543,7 +573,7 @@ inline const char *getGpsDiagMessage() {
return "SIN DATOS RX";
}
if (isGpsSynced()) {
- return "PRECISION OPTIMA";
+ return getGpsDopRating();
}
if (gpsSats > 0) {
return "SINTONIZANDO SATS";
diff --git a/sketch/obd2_gauge/gps_nav.h b/sketch/obd2_gauge/gps_nav.h
index b07aa7e..8e6af6f 100644
--- a/sketch/obd2_gauge/gps_nav.h
+++ b/sketch/obd2_gauge/gps_nav.h
@@ -40,9 +40,22 @@ static uint8_t oldNavWaveState = 255;
static bool oldNavSynced = false;
static uint32_t lastNavPageEnterTime = 0;
+static TFT_eSprite sprNavDiag(&tft);
+static bool sprNavDiagAllocated = false;
+
+inline void initNavDiagSprite() {
+ if (!sprNavDiagAllocated) {
+ if (sprNavDiag.createSprite(138, 16) != nullptr) {
+ sprNavDiagAllocated = true;
+ }
+ }
+}
+
// Synchronize tracking variables without touching TFT display (zero flicker)
-inline void syncGpsNavTrackers() {
- lastNavPageEnterTime = millis();
+inline void syncGpsNavTrackers(bool isPageEnter = false) {
+ if (isPageEnter) {
+ lastNavPageEnterTime = millis();
+ }
bool synced = isGpsSynced();
bool altValid = isGpsAltitudeValid();
lastNavAltRound = altValid ? (int)round(gpsAltitudeM) : -999;
@@ -85,6 +98,32 @@ inline void drawLargeSatelliteIconSprite(TFT_eSprite &s, int x, int y, uint16_t
drawLargeGpsSatelliteIcon(s, x, y, TFT_WHITE, color, bgColor, waveState);
}
+// Draw diagnostic message with static, right-justified "KBPS" unit for scanning/locked baud
+template
+inline void drawGpsNavDiag(T &dst, const char *msg, int c2X, int c2Y, uint16_t color, uint16_t bgCol, int clip_h = 240) {
+ if (strncmp(msg, "BUSCANDO", 8) == 0 || strncmp(msg, "FIJADO", 6) == 0) {
+ bool isBuscando = (strncmp(msg, "BUSCANDO", 8) == 0);
+ const char *prefix = isBuscando ? "BUSCANDO:" : "FIJADO:";
+ drawGpsNavSubword(dst, prefix, c2X + 16, c2Y + 90, color, bgCol, clip_h);
+ drawGpsNavSubword(dst, "KBPS", c2X + 116, c2Y + 90, color, bgCol, clip_h);
+
+ const char *p = msg + (isBuscando ? 8 : 6);
+ while (*p == ':' || *p == ' ') p++;
+ char valBuf[10];
+ int vi = 0;
+ while (*p != '\0' && *p != ' ' && vi < (int)sizeof(valBuf) - 1) {
+ valBuf[vi++] = *p++;
+ }
+ valBuf[vi] = '\0';
+
+ int valW = getNavSubwordWidth(valBuf);
+ int valX = c2X + 113 - valW;
+ drawGpsNavSubword(dst, valBuf, valX, c2Y + 90, color, bgCol, clip_h);
+ } else {
+ drawGpsNavSubword(dst, msg, c2X + 16, c2Y + 90, color, bgCol, clip_h);
+ }
+}
+
// Draw full Page 3 (GPS Navigation & Telemetry Screen: Altitud, Rumbo, Satélites, Coordenadas)
inline void drawGpsNavScreen(int x_offset = 0) {
if (x_offset == 0) {
@@ -103,12 +142,11 @@ inline void drawGpsNavScreen(int x_offset = 0) {
uint16_t cardBorder = 0x2945; // Subtle edge border
bool synced = isGpsSynced();
- // Center: 5 Pagination Indicator Dots [○ ○ ○ ● ○] (Page 3 of 5 active)
- tft.fillCircle(132 + x_offset, 2, 2, 0x5AEB); // Page 0 inactive
- tft.fillCircle(146 + x_offset, 2, 2, 0x5AEB); // Page 1 inactive
- tft.fillCircle(160 + x_offset, 2, 2, 0x5AEB); // Page 2 inactive
- tft.fillCircle(174 + x_offset, 2, 2, priCol); // Page 3 active
- tft.fillCircle(188 + x_offset, 2, 2, 0x5AEB); // Page 4 inactive
+ // Center: 4 Pagination Indicator Dots [○ ○ ○ ●] (Page 3 of 4 active)
+ tft.fillCircle(139 + x_offset, 2, 2, 0x5AEB); // Page 0 inactive
+ tft.fillCircle(153 + x_offset, 2, 2, 0x5AEB); // Page 1 inactive
+ tft.fillCircle(167 + x_offset, 2, 2, 0x5AEB); // Page 2 inactive
+ tft.fillCircle(181 + x_offset, 2, 2, priCol); // Page 3 active
// 2. CARD 0 (Top Left): ALTITUD (x = 4, y = 19, w = 152, h = 102)
int c0X = 4 + x_offset;
@@ -124,7 +162,7 @@ inline void drawGpsNavScreen(int x_offset = 0) {
bool altValid = isGpsAltitudeValid();
String altStr = altValid ? String((int)round(gpsAltitudeM)) : "---";
- drawDseg7RightString34(tft, altStr.c_str(), c0X + 140, c0Y + 28, c0Col, cardBg, 0, 240, isRainbowTheme());
+ drawDseg7RightString34(tft, altStr.c_str(), c0X + 140, c0Y + 28, c0Col, cardBg, 126, 240, isRainbowTheme());
// 3. CARD 1 (Top Right): RUMBO / BRÚJULA (x = 164, y = 19, w = 152, h = 102)
int c1X = 164 + x_offset;
@@ -137,7 +175,7 @@ inline void drawGpsNavScreen(int x_offset = 0) {
bool hasCourse = synced && isGpsCourseValid();
String crsStr = hasCourse ? String((int)round(gpsCourseDeg)) : "---";
- drawDseg7RightString34(tft, crsStr.c_str(), c1X + 130, c1Y + 28, c1Col, cardBg, 0, 240, isRainbowTheme());
+ drawDseg7RightString34(tft, crsStr.c_str(), c1X + 130, c1Y + 28, c1Col, cardBg, 95, 240, isRainbowTheme());
if (hasCourse) {
tft.drawCircle(c1X + 137, c1Y + 33, 3, c1Col);
tft.drawCircle(c1X + 137, c1Y + 33, 2, c1Col);
@@ -164,13 +202,21 @@ inline void drawGpsNavScreen(int x_offset = 0) {
// Large Satellites Count (DSEG7 34pt): Shows "---" while scanning/probing UART, "0" or count when locked
String satStr = isGpsUartLocked() ? String(gpsSats) : "---";
- drawDseg7RightString34(tft, satStr.c_str(), c2X + 140, c2Y + 26, c2Col, cardBg, 0, 240, isRainbowTheme());
+ drawDseg7RightString34(tft, satStr.c_str(), c2X + 140, c2Y + 26, c2Col, cardBg, 85, 240, isRainbowTheme());
// Telemetry details: HDOP precision & live diagnostic status
String hdopStr = "HDOP: " + (synced ? String(gpsHdop, 1) : "---");
drawGpsNavSubword(tft, hdopStr.c_str(), c2X + 16, c2Y + 70, TFT_WHITE, cardBg, 240);
- drawGpsNavSubword(tft, getGpsDiagMessage(), c2X + 16, c2Y + 90, synced ? 0x9CD3 : priCol, cardBg, 240);
+ uint16_t diagCol = synced ? getGpsDopColor() : priCol;
+ initNavDiagSprite();
+ if (sprNavDiagAllocated) {
+ sprNavDiag.fillSprite(cardBg);
+ drawGpsNavDiag(sprNavDiag, getGpsDiagMessage(), -12, -88, diagCol, cardBg, 16);
+ sprNavDiag.pushSprite(c2X + 12, c2Y + 88);
+ } else {
+ drawGpsNavDiag(tft, getGpsDiagMessage(), c2X, c2Y, diagCol, cardBg, 240);
+ }
// 5. CARD 3 (Bottom Right): COORDENADAS GPS (x = 164, y = 125, w = 152, h = 110)
int c3X = 164 + x_offset;
@@ -190,7 +236,7 @@ inline void drawGpsNavScreen(int x_offset = 0) {
drawCoordsAAValue(tft, lonStr.c_str(), c3X + 16, c3Y + 76, c3Col, cardBg, 240);
// Sync state trackers
- syncGpsNavTrackers();
+ syncGpsNavTrackers(true);
}
// 100% Flicker-Free Live Refresh for resting Page 3 (GPS Navigation & Telemetry)
@@ -201,7 +247,7 @@ inline void updateGpsNavScreen(bool forceRedraw = false) {
// do not wipe or repaint cards over SPI. Any incoming serial bytes from GPS
// are safely absorbed into tracking variables so there is ZERO blackout/blink!
if (!forceRedraw && (millis() - lastNavPageEnterTime < 400)) {
- syncGpsNavTrackers();
+ syncGpsNavTrackers(false);
return;
}
@@ -238,7 +284,7 @@ inline void updateGpsNavScreen(bool forceRedraw = false) {
oldNavCourse = gpsCourseDeg;
lastNavHasCourse = hasCourse;
String crsStr = hasCourse ? String(curCrsRound) : "---";
- drawDseg7RightString34(tft, crsStr.c_str(), c1X + 130, c1Y + 28, c1Col, cardBg, 85, 240, isRainbowTheme());
+ drawDseg7RightString34(tft, crsStr.c_str(), c1X + 130, c1Y + 28, c1Col, cardBg, 95, 240, isRainbowTheme());
if (hasCourse) {
tft.drawCircle(c1X + 137, c1Y + 33, 3, c1Col);
@@ -287,8 +333,16 @@ inline void updateGpsNavScreen(bool forceRedraw = false) {
strncpy(lastNavCardDiag, curDiag, sizeof(lastNavCardDiag) - 1);
lastNavCardDiag[sizeof(lastNavCardDiag) - 1] = '\0';
lastNavDiagSynced = synced;
- tft.fillRect(c2X + 16, c2Y + 88, 130, 16, cardBg);
- drawGpsNavSubword(tft, curDiag, c2X + 16, c2Y + 90, synced ? 0x9CD3 : c2Col, cardBg, 240);
+ uint16_t diagCol = synced ? getGpsDopColor() : c2Col;
+ initNavDiagSprite();
+ if (sprNavDiagAllocated) {
+ sprNavDiag.fillSprite(cardBg);
+ drawGpsNavDiag(sprNavDiag, curDiag, -12, -88, diagCol, cardBg, 16);
+ sprNavDiag.pushSprite(c2X + 12, c2Y + 88);
+ } else {
+ tft.fillRect(c2X + 12, c2Y + 88, 138, 16, cardBg);
+ drawGpsNavDiag(tft, curDiag, c2X, c2Y, diagCol, cardBg, 240);
+ }
}
// 3d. Large Satellite Icon: only redrawn if waveState or sync changes
@@ -344,7 +398,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
String altStr = altValid ? String((int)round(gpsAltitudeM)) : "---";
int valY = c0Y + 28;
if (valY < slice_h && valY + 38 > 0) {
- drawDseg7RightString34(s, altStr.c_str(), c0X + 140, valY, c0Col, cardBg, 0, slice_h, isRainbowTheme());
+ drawDseg7RightString34(s, altStr.c_str(), c0X + 140, valY, c0Col, cardBg, 126, slice_h, isRainbowTheme());
}
}
@@ -361,7 +415,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
String crsStr = hasCourse ? String((int)round(gpsCourseDeg)) : "---";
int valY = c1Y + 28;
if (valY < slice_h && valY + 38 > 0) {
- drawDseg7RightString34(s, crsStr.c_str(), c1X + 130, valY, c1Col, cardBg, 0, slice_h, isRainbowTheme());
+ drawDseg7RightString34(s, crsStr.c_str(), c1X + 130, valY, c1Col, cardBg, 95, slice_h, isRainbowTheme());
if (hasCourse) {
int circY = c1Y + 33;
if (circY + 4 > 0 && circY - 4 < slice_h) {
@@ -392,11 +446,12 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
int satY = c2Y + 26;
if (satY < slice_h && satY + 38 > 0) {
String satStr = isGpsUartLocked() ? String(gpsSats) : "---";
- drawDseg7RightString34(s, satStr.c_str(), c2X + 140, satY, c2Col, cardBg, 0, slice_h, isRainbowTheme());
+ drawDseg7RightString34(s, satStr.c_str(), c2X + 140, satY, c2Col, cardBg, 85, slice_h, isRainbowTheme());
}
String hdopStr = "HDOP: " + (synced ? String(gpsHdop, 1) : "---");
drawGpsNavSubword(s, hdopStr.c_str(), c2X + 16, c2Y + 70, TFT_WHITE, cardBg, slice_h);
- drawGpsNavSubword(s, getGpsDiagMessage(), c2X + 16, c2Y + 90, synced ? 0x9CD3 : c2Col, cardBg, slice_h);
+ uint16_t diagCol = synced ? getGpsDopColor() : c2Col;
+ drawGpsNavDiag(s, getGpsDiagMessage(), c2X, c2Y, diagCol, cardBg, slice_h);
}
// 5. CARD 3 (Bottom Right): COORDENADAS (y = 125 to 235)
diff --git a/sketch/obd2_gauge/kmh_aa_data.h b/sketch/obd2_gauge/kmh_aa_data.h
new file mode 100644
index 0000000..fc45d1a
--- /dev/null
+++ b/sketch/obd2_gauge/kmh_aa_data.h
@@ -0,0 +1,234 @@
+#pragma once
+#include
+
+#define KMH_AA_WIDTH 108
+#define KMH_AA_HEIGHT 28
+
+#define RPM_AA_WIDTH 60
+#define RPM_AA_HEIGHT 20
+
+#define DEGC_AA_WIDTH 33
+#define DEGC_AA_HEIGHT 25
+
+#define VOLT_AA_WIDTH 22
+#define VOLT_AA_HEIGHT 25
+
+#define TIT_REF_WIDTH 108
+#define TIT_REF_HEIGHT 11
+
+#define TIT_BAT_WIDTH 60
+#define TIT_BAT_HEIGHT 11
+
+// 8-bit Anti-Aliased 'kmh' (108x28)
+static const uint8_t s_kmhAlpha[3024] PROGMEM = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0x10, 0x00, 0x00, 0x4B, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xF5, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0xB5, 0x01, 0x00, 0x4B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x64,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0x2F, 0x00, 0x00, 0x00, 0x78, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xFF, 0xFF, 0xFF, 0xFF, 0xE6, 0x15, 0x00, 0x00, 0x78, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x37,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xEA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0x00, 0x00, 0x00, 0x00, 0xA5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xFA, 0xFF, 0xFF, 0xFF, 0xFD, 0x44, 0x00, 0x00, 0x00, 0xA5, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0C,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xC4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xEA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xDB, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x00, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x07, 0x00, 0x00, 0x00, 0x00, 0x05, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC8, 0x04, 0x00, 0x00, 0x00, 0x05, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xB0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x59, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xDB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x56, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x86, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xCB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xCD, 0xFF, 0xFF, 0xFF, 0xFF, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x29, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xB3, 0xFF, 0xFF, 0xFF, 0xFF, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2D, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0xFF, 0xFF, 0xFF, 0xFF, 0xD8, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB3, 0xFF, 0xFF, 0xFF, 0xFF, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x58, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xC8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xFE, 0xFF, 0xFF, 0xFF, 0xF8, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA2, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0D, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0x00, 0x00, 0x00, 0x27, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xEA, 0xFF, 0xFF, 0xFF, 0xFF, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA2, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0x6C, 0x6C, 0x6C, 0xD4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xAA, 0xFF, 0xFF, 0xFF, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xF2, 0xFF, 0xFF, 0xFF, 0xEA, 0xBE, 0xFF, 0xFF, 0xFF, 0xFF, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xBB, 0xFF, 0xFF, 0xFF, 0xFF, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0x75, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x67, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0xFF, 0xFF, 0xFF, 0xFF, 0xD9, 0x51, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0x00, 0x00, 0x00, 0x00, 0xB2, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0xD6, 0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xFF, 0xFF, 0xFF, 0xFF, 0xE5, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x48, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0xFF, 0xFF, 0xAC, 0x0A, 0xF6, 0xFF, 0xFF, 0xFF, 0x60, 0x00, 0x00, 0x00, 0x55, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x07, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0xFB, 0xFF, 0xFF, 0xFF, 0xFD, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1B, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF5, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xB2, 0xFF, 0xFF, 0xFF, 0xA9, 0x00, 0x00, 0x10, 0xE6, 0xFF, 0xFF, 0xFF, 0xEB, 0x15, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xDE, 0xFF, 0xFF, 0xFF, 0xFF, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0x52, 0x00, 0x63, 0xFF, 0xFF, 0xFF, 0xEF, 0x03, 0x00, 0x9A, 0xFF, 0xFF, 0xFF, 0xFF, 0x56, 0x00, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA7, 0xFF, 0xFF, 0xFF, 0xFF, 0xC6, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x45, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x25, 0x00, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0x3C, 0x3D, 0xFE, 0xFF, 0xFF, 0xFF, 0xAC, 0x00, 0x00, 0x8A, 0xFF, 0xFF, 0xFF, 0xFF, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0x94, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x48, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x62, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0x02, 0x00, 0x00, 0xC4, 0xFF, 0xFF, 0xFF, 0x8C, 0xD7, 0xFF, 0xFF, 0xFF, 0xEC, 0x16, 0x00, 0x00, 0xB7, 0xFF, 0xFF, 0xFF, 0xFF, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xF4, 0xFF, 0xFF, 0xFF, 0xFF, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x67, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x75, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE8, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x00, 0x00, 0x00, 0x74, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x00, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0xFF, 0xFF, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xA2, 0xFF, 0xFF, 0xFF, 0xFF, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA2, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0x00, 0x00, 0x00, 0x24, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAE, 0x00, 0x00, 0x00, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0xFF, 0xFF, 0xFF, 0xFF, 0xD6, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA2, 0xFF, 0xFF, 0xFF, 0xFF, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0x71, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEC, 0x17, 0x00, 0x00, 0x00, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0x44, 0x00, 0x00, 0x00, 0x00, 0x85, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x6B, 0xFF, 0xFF, 0xFF, 0xFF, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xB3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x29, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x17, 0x00, 0x00, 0x00, 0x00, 0x36, 0xFF, 0xFF, 0xFF, 0xFF, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0xFF, 0xFF, 0xFF, 0xFF, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x02, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0x86, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x56, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x17, 0x00, 0x00, 0x00, 0x00, 0x56, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xE5, 0xFF, 0xFF, 0xED, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC5, 0xFF, 0xFF, 0xFF, 0xFF, 0x79, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x59, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x00, 0x00, 0x00, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x14, 0x14, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0x4C, 0x00, 0x00, 0x00, 0x3B, 0xFC, 0xFF, 0xFF, 0xFF, 0xFD, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xB0, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xFF, 0xFF, 0xFF, 0xFF, 0xEB, 0x09, 0x00, 0x00, 0x00, 0xB0, 0xFF, 0xFF, 0xFF, 0xFF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x10, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x67, 0x00, 0x00, 0x00, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x00, 0x00, 0x00, 0xAB, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0B, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE9, 0xFF, 0xFF, 0xFF, 0xFF, 0xD8, 0x01, 0x00, 0x0B, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0xFF, 0xFF, 0xFF, 0xFF, 0xC6, 0x00, 0x00, 0x67, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xFF, 0xFF, 0xFF, 0xFF, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+// 8-bit Anti-Aliased 'rpm' (60x20)
+static const uint8_t s_rpmAlpha[1200] PROGMEM = {
+ 0x00, 0x00, 0x00, 0x09, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0x00, 0x00, 0x00, 0x00, 0x09, 0xFD, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xFF, 0xFF, 0xFF, 0xB9,
+ 0x00, 0x00, 0x00, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x00, 0x00, 0x00, 0x33, 0xFF, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xD8, 0xFF, 0xFF, 0xFF, 0x8D,
+ 0x00, 0x00, 0x00, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x3C, 0x00, 0x00, 0x00, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x3C, 0x00, 0x00, 0x5F, 0xFF, 0xFF, 0xFF, 0xFC, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x61,
+ 0x00, 0x00, 0x00, 0x8B, 0xFF, 0xFF, 0xFF, 0x4E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xA3, 0xFF, 0xFF, 0xFF, 0xB1, 0x00, 0x00, 0x00, 0x8B, 0xFF, 0xFF, 0xFF, 0x4E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xA6, 0xFF, 0xFF, 0xFF, 0xB1, 0x00, 0x00, 0x8B, 0xFF, 0xFF, 0xFF, 0xFF, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0x35,
+ 0x00, 0x00, 0x00, 0xB7, 0xFF, 0xFF, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xFF, 0xFF, 0xFF, 0x87, 0x00, 0x00, 0x00, 0xB7, 0xFF, 0xFF, 0xFF, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xFF, 0xFF, 0xFF, 0x88, 0x00, 0x00, 0xB7, 0xFF, 0xFF, 0xFF, 0xFF, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0B,
+ 0x00, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xFF, 0xFF, 0xFF, 0x5B, 0x00, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0xFF, 0xFF, 0xFF, 0x5B, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0x00,
+ 0x00, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x11, 0xE9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB1, 0x00,
+ 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xFF, 0xFF, 0xFB, 0x06, 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB9, 0xFF, 0xFF, 0xFB, 0x07, 0x00, 0x3C, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0x74, 0x00, 0x00, 0x00, 0x00, 0x99, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0x85, 0x00,
+ 0x00, 0x00, 0x68, 0xFF, 0xFF, 0xFF, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xFC, 0xFF, 0xFF, 0xD5, 0x00, 0x00, 0x00, 0x68, 0xFF, 0xFF, 0xFF, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0xD6, 0x00, 0x00, 0x68, 0xFF, 0xFF, 0xFF, 0xA2, 0xFF, 0xFF, 0xBB, 0x00, 0x00, 0x00, 0x39, 0xFE, 0xFF, 0xFF, 0x9D, 0xFF, 0xFF, 0xFF, 0x59, 0x00,
+ 0x00, 0x00, 0x94, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x59, 0x00, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0xFF, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8E, 0xFF, 0xFF, 0xFF, 0xA9, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0xFF, 0x2B, 0xFB, 0xFF, 0xF8, 0x0A, 0x00, 0x04, 0xD2, 0xFF, 0xFF, 0xA8, 0x7A, 0xFF, 0xFF, 0xFF, 0x2D, 0x00,
+ 0x00, 0x00, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x40, 0x00, 0x00, 0x00, 0x00, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF5, 0x45, 0x00, 0x00, 0xC1, 0xFF, 0xFF, 0xED, 0x00, 0xC0, 0xFF, 0xFF, 0x4A, 0x00, 0x77, 0xFF, 0xFF, 0xEC, 0x15, 0xA7, 0xFF, 0xFF, 0xFB, 0x06, 0x00,
+ 0x00, 0x00, 0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x10, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE9, 0x36, 0x00, 0x00, 0x00, 0xEC, 0xFF, 0xFF, 0xC0, 0x00, 0x72, 0xFF, 0xFF, 0x92, 0x20, 0xF5, 0xFF, 0xFF, 0x5B, 0x00, 0xD3, 0xFF, 0xFF, 0xD5, 0x00, 0x00,
+ 0x00, 0x19, 0xFF, 0xFF, 0xFF, 0xB0, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xC2, 0xFF, 0xFF, 0xFF, 0xAA, 0x00, 0x00, 0x00, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDA, 0x22, 0x00, 0x00, 0x00, 0x19, 0xFF, 0xFF, 0xFF, 0x94, 0x00, 0x23, 0xFF, 0xFF, 0xDA, 0xB5, 0xFF, 0xFF, 0xB4, 0x00, 0x05, 0xFA, 0xFF, 0xFF, 0xA9, 0x00, 0x00,
+ 0x00, 0x45, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF4, 0xFF, 0xFF, 0xFF, 0x12, 0x00, 0x00, 0x45, 0xFF, 0xFF, 0xFF, 0x8F, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x08, 0x00, 0x00, 0x00, 0x00, 0x45, 0xFF, 0xFF, 0xFF, 0x68, 0x00, 0x00, 0xD6, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x1C, 0x00, 0x2C, 0xFF, 0xFF, 0xFF, 0x7D, 0x00, 0x00,
+ 0x00, 0x71, 0xFF, 0xFF, 0xFF, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xFF, 0xFF, 0xE9, 0x00, 0x00, 0x00, 0x71, 0xFF, 0xFF, 0xFF, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0xFF, 0xFF, 0xFF, 0x3B, 0x00, 0x00, 0x88, 0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0x00, 0x00, 0x58, 0xFF, 0xFF, 0xFF, 0x51, 0x00, 0x00,
+ 0x00, 0x9E, 0xFF, 0xFF, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xFF, 0xFF, 0xFF, 0xBE, 0x00, 0x00, 0x00, 0x9E, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x39, 0xFF, 0xFF, 0xFF, 0xBE, 0x01, 0x00, 0x00, 0x85, 0xFF, 0xFF, 0xFF, 0x25, 0x00, 0x00,
+ 0x00, 0xCA, 0xFF, 0xFF, 0xFA, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0xFF, 0xFF, 0xFF, 0x93, 0x00, 0x00, 0x00, 0xCA, 0xFF, 0xFF, 0xFC, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xFF, 0xFF, 0xE3, 0x00, 0x00, 0x00, 0x02, 0xE8, 0xFF, 0xF5, 0x23, 0x00, 0x00, 0x00, 0xB1, 0xFF, 0xFF, 0xF6, 0x02, 0x00, 0x00,
+ 0x01, 0xF4, 0xFF, 0xFF, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xFF, 0xFF, 0xFF, 0x67, 0x00, 0x00, 0x01, 0xF4, 0xFF, 0xFF, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF4, 0xFF, 0xFF, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x26, 0x34, 0x24, 0x00, 0x00, 0x00, 0x00, 0xDD, 0xFF, 0xFF, 0xCD, 0x00, 0x00, 0x00,
+ 0x22, 0xFF, 0xFF, 0xFF, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0xFF, 0xFF, 0xFF, 0x3C, 0x00, 0x00, 0x22, 0xFF, 0xFF, 0xFF, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xFF, 0xFF, 0xFF, 0x8A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xFE, 0xFF, 0xFF, 0xA1, 0x00, 0x00, 0x00,
+ 0x4E, 0xFF, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0xFF, 0xFF, 0xFF, 0x11, 0x00, 0x00, 0x4E, 0xFF, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xFF, 0xFF, 0xFF, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0xFF, 0xFF, 0xFF, 0x75, 0x00, 0x00, 0x00,
+};
+
+// 8-bit Anti-Aliased 'degc' (33x25)
+static const uint8_t s_degcAlpha[825] PROGMEM = {
+ 0x00, 0x00, 0x1A, 0xCB, 0xFF, 0xFF, 0xFF, 0xFE, 0x52, 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, 0x33, 0xE4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF4, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3B, 0x00, 0x00, 0x00,
+ 0x2C, 0xF5, 0xFF, 0xFF, 0xF7, 0xE8, 0xF8, 0xFF, 0xFF, 0xE0, 0x14, 0x00, 0x00, 0x00, 0x01, 0x8A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x21, 0x00, 0x00,
+ 0x6A, 0xFF, 0xFF, 0xDA, 0x24, 0x00, 0x3B, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x00, 0x08, 0xAB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x0F, 0x00,
+ 0x9A, 0xFF, 0xFF, 0x7D, 0x00, 0x00, 0x1A, 0xFF, 0xFF, 0xFA, 0x06, 0x00, 0x08, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC6, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0x04,
+ 0xC9, 0xFF, 0xFF, 0x9E, 0x00, 0x04, 0x95, 0xFF, 0xFF, 0xD1, 0x00, 0x00, 0x35, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x22,
+ 0x9B, 0xFF, 0xFF, 0xFF, 0xE9, 0xEE, 0xFF, 0xFF, 0xFF, 0x93, 0x00, 0x00, 0x62, 0xFF, 0xFF, 0xFF, 0xFF, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xFF, 0xFF, 0xFF, 0xF3, 0x01,
+ 0x06, 0xC2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x92, 0x03, 0x00, 0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0xFD, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0xFF, 0xFF, 0xFF, 0xC5, 0x00,
+ 0x00, 0x14, 0xDE, 0xFF, 0xFF, 0xFF, 0xFA, 0x65, 0x00, 0x00, 0x00, 0x00, 0xBC, 0xFF, 0xFF, 0xFF, 0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x68, 0x68, 0x68, 0x43, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xFF, 0xFF, 0xFF, 0xAE, 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, 0x00, 0x15, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 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, 0x00, 0x42, 0xFF, 0xFF, 0xFF, 0xFF, 0x57, 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, 0x00, 0x6F, 0xFF, 0xFF, 0xFF, 0xFF, 0x2B, 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, 0x00, 0x9B, 0xFF, 0xFF, 0xFF, 0xFA, 0x05, 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, 0x00, 0xC8, 0xFF, 0xFF, 0xFF, 0xD3, 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, 0x00, 0x01, 0xF3, 0xFF, 0xFF, 0xFF, 0xA8, 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, 0x00, 0x22, 0xFF, 0xFF, 0xFF, 0xFF, 0x7C, 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, 0x00, 0x4F, 0xFF, 0xFF, 0xFF, 0xFF, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x6C, 0x6C, 0x6C, 0x6C, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xFF, 0xFF, 0xFF, 0xFF, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB9, 0xFF, 0xFF, 0xFF, 0xE7, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0xEB, 0xFF, 0xFF, 0xFF, 0xB8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xD5, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xD3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE9, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x3C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE8, 0x37, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD4, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB8, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+// 8-bit Anti-Aliased 'volt' (22x25)
+static const uint8_t s_voltAlpha[550] PROGMEM = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xDA, 0xFF, 0xFF, 0xFF, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBC, 0xFF, 0xFF, 0xFF, 0xFF, 0x38,
+ 0xAC, 0xFF, 0xFF, 0xFF, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xFF, 0xFF, 0xFF, 0xFF, 0xB0, 0x00,
+ 0x7E, 0xFF, 0xFF, 0xFF, 0xF7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB9, 0xFF, 0xFF, 0xFF, 0xFD, 0x2C, 0x00,
+ 0x51, 0xFF, 0xFF, 0xFF, 0xFF, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0xFF, 0xFF, 0xFF, 0xFF, 0xA2, 0x00, 0x00,
+ 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7, 0xFF, 0xFF, 0xFF, 0xFA, 0x21, 0x00, 0x00,
+ 0x01, 0xF3, 0xFF, 0xFF, 0xFF, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0xFF, 0xFF, 0xFF, 0xFF, 0x94, 0x00, 0x00, 0x00,
+ 0x00, 0xC8, 0xFF, 0xFF, 0xFF, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB4, 0xFF, 0xFF, 0xFF, 0xF5, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x9A, 0xFF, 0xFF, 0xFF, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0x86, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x6C, 0xFF, 0xFF, 0xFF, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB2, 0xFF, 0xFF, 0xFF, 0xEF, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFA, 0x03, 0x00, 0x00, 0x00, 0x00, 0x31, 0xFF, 0xFF, 0xFF, 0xFF, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0x23, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xFF, 0xFF, 0xFF, 0xE7, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xE3, 0xFF, 0xFF, 0xFF, 0x48, 0x00, 0x00, 0x00, 0x2F, 0xFE, 0xFF, 0xFF, 0xFF, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xB6, 0xFF, 0xFF, 0xFF, 0x6D, 0x00, 0x00, 0x00, 0xAD, 0xFF, 0xFF, 0xFF, 0xDE, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x88, 0xFF, 0xFF, 0xFF, 0x92, 0x00, 0x00, 0x2D, 0xFE, 0xFF, 0xFF, 0xFF, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x5A, 0xFF, 0xFF, 0xFF, 0xB8, 0x00, 0x00, 0xAB, 0xFF, 0xFF, 0xFF, 0xD3, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x2D, 0xFF, 0xFF, 0xFF, 0xDD, 0x00, 0x2B, 0xFE, 0xFF, 0xFF, 0xFF, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0xF9, 0xFF, 0xFF, 0xFC, 0x06, 0xA8, 0xFF, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xD1, 0xFF, 0xFF, 0xFF, 0x51, 0xFD, 0xFF, 0xFF, 0xFF, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xA4, 0xFF, 0xFF, 0xFF, 0xE6, 0xFF, 0xFF, 0xFF, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x76, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x48, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xBF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+// 8-bit Anti-Aliased 'titRef' (108x11)
+static const uint8_t s_titRefAlpha[1188] PROGMEM = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCC, 0x0F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCC, 0x0F, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x0A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x0B, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCC, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x41, 0xFF, 0xEE, 0x06, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFA, 0x31, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80,
+ 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xDC, 0xFF, 0xC7, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0x62, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0x62, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xDC, 0xFF, 0xC7, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0xBB, 0xFF, 0xEE, 0xC4, 0xC4, 0xC4, 0xEB, 0xFF, 0xBC, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0x62, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xDC, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xFF, 0xFF, 0x4D, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xD5, 0x08, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0xC4, 0xC4, 0xC4, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0x62,
+ 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x0A, 0xFD, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x0A, 0xFD, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFE, 0x2D, 0x00, 0x00, 0x00, 0x26, 0xFF, 0xFC, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x0A, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x05, 0xED, 0xF6, 0xFF, 0xA6, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x8E, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7E, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x4D, 0xFF, 0x91, 0xE4, 0xF4, 0x0A, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0xEF, 0xFE, 0x42, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x22, 0xFE, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x22, 0xFE, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x22, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0xA7, 0xFF, 0x40, 0x93, 0xFF, 0x58, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x5B, 0xFF, 0xE2, 0x0F, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x84, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x84, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x68, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x84, 0x00, 0x00, 0x0B, 0xF4, 0xEC, 0x03, 0x40, 0xFF, 0xB1, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0xA9, 0xFF, 0xA2, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
+ 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xF1, 0xFF, 0x4E, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0x62, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xF1, 0xFF, 0x4E, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x4F, 0xC4, 0xC4, 0xFF, 0xFC, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xF1, 0xFF, 0x4E, 0x00, 0x00, 0x5A, 0xFF, 0xA0, 0x00, 0x03, 0xEA, 0xF9, 0x11, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x12, 0xE5, 0xFF, 0x53, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x45, 0xFF, 0xF4, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x45, 0xFF, 0xF4, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x45, 0xFF, 0xF4, 0x00, 0x00, 0xB3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x47, 0xFE, 0xEC, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFE, 0x28, 0x00, 0x00, 0x00, 0x22, 0xFF, 0xFC, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x12, 0xFA, 0xFE, 0xD0, 0xD0, 0xD0, 0xDD, 0xFF, 0xBC, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0x62, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0xBB, 0xFF, 0xEE, 0xC4, 0xC4, 0xC4, 0xE9, 0xFF, 0xB7, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0x62, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x66, 0xFF, 0xBA, 0x00, 0x00, 0x00, 0x12, 0xF8, 0xFD, 0x18, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x0A, 0xD8, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0x62,
+ 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,
+};
+
+// 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,
+ 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xDA, 0xFF, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xFF, 0xFF, 0x4D, 0x00, 0x00, 0xC4, 0xC4, 0xC4, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0x62, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xDC, 0xFF, 0xC7, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xFF, 0xFF, 0x4D, 0x00, 0x00, 0x00,
+ 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x07, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x05, 0xED, 0xF6, 0xFF, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x0A, 0xFD, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x05, 0xED, 0xF6, 0xFF, 0xA6, 0x00, 0x00, 0x00,
+ 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x08, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x4D, 0xFF, 0x91, 0xE4, 0xF4, 0x0A, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x4D, 0xFF, 0x91, 0xE4, 0xF4, 0x0A, 0x00, 0x00,
+ 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xDB, 0xFF, 0xDA, 0x00, 0x00, 0x00, 0xA7, 0xFF, 0x40, 0x93, 0xFF, 0x58, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x22, 0xFE, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xA7, 0xFF, 0x40, 0x93, 0xFF, 0x58, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0x4C, 0x00, 0x0B, 0xF4, 0xEC, 0x03, 0x40, 0xFF, 0xB1, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x84, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x0B, 0xF4, 0xEC, 0x03, 0x40, 0xFF, 0xB1, 0x00, 0x00,
+ 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x02, 0x6D, 0xFF, 0xF9, 0x00, 0x5A, 0xFF, 0xA0, 0x00, 0x03, 0xEA, 0xF9, 0x11, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xF1, 0xFF, 0x4E, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x5A, 0xFF, 0xA0, 0x00, 0x03, 0xEA, 0xF9, 0x11, 0x00,
+ 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0xB3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x45, 0xFF, 0xF4, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0xB3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x63, 0x00,
+ 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x34, 0xFF, 0xFC, 0x12, 0xFA, 0xFE, 0xD0, 0xD0, 0xD0, 0xDD, 0xFF, 0xBC, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x12, 0xFA, 0xFE, 0xD0, 0xD0, 0xD0, 0xDD, 0xFF, 0xBC, 0x00,
+ 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC5, 0xFC, 0xFF, 0xBF, 0x66, 0xFF, 0xBA, 0x00, 0x00, 0x00, 0x12, 0xF8, 0xFD, 0x18, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0x62, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x66, 0xFF, 0xBA, 0x00, 0x00, 0x00, 0x12, 0xF8, 0xFD, 0x18,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBA, 0x0A, 0xBF, 0xFF, 0x56, 0x00, 0x00, 0x00, 0x00, 0xA6, 0xFF, 0x6F, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0xBF, 0xFF, 0x56, 0x00, 0x00, 0x00, 0x00, 0xA6, 0xFF, 0x6F,
+};
+
+// Draw anti-aliased bitmap to destination (TFT or Sprite) with alpha blending, rainbow and Yapa multi-color per letter
+template
+inline void drawKmhAaBitmap(T &dst, int bx, int by, int w, int h, const uint8_t *alpha,
+ uint16_t color, uint16_t bgCol = 0x10A2, int clip_h = 240, bool rainbow = false) {
+ dst.startWrite();
+ for (int j = 0; j < h; j++) {
+ int py = by + j;
+ if (py < 0 || py >= clip_h) continue;
+ int rowOffset = j * w;
+ for (int i = 0; i < w; i++) {
+ int px = bx + i;
+ if (px < 0 || px >= 320) continue;
+ uint8_t a = pgm_read_byte(alpha + rowOffset + i);
+ if (a > 6) {
+ uint16_t curCol = color;
+ if (rainbow) {
+ curCol = getRainbowScreenColor(px, py);
+ } else if (isYapaTheme()) {
+ if (alpha == s_rpmAlpha) {
+ curCol = (i < 20) ? getYapaColor(0) : ((i < 39) ? getYapaColor(1) : getYapaColor(2));
+ } else if (alpha == s_kmhAlpha) {
+ curCol = (i < 31) ? getYapaColor(0) : ((i < 63) ? getYapaColor(1) : ((i < 76) ? getYapaColor(2) : getYapaColor(3)));
+ } else if (alpha == s_degcAlpha) {
+ curCol = (i < 13) ? getYapaColor(0) : getYapaColor(1);
+ } else if (alpha == s_voltAlpha) {
+ curCol = getYapaColor(2);
+ } else if (alpha == s_titRefAlpha) {
+ curCol = getYapaColor((i * 12) / 108);
+ } else if (alpha == s_titBatAlpha) {
+ curCol = getYapaColor((i * 7) / 60);
+ }
+ }
+ uint16_t pixCol = (a >= 250) ? curCol : dst.alphaBlend(a, curCol, bgCol);
+ dst.drawPixel(px, py, pixCol);
+ }
+ }
+ }
+ dst.endWrite();
+}
+
+template
+inline void drawKmH_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, KMH_AA_WIDTH, KMH_AA_HEIGHT, s_kmhAlpha, color, bgCol, clip_h, rainbow);
+}
+
+template
+inline void drawRpm_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, RPM_AA_WIDTH, RPM_AA_HEIGHT, s_rpmAlpha, color, bgCol, clip_h, rainbow);
+}
+
+template
+inline void drawDegreeC_AA(T &dst, int bx, int by, uint16_t color = 0xC618, uint16_t bgCol = 0x10A2, int clip_h = 240, bool rainbow = false) {
+ drawKmhAaBitmap(dst, bx, by, DEGC_AA_WIDTH, DEGC_AA_HEIGHT, s_degcAlpha, color, bgCol, clip_h, rainbow);
+}
+
+template
+inline void drawVolt_AA(T &dst, int bx, int by, uint16_t color = 0xC618, uint16_t bgCol = 0x10A2, int clip_h = 240, bool rainbow = false) {
+ drawKmhAaBitmap(dst, bx, by, VOLT_AA_WIDTH, VOLT_AA_HEIGHT, s_voltAlpha, color, bgCol, clip_h, rainbow);
+}
+
+template
+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);
+}
+
+template
+inline void drawTitBat_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_BAT_WIDTH, TIT_BAT_HEIGHT, s_titBatAlpha, color, bgCol, clip_h, rainbow);
+}
diff --git a/sketch/obd2_gauge/meter.h b/sketch/obd2_gauge/meter.h
index 44288e9..d028f4b 100644
--- a/sketch/obd2_gauge/meter.h
+++ b/sketch/obd2_gauge/meter.h
@@ -256,76 +256,9 @@ void drawFullScreenTitle(const String &label, uint16_t color) {
// Tactical ECO / OPT Badge (Ford Ka Endura 1.3 Sweet Spot: 1900 - 2550 RPM &
// 45% - 82% Load)
void updateEcoBadge(bool forceRedraw = false) {
- // CRITICAL: ECO badge ONLY belongs on Page 0 (RPM quadrant or fullscreen
- // RPM), NEVER on Page 1 (Tri-Temp)!
- if (currentScreenPage != 0) {
- badgeEcoDrawn = false;
- return;
- }
-
- // If in fullscreen on Page 0, only display if the fullscreen cell is RPM
- bool isQuad = (fullScreenCell < 0 || fullScreenCell >= 4);
- if (!isQuad && pidConfig[pidInCell[fullScreenCell]][3].toInt() != 3) {
- badgeEcoDrawn = false;
- return;
- }
-
bool newEco = (ignitionOn && currentRpm >= 1900.0f && currentRpm <= 2550.0f &&
currentEngineLoad >= 45.0f && currentEngineLoad <= 82.0f);
isEcoOptimal = newEco;
-
- if (forceRedraw) {
- badgeEcoDrawn = !newEco; // Force redraw/re-erase
- }
-
- if (newEco == badgeEcoDrawn) {
- return;
- }
-
- int bx = 0, by = 0, bw = 0, bh = 0;
-
- if (isQuad) {
- // 4-Quadrant View: Positioned in top-right of RPM Quadrant (Cell 2)
- int rpmCell = 2;
- for (int c = 0; c < 4; c++) {
- if (pidConfig[pidInCell[c]][3].toInt() == 3) {
- rpmCell = c;
- break;
- }
- }
- bx = cell4_x[rpmCell] + 100;
- by = cell4_y[rpmCell] + 4;
- bw = 48;
- bh = 18;
- } else {
- // Full-Screen View: Positioned in top-right header band
- bx = 260;
- by = 11;
- bw = 50;
- bh = 20;
- }
-
- if (newEco) {
- // Elegant tactical pill badge [ ECO ]
- const uint16_t bgFill = 0x0202; // Deep dark forest emerald green
- const uint16_t borderCol = 0x07E0; // Vibrant emerald neon green
- tft.fillRoundRect(bx, by, bw, bh, 4, bgFill);
- tft.drawRoundRect(bx, by, bw, bh, 4, borderCol);
- tft.setTextColor(TFT_WHITE, bgFill);
- if (isQuad) {
- tft.drawCentreString("ECO", bx + (bw / 2), by + 1, 2);
- } else {
- tft.drawCentreString("ECO", bx + (bw / 2), by + 2, 2);
- }
- } else {
- // Cleanly restore background gradient lines with zero visual trace
- int ry = (isQuad && by >= 120) ? 121 : 0;
- for (int y = by; y < by + bh; y++) {
- uint16_t bgCol = getInstrumentBgColor(y, isQuad, ry);
- tft.drawFastHLine(bx, y, bw, bgCol);
- }
- }
-
badgeEcoDrawn = newEco;
}
@@ -1052,13 +985,12 @@ inline int calcMeterBlocks(float val, int min, int max) {
return b;
}
-// Restore natural vignette vertical gradient where GPS icon sits
inline void clearPage0GpsIcon() {
- uint16_t c1 = tft.alphaBlend((uint8_t)(255 * (82 - 3) / 82),
- getThemeBgColor(), TFT_BLACK);
- uint16_t c2 = tft.alphaBlend((uint8_t)(255 * (82 - 25) / 82),
- getThemeBgColor(), TFT_BLACK);
- tft.fillRectVGradient(294, 3, 24, 22, c1, c2);
+ // Page 0 uses the integrated GPS/OBD status pill inside the speedometer card
+}
+
+inline void drawPage0GpsIcon(uint8_t waveState, bool synced) {
+ // Page 0 uses the integrated GPS/OBD status pill inside the speedometer card
}
inline void clearFullscreenGpsIcon() {
@@ -1069,19 +1001,6 @@ inline void clearFullscreenGpsIcon() {
tft.fillRectVGradient(294, 3, 24, 22, c1, c2);
}
-// Draw animated GPS satellite icon in top right of Quadrant 1 (Velocidad: cell
-// 1)
-inline void drawPage0GpsIcon(uint8_t waveState, bool synced) {
- if (currentScreenPage != 0 || fullScreenCell != -1)
- return;
- clearPage0GpsIcon();
- uint16_t waveCol = getThemePrimaryColor();
- uint16_t bodyCol = TFT_WHITE;
- uint16_t bgCol = tft.alphaBlend((uint8_t)(255 * (82 - 3) / 82),
- getThemeBgColor(), TFT_BLACK);
- drawGpsSatelliteIcon(tft, 296, 3, bodyCol, waveCol, bgCol, waveState);
-}
-
// Draw animated GPS satellite icon in top right of Fullscreen Velocidad (cell
// 1)
inline void drawFullscreenGpsIcon(uint8_t waveState, bool synced) {
@@ -1187,7 +1106,7 @@ void arcMeter4(uint8_t cell, uint8_t pid, bool redrawHeader = true) {
int warn = warningValue[pid].toInt();
String unit = pidConfig[pid][1];
- if (last_valid_data[cell] != -999) {
+ if (last_valid_data[cell] > -900.0f) {
old_data[cell] = -999;
plotArc4(cell, last_valid_data[cell], warn, min, max, digit, unit);
} else {
@@ -1325,7 +1244,7 @@ void fullScreenMeter(uint8_t cell, uint8_t pid, bool redrawHeader = true) {
bool digit = pidConfig[pid][7].toInt();
int warn = warningValue[pid].toInt();
- if (last_valid_data[cell] != -999) {
+ if (last_valid_data[cell] > -900.0f) {
old_data[cell] = -999;
plotFullScreen(cell, last_valid_data[cell], warn, min, max, digit, unit);
} else {
@@ -1440,6 +1359,12 @@ void plotNoData(uint8_t cell) {
wasInRedAlarm[cell] = false;
uint8_t pidIdx = pidInCell[cell];
+ if (pidIdx == 0) {
+ tempEngineCoolant = -999.0f;
+ if (currentScreenPage == 1 && fullScreenCell == -1) {
+ updateTriTempCoolant(-999.0f);
+ }
+ }
if (fullScreenCell >= 0 && fullScreenCell < 4) {
if (cell == fullScreenCell) {
@@ -1456,19 +1381,7 @@ void plotNoData(uint8_t cell) {
tft.unloadFont();
}
} else if (currentScreenPage == 0) {
- int x = cell4_x[cell];
- int y = cell4_y[cell];
- clearPidIcon(x + 5, y + 3, pidConfig[pidIdx][2]);
- drawPidIcon(x + 5, y + 3, pidConfig[pidIdx][2]);
- for (uint8_t i = 0; i < NUM_BAR_BLOCKS; i++) {
- drawQuadBarBlock(cell, i, METER_INACTIVE_COLOR);
- }
- tft.loadFont(DSEG7_Modern_BoldItalic_34);
- tft.setTextColor(getThemeCellColor(cell), TFT_BLACK);
- tft.setTextPadding(114);
- tft.drawRightString("---", x + 152, y + 74, 1);
- tft.setTextPadding(0);
- tft.unloadFont();
+ updateClusterScreen(false);
}
}
@@ -1597,103 +1510,10 @@ inline void drawQuadBarBlockToSprite(TFT_eSprite &s, int16_t qx, int16_t qy,
}
}
-// Render Page 0 (4 Quadrants) into double-buffered Sprite slice
+// Render Page 0 (Cockpit Instrumental) into double-buffered Sprite slice
inline void renderPage0ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
int slice_h) {
- checkUpdateThemeGradients();
- uint16_t divCol = getThemeDivColor();
- uint16_t priCol = getThemePrimaryColor();
-
- for (uint8_t c = 0; c < 4; c++) {
- int qx = cell4_x[c] + x_offset;
- int qy = cell4_y[c] - y_slice;
- int rx = (c & 1) ? (161 + x_offset) : (0 + x_offset);
- int global_ry = (c & 2) ? 121 : 0;
- int ry = global_ry - y_slice;
-
- if (qx + 160 <= 0 || qx >= 320 || ry + 119 <= 0 || ry >= slice_h)
- continue;
-
- // 1. Exact dark vignette gradient matching real arcMeter4
- s.fillRect(rx, ry, 159, 119, TFT_BLACK);
- int gradStartY = (ry < 0) ? -ry : 0;
- int gradEndY = (ry + 82 > slice_h) ? (slice_h - ry) : 82;
- for (int j = gradStartY; j < gradEndY; j++) {
- int curY = ry + j;
- if (curY >= 0 && curY < slice_h) {
- s.drawFastHLine(rx, curY, 159, themeGradQuad[j]);
- }
- }
-
- uint8_t pidIdx = pidInCell[c];
-
- // 2. Real high-definition parameter icon (thermometer, speedometer,
- // tachometer, battery)
- drawPidIconToSprite(s, qx + 5, qy + 3, pidConfig[pidIdx][2], y_slice,
- slice_h, global_ry);
-
- // 3. Exact 20 curved segmented gauge blocks along the arc (true
- // mathematical polygons)
- int warn = warningValue[pidIdx].toInt();
- int min = pidConfig[pidIdx][4].toInt();
- int max = pidConfig[pidIdx][5].toInt();
- bool digit = pidConfig[pidIdx][7].toInt();
- float val = last_valid_data[c];
-
- int numActive = (val > -900.0f) ? calcMeterBlocks(val, min, max) : 0;
- uint16_t activeCol =
- (val > -900.0f) ? getMeterColor(pidIdx, val, warn) : priCol;
-
- for (uint8_t i = 0; i < NUM_BAR_BLOCKS; i++) {
- uint16_t bCol = (i < numActive) ? activeCol : METER_INACTIVE_COLOR;
- drawQuadBarBlockToSprite(s, qx, qy, i, bCol, slice_h);
- }
-
- // 4. Digital value string using authentic DSEG7 34pt font with rainbow gradient
- 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;
- if (textY < slice_h && textY + 40 > 0) {
- drawDseg7RightString34(s, numStr.c_str(), qx + 152, textY, txtCol, TFT_BLACK, 0, slice_h, rainbowTxt);
- }
-
- // GPS Satellite icon in top right of Quadrant 1 (Velocidad: cell 1)
- if (c == 1 && isGpsSpeedValid() && y_slice == 0) {
- uint16_t waveCol = isRainbowTheme() ? (uint16_t)0x07E0 : priCol;
- uint16_t bodyCol = TFT_WHITE;
- uint16_t bgCol = themeGradQuad[3];
- drawGpsSatelliteIcon(s, qx + 136, 3, bodyCol, waveCol, bgCol,
- getGpsWaveAnimState());
- }
- }
-
- // 5. Eco Badge if active
- if (isEcoOptimal) {
- int rpmCell = 2;
- for (int c = 0; c < 4; c++) {
- if (pidConfig[pidInCell[c]][3].toInt() == 3) {
- rpmCell = c;
- break;
- }
- }
- int bx = cell4_x[rpmCell] + 100 + x_offset;
- int by = cell4_y[rpmCell] + 4 - y_slice;
- int bw = 48, bh = 18;
- if (by + bh > 0 && by < slice_h && bx + bw > 0 && bx < 320) {
- s.fillRoundRect(bx, by, bw, bh, 4, 0x0202);
- s.drawRoundRect(bx, by, bw, bh, 4, 0x07E0);
- s.setTextColor(TFT_WHITE, 0x0202);
- s.drawCentreString("ECO", bx + (bw / 2), by + 1, 2);
- }
- }
-
- // 6. Crosshair dividers
- s.fillRect(159 + x_offset, 0, 2, slice_h, divCol);
- int hLineY = 119 - y_slice;
- if (hLineY >= 0 && hLineY < slice_h) {
- s.fillRect(x_offset, hLineY, 320, 2, divCol);
- }
+ renderClusterScreenToSprite(s, x_offset, y_slice, slice_h);
}
// Render Page 1 (3 Temperature Cards) into double-buffered Sprite slice
@@ -1762,13 +1582,17 @@ inline void renderPage1ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
if (sprTriTitleReady && sprTriTitle[cardIdx].created()) {
sprTriTitle[cardIdx].pushToSprite(&s, cardX + 16, cardY + 5);
} else {
- s.setTextColor(TFT_WHITE, cardBg);
- s.drawString(titleStr, cardX + 16, cardY + 5, 4);
+ if (isYapaTheme()) {
+ drawYapaStringToSprite(s, titleStr, cardX + 16, cardY + 5, 4);
+ } else {
+ s.setTextColor(TFT_WHITE, cardBg);
+ s.drawString(titleStr, cardX + 16, cardY + 5, 4);
+ }
}
if (isYapaTheme()) {
drawYapaStringToSprite(s, subStr, cardX + 16, cardY + 32, 1);
} else {
- s.setTextColor(0x8410, cardBg);
+ s.setTextColor(accentCol, cardBg);
s.drawString(subStr, cardX + 16, cardY + 32, 1);
}
@@ -1782,24 +1606,26 @@ inline void renderPage1ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
ratio = 0.0f;
if (ratio > 1.0f)
ratio = 1.0f;
- int fillW = (int)(barW * ratio);
- if (fillW < 4)
+ int fillW = (val <= -900.0f) ? 0 : (int)(barW * ratio);
+ if (fillW < 4 && val > -900.0f)
fillW = 4;
- s.fillRoundRect(barX, barY, fillW, 8, 3, accentCol);
+ if (fillW > 0)
+ s.fillRoundRect(barX, barY, fillW, 8, 3, accentCol);
// Value text using authentic DSEG7 34pt font with rainbow gradient
String numStr =
- (val <= -900.0f) ? "---" : (isInt ? String((int)val) : String(val, 1));
+ (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, accentCol, 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 : 0xC618));
+ 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);
}
}
@@ -1839,28 +1665,24 @@ inline void animateBlackTransition(int8_t toPage = -1) {
sprTrans.fillSprite(TFT_BLACK);
if (toPage == 0)
- renderPage0ToSprite(sprTrans, 0, y_slice, slice_h);
+ renderClusterScreenToSprite(sprTrans, 0, y_slice, slice_h);
else if (toPage == 1)
renderPage1ToSprite(sprTrans, 0, y_slice, slice_h);
else if (toPage == 2)
renderPage2ToSprite(sprTrans, 0, y_slice, slice_h);
- else if (toPage == 3)
- renderPage3ToSprite(sprTrans, 0, y_slice, slice_h);
else
- renderPage4ToSprite(sprTrans, 0, y_slice, slice_h);
+ renderPage3ToSprite(sprTrans, 0, y_slice, slice_h);
- // Pagination indicator dots on top slice (5 dots for 5 pages)
+ // Pagination indicator dots on top slice (4 dots for 4 pages)
if (y_slice == 0) {
- sprTrans.fillCircle(132, 2, 2,
+ sprTrans.fillCircle(139, 2, 2,
(toPage == 0) ? getThemePrimaryColor() : 0x5AEB);
- sprTrans.fillCircle(146, 2, 2,
+ sprTrans.fillCircle(153, 2, 2,
(toPage == 1) ? getThemePrimaryColor() : 0x5AEB);
- sprTrans.fillCircle(160, 2, 2,
+ sprTrans.fillCircle(167, 2, 2,
(toPage == 2) ? getThemePrimaryColor() : 0x5AEB);
- sprTrans.fillCircle(174, 2, 2,
+ sprTrans.fillCircle(181, 2, 2,
(toPage == 3) ? getThemePrimaryColor() : 0x5AEB);
- sprTrans.fillCircle(188, 2, 2,
- (toPage == 4) ? getThemePrimaryColor() : 0x5AEB);
}
sprTrans.pushSprite(0, y_slice);
@@ -1915,26 +1737,22 @@ inline void animateFadeTransition(int8_t fromPage = -1, int8_t toPage = -1) {
sprFadeB.fillSprite(TFT_BLACK);
if (fromPage == 0)
- renderPage0ToSprite(sprTrans, 0, y_slice, slice_h);
+ renderClusterScreenToSprite(sprTrans, 0, y_slice, slice_h);
else if (fromPage == 1)
renderPage1ToSprite(sprTrans, 0, y_slice, slice_h);
else if (fromPage == 2)
renderPage2ToSprite(sprTrans, 0, y_slice, slice_h);
- else if (fromPage == 3)
- renderPage3ToSprite(sprTrans, 0, y_slice, slice_h);
else
- renderPage4ToSprite(sprTrans, 0, y_slice, slice_h);
+ renderPage3ToSprite(sprTrans, 0, y_slice, slice_h);
if (toPage == 0)
- renderPage0ToSprite(sprFadeB, 0, y_slice, slice_h);
+ renderClusterScreenToSprite(sprFadeB, 0, y_slice, slice_h);
else if (toPage == 1)
renderPage1ToSprite(sprFadeB, 0, y_slice, slice_h);
else if (toPage == 2)
renderPage2ToSprite(sprFadeB, 0, y_slice, slice_h);
- else if (toPage == 3)
- renderPage3ToSprite(sprFadeB, 0, y_slice, slice_h);
else
- renderPage4ToSprite(sprFadeB, 0, y_slice, slice_h);
+ renderPage3ToSprite(sprFadeB, 0, y_slice, slice_h);
uint16_t *pA = (uint16_t *)sprTrans.getPointer();
uint16_t *pB = (uint16_t *)sprFadeB.getPointer();
@@ -1964,19 +1782,17 @@ inline void animateFadeTransition(int8_t fromPage = -1, int8_t toPage = -1) {
}
}
- // Pagination indicator dots on top slice (5 dots for 5 pages)
+ // Pagination indicator dots on top slice (4 dots for 4 pages)
if (y_slice == 0) {
uint8_t activeDot = (alpha < 16) ? fromPage : toPage;
- sprTrans.fillCircle(132, 2, 2,
+ sprTrans.fillCircle(139, 2, 2,
(activeDot == 0) ? getThemePrimaryColor() : 0x5AEB);
- sprTrans.fillCircle(146, 2, 2,
+ sprTrans.fillCircle(153, 2, 2,
(activeDot == 1) ? getThemePrimaryColor() : 0x5AEB);
- sprTrans.fillCircle(160, 2, 2,
+ sprTrans.fillCircle(167, 2, 2,
(activeDot == 2) ? getThemePrimaryColor() : 0x5AEB);
- sprTrans.fillCircle(174, 2, 2,
+ sprTrans.fillCircle(181, 2, 2,
(activeDot == 3) ? getThemePrimaryColor() : 0x5AEB);
- sprTrans.fillCircle(188, 2, 2,
- (activeDot == 4) ? getThemePrimaryColor() : 0x5AEB);
}
sprTrans.pushSprite(0, y_slice);
@@ -1997,7 +1813,7 @@ inline void performScreenTransition(bool swipeLeft, int8_t fromPage = -1,
if (fromPage < 0)
fromPage = currentScreenPage;
if (toPage < 0)
- toPage = (fromPage + 1) % 5;
+ toPage = (fromPage + 1) % 4;
if (transitionType == 1) {
animateFadeTransition(fromPage, toPage);
} else {
@@ -2030,16 +1846,14 @@ void initScreen(bool redrawTFT) {
// Screen is ALREADY 100% rendered with zero flicker by the double-buffered
// transition engine! ONLY sync tracking variables so live updates continue
// seamlessly without redrawing or blanking!
- if (currentScreenPage == 1) {
+ if (currentScreenPage == 0) {
+ syncClusterTrackers();
+ } else if (currentScreenPage == 1) {
syncTriTempTrackers();
} else if (currentScreenPage == 2) {
syncGpsClockTrackers();
} else if (currentScreenPage == 3) {
- syncGpsNavTrackers();
- } else if (currentScreenPage == 4) {
- syncClusterTrackers();
- } else if (currentScreenPage == 0 && fullScreenCell == -1) {
- syncPage0Trackers();
+ syncGpsNavTrackers(true);
}
#ifdef SERIAL_DEBUG
Serial.printf(">>> [DB] initScreen (NO REDRAW) FINISHED OK (heap: %u)\n",
@@ -2052,11 +1866,11 @@ void initScreen(bool redrawTFT) {
old_data[i] = -999;
}
- if (fullScreenCell >= 0 && fullScreenCell < 4) {
+ if (currentScreenPage == 0) {
#ifdef SERIAL_DEBUG
- Serial.println(">>> [DB] initScreen: drawing fullScreenMeter");
+ Serial.println(">>> [DB] initScreen: drawing drawClusterScreen(0)");
#endif
- fullScreenMeter(fullScreenCell, pidInCell[fullScreenCell]);
+ drawClusterScreen(0);
} else if (currentScreenPage == 1) {
#ifdef SERIAL_DEBUG
Serial.println(">>> [DB] initScreen: drawing drawTriTempScreen(0)");
@@ -2072,27 +1886,6 @@ void initScreen(bool redrawTFT) {
Serial.println(">>> [DB] initScreen: drawing drawGpsNavScreen(0)");
#endif
drawGpsNavScreen(0);
- } else if (currentScreenPage == 4) {
-#ifdef SERIAL_DEBUG
- Serial.println(">>> [DB] initScreen: drawing drawClusterScreen(0)");
-#endif
- drawClusterScreen(0);
- } else {
-#ifdef SERIAL_DEBUG
- Serial.println(">>> [DB] initScreen: drawing Page 0 quadrants");
-#endif
- tft.fillScreen(TFT_BLACK); // clear screen
- for (uint8_t c = 0; c < 4; c++) {
- uint8_t pid = pidInCell[c];
- arcMeter4(c, pid);
- }
- drawQuadrantDividers();
- // Pagination indicator dots [● ○ ○ ○ ○] (Page 0 active)
- tft.fillCircle(132, 2, 2, getThemePrimaryColor());
- tft.fillCircle(146, 2, 2, 0x5AEB);
- tft.fillCircle(160, 2, 2, 0x5AEB);
- tft.fillCircle(174, 2, 2, 0x5AEB);
- tft.fillCircle(188, 2, 2, 0x5AEB);
}
if (currentScreenPage == 0 && fullScreenCell == -1) {
badgeEcoDrawn = false;
@@ -2232,10 +2025,8 @@ inline void updateGpsSpeedometer() {
last_valid_data[1] = spd;
int warn = warningValue[3].toInt();
- if (fullScreenCell == 1) {
- plotFullScreen(1, spd, warn, 0, 160, false, "km/h");
- } else if (currentScreenPage == 0 && fullScreenCell == -1) {
- plotArc4(1, spd, warn, 0, 160, false, "km/h");
+ if (currentScreenPage == 0) {
+ updateClusterScreen(false);
}
}
}
@@ -2420,13 +2211,7 @@ void updateMeter(uint8_t pidNo, String response) { // update parameter on screen
last_valid_data[pidNo] = data;
- if (fullScreenCell >= 0 && fullScreenCell < 4) {
- if (pidNo == fullScreenCell) {
- plotFullScreen(fullScreenCell, data, warn, min, max, digit, unit);
- }
- } else if (currentScreenPage == 0) {
- plotArc4(pidNo, data, warn, min, max, digit, unit);
- } else if (currentScreenPage == 4) {
+ if (currentScreenPage == 0) {
updateClusterScreen(false);
}
bt_message = ""; // reset
@@ -2656,7 +2441,7 @@ void handleArcAlarmFlash() {
if (fullScreenCell >= 0 && fullScreenCell < 4) {
uint8_t cell = fullScreenCell;
- if (last_valid_data[cell] != -999) {
+ if (last_valid_data[cell] > -900.0f) {
uint8_t pidIdx = pidInCell[cell];
int warn = warningValue[pidIdx].toInt();
float data = last_valid_data[cell];
@@ -2720,9 +2505,9 @@ void handleArcAlarmFlash() {
}
}
} else if (currentScreenPage == 0) {
- // 4 quadrants mode
+ // Cockpit Instrumental: check alarms for buzzer without drawing old quadrants
for (uint8_t cell = 0; cell < 4; cell++) {
- if (last_valid_data[cell] != -999) {
+ if (last_valid_data[cell] > -900.0f) {
uint8_t pidIdx = pidInCell[cell];
int warn = warningValue[pidIdx].toInt();
float data = last_valid_data[cell];
@@ -2731,65 +2516,13 @@ void handleArcAlarmFlash() {
bool isAlarmActive = alarmEnabled[pidIdx];
if (inRed && isAlarmActive)
anyInRedAlarm = true;
- bool digit = pidConfig[pidIdx][7].toInt();
- String numStr = String(data, digit);
- int x = cell4_x[cell];
- int y = cell4_y[cell];
-
- if (wasInRedAlarm[cell] && !inRed) {
- // Exited red alarm: restore steady arc, text, and icon
- wasInRedAlarm[cell] = false;
- drawPidIcon(x + 5, y + 3, pidConfig[pidIdx][2]);
- int min = pidConfig[pidIdx][4].toInt();
- int max = pidConfig[pidIdx][5].toInt();
- int newBlocks = calcMeterBlocks(data, min, max);
- for (uint8_t i = 0; i < newBlocks; i++) {
- drawQuadBarBlock(cell, i, col);
- }
- for (uint8_t i = newBlocks; i < NUM_BAR_BLOCKS; i++) {
- drawQuadBarBlock(cell, i, METER_INACTIVE_COLOR);
- }
- tft.loadFont(DSEG7_Modern_BoldItalic_34);
- tft.setTextColor(getThemeCellColor(cell), TFT_BLACK);
- tft.setTextPadding(114);
- tft.drawRightString(numStr.c_str(), x + 152, y + 74, 1);
- tft.setTextPadding(0);
- tft.unloadFont();
- } else if (inRed) {
- wasInRedAlarm[cell] = true;
- if (isAlarmActive) {
- if (toggleTick) {
- if (arcFlashVisible) {
- drawPidIcon(x + 5, y + 3, pidConfig[pidIdx][2]);
- } else {
- clearPidIcon(x + 5, y + 3, pidConfig[pidIdx][2]);
- }
- int min = pidConfig[pidIdx][4].toInt();
- int max = pidConfig[pidIdx][5].toInt();
- int newBlocks = calcMeterBlocks(data, min, max);
-
- if (arcFlashVisible) {
- for (uint8_t i = 0; i < newBlocks; i++) {
- drawQuadBarBlock(cell, i, TFT_RED);
- }
- for (uint8_t i = newBlocks; i < NUM_BAR_BLOCKS; i++) {
- drawQuadBarBlock(cell, i, METER_INACTIVE_COLOR);
- }
- } else {
- for (uint8_t i = 0; i < NUM_BAR_BLOCKS; i++) {
- drawQuadBarBlock(cell, i, TFT_BLACK);
- }
- }
- }
- }
- }
}
}
} else {
// Page 1 (Tri-Temperature screen): check alarm conditions for acoustic
// warning without touching screen graphics
for (uint8_t cell = 0; cell < 4; cell++) {
- if (last_valid_data[cell] != -999) {
+ if (last_valid_data[cell] > -900.0f) {
uint8_t pidIdx = pidInCell[cell];
int warn = warningValue[pidIdx].toInt();
float data = last_valid_data[cell];
diff --git a/sketch/obd2_gauge/obd2_gauge.ino b/sketch/obd2_gauge/obd2_gauge.ino
index 241a8ea..68ab684 100644
--- a/sketch/obd2_gauge/obd2_gauge.ino
+++ b/sketch/obd2_gauge/obd2_gauge.ino
@@ -301,16 +301,16 @@ int8_t currentScreenPage =
inline void setScreenPage(int8_t page) {
if (page < 0)
page = 0;
- if (page > 4)
- page = 4;
+ if (page > 3)
+ page = 3;
currentScreenPage = page;
pref.begin("setting", false);
pref.putChar("scr_page", page);
pref.end();
}
-float tempEngineCoolant = 88.0f; // Temperatura de refrigerante de motor (OBD2)
-float tempInterior = 22.5f; // Temperatura interior habitáculo (DS18B20 #1)
-float tempExterior = 16.8f; // Temperatura exterior ambiente (DS18B20 #2)
+float tempEngineCoolant = -999.0f; // Temperatura de refrigerante de motor (OBD2)
+float tempInterior = -999.0f; // Temperatura interior habitáculo (DS18B20 #1)
+float tempExterior = -999.0f; // Temperatura exterior ambiente (DS18B20 #2)
uint8_t dsSensorCount = 0; // Cantidad de sensores 1-Wire detectados
// User configuration here to change display >
@@ -344,12 +344,12 @@ uint8_t btDeviceCount = 0; // discovered bluetooth devices counter
esp_bd_addr_t client_addr = {0x00, 0x00, 0x00,
0x00, 0x00, 0x00}; // obdII mac addr
esp_bd_addr_t recent_client_addr = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // keep last btaddr in RTC memory
+ 0x6C, 0x62, 0x6D, 0x14, 0x08, 0xFD}; // Default to PC emulator MAC
const String client_name = "OBDII"; // adaptor name to search
esp_spp_sec_t sec_mask =
ESP_SPP_SEC_NONE; // or ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE to
// request pincode confirmation
-esp_spp_role_t role = ESP_SPP_ROLE_SLAVE; // or ESP_SPP_ROLE_MASTER
+esp_spp_role_t role = ESP_SPP_ROLE_MASTER; // Initiator is SPP MASTER
bool foundOBD2 = false;
BluetoothSerial BTSerial; // bluetooth serial device
@@ -548,8 +548,12 @@ volatile bool btConnectedTrigger = false;
volatile bool systemSleeping = false;
void btBackgroundTask(void *pvParameters) {
+ const esp_bd_addr_t EMU_ADDR = {0x6C, 0x62, 0x6D, 0x14, 0x08, 0xFD};
while (true) {
if (!systemSleeping && !foundOBD2) {
+ bool connected = false;
+
+ // 1. Intentar conectar a la direccion reciente guardada si es valida
bool hasRecentAddr = false;
for (uint8_t i = 0; i < 6; i++) {
if (recent_client_addr[i] != 0) {
@@ -563,29 +567,66 @@ void btBackgroundTask(void *pvParameters) {
vTaskDelay(pdMS_TO_TICKS(100));
if (BTSerial.connect(recent_client_addr, 0, sec_mask, role)) {
if (BTSerial.connected() && !systemSleeping) {
- sendATCommand("ATE0", 800);
- sendATCommand("ATL0", 800);
- sendATCommand("ATH0", 800);
- sendATCommand("ATAT1", 800);
- sendATCommand("ATSP1", 800);
- String r0100 = sendATCommand("0100", 2500);
- ignitionOn = (r0100.indexOf("41 00") != -1);
- foundOBD2 = true;
- prompt = true;
- bt_message = "";
- updateRearLedTheme();
- btConnectedTrigger =
- true; // Signal Core 1 (UI thread) to switch screen!
- } else {
- BTSerial.disconnect();
- setRearLedOff();
- foundOBD2 = false;
+ connected = true;
}
- } else {
- setRearLedOff();
- foundOBD2 = false;
}
}
+
+ // 2. Si fallo o no habia direccion, intentar conectar a la MAC del emulador PC
+ if (!connected && !systemSleeping) {
+ bool isRecentSameAsEmu = true;
+ for (uint8_t i = 0; i < 6; i++) {
+ if (recent_client_addr[i] != EMU_ADDR[i]) {
+ isRecentSameAsEmu = false;
+ break;
+ }
+ }
+ if (!isRecentSameAsEmu) {
+ setRearLedColor(0, 0, 200);
+ BTSerial.disconnect();
+ vTaskDelay(pdMS_TO_TICKS(100));
+ if (BTSerial.connect(const_cast(EMU_ADDR), 0, sec_mask, role)) {
+ if (BTSerial.connected() && !systemSleeping) {
+ connected = true;
+ memcpy(recent_client_addr, EMU_ADDR, 6);
+ pref.putBytes("recent_client", recent_client_addr, 6);
+ }
+ }
+ }
+ }
+
+ // 3. Si aun no conecto, buscar y conectar por nombre "OBDII" (inquiry scan)
+ if (!connected && !systemSleeping) {
+ setRearLedColor(0, 0, 200);
+ BTSerial.disconnect();
+ vTaskDelay(pdMS_TO_TICKS(100));
+ if (BTSerial.connect("OBDII")) {
+ if (BTSerial.connected() && !systemSleeping) {
+ connected = true;
+ }
+ }
+ }
+
+ // Si logro conectar con cualquiera de los metodos:
+ if (connected && !systemSleeping) {
+ sendATCommand("ATE0", 800);
+ sendATCommand("ATL0", 800);
+ sendATCommand("ATH0", 800);
+ sendATCommand("ATAT1", 800);
+ sendATCommand("ATSP1", 800);
+ String r0100 = sendATCommand("0100", 2500);
+ ignitionOn = (r0100.indexOf("41 00") != -1);
+ foundOBD2 = true;
+ prompt = true;
+ bt_message = "";
+ updateRearLedTheme();
+ btConnectedTrigger =
+ true; // Signal Core 1 (UI thread) to switch screen!
+ } else {
+ BTSerial.disconnect();
+ setRearLedOff();
+ foundOBD2 = false;
+ }
}
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(3500));
}
@@ -711,7 +752,7 @@ recent_client_addr : {0x00,0x00,0x00,0x00,0x00,0x00} array of bytes[6]
if (transitionType > 1)
transitionType = 0;
currentScreenPage = pref.getChar("scr_page", 0);
- if (currentScreenPage < 0 || currentScreenPage > 4)
+ if (currentScreenPage < 0 || currentScreenPage > 3)
currentScreenPage = 0;
if (brightnessIndex > 0) {
targetBacklight = BRIGHTNESS_PWM_TABLE[brightnessIndex];
@@ -853,6 +894,11 @@ void loop() {
updateGpsSpeedometer();
updateGpsIconAnim();
+ // Non-blocking live refresh for resting Page 0 (Cockpit Instrumental Deportivo)
+ if (currentScreenPage == 0 && fullScreenCell == -1) {
+ updateClusterScreen(false);
+ }
+
// Non-blocking live refresh for resting Page 2 (GPS Clock & Date)
if (currentScreenPage == 2 && fullScreenCell == -1) {
updateGpsClockScreen(false);
@@ -863,11 +909,6 @@ void loop() {
updateGpsNavScreen(false);
}
- // Non-blocking live refresh for resting Page 4 (Cockpit Instrumental Deportivo)
- if (currentScreenPage == 4 && fullScreenCell == -1) {
- updateClusterScreen(false);
- }
-
if (wokeFromSleep) {
wokeFromSleep = false;
lastActivityTime = millis();
@@ -927,10 +968,10 @@ void loop() {
// (disabled in full-screen mode)
if (!longPressHandled) {
if (fullScreenCell == -1 && liveDeltaX < -28 && abs(liveDeltaY) < 85) {
- // SWIPE LEFT: Transition to next page (0 -> 1 -> 2 -> 3 -> 4 -> 0)
+ // SWIPE LEFT: Transition to next page (0 -> 1 -> 2 -> 3 -> 0)
longPressHandled = true;
touchBeep();
- uint8_t nextPage = (currentScreenPage + 1) % 5;
+ uint8_t nextPage = (currentScreenPage + 1) % 4;
performScreenTransition(true, currentScreenPage, nextPage);
setScreenPage(nextPage);
initScreen(false); // Seamless: keep transition frame without blanking
@@ -940,11 +981,11 @@ void loop() {
lastActivityTime = millis();
} else if (fullScreenCell == -1 && liveDeltaX > 28 &&
abs(liveDeltaY) < 85) {
- // SWIPE RIGHT: Transition to previous page (0 -> 4 -> 3 -> 2 -> 1 -> 0)
+ // SWIPE RIGHT: Transition to previous page (0 -> 3 -> 2 -> 1 -> 0)
longPressHandled = true;
touchBeep();
uint8_t nextPage =
- (currentScreenPage == 0) ? 4 : (currentScreenPage - 1);
+ (currentScreenPage == 0) ? 3 : (currentScreenPage - 1);
performScreenTransition(false, currentScreenPage, nextPage);
setScreenPage(nextPage);
initScreen(false); // Seamless: keep transition frame without blanking
@@ -984,7 +1025,7 @@ void loop() {
// SWIPE LEFT
longPressHandled = true;
touchBeep();
- uint8_t nextPage = (currentScreenPage + 1) % 5;
+ uint8_t nextPage = (currentScreenPage + 1) % 4;
performScreenTransition(true, currentScreenPage, nextPage);
setScreenPage(nextPage);
initScreen(false); // Seamless: keep transition frame without blanking
@@ -997,7 +1038,7 @@ void loop() {
longPressHandled = true;
touchBeep();
uint8_t nextPage =
- (currentScreenPage == 0) ? 4 : (currentScreenPage - 1);
+ (currentScreenPage == 0) ? 3 : (currentScreenPage - 1);
performScreenTransition(false, currentScreenPage, nextPage);
setScreenPage(nextPage);
initScreen(false); // Seamless: keep transition frame without blanking
@@ -1010,16 +1051,8 @@ void loop() {
// Valid TAP detected!
touchBeep();
if (currentScreenPage == 0) {
- if (fullScreenCell == -1) {
- // Switch to full screen for tapped quadrant
- int quad = (lastTouchX < 160) ? ((lastTouchY < 120) ? 0 : 2)
- : ((lastTouchY < 120) ? 1 : 3);
- fullScreenCell = quad;
- } else {
- // Return to 4 quadrants view
- fullScreenCell = -1;
- }
- initScreen();
+ // Tap on Cockpit Instrumental: immediate cluster refresh
+ updateClusterScreen(true);
} else if (currentScreenPage == 1) {
// Tap on Tri-Temp screen: immediate sensor refresh
updateTriTempSensors(true);
@@ -1029,9 +1062,6 @@ void loop() {
} else if (currentScreenPage == 3) {
// Tap on GPS Navigation screen: immediate telemetry refresh
updateGpsNavScreen(true);
- } else if (currentScreenPage == 4) {
- // Tap on Cockpit Instrumental: immediate cluster refresh
- updateClusterScreen(true);
}
waitForTouchRelease();
lastActivityTime = millis();
@@ -1075,6 +1105,12 @@ void loop() {
if (!isBtConnected && wasBtConnected) {
wasBtConnected = false;
currentRpm = 0.0f;
+ tempEngineCoolant = -999.0f;
+ for (uint8_t c = 0; c < 4; c++) {
+ if (c != 1 || !isGpsSpeedValid()) {
+ last_valid_data[c] = -999.0f;
+ }
+ }
ignitionOn = false;
foundOBD2 = false;
uint8_t fallbackPage = isGpsSynced() ? 2 : 1;
@@ -1089,14 +1125,16 @@ void loop() {
if (!isBtConnected) {
currentRpm = 0.0f;
- ignitionOn = false;
- if (currentScreenPage == 0 || fullScreenCell != -1) {
- for (uint8_t c = 0; c < 4; c++) {
- if (c == 1 && isGpsSpeedValid())
- continue; // Mantener la velocidad de GPS en el velocimetro
- plotNoData(c);
+ tempEngineCoolant = -999.0f;
+ for (uint8_t c = 0; c < 4; c++) {
+ if (c != 1 || !isGpsSpeedValid()) {
+ last_valid_data[c] = -999.0f;
}
}
+ ignitionOn = false;
+ if (currentScreenPage == 0) {
+ updateClusterScreen(false);
+ }
foundOBD2 =
false; // Core 0 background task will handle reconnection smoothly!
return;
diff --git a/sketch/obd2_gauge/rainbow_text.h b/sketch/obd2_gauge/rainbow_text.h
index 4b54142..8857d53 100644
--- a/sketch/obd2_gauge/rainbow_text.h
+++ b/sketch/obd2_gauge/rainbow_text.h
@@ -2,6 +2,7 @@
#include
#include
#include "DSEG7_Modern_BoldItalic_34.h"
+#include "DSEG7_Modern_BoldItalic_44.h"
#include "DSEG7_Modern_BoldItalic_80.h"
extern bool isRainbowTheme();
@@ -132,9 +133,9 @@ inline void drawDseg7RightString34(T &dst, const char *str, int rightX, int y0,
int totalW = getDseg7StringWidth34(str);
int startX = rightX - totalW;
- if (padding > totalW) {
- int clearW = padding - totalW;
- int clearX = rightX - padding;
+ if (padding > 0) {
+ int clearW = (padding > totalW) ? padding : totalW;
+ int clearX = rightX - clearW;
if (clearX < 0) {
clearW += clearX;
clearX = 0;
@@ -154,10 +155,93 @@ inline void drawDseg7RightString34(T &dst, const char *str, int rightX, int y0,
}
}
+// Measure width in pixels of a string rendered in DSEG7 44pt (tight tracking adv=31)
+inline int getDseg7StringWidth44(const char *str) {
+ int w = 0;
+ for (int i = 0; str[i] != '\0'; i++) {
+ char c = str[i];
+ if (c == '.') w += 10;
+ else if (c == ' ') w += 31;
+ else w += 31;
+ }
+ return w + 5;
+}
+
+// Draw a single DSEG7 44pt character directly to destination
+template
+inline void drawDseg7Digit44(T &dst, int x0, int y0, char c, uint16_t color,
+ uint16_t bgCol, int clip_h = 240, bool rainbow = false, int screen_offset_x = 0) {
+ uint32_t offset = 0;
+ uint8_t w = 36;
+ if (c >= '0' && c <= '9') {
+ offset = 388 + (c - '0') * 1584;
+ } else if (c == '.') {
+ offset = 16228;
+ w = 10;
+ } else if (c == '-') {
+ offset = 16668;
+ } else {
+ return;
+ }
+
+ dst.startWrite();
+ for (int dy = 0; dy < 44; dy++) {
+ int curY = y0 + dy;
+ if (curY < 0 || curY >= clip_h)
+ continue;
+ const uint8_t *row = &DSEG7_Modern_BoldItalic_44[offset + dy * w];
+ for (int dx = 0; dx < w; dx++) {
+ int curX = x0 + dx;
+ if (curX < 0 || curX >= 320)
+ continue;
+ uint8_t a = pgm_read_byte(row + dx);
+ if (a == 0)
+ continue;
+ uint16_t fg = rainbow ? getRainbowScreenColor(curX + screen_offset_x, curY) : color;
+ if (a >= 240) {
+ dst.drawPixel(curX, curY, fg);
+ } else {
+ dst.drawPixel(curX, curY, dst.alphaBlend(a, fg, bgCol));
+ }
+ }
+ }
+ dst.endWrite();
+}
+
+// Draw a DSEG7 44pt string right-aligned with automatic background padding and rainbow support
+template
+inline void drawDseg7RightString44(T &dst, const char *str, int rightX, int y0,
+ uint16_t color, uint16_t bgCol, int padding = 0,
+ int clip_h = 240, bool rainbow = false, int screen_offset_x = 0) {
+ int totalW = getDseg7StringWidth44(str);
+ int startX = rightX - totalW;
+
+ if (padding > 0) {
+ int clearW = (padding > totalW) ? padding : totalW;
+ int clearX = rightX - clearW;
+ if (clearX < 0) {
+ clearW += clearX;
+ clearX = 0;
+ }
+ if (clearW > 0) {
+ dst.fillRect(clearX, y0, clearW, 44, bgCol);
+ }
+ }
+
+ int curX = startX;
+ for (int i = 0; str[i] != '\0'; i++) {
+ char c = str[i];
+ uint16_t charColor = isYapaTheme() ? getYapaColor(i) : color;
+ drawDseg7Digit44(dst, curX, y0, c, charColor, bgCol, clip_h, rainbow, screen_offset_x);
+ if (c == '.') curX += 10;
+ else curX += 31;
+ }
+}
+
// Draw a single DSEG7 80pt digit directly to destination with rainbow gradient
template
inline void drawDseg7Digit80(T &dst, int x0, int y0, uint8_t digit,
- uint16_t color, uint16_t bgCol, int clip_h = 240, bool rainbow = false) {
+ uint16_t color, uint16_t bgCol, int clip_h = 240, bool rainbow = false, int screen_offset_x = 0) {
if (digit > 9) return;
const uint8_t *bmp = &DSEG7_Modern_BoldItalic_80[388 + digit * 5200];
dst.startWrite();
@@ -173,7 +257,7 @@ inline void drawDseg7Digit80(T &dst, int x0, int y0, uint8_t digit,
continue;
uint8_t a = pgm_read_byte(row + dx);
if (a == 0) continue;
- uint16_t fg = rainbow ? getRainbowScreenColor(curX, curY) : color;
+ uint16_t fg = rainbow ? getRainbowScreenColor(curX + screen_offset_x, curY) : color;
if (a >= 240) {
dst.drawPixel(curX, curY, fg);
} else {
@@ -188,16 +272,17 @@ inline void drawDseg7Digit80(T &dst, int x0, int y0, uint8_t digit,
template
inline void drawDseg7RightString80(T &dst, const char *str, int rightX, int y0,
uint16_t color, uint16_t bgCol, int padding = 0,
- int clip_h = 240, bool rainbow = false) {
- // Ancho aproximado por digito 80pt: advance de 65px
- int len = 0;
- for (int i = 0; str[i] != '\0'; i++) len++;
- int totalW = len * 65;
+ int clip_h = 240, bool rainbow = false, int screen_offset_x = 0) {
+ int totalW = 0;
+ for (int i = 0; str[i] != '\0'; i++) {
+ if (str[i] == '-') totalW += 50;
+ else totalW += 65;
+ }
int startX = rightX - totalW;
- if (padding > totalW) {
- int clearW = padding - totalW;
- int clearX = rightX - padding;
+ if (padding > 0) {
+ int clearW = (padding > totalW) ? padding : totalW;
+ int clearX = rightX - clearW;
if (clearX < 0) {
clearW += clearX;
clearX = 0;
@@ -212,15 +297,159 @@ inline void drawDseg7RightString80(T &dst, const char *str, int rightX, int y0,
char c = str[i];
uint16_t charColor = isYapaTheme() ? getYapaColor(i) : color;
if (c >= '0' && c <= '9') {
- drawDseg7Digit80(dst, curX, y0, c - '0', charColor, bgCol, clip_h, rainbow);
+ drawDseg7Digit80(dst, curX, y0, c - '0', charColor, bgCol, clip_h, rainbow, screen_offset_x);
+ curX += 65;
} else if (c == '-') {
- // Si es '-', dibujamos barra horizontal central de DSEG7
- dst.fillRect(curX + 10, y0 + 36, 45, 8, rainbow ? getRainbowScreenColor(curX + 32, y0 + 40) : charColor);
+ for (int dy = 0; dy < 6; dy++) {
+ int py = y0 + 37 + dy;
+ if (py >= 0 && py < clip_h) {
+ int startDx = 11 - dy / 2;
+ dst.fillRect(curX + startDx, py, 28, 1, rainbow ? getRainbowScreenColor(curX + screen_offset_x + startDx + 14, py) : charColor);
+ }
+ }
+ curX += 50;
+ } else {
+ curX += 65;
}
- curX += 65;
}
}
+// Fast zero-flicker line-buffered DSEG7 44pt renderer (direct to TFT, NO heap allocation!)
+template
+inline void drawDseg7RightFast44(T &dst, const char *str, int rightX, int y0,
+ uint16_t color, uint16_t bgCol, int minTotalW = 0,
+ 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;
+
+ uint16_t lineBuf[160];
+
+ dst.startWrite();
+ for (int dy = 0; dy < 44; dy++) {
+ int curY = y0 + dy;
+ if (curY < 0 || curY >= 240) continue;
+
+ for (int i = 0; i < renderW; i++) {
+ lineBuf[i] = bgCol;
+ }
+
+ int curX = textOffsetX;
+ for (int idx = 0; str[idx] != '\0'; idx++) {
+ char c = str[idx];
+ uint32_t offset = 0;
+ uint8_t w = 36;
+ uint8_t adv = 31;
+ if (c >= '0' && c <= '9') {
+ offset = 388 + (c - '0') * 1584;
+ } else if (c == '.') {
+ offset = 16228;
+ w = 10;
+ adv = 10;
+ } else if (c == '-') {
+ offset = 16668;
+ } else {
+ curX += adv;
+ continue;
+ }
+
+ const uint8_t *row = &DSEG7_Modern_BoldItalic_44[offset + dy * w];
+ uint16_t charColor = isYapaTheme() ? getYapaColor(idx) : color;
+
+ for (int dx = 0; dx < w; dx++) {
+ int bufX = curX + dx;
+ if (bufX < 0 || bufX >= renderW) continue;
+ uint8_t a = pgm_read_byte(row + dx);
+ if (a == 0) continue;
+ int screenX = startX + bufX;
+ uint16_t fg = rainbow ? getRainbowScreenColor(screenX, curY) : charColor;
+ if (a >= 240) {
+ lineBuf[bufX] = fg;
+ } else {
+ lineBuf[bufX] = dst.alphaBlend(a, fg, bgCol);
+ }
+ }
+ curX += adv;
+ }
+
+ dst.pushImage(startX, curY, renderW, 1, lineBuf);
+ }
+ dst.endWrite();
+}
+
+// Fast zero-flicker line-buffered DSEG7 80pt renderer (direct to TFT, NO heap allocation!)
+template
+inline void drawDseg7RightFast80(T &dst, const char *str, int rightX, int y0,
+ uint16_t color, uint16_t bgCol, int minTotalW = 0,
+ bool rainbow = false) {
+ int totalW = 0;
+ for (int i = 0; str[i] != '\0'; i++) {
+ if (str[i] == '-') totalW += 50;
+ 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;
+ }
+ int textOffsetX = renderW - totalW;
+
+ uint16_t lineBuf[200];
+
+ dst.startWrite();
+ for (int dy = 0; dy < 80; dy++) {
+ int curY = y0 + dy;
+ if (curY < 0 || curY >= 240) continue;
+
+ for (int i = 0; i < renderW; i++) {
+ lineBuf[i] = bgCol;
+ }
+
+ int curX = textOffsetX;
+ for (int idx = 0; str[idx] != '\0'; idx++) {
+ char c = str[idx];
+ uint16_t charColor = isYapaTheme() ? getYapaColor(idx) : color;
+ if (c >= '0' && c <= '9') {
+ const uint8_t *bmp = &DSEG7_Modern_BoldItalic_80[388 + (c - '0') * 5200 + dy * 65];
+ for (int dx = 0; dx < 65; dx++) {
+ int bufX = curX + dx;
+ if (bufX < 0 || bufX >= renderW) continue;
+ uint8_t a = pgm_read_byte(bmp + dx);
+ if (a == 0) continue;
+ int screenX = startX + bufX;
+ uint16_t fg = rainbow ? getRainbowScreenColor(screenX, curY) : charColor;
+ if (a >= 240) {
+ lineBuf[bufX] = fg;
+ } else {
+ lineBuf[bufX] = dst.alphaBlend(a, fg, bgCol);
+ }
+ }
+ curX += 65;
+ } else if (c == '-') {
+ if (dy >= 37 && dy < 43) {
+ int startDx = 11 - (dy - 37) / 2;
+ for (int dx = startDx; dx < startDx + 28; dx++) {
+ int bufX = curX + dx;
+ if (bufX >= 0 && bufX < renderW) {
+ lineBuf[bufX] = rainbow ? getRainbowScreenColor(startX + bufX, curY) : charColor;
+ }
+ }
+ }
+ curX += 50;
+ } else {
+ curX += 65;
+ }
+ }
+
+ dst.pushImage(startX, curY, renderW, 1, lineBuf);
+ }
+ dst.endWrite();
+}
+
// Apply spectral rainbow coloring to any pre-rendered text sprite with 100% anti-aliasing preservation
inline void applyRainbowToSprite(TFT_eSprite &s, int screenX, int screenY, uint16_t bgCol = 0x10A2) {
if (!s.created()) return;
diff --git a/sketch/obd2_gauge/tri_temp.h b/sketch/obd2_gauge/tri_temp.h
index 964c27a..41c3e0b 100644
--- a/sketch/obd2_gauge/tri_temp.h
+++ b/sketch/obd2_gauge/tri_temp.h
@@ -47,6 +47,11 @@ static TFT_eSprite sprTriTitle[3] = { TFT_eSprite(&tft), TFT_eSprite(&tft), TFT_
static bool sprTriTitleReady = false;
inline void invalidateTriTempTitles() {
+ for (int i = 0; i < 3; i++) {
+ if (sprTriTitle[i].created()) {
+ sprTriTitle[i].deleteSprite();
+ }
+ }
sprTriTitleReady = false;
}
@@ -167,7 +172,12 @@ inline void draw7SegDegreeC(TFT_eSPI &dst, int bx, int by, uint16_t color = 0xC6
if (px < 0 || px >= dst.width()) continue;
uint8_t a = pgm_read_byte(&s_dsegUnitAlpha[rowOffset + i]);
if (a > 8) {
- uint16_t curCol = rainbow ? getRainbowScreenColor(px, py) : color;
+ uint16_t curCol = color;
+ if (rainbow) {
+ curCol = getRainbowScreenColor(px, py);
+ } else if (isYapaTheme()) {
+ curCol = (i < 12) ? getYapaColor(0) : getYapaColor(1);
+ }
uint16_t pixCol = (a >= 250) ? curCol : fastBlend(a, curCol, bgCol);
dst.drawPixel(px, py, pixCol);
}
@@ -247,13 +257,13 @@ inline void drawTriTempCard(uint8_t cardIdx, float val, int x_offset = 0) {
else accentCol = TFT_RED;
} else if (cardIdx == 1) {
titleStr = "INTERIOR";
- subStr = (dsSensorCount > 0) ? "DS18B20 SENSOR 1 (GPIO 22)" : "DS18B20 SENSOR 1";
+ subStr = (dsSensorCount > 0) ? "DS18B20 SENSOR 1 (GPIO 22)" : "DS18B20 NO DETECTADO";
minRange = 10.0f;
maxRange = 40.0f;
accentCol = isRainbowTheme() ? (uint16_t)0x07E0 : (isYapaTheme() ? getYapaColor(1) : getThemeAccentColor());
} else {
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)" : "DS18B20 NO DETECTADO";
minRange = -10.0f;
maxRange = 50.0f;
accentCol = isRainbowTheme() ? (uint16_t)0xFD20 : (isYapaTheme() ? getYapaColor(2) : getThemePrimaryColor());
@@ -298,7 +308,7 @@ inline void drawTriTempCard(uint8_t cardIdx, float val, int x_offset = 0) {
tft.unloadFont();
}
- tft.setTextColor(0x8410, cardBg); // Soft gray
+ tft.setTextColor(accentCol, cardBg);
tft.drawString(subStr, cardX + 16, cardY + 32, 1);
}
@@ -310,34 +320,35 @@ inline void drawTriTempCard(uint8_t cardIdx, float val, int x_offset = 0) {
float ratio = (val - minRange) / (maxRange - minRange);
if (ratio < 0.0f) ratio = 0.0f;
if (ratio > 1.0f) ratio = 1.0f;
- int fillW = (int)(barW * ratio);
- if (fillW < 4) fillW = 4;
+ int fillW = (val <= -900.0f) ? 0 : (int)(barW * ratio);
+ if (fillW < 4 && val > -900.0f) fillW = 4;
if (x_offset == 0) {
- drawTriTempBar(barX, barY, fillW, accentCol);
+ drawTriTempBar(barX, barY, fillW, (val <= -900.0f) ? (uint16_t)0x18C3 : accentCol);
oldTriFillW[cardIdx] = fillW;
oldTriAccentCol[cardIdx] = accentCol;
} else {
tft.fillRoundRect(barX, barY, barW, 8, 3, 0x18C3);
- tft.fillRoundRect(barX, barY, fillW, 8, 3, accentCol);
+ if (fillW > 0) {
+ tft.fillRoundRect(barX, barY, fillW, 8, 3, accentCol);
+ }
}
// 4. Digital Number using DSEG7 Modern Bold Italic 34pt
String numStr;
+ uint16_t numCol = accentCol;
if (val <= -900.0f) {
numStr = "---";
- } else if (isInt) {
- numStr = String((int)val);
} else {
- numStr = String(val, 1);
+ numStr = String((int)round(val));
}
bool rainbowTxt = isRainbowTheme() && (accentCol != TFT_RED);
- drawDseg7RightString34(tft, numStr.c_str(), cardX + 262, cardY + 20, accentCol, cardBg, 100, 240, rainbowTxt);
+ drawDseg7RightString34(tft, numStr.c_str(), cardX + 262, cardY + 20, numCol, cardBg, 100, 240, rainbowTxt);
// Unit "°C" in authentic digital 7-segment italic typography matching numbers
bool rainbowUnit = isRainbowTheme() && (accentCol != TFT_RED);
- uint16_t unitCol = rainbowUnit ? getRainbowScreenColor(cardX + 268, cardY + 22) : (isYapaTheme() && accentCol != TFT_RED ? getYapaColor(numStr.length()) : (accentCol == TFT_RED ? TFT_RED : 0xC618));
+ uint16_t unitCol = rainbowUnit ? getRainbowScreenColor(cardX + 268, cardY + 22) : (isYapaTheme() && accentCol != TFT_RED ? getYapaColor(numStr.length()) : (accentCol == TFT_RED ? TFT_RED : accentCol));
draw7SegDegreeC(tft, cardX + 268, cardY + 22, unitCol, cardBg, rainbowUnit);
}
@@ -388,7 +399,7 @@ inline void updateTriTempCardValue(uint8_t cardIdx, float val, float oldVal) {
sX += cw;
}
} else {
- tft.setTextColor(0x8410, cardBg);
+ tft.setTextColor(accentCol, cardBg);
tft.drawString(subStr, cardX + 16, cardY + 32, 1);
}
}
@@ -420,27 +431,26 @@ inline void updateTriTempCardValue(uint8_t cardIdx, float val, float oldVal) {
float ratio = (val - minRange) / (maxRange - minRange);
if (ratio < 0.0f) ratio = 0.0f;
if (ratio > 1.0f) ratio = 1.0f;
- int fillW = (int)(barW * ratio);
- if (fillW < 4) fillW = 4;
+ int fillW = (val <= -900.0f) ? 0 : (int)(barW * ratio);
+ if (fillW < 4 && val > -900.0f) fillW = 4;
if (fillW != oldTriFillW[cardIdx] || accentCol != oldTriAccentCol[cardIdx]) {
- drawTriTempBar(barX, barY, fillW, accentCol);
+ drawTriTempBar(barX, barY, fillW, (val <= -900.0f) ? (uint16_t)0x18C3 : accentCol);
oldTriFillW[cardIdx] = fillW;
oldTriAccentCol[cardIdx] = accentCol;
}
// 3. Digital Number update using DSEG7 Modern Bold Italic 34pt (setTextPadding prevents flicker)
String numStr;
+ uint16_t numCol = accentCol;
if (val <= -900.0f) {
numStr = "---";
- } else if (isInt) {
- numStr = String((int)val);
} else {
- numStr = String(val, 1);
+ numStr = String((int)round(val));
}
bool rainbowTxt = isRainbowTheme() && (accentCol != TFT_RED);
- drawDseg7RightString34(tft, numStr.c_str(), cardX + 262, cardY + 20, accentCol, cardBg, 100, 240, rainbowTxt);
+ drawDseg7RightString34(tft, numStr.c_str(), cardX + 262, cardY + 20, numCol, cardBg, 100, 240, rainbowTxt);
}
// Synchronize tracking variables without touching TFT display (zero flicker)
@@ -463,8 +473,8 @@ inline void syncTriTempTrackers() {
float ratio = (vals[i] - minR[i]) / (maxR[i] - minR[i]);
if (ratio < 0.0f) ratio = 0.0f;
if (ratio > 1.0f) ratio = 1.0f;
- int fillW = (int)(120 * ratio);
- if (fillW < 4) fillW = 4;
+ int fillW = (vals[i] <= -900.0f) ? 0 : (int)(120 * ratio);
+ if (fillW < 4 && vals[i] > -900.0f) fillW = 4;
oldTriFillW[i] = fillW;
uint16_t acc = defAcc[i];
@@ -492,12 +502,11 @@ inline void drawTriTempScreen(int x_offset = 0) {
syncTriTempTrackers();
- // Draw Page Indicator: [○ ● ○ ○ ○] (Page 2 of 5 active)
- tft.fillCircle(132 + x_offset, 2, 2, 0x5AEB); // Page 0 inactive
- tft.fillCircle(146 + x_offset, 2, 2, getThemePrimaryColor()); // Page 1 active
- tft.fillCircle(160 + x_offset, 2, 2, 0x5AEB); // Page 2 inactive
- tft.fillCircle(174 + x_offset, 2, 2, 0x5AEB); // Page 3 inactive
- tft.fillCircle(188 + x_offset, 2, 2, 0x5AEB); // Page 4 inactive
+ // Draw Page Indicator: [○ ● ○ ○] (Page 1 of 4 active)
+ tft.fillCircle(139 + x_offset, 2, 2, 0x5AEB); // Page 0 inactive
+ tft.fillCircle(153 + x_offset, 2, 2, getThemePrimaryColor()); // Page 1 active
+ tft.fillCircle(167 + x_offset, 2, 2, 0x5AEB); // Page 2 inactive
+ tft.fillCircle(181 + x_offset, 2, 2, 0x5AEB); // Page 3 inactive
}
// Update coolant reading dynamically without redrawing entire screen
@@ -529,23 +538,25 @@ inline void updateTriTempSensors(bool forceRedraw = false) {
if (dsSensorCount > 0) {
float t1 = dsSensors.getTempCByIndex(0);
- if (t1 > -50.0f && t1 < 100.0f) {
+ if (t1 > -50.0f && t1 < 100.0f && t1 != 85.0f && t1 != -127.0f) {
tempInterior = t1;
+ } else {
+ tempInterior = -999.0f;
}
if (dsSensorCount > 1) {
float t2 = dsSensors.getTempCByIndex(1);
- if (t2 > -50.0f && t2 < 100.0f) {
+ if (t2 > -50.0f && t2 < 100.0f && t2 != 85.0f && t2 != -127.0f) {
tempExterior = t2;
+ } else {
+ tempExterior = -999.0f;
}
} else {
- // Demo exterior offset if only 1 physical sensor connected
- tempExterior = tempInterior - 4.5f;
+ tempExterior = -999.0f;
}
dsSensors.requestTemperatures(); // non-blocking next reading request
} else {
- // Simulated demo readings if sensors not yet plugged in
- tempInterior = 22.4f + 0.3f * sin(millis() / 4000.0f);
- tempExterior = 16.8f + 0.2f * cos(millis() / 5000.0f);
+ tempInterior = -999.0f;
+ tempExterior = -999.0f;
}
if (currentScreenPage == 1 && fullScreenCell == -1) {
@@ -557,11 +568,15 @@ inline void updateTriTempSensors(bool forceRedraw = false) {
oldTriInterior = tempInterior;
oldTriExterior = tempExterior;
} else {
- if (forceRedraw || abs(tempInterior - oldTriInterior) >= 0.1f) {
+ int curI = (tempInterior > -900.0f) ? (int)round(tempInterior) : -999;
+ int oldI = (oldTriInterior > -900.0f) ? (int)round(oldTriInterior) : -999;
+ if (forceRedraw || curI != oldI) {
updateTriTempCardValue(1, tempInterior, oldTriInterior);
oldTriInterior = tempInterior;
}
- if (forceRedraw || abs(tempExterior - oldTriExterior) >= 0.1f) {
+ int curE = (tempExterior > -900.0f) ? (int)round(tempExterior) : -999;
+ int oldE = (oldTriExterior > -900.0f) ? (int)round(oldTriExterior) : -999;
+ if (forceRedraw || curE != oldE) {
updateTriTempCardValue(2, tempExterior, oldTriExterior);
oldTriExterior = tempExterior;
}