diff --git a/sketch/obd2_gauge/cluster_screen.h b/sketch/obd2_gauge/cluster_screen.h index dc5b727..e27f07e 100644 --- a/sketch/obd2_gauge/cluster_screen.h +++ b/sketch/obd2_gauge/cluster_screen.h @@ -100,7 +100,7 @@ inline void drawClusterBaseCards(int x_offset = 0) { // 1. TACHOMETER CARD (x: 8, y: 8, w: 304, h: 58) tft.fillRoundRect(8 + x_offset, 8, 304, 58, 5, cardBg); tft.drawRoundRect(8 + x_offset, 8, 304, 58, 5, border); - tft.fillRoundRect(14 + x_offset, 14, 4, 46, 2, cRpm); + tft.drawRect(14 + x_offset, 14, 4, 46, cRpm); // Tachometer triangle VU-meter inactive background slots (14 steps, ramp h: 5..31 px, x: 24..106) for (int s = 0; s < 14; s++) { int barX = 24 + s * 6 + x_offset; @@ -114,20 +114,20 @@ inline void drawClusterBaseCards(int x_offset = 0) { // 2. SPEEDOMETER CARD (x: 8, y: 70, w: 304, h: 100) tft.fillRoundRect(8 + x_offset, 70, 304, 100, 5, cardBg); tft.drawRoundRect(8 + x_offset, 70, 304, 100, 5, border); - tft.fillRoundRect(14 + x_offset, 78, 4, 84, 2, cSpd); + tft.drawRect(14 + x_offset, 78, 4, 84, cSpd); // Large anti-aliased "KM/H" (Chakra Petch Bold Italic) at far right (baseline at y = 160) drawKmH_AA(tft, 198 + x_offset, 132, cSpd, cardBg, 240, isRainbowTheme()); // 3. COOLANT TEMP CARD (x: 8, y: 174, w: 148, h: 62) tft.fillRoundRect(8 + x_offset, 174, 148, 62, 5, cardBg); tft.drawRoundRect(8 + x_offset, 174, 148, 62, 5, border); - tft.fillRoundRect(14 + x_offset, 182, 4, 46, 2, cCool); + tft.drawRect(14 + x_offset, 182, 4, 46, cCool); draw7SegDegreeC(tft, 118 + x_offset, 201, cCool, cardBg, isRainbowTheme()); // 4. BATTERY VOLTAGE CARD (x: 164, y: 174, w: 148, h: 62) tft.fillRoundRect(164 + x_offset, 174, 148, 62, 5, cardBg); tft.drawRoundRect(164 + x_offset, 174, 148, 62, 5, border); - tft.fillRoundRect(170 + x_offset, 182, 4, 46, 2, cVolt); + tft.drawRect(170 + x_offset, 182, 4, 46, cVolt); drawVolt_AA(tft, 286 + x_offset, 202, cVolt, cardBg, 240, isRainbowTheme()); } @@ -277,14 +277,14 @@ inline void updateClusterScreen(bool forceRedraw) { if (coolCol != oldClusterCoolCol || forceRedraw) { oldClusterCoolCol = coolCol; - tft.fillRoundRect(14, 182, 4, 46, 2, coolCol); + tft.drawRect(14, 182, 4, 46, coolCol); draw7SegDegreeC(tft, 118, 201, coolCol, cardBg, rainbowTxt && (coolCol != TFT_RED)); } } else { drawDseg7RightFast44(tft, "---", 114, 183, cCool, cardBg, 90, isRainbowTheme()); if (cCool != oldClusterCoolCol || forceRedraw) { oldClusterCoolCol = cCool; - tft.fillRoundRect(14, 182, 4, 46, 2, cCool); + tft.drawRect(14, 182, 4, 46, cCool); draw7SegDegreeC(tft, 118, 201, cCool, cardBg, isRainbowTheme()); } } @@ -305,14 +305,14 @@ inline void updateClusterScreen(bool forceRedraw) { if (voltCol != oldClusterVoltCol || forceRedraw) { oldClusterVoltCol = voltCol; - tft.fillRoundRect(170, 182, 4, 46, 2, voltCol); + tft.drawRect(170, 182, 4, 46, voltCol); drawVolt_AA(tft, 286, 202, voltCol, cardBg, 240, rainbowTxt); } } else { drawDseg7RightFast44(tft, "---", 282, 183, cVolt, cardBg, 106, isRainbowTheme()); if (cVolt != oldClusterVoltCol || forceRedraw) { oldClusterVoltCol = cVolt; - tft.fillRoundRect(170, 182, 4, 46, 2, cVolt); + tft.drawRect(170, 182, 4, 46, cVolt); drawVolt_AA(tft, 286, 202, cVolt, cardBg, 240, isRainbowTheme()); } } @@ -345,7 +345,7 @@ inline void renderClusterScreenToSprite(TFT_eSprite &s, int x_offset, int y_slic if (c0Y < slice_h && c0Y + 58 > 0) { s.fillRoundRect(8, c0Y, 304, 58, 5, cardBg); s.drawRoundRect(8, c0Y, 304, 58, 5, border); - s.fillRoundRect(14, c0Y + 6, 4, 46, 2, cRpm); + s.drawRect(14, c0Y + 6, 4, 46, cRpm); float curRpmF = getClusterRpmVal(); int curRpm = (int)round(curRpmF); bool rainbowTxt = isRainbowTheme(); @@ -400,7 +400,7 @@ inline void renderClusterScreenToSprite(TFT_eSprite &s, int x_offset, int y_slic if (c1Y < slice_h && c1Y + 100 > 0) { s.fillRoundRect(8, c1Y, 304, 100, 5, cardBg); s.drawRoundRect(8, c1Y, 304, 100, 5, border); - s.fillRoundRect(14, c1Y + 8, 4, 84, 2, cSpd); + s.drawRect(14, c1Y + 8, 4, 84, cSpd); bool isGps = isClusterSpeedFromGps(); if (isGps) { @@ -429,7 +429,7 @@ inline void renderClusterScreenToSprite(TFT_eSprite &s, int x_offset, int y_slic float curCoolF = getClusterCoolantVal(); int curCool = (curCoolF > -900.0f) ? (int)round(curCoolF) : -999; uint16_t coolCol = (curCool > -900) ? ((curCool >= 105) ? TFT_RED : ((curCool < 70) ? 0x05BF : cCool)) : cCool; - s.fillRoundRect(14, c2Y + 8, 4, 46, 2, coolCol); + s.drawRect(14, c2Y + 8, 4, 46, coolCol); if (curCool > -900) { String valStr = String(curCool); bool rainbowTxt = isRainbowTheme(); @@ -448,7 +448,7 @@ inline void renderClusterScreenToSprite(TFT_eSprite &s, int x_offset, int y_slic s.drawRoundRect(164, c3Y, 148, 62, 5, border); float curVolt = getClusterVoltVal(); uint16_t voltCol = (curVolt > 0.0f) ? ((curVolt < 11.8f || curVolt > 15.0f) ? TFT_RED : ((curVolt >= 13.5f) ? 0x07E0 : cVolt)) : cVolt; - s.fillRoundRect(170, c3Y + 8, 4, 46, 2, voltCol); + s.drawRect(170, c3Y + 8, 4, 46, voltCol); if (curVolt > 0.0f) { String voltStr = String(curVolt, 1); bool rainbowTxt = isRainbowTheme(); diff --git a/sketch/obd2_gauge/gps_clock.h b/sketch/obd2_gauge/gps_clock.h index 75e6d13..bd4378f 100644 --- a/sketch/obd2_gauge/gps_clock.h +++ b/sketch/obd2_gauge/gps_clock.h @@ -128,7 +128,7 @@ inline void drawDateCardContent(int cardX, int cardY, int cardW, int cardH, String dayStr = (gpsDay < 10) ? "0" + String(gpsDay) : String(gpsDay); // Left vertical accent bar - tft.fillRoundRect(cardX + 6, cardY + 8, 4, cardH - 16, 2, accCol); + tft.drawRect(cardX + 6, cardY + 8, 4, cardH - 16, accCol); // --- CALENDAR BADGE (Left side: 80x82 px) --- int bx = cardX + 20; @@ -364,7 +364,7 @@ inline void drawGpsClockScreen(int x_offset = 0) { tft.drawRoundRect(clkX, clkY, clkW, clkH, 6, cardBorder); // Left vertical accent bar - tft.fillRoundRect(clkX + 6, clkY + 8, 4, clkH - 16, 2, clkAccCol); + tft.drawRect(clkX + 6, clkY + 8, 4, clkH - 16, clkAccCol); // Digits: Hours HH & Minutes MM with smooth rainbow gradient uint8_t h10 = (gpsHour / 10) % 10; @@ -480,7 +480,7 @@ inline void renderPage2ToSprite(TFT_eSprite &s, int x_offset, int y_slice, if (clkX + clkW > 0 && clkX < 320 && clkY + clkH > 0 && clkY < slice_h) { s.fillRoundRect(clkX, clkY, clkW, clkH, 6, cardBg); s.drawRoundRect(clkX, clkY, clkW, clkH, 6, cardBorder); - s.fillRoundRect(clkX + 6, clkY + 8, 4, clkH - 16, 2, clkAccCol); + s.drawRect(clkX + 6, clkY + 8, 4, clkH - 16, clkAccCol); // Colon dots during transition (italic 7-segment style with anti-aliasing) draw7SegColon(s, clkX, clkY, clkAccCol, cardBg, slice_h, isRainbowTheme()); @@ -518,7 +518,7 @@ inline void renderPage2ToSprite(TFT_eSprite &s, int x_offset, int y_slice, uint16_t yearCol = isRainbowTheme() ? (uint16_t)0xFFE0 : accCol; s.fillRoundRect(dateX, dateY, dateW, dateH, 6, cardBg); s.drawRoundRect(dateX, dateY, dateW, dateH, 6, cardBorder); - s.fillRoundRect(dateX + 6, dateY + 8, 4, dateH - 16, 2, dateAccCol); + s.drawRect(dateX + 6, dateY + 8, 4, dateH - 16, dateAccCol); // Badge container int bx = dateX + 20; diff --git a/sketch/obd2_gauge/gps_nav.h b/sketch/obd2_gauge/gps_nav.h index 8e6af6f..607f962 100644 --- a/sketch/obd2_gauge/gps_nav.h +++ b/sketch/obd2_gauge/gps_nav.h @@ -155,7 +155,7 @@ inline void drawGpsNavScreen(int x_offset = 0) { int cH1 = 102; tft.fillRoundRect(c0X, c0Y, cW, cH1, 6, cardBg); tft.drawRoundRect(c0X, c0Y, cW, cH1, 6, cardBorder); - tft.fillRoundRect(c0X + 6, c0Y + 8, 4, cH1 - 16, 2, c0Col); + tft.drawRect(c0X + 6, c0Y + 8, 4, cH1 - 16, c0Col); drawGpsNavTitle(tft, "ALTITUD", c0X + 16, c0Y + 8, 0x8410, cardBg, 240); drawGpsNavSubword(tft, "MSNM", c0X + 96, c0Y + 9, 0x9CD3, cardBg, 240); @@ -169,7 +169,7 @@ inline void drawGpsNavScreen(int x_offset = 0) { int c1Y = 19; tft.fillRoundRect(c1X, c1Y, cW, cH1, 6, cardBg); tft.drawRoundRect(c1X, c1Y, cW, cH1, 6, cardBorder); - tft.fillRoundRect(c1X + 6, c1Y + 8, 4, cH1 - 16, 2, c1Col); + tft.drawRect(c1X + 6, c1Y + 8, 4, cH1 - 16, c1Col); drawGpsNavTitle(tft, "RUMBO / BRUJULA", c1X + 16, c1Y + 8, 0x8410, cardBg, 240); @@ -192,7 +192,7 @@ inline void drawGpsNavScreen(int x_offset = 0) { int cH2 = 110; tft.fillRoundRect(c2X, c2Y, cW, cH2, 6, cardBg); tft.drawRoundRect(c2X, c2Y, cW, cH2, 6, cardBorder); - tft.fillRoundRect(c2X + 6, c2Y + 8, 4, cH2 - 16, 2, c2Col); + tft.drawRect(c2X + 6, c2Y + 8, 4, cH2 - 16, c2Col); drawGpsNavTitle(tft, "SATELITES GPS", c2X + 16, c2Y + 8, 0x8410, cardBg, 240); @@ -223,7 +223,7 @@ inline void drawGpsNavScreen(int x_offset = 0) { int c3Y = 125; tft.fillRoundRect(c3X, c3Y, cW, cH2, 6, cardBg); tft.drawRoundRect(c3X, c3Y, cW, cH2, 6, cardBorder); - tft.fillRoundRect(c3X + 6, c3Y + 8, 4, cH2 - 16, 2, c3Col); + tft.drawRect(c3X + 6, c3Y + 8, 4, cH2 - 16, c3Col); drawGpsNavTitle(tft, "COORDENADAS", c3X + 16, c3Y + 8, 0x8410, cardBg, 240); @@ -390,7 +390,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s if (c0X + cW > 0 && c0X < 320 && c0Y + cH1 > 0 && c0Y < slice_h) { s.fillRoundRect(c0X, c0Y, cW, cH1, 6, cardBg); s.drawRoundRect(c0X, c0Y, cW, cH1, 6, cardBorder); - s.fillRoundRect(c0X + 6, c0Y + 8, 4, cH1 - 16, 2, c0Col); + s.drawRect(c0X + 6, c0Y + 8, 4, cH1 - 16, c0Col); drawGpsNavTitle(s, "ALTITUD", c0X + 16, c0Y + 8, 0x8410, cardBg, slice_h); drawGpsNavSubword(s, "MSNM", c0X + 96, c0Y + 9, 0x9CD3, cardBg, slice_h); @@ -408,7 +408,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s if (c1X + cW > 0 && c1X < 320 && c1Y + cH1 > 0 && c1Y < slice_h) { s.fillRoundRect(c1X, c1Y, cW, cH1, 6, cardBg); s.drawRoundRect(c1X, c1Y, cW, cH1, 6, cardBorder); - s.fillRoundRect(c1X + 6, c1Y + 8, 4, cH1 - 16, 2, c1Col); + s.drawRect(c1X + 6, c1Y + 8, 4, cH1 - 16, c1Col); drawGpsNavTitle(s, "RUMBO / BRUJULA", c1X + 16, c1Y + 8, 0x8410, cardBg, slice_h); bool hasCourse = synced && isGpsCourseValid(); @@ -435,7 +435,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s if (c2X + cW > 0 && c2X < 320 && c2Y + cH2 > 0 && c2Y < slice_h) { s.fillRoundRect(c2X, c2Y, cW, cH2, 6, cardBg); s.drawRoundRect(c2X, c2Y, cW, cH2, 6, cardBorder); - s.fillRoundRect(c2X + 6, c2Y + 8, 4, cH2 - 16, 2, c2Col); + s.drawRect(c2X + 6, c2Y + 8, 4, cH2 - 16, c2Col); drawGpsNavTitle(s, "SATELITES GPS", c2X + 16, c2Y + 8, 0x8410, cardBg, slice_h); uint8_t waveState = getGpsWaveAnimState(); @@ -460,7 +460,7 @@ inline void renderPage3ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s if (c3X + cW > 0 && c3X < 320 && c3Y + cH2 > 0 && c3Y < slice_h) { s.fillRoundRect(c3X, c3Y, cW, cH2, 6, cardBg); s.drawRoundRect(c3X, c3Y, cW, cH2, 6, cardBorder); - s.fillRoundRect(c3X + 6, c3Y + 8, 4, cH2 - 16, 2, c3Col); + s.drawRect(c3X + 6, c3Y + 8, 4, cH2 - 16, c3Col); drawGpsNavTitle(s, "COORDENADAS", c3X + 16, c3Y + 8, 0x8410, cardBg, slice_h); diff --git a/sketch/obd2_gauge/meter.h b/sketch/obd2_gauge/meter.h index cbe9c68..48aff93 100644 --- a/sketch/obd2_gauge/meter.h +++ b/sketch/obd2_gauge/meter.h @@ -1583,7 +1583,7 @@ inline void renderPage1ToSprite(TFT_eSprite &s, int x_offset, int y_slice, accentCol = isRainbowTheme() ? (uint16_t)0xFD20 : getThemePrimaryColor(); } - s.fillRoundRect(cardX + 6, cardY + 6, 4, cardH - 12, 2, accentCol); + s.drawRect(cardX + 6, cardY + 6, 4, cardH - 12, accentCol); // Smooth anti-aliased title in transition sprite (zero mallocs, 100% // smooth) if (!sprTriTitleReady) diff --git a/sketch/obd2_gauge/obd2_gauge.ino b/sketch/obd2_gauge/obd2_gauge.ino index 68ab684..660b072 100644 --- a/sketch/obd2_gauge/obd2_gauge.ino +++ b/sketch/obd2_gauge/obd2_gauge.ino @@ -70,16 +70,16 @@ warn = default warning value */ const String pidConfig[5][9] = { //[pid][data] - {"Temperatura", "`C", "0105", "1", "60", "120", "2", "0", - "110"}, // 0 = Temperatura (0105) + {"Temperatura", "`C", "0105", "1", "60", "120", "14", "0", + "110"}, // 0 = Temperatura (0105): skip 14 (~1.5s) {"RPM", "rpm", "010C", "3", "0", "6000", "0", "0", - "5000"}, // 1 = RPM (010C) - {"Voltaje", "V", "ATRV", "8", "10", "16", "1", "1", - "15"}, // 2 = Voltaje Bateria (ATRV) - {"Velocidad", "km/h", "010D", "7", "0", "160", "0", "0", - "130"}, // 3 = Velocidad (010D) - {"Carga", "%", "0104", "2", "0", "100", "0", "0", - "85"} // 4 = Carga Motor (0104) + "5000"}, // 1 = RPM (010C): skip 0 (queries EVERY cycle) + {"Voltaje", "V", "ATRV", "8", "10", "16", "10", "1", + "15"}, // 2 = Voltaje Bateria (ATRV): skip 10 (~1s) + {"Velocidad", "km/h", "010D", "7", "0", "160", "2", "0", + "130"}, // 3 = Velocidad (010D): skip 2 (or skipped if GPS speed valid) + {"Carga", "%", "0104", "2", "0", "100", "12", "0", + "85"} // 4 = Carga Motor (0104): skip 12 (~1.5s) }; // hold warning value @@ -344,7 +344,7 @@ 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 = { - 0x6C, 0x62, 0x6D, 0x14, 0x08, 0xFD}; // Default to PC emulator MAC + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // Real OBD2 MAC address stored in NVS 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 @@ -546,89 +546,141 @@ void initializeELM327() { TaskHandle_t btTaskHandle = NULL; volatile bool btConnectedTrigger = false; volatile bool systemSleeping = false; +bool gpsClockAutoSwitched = 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; +// SPP Event Callback: Automatically captures and stores the remote OBD2 MAC address to NVS +void sppCallback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { + if (event == ESP_SPP_OPEN_EVT) { + if (param->open.status == ESP_SPP_SUCCESS) { + bool isZero = true; for (uint8_t i = 0; i < 6; i++) { - if (recent_client_addr[i] != 0) { - hasRecentAddr = true; + if (param->open.rem_bda[i] != 0) { + isZero = false; break; } } - if (hasRecentAddr) { - setRearLedColor(0, 0, 200); - BTSerial.disconnect(); - vTaskDelay(pdMS_TO_TICKS(100)); - if (BTSerial.connect(recent_client_addr, 0, sec_mask, role)) { - if (BTSerial.connected() && !systemSleeping) { + if (!isZero) { + memcpy(recent_client_addr, param->open.rem_bda, 6); + pref.begin("setting", false); + pref.putBytes("recent_client", recent_client_addr, 6); + pref.end(); +#ifdef SERIAL_DEBUG + Serial.printf("[BT] SPP Connected! Stored OBD MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", + recent_client_addr[0], recent_client_addr[1], recent_client_addr[2], + recent_client_addr[3], recent_client_addr[4], recent_client_addr[5]); +#endif + } + } + } +} + +void btBackgroundTask(void *pvParameters) { + while (true) { + // Only attempt connection if not sleeping, not declared found, and not already connected! + if (!systemSleeping && !foundOBD2 && !BTSerial.connected()) { + bool connected = false; + + // Check if recent_client_addr is valid (non-zero and not old default emulator) + bool hasRecentAddr = false; + const esp_bd_addr_t OLD_EMU = {0x6C, 0x62, 0x6D, 0x14, 0x08, 0xFD}; + bool isOldEmu = true; + for (uint8_t i = 0; i < 6; i++) { + if (recent_client_addr[i] != 0) hasRecentAddr = true; + if (recent_client_addr[i] != OLD_EMU[i]) isOldEmu = false; + } + if (isOldEmu) hasRecentAddr = false; + + // 1. Direct connection to saved MAC address (Ultra fast: < 1s, reliable even if OBD2 was already ON) + if (hasRecentAddr && !systemSleeping) { + setRearLedColor(0, 0, 200); // Blue LED + if (BTSerial.connect(recent_client_addr, 0, ESP_SPP_SEC_NONE, ESP_SPP_ROLE_MASTER)) { + if (BTSerial.connected(1000) && !systemSleeping) { connected = true; } } } - // 2. Si fallo o no habia direccion, intentar conectar a la MAC del emulador PC + // 2. If direct connection failed or no address saved, connect by name "OBDII" 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; + setRearLedColor(0, 0, 200); + BTSerial.disconnect(); + vTaskDelay(pdMS_TO_TICKS(150)); + if (BTSerial.connect("OBDII")) { + if (BTSerial.connected(1000) && !systemSleeping) { + connected = true; } } - 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. Fallback: Escaneo y descubrimiento de dispositivos OBD (OBDII, ELM, etc.) + if (!connected && !systemSleeping) { + setRearLedColor(0, 0, 200); + BTSerial.disconnect(); + vTaskDelay(pdMS_TO_TICKS(150)); + static esp_bd_addr_t discoveredAddr; + static volatile bool discFound = false; + discFound = false; + + BTSerial.discoverAsync([](BTAdvertisedDevice *pDevice) { + String dName = pDevice->getName().c_str(); + dName.toUpperCase(); + if (dName.indexOf("OBD") != -1 || dName.indexOf("ELM") != -1 || + dName.indexOf("VGATE") != -1 || dName.indexOf("VIECAR") != -1) { + esp_bd_addr_t *nativeAddr = pDevice->getAddress().getNative(); + if (nativeAddr != nullptr) { + memcpy(discoveredAddr, *nativeAddr, 6); + discFound = true; } } - } - } + }); - // 3. Si aun no conecto, buscar y conectar por nombre "OBDII" (inquiry scan) - if (!connected && !systemSleeping) { - setRearLedColor(0, 0, 200); - BTSerial.disconnect(); + uint32_t startDisc = millis(); + while (millis() - startDisc < 4000 && !discFound && !systemSleeping) { + vTaskDelay(pdMS_TO_TICKS(100)); + } + BTSerial.discoverAsyncStop(); vTaskDelay(pdMS_TO_TICKS(100)); - if (BTSerial.connect("OBDII")) { - if (BTSerial.connected() && !systemSleeping) { - connected = true; + + if (discFound && !systemSleeping) { + if (BTSerial.connect(discoveredAddr, 0, ESP_SPP_SEC_NONE, ESP_SPP_ROLE_MASTER)) { + if (BTSerial.connected(1000) && !systemSleeping) { + connected = true; + memcpy(recent_client_addr, discoveredAddr, 6); + pref.begin("setting", false); + pref.putBytes("recent_client", recent_client_addr, 6); + pref.end(); + } } } } // 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); + // Enviar ATZ para limpiar buffers y reiniciar chip ELM327 (clave cuando el OBD2 ya estaba enchufado antes) + sendATCommand("ATZ", 1500); + vTaskDelay(pdMS_TO_TICKS(300)); + sendATCommand("ATE0", 500); // Echo OFF + sendATCommand("ATL0", 500); // Linefeed OFF + sendATCommand("ATS0", 500); // Spaces OFF (mucho mas veloz) + sendATCommand("ATH0", 500); // Headers OFF + sendATCommand("ATAT1", 500); // Adaptive timing + sendATCommand("ATSP1", 500); // Ford J1850 PWM + String r0100 = sendATCommand("0100", 2500); - ignitionOn = (r0100.indexOf("41 00") != -1); + ignitionOn = (r0100.indexOf("4100") != -1 || r0100.indexOf("41 00") != -1); foundOBD2 = true; prompt = true; bt_message = ""; updateRearLedTheme(); - btConnectedTrigger = - true; // Signal Core 1 (UI thread) to switch screen! + btConnectedTrigger = true; } else { - BTSerial.disconnect(); - setRearLedOff(); + if (!BTSerial.connected()) { + setRearLedOff(); + } foundOBD2 = false; } } - ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(3500)); + ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(2000)); } } @@ -759,9 +811,23 @@ recent_client_addr : {0x00,0x00,0x00,0x00,0x00,0x00} array of bytes[6] currentBacklight = (float)targetBacklight; ledcWrite(backlightChannel, targetBacklight); } - lastActivityTime = millis(); - pref.getBytes("recent_client", recent_client_addr, - pref.getBytesLength("recent_client")); // read last bt address + if (pref.isKey("recent_client")) { + pref.getBytes("recent_client", recent_client_addr, 6); + const esp_bd_addr_t OLD_EMU_MAC = {0x6C, 0x62, 0x6D, 0x14, 0x08, 0xFD}; + bool isOldEmu = true; + for (uint8_t i = 0; i < 6; i++) { + if (recent_client_addr[i] != OLD_EMU_MAC[i]) { + isOldEmu = false; + break; + } + } + if (isOldEmu) { + memset(recent_client_addr, 0, 6); + pref.remove("recent_client"); + } + } else { + memset(recent_client_addr, 0, 6); + } updateRearLedTheme(); // Sync rear RGB LED with loaded theme Terminal("[ OK ] Settings: Theme " + String(GAUGE_THEMES[themeIndex].name) + @@ -822,6 +888,11 @@ recent_client_addr : {0x00,0x00,0x00,0x00,0x00,0x00} array of bytes[6] Terminal("[ OK ] Bluetooth SPP Master stack online"); } + // Automatically capture and persist remote OBD2 MAC address upon connection + BTSerial.register_callback(sppCallback); + BTSerial.setPin("1234"); + BTSerial.enableSSP(); + #ifdef SKIP_CONNECTION foundOBD2 = true; #else @@ -853,24 +924,45 @@ void loop() { - // Trigger automatico: Cuando el Bluetooth conecta en segundo plano (Core 0), - // pasar a pantalla OBD! + // 1. Trigger automatico Bluetooth: + // Solo en arranque en frio inicial si el usuario NO navego manualmente + static bool userNavigatedManually = false; + static bool initialBtAutoSwitched = false; if (btConnectedTrigger) { btConnectedTrigger = false; - setScreenPage(0); // Cambiar a tablero de 4 cuadrantes OBD! - fullScreenCell = -1; - initScreen(true); - touchBeep(); - lastActivityTime = millis(); + if (!userNavigatedManually && !initialBtAutoSwitched && currentScreenPage == 2 && fullScreenCell == -1) { + initialBtAutoSwitched = true; + setScreenPage(0); // Cambiar a tablero de 4 cuadrantes OBD solo en el primer arranque + fullScreenCell = -1; + initScreen(true); + touchBeep(); + lastActivityTime = millis(); + } } - // Evaluacion inicial: Si tras el arranque no encuentra ni Bluetooth ni GPS, - // pasar a la pantalla de las Temperaturas (Pagina 1) + // 2. Trigger automatico GPS: + // Solo si el usuario no ha navegado manualmente + bool isBt = BTSerial.connected() && foundOBD2; + if (!isBt && !userNavigatedManually) { + if (isGpsSynced() && !gpsClockAutoSwitched) { + gpsClockAutoSwitched = true; + if (currentScreenPage != 2 && fullScreenCell == -1) { + setScreenPage(2); + fullScreenCell = -1; + initScreen(true); + touchBeep(); + lastActivityTime = millis(); + } + } + } else if (isBt) { + gpsClockAutoSwitched = false; + } + + // Evaluacion inicial: Si tras el arranque no encuentra ni Bluetooth ni GPS y sin navegacion manual: static bool bootSearchEvaluated = false; if (!bootSearchEvaluated && millis() >= 7000) { bootSearchEvaluated = true; - bool isBt = BTSerial.connected() && foundOBD2; - if (!isBt && !isGpsSynced()) { + if (!isBt && !isGpsSynced() && !userNavigatedManually) { if (currentScreenPage == 2 && fullScreenCell == -1) { setScreenPage(1); // Ir a la pantalla de las Temperaturas (Pagina 1) fullScreenCell = -1; @@ -927,6 +1019,7 @@ void loop() { if (isTouched || btnPressed) { lastActivityTime = millis(); + userNavigatedManually = true; } static bool isTouching = false; @@ -1094,49 +1187,41 @@ void loop() { } #ifndef SKIP_CONNECTION - // Check Bluetooth connection state: if adapter disconnected or lost power, - // switch immediately to GPS Clock Screen (Page 2) and show "---" on OBD - // parameters - static bool wasBtConnected = false; - bool isBtConnected = BTSerial.connected() && foundOBD2; + // Monitor de enlace Bluetooth con debounce sólido de 4 segundos + static uint32_t btDisconnectDebounce = 0; + bool isPhysConnected = BTSerial.connected(); - // Si se pierde el enlace Bluetooth: si hay GPS pasa al Reloj (Pag 2), si no - // hay GPS pasa a Temperaturas (Pag 1) - 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; + if (isPhysConnected && foundOBD2) { + btDisconnectDebounce = 0; // Enlace sano y activo + } else if (foundOBD2) { + if (btDisconnectDebounce == 0) { + btDisconnectDebounce = millis(); + } + // Solo si el corte físico persiste por más de 4000ms seguidos declarar pérdida de enlace: + if (millis() - btDisconnectDebounce >= 4000) { + foundOBD2 = false; + ignitionOn = 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; + } + } + setRearLedOff(); + if (currentScreenPage == 0) { + updateClusterScreen(false); + } else if (currentScreenPage == 1) { + updateTriTempCoolant(-999.0f); + } + // Despertar la tarea de reconexión automática en segundo plano + if (btTaskHandle != NULL) { + xTaskNotifyGive(btTaskHandle); } } - ignitionOn = false; - foundOBD2 = false; - uint8_t fallbackPage = isGpsSynced() ? 2 : 1; - setScreenPage(fallbackPage); - fullScreenCell = -1; - initScreen(true); - setRearLedOff(); - lastActivityTime = millis(); return; - } - wasBtConnected = isBtConnected; - - if (!isBtConnected) { - 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; - if (currentScreenPage == 0) { - updateClusterScreen(false); - } - foundOBD2 = - false; // Core 0 background task will handle reconnection smoothly! + } else { + // Si aún no está conectado, retornar sin tocar ni cambiar la pantalla del usuario return; } #endif @@ -1161,16 +1246,7 @@ void loop() { while (BTSerial.available() > 0) { char incomingChar = BTSerial.read(); if (incomingChar == '>') { // check prompt - BTSerial.flush(); // clear tx -#ifdef SERIAL_DEBUG - Serial.println(bt_message); -#endif -#ifdef TERMINAL // for degbugging - getAB(bt_message, pidList[pidIndex]); - Terminal(bt_message + "->" + String(A) + "," + String(B), 0, 48, 320, - 191); -#endif - prompt = true; // pid response ready to calculate & display + prompt = true; // pid response ready to calculate & display (sin delay de flush!) } else { bt_message.concat(incomingChar); // keep elm327 respond } @@ -1220,11 +1296,17 @@ void loop() { autoDim(); // auto backlight handle } if (pidCurrentSkip[pidIndex] <= 0) { // end of skip loop , go next index - pidCurrentSkip[pidIndex] = - pidReadSkip[pidIndex]; // read max delay loop to current - skip = false; - BTSerial.print(pidList[pidIndex] + "\r"); // send PID request - lastPidSendTime = millis(); + // Si este PID es Velocidad (010D) y el GPS ya está entregando velocidad válida, saltearlo sin enviar a OBD: + if (pidIndex == 1 && isGpsSpeedValid()) { + prompt = true; + skip = true; + } else { + pidCurrentSkip[pidIndex] = + pidReadSkip[pidIndex]; // read max delay loop to current + skip = false; + BTSerial.print(pidList[pidIndex] + "\r"); // send PID request + lastPidSendTime = millis(); + } } else { // skip sending request pidCurrentSkip[pidIndex] = pidCurrentSkip[pidIndex] - 1; // decrese loop count diff --git a/sketch/obd2_gauge/tri_temp.h b/sketch/obd2_gauge/tri_temp.h index c5e96b8..c3ef352 100644 --- a/sketch/obd2_gauge/tri_temp.h +++ b/sketch/obd2_gauge/tri_temp.h @@ -271,7 +271,7 @@ inline void drawTriTempCard(uint8_t cardIdx, float val, int x_offset = 0) { } // Left vertical accent bar - tft.fillRoundRect(cardX + 6, cardY + 6, 4, cardH - 12, 2, accentCol); + tft.drawRect(cardX + 6, cardY + 6, 4, cardH - 12, accentCol); if (isYapaTheme()) { tft.loadFont(Latin_Sans_Bold_26); @@ -418,7 +418,7 @@ inline void updateTriTempCardValue(uint8_t cardIdx, float val, float oldVal) { // 1. Accent color changed: update vertical accent bar and unit "°C" if (accentCol != oldAccentCol) { - tft.fillRoundRect(cardX + 6, cardY + 6, 4, cardH - 12, 2, accentCol); + tft.drawRect(cardX + 6, cardY + 6, 4, cardH - 12, accentCol); bool rainbowUnit = isRainbowTheme() && (accentCol != TFT_RED); uint16_t unitCol = rainbowUnit ? getRainbowScreenColor(cardX + 268, cardY + 22) : (isYapaTheme() && accentCol != TFT_RED ? getYapaColor(3) : (accentCol == TFT_RED ? TFT_RED : 0xC618)); tft.fillRect(cardX + 268, cardY + 22, 34, 26, cardBg);