diff --git a/sketch/obd2_gauge/meter.h b/sketch/obd2_gauge/meter.h index d3b298e..38a6a32 100644 --- a/sketch/obd2_gauge/meter.h +++ b/sketch/obd2_gauge/meter.h @@ -37,6 +37,11 @@ uint8_t engine_off_count = 0; // counter to indicate engine is off then sleep float old_data[maxpidIndex] = {-999, -999, -999, -999}; // keep old data float last_valid_data[maxpidIndex] = {-999, -999, -999, -999}; // keep last valid data for instant redraw +// Arc warning flash (blinking alarm when gauge is in TFT_RED) +bool arcFlashVisible = true; +uint32_t lastArcFlashTime = 0; +bool wasInRedAlarm[4] = { false, false, false, false }; + // backlight variable average counter uint8_t low_count = 0; uint8_t high_count = 0; @@ -818,20 +823,45 @@ void plotArc4(uint8_t cell, float data, int warn, int min, int max, bool digit, oldBlocks = NUM_BAR_BLOCKS; bool colorChanged = (col != oldCol); - if (colorChanged || old_data[cell] < min) { - for (uint8_t i = 0; i < newBlocks; i++) { - drawQuadBarBlock(cell, i, col); + if (col == TFT_RED) { + wasInRedAlarm[cell] = true; + if (colorChanged) { + arcFlashVisible = true; + lastArcFlashTime = millis(); + 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, TFT_DARKGREY); + } + } else if (arcFlashVisible) { + if (newBlocks > oldBlocks) { + for (uint8_t i = oldBlocks; i < newBlocks; i++) { + drawQuadBarBlock(cell, i, TFT_RED); + } + } else if (newBlocks < oldBlocks) { + for (uint8_t i = newBlocks; i < oldBlocks; i++) { + drawQuadBarBlock(cell, i, TFT_DARKGREY); + } + } } - for (uint8_t i = newBlocks; i < NUM_BAR_BLOCKS; i++) { - drawQuadBarBlock(cell, i, TFT_DARKGREY); - } - } else if (newBlocks > oldBlocks) { - for (uint8_t i = oldBlocks; i < newBlocks; i++) { - drawQuadBarBlock(cell, i, col); - } - } else if (newBlocks < oldBlocks) { - for (uint8_t i = newBlocks; i < oldBlocks; i++) { - drawQuadBarBlock(cell, i, TFT_DARKGREY); + } else { + wasInRedAlarm[cell] = false; + if (colorChanged || old_data[cell] < min) { + 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, TFT_DARKGREY); + } + } else if (newBlocks > oldBlocks) { + for (uint8_t i = oldBlocks; i < newBlocks; i++) { + drawQuadBarBlock(cell, i, col); + } + } else if (newBlocks < oldBlocks) { + for (uint8_t i = newBlocks; i < oldBlocks; i++) { + drawQuadBarBlock(cell, i, TFT_DARKGREY); + } } } @@ -960,20 +990,45 @@ void plotFullScreen(uint8_t cell, float data, int warn, int min, int max, oldBlocks = NUM_BAR_BLOCKS; bool colorChanged = (col != oldCol); - if (colorChanged || old_data[cell] < min) { - for (uint8_t i = 0; i < newBlocks; i++) { - drawBarBlock(i, col); + if (col == TFT_RED) { + wasInRedAlarm[cell] = true; + if (colorChanged) { + arcFlashVisible = true; + lastArcFlashTime = millis(); + for (uint8_t i = 0; i < newBlocks; i++) { + drawBarBlock(i, TFT_RED); + } + for (uint8_t i = newBlocks; i < NUM_BAR_BLOCKS; i++) { + drawBarBlock(i, TFT_DARKGREY); + } + } else if (arcFlashVisible) { + if (newBlocks > oldBlocks) { + for (uint8_t i = oldBlocks; i < newBlocks; i++) { + drawBarBlock(i, TFT_RED); + } + } else if (newBlocks < oldBlocks) { + for (uint8_t i = newBlocks; i < oldBlocks; i++) { + drawBarBlock(i, TFT_DARKGREY); + } + } } - for (uint8_t i = newBlocks; i < NUM_BAR_BLOCKS; i++) { - drawBarBlock(i, TFT_DARKGREY); - } - } else if (newBlocks > oldBlocks) { - for (uint8_t i = oldBlocks; i < newBlocks; i++) { - drawBarBlock(i, col); - } - } else if (newBlocks < oldBlocks) { - for (uint8_t i = newBlocks; i < oldBlocks; i++) { - drawBarBlock(i, TFT_DARKGREY); + } else { + wasInRedAlarm[cell] = false; + if (colorChanged || old_data[cell] < min) { + for (uint8_t i = 0; i < newBlocks; i++) { + drawBarBlock(i, col); + } + for (uint8_t i = newBlocks; i < NUM_BAR_BLOCKS; i++) { + drawBarBlock(i, TFT_DARKGREY); + } + } else if (newBlocks > oldBlocks) { + for (uint8_t i = oldBlocks; i < newBlocks; i++) { + drawBarBlock(i, col); + } + } else if (newBlocks < oldBlocks) { + for (uint8_t i = newBlocks; i < oldBlocks; i++) { + drawBarBlock(i, TFT_DARKGREY); + } } } @@ -1197,6 +1252,115 @@ void updateMeter(uint8_t pidNo, String response) { // update parameter on screen } /*-------------------------------*/ +// Non-blocking handler to blink/flash arcs on alarm (when in TFT_RED) +void handleArcAlarmFlash() { + uint32_t now = millis(); + bool toggleTick = false; + if (now - lastArcFlashTime >= 250) { + lastArcFlashTime = now; + arcFlashVisible = !arcFlashVisible; + toggleTick = true; + } + + if (fullScreenCell >= 0 && fullScreenCell < 4) { + uint8_t cell = fullScreenCell; + if (!cellIsNumeric[cell] && last_valid_data[cell] != -999) { + uint8_t pidIdx = pidInCell[cell]; + int warn = warningValue[pidIdx].toInt(); + float data = last_valid_data[cell]; + uint16_t col = getMeterColor(pidIdx, data, warn); + bool inRed = (col == TFT_RED); + + if (wasInRedAlarm[cell] && !inRed) { + // Exited red alarm: restore steady arc + wasInRedAlarm[cell] = false; + int min = pidConfig[pidIdx][4].toInt(); + int max = pidConfig[pidIdx][5].toInt(); + int newBlocks = map(data, min, max, 0, NUM_BAR_BLOCKS); + if (newBlocks < 0) newBlocks = 0; + if (newBlocks > NUM_BAR_BLOCKS) newBlocks = NUM_BAR_BLOCKS; + for (uint8_t i = 0; i < newBlocks; i++) { + drawBarBlock(i, col); + } + for (uint8_t i = newBlocks; i < NUM_BAR_BLOCKS; i++) { + drawBarBlock(i, TFT_DARKGREY); + } + } else if (inRed) { + wasInRedAlarm[cell] = true; + if (toggleTick) { + int min = pidConfig[pidIdx][4].toInt(); + int max = pidConfig[pidIdx][5].toInt(); + int newBlocks = map(data, min, max, 0, NUM_BAR_BLOCKS); + if (newBlocks < 0) newBlocks = 0; + if (newBlocks > NUM_BAR_BLOCKS) newBlocks = NUM_BAR_BLOCKS; + + if (arcFlashVisible) { + for (uint8_t i = 0; i < newBlocks; i++) { + drawBarBlock(i, TFT_RED); + } + for (uint8_t i = newBlocks; i < NUM_BAR_BLOCKS; i++) { + drawBarBlock(i, TFT_DARKGREY); + } + } else { + for (uint8_t i = 0; i < NUM_BAR_BLOCKS; i++) { + drawBarBlock(i, TFT_BLACK); + } + } + } + } + } + } else { + // 4 quadrants mode + for (uint8_t cell = 0; cell < 4; cell++) { + if (!cellIsNumeric[cell] && last_valid_data[cell] != -999) { + uint8_t pidIdx = pidInCell[cell]; + int warn = warningValue[pidIdx].toInt(); + float data = last_valid_data[cell]; + uint16_t col = getMeterColor(pidIdx, data, warn); + bool inRed = (col == TFT_RED); + + if (wasInRedAlarm[cell] && !inRed) { + // Exited red alarm: restore steady arc + wasInRedAlarm[cell] = false; + int min = pidConfig[pidIdx][4].toInt(); + int max = pidConfig[pidIdx][5].toInt(); + int newBlocks = map(data, min, max, 0, NUM_BAR_BLOCKS); + if (newBlocks < 0) newBlocks = 0; + if (newBlocks > NUM_BAR_BLOCKS) newBlocks = NUM_BAR_BLOCKS; + 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, TFT_DARKGREY); + } + } else if (inRed) { + wasInRedAlarm[cell] = true; + if (toggleTick) { + int min = pidConfig[pidIdx][4].toInt(); + int max = pidConfig[pidIdx][5].toInt(); + int newBlocks = map(data, min, max, 0, NUM_BAR_BLOCKS); + if (newBlocks < 0) newBlocks = 0; + if (newBlocks > NUM_BAR_BLOCKS) newBlocks = NUM_BAR_BLOCKS; + + 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, TFT_DARKGREY); + } + } else { + for (uint8_t i = 0; i < NUM_BAR_BLOCKS; i++) { + drawQuadBarBlock(cell, i, TFT_BLACK); + } + } + } + } + } + } + } +} + // Target and current brightness variables for smooth fade uint8_t targetBacklight = 255; float currentBacklight = 255.0f; diff --git a/sketch/obd2_gauge/obd2_gauge.ino b/sketch/obd2_gauge/obd2_gauge.ino index 432de8a..e99b482 100644 --- a/sketch/obd2_gauge/obd2_gauge.ino +++ b/sketch/obd2_gauge/obd2_gauge.ino @@ -335,7 +335,8 @@ recent_client_addr : {0x00,0x00,0x00,0x00,0x00,0x00} array of bytes[6] /*###################################*/ void loop() { - updateBacklightFade(); // non-blocking smooth backlight fade handler + updateBacklightFade(); // non-blocking smooth backlight fade handler + handleArcAlarmFlash(); // non-blocking arc alarm flash handler (blinks when in TFT_RED) // SCAN TOUCHSCREEN // - Single click on a quadrant: toggles between clock (arc) and number