diff --git a/sketch/obd2_gauge/config.h b/sketch/obd2_gauge/config.h index c2fb7ee..b649408 100644 --- a/sketch/obd2_gauge/config.h +++ b/sketch/obd2_gauge/config.h @@ -33,13 +33,14 @@ void clickSound() { casioBeep(4096, 25); } -// Configuration menu list (3 options) -const String menuList[3] = { - "Ajuste de Alarmas", // 0 - "Volumen de Bip", // 1 - "Salir" // 2 +// Configuration menu list (4 options) +const String menuList[4] = { + "Activación de Alarmas", // 0 + "Sonido de Alarma", // 1 + "Volumen de Bip", // 2 + "Salir" // 3 }; -const uint8_t maxMenu = 3; +const uint8_t maxMenu = 4; // Menu list for warning setting (4 vehicle gauges) const String parameterList[4][2] = { @@ -56,7 +57,7 @@ void warningMenu() { tft.fillScreen(TFT_BLACK); tft.fillRectVGradient(0, 0, 320, 26, TFT_ORANGE, 0xfc00); tft.setTextColor(TFT_BLACK); - tft.drawCentreString("[--- Ajuste de Alarmas ---]", 159, 0, 4); + tft.drawCentreString("[--- Activación de Alarmas ---]", 159, 0, 4); auto drawAllRows = [&]() { for (uint8_t i = 0; i < 4; i++) { @@ -119,7 +120,167 @@ void warningMenu() { } // ------------------------------------------------------------- -// Sub-screen 1: Volumen de Bip (Ajuste Tactil Directo) +// Sub-screen 1: Sonido de Alarma (Tipo de Sonido y Volumen) +// ------------------------------------------------------------- +const char* const ALARM_NAME_TABLE[4] = { + "Casio Doble", + "Sincronizado", + "Bitonal Auto", + "Triple Urgente" +}; + +void playAlarmSample(uint8_t type, uint8_t vol) { + if (vol == 0) return; + uint16_t duty = VOL_DUTY_TABLE[vol > 5 ? 3 : vol]; + + if (type == 0) { // Casio Doble + ledcSetup(buzzerChannel, 4096, 10); + ledcWrite(buzzerChannel, duty); + delay(25); + ledcWrite(buzzerChannel, 0); + delay(35); + ledcSetup(buzzerChannel, 4096, 10); + ledcWrite(buzzerChannel, duty); + delay(25); + ledcWrite(buzzerChannel, 0); + } else if (type == 1) { // Sincronizado + ledcSetup(buzzerChannel, 4096, 10); + ledcWrite(buzzerChannel, duty); + delay(30); + ledcWrite(buzzerChannel, 0); + delay(100); + ledcSetup(buzzerChannel, 4096, 10); + ledcWrite(buzzerChannel, duty); + delay(30); + ledcWrite(buzzerChannel, 0); + } else if (type == 2) { // Bitonal Auto + ledcSetup(buzzerChannel, 4096, 10); + ledcWrite(buzzerChannel, duty); + delay(40); + ledcWrite(buzzerChannel, 0); + delay(20); + ledcSetup(buzzerChannel, 3072, 10); + ledcWrite(buzzerChannel, duty); + delay(40); + ledcWrite(buzzerChannel, 0); + } else if (type == 3) { // Triple Urgente + for (uint8_t k = 0; k < 3; k++) { + ledcSetup(buzzerChannel, 4096, 10); + ledcWrite(buzzerChannel, duty); + delay(20); + ledcWrite(buzzerChannel, 0); + if (k < 2) delay(25); + } + } +} + +void alarmSoundMenu() { + tft.fillScreen(TFT_BLACK); + tft.fillRectVGradient(0, 0, 320, 26, TFT_ORANGE, 0xfc00); + tft.setTextColor(TFT_BLACK); + tft.drawCentreString("[--- Sonido de Alarma ---]", 159, 0, 4); + + auto drawAlarmUI = []() { + tft.fillRect(0, 28, 320, 160, TFT_BLACK); + + // 4 Pattern Tiles (2x2 grid) + for (uint8_t i = 0; i < 4; i++) { + int16_t x = (i % 2 == 0) ? 10 : 166; + int16_t y = (i < 2) ? 34 : 74; + int16_t w = 144; + int16_t h = 34; + bool isSel = (i == alarmSoundType); + + uint16_t bg = isSel ? TFT_BLUE : 0x18E3; + tft.fillRoundRect(x, y, w, h, 5, bg); + if (isSel) { + tft.drawRoundRect(x, y, w, h, 5, TFT_CYAN); + tft.drawRoundRect(x + 1, y + 1, w - 2, h - 2, 4, TFT_WHITE); + } + tft.setTextColor(isSel ? TFT_WHITE : 0xBDD7, bg); + tft.drawCentreString(ALARM_NAME_TABLE[i], x + w / 2, y + 8, 2); + } + + // Volume level label + tft.setTextColor(TFT_WHITE, TFT_BLACK); + String lvlStr = "Volumen: Nivel " + String(alarmVolumeLevel) + " de 5 (" + VOL_NAME_TABLE[alarmVolumeLevel] + ")"; + tft.drawCentreString(lvlStr.c_str(), 160, 116, 2); + + // 6 Direct Touch Tiles (0 to 5) for Alarm Volume + for (uint8_t i = 0; i <= 5; i++) { + int16_t bx = 7 + i * 52; + int16_t by = 136; + int16_t bw = 46; + int16_t bh = 46; + bool isSel = (i == alarmVolumeLevel); + + uint16_t bgCol = isSel ? (i == 0 ? 0x8000 : (i >= 4 ? TFT_ORANGE : (i >= 2 ? TFT_GREEN : 0x03E0))) : 0x18E3; + tft.fillRoundRect(bx, by, bw, bh, 5, bgCol); + if (isSel) { + tft.drawRoundRect(bx, by, bw, bh, 5, TFT_WHITE); + tft.drawRoundRect(bx + 1, by + 1, bw - 2, bh - 2, 4, TFT_WHITE); + } + tft.setTextColor(isSel ? TFT_WHITE : 0xAD55, bgCol); + tft.drawCentreString(String(i).c_str(), bx + bw / 2, by + 6, 6); + } + }; + + drawAlarmUI(); + + // Bottom action button: [ GUARDAR Y SALIR ] + tft.fillRoundRect(25, 192, 270, 40, 5, 0x02EC); // Rich Ocean Blue + tft.setTextColor(TFT_WHITE); + tft.drawCentreString("GUARDAR Y SALIR", 160, 200, 4); + delay(200); + + while (true) { + autoDim(); + uint16_t tx = 0, ty = 0; + if (getTouch(&tx, &ty)) { + // 1. Touch on pattern selection (y: 32 to 112) + if (ty >= 32 && ty <= 112) { + int8_t col = (tx < 160) ? 0 : 1; + int8_t row = (ty < 72) ? 0 : 1; + uint8_t selType = row * 2 + col; + if (selType < 4) { + alarmSoundType = selType; + drawAlarmUI(); + playAlarmSample(alarmSoundType, alarmVolumeLevel); + } + while (isScreenPressed()) { delay(10); yield(); } + delay(50); + } + // 2. Touch on alarm volume tiles (y: 130 to 186) + else if (ty >= 130 && ty <= 186) { + int8_t touchedLvl = (tx - 7) / 52; + if (touchedLvl >= 0 && touchedLvl <= 5) { + alarmVolumeLevel = touchedLvl; + drawAlarmUI(); + playAlarmSample(alarmSoundType, alarmVolumeLevel); + } + while (isScreenPressed()) { delay(10); yield(); } + delay(50); + } + // 3. Bottom button [ GUARDAR Y SALIR ] or top bar + else if ((ty >= 188 && ty <= 238 && tx >= 20 && tx <= 300) || ty < 30) { + casioBeep(4096, 25); + pref.begin("setting", false); + pref.putUChar("alm_vol", alarmVolumeLevel); + pref.putUChar("alm_type", alarmSoundType); + pref.end(); + while (isScreenPressed()) { delay(10); yield(); } + break; + } + } else if (digitalRead(SELECTOR_PIN) == LOW) { + delay(200); + break; + } + yield(); + } +} + +// ------------------------------------------------------------- +// Sub-screen 2: Volumen de Bip (Ajuste Tactil Directo) // ------------------------------------------------------------- void volumeMenu() { tft.fillScreen(TFT_BLACK); @@ -205,7 +366,6 @@ void volumeMenu() { } } -// ------------------------------------------------------------- // ------------------------------------------------------------- // Menu Principal // ------------------------------------------------------------- @@ -213,17 +373,17 @@ void listMenu(uint8_t choice) { tft.fillRect(0, 28, 320, 212, TFT_BLACK); for (uint8_t i = 0; i < maxMenu; i++) { - int y = 46 + i * 64; + int y = 34 + i * 50; if (choice == i) { - tft.fillRoundRect(10, y, 300, 48, 6, TFT_BLUE); - tft.drawRoundRect(10, y, 300, 48, 6, TFT_CYAN); - tft.drawRoundRect(11, y + 1, 298, 46, 5, TFT_CYAN); + tft.fillRoundRect(10, y, 300, 42, 5, TFT_BLUE); + tft.drawRoundRect(10, y, 300, 42, 5, TFT_CYAN); + tft.drawRoundRect(11, y + 1, 298, 40, 4, TFT_CYAN); tft.setTextColor(TFT_WHITE, TFT_BLUE); } else { - tft.fillRoundRect(10, y, 300, 48, 6, 0x18E3); // subtle dark card + tft.fillRoundRect(10, y, 300, 42, 5, 0x18E3); // subtle dark card tft.setTextColor(TFT_WHITE, 0x18E3); } - tft.drawString(menuList[i], 25, y + 12, 4); + tft.drawString(menuList[i], 22, y + 9, 4); } } @@ -246,9 +406,10 @@ void configMenu() { if (getTouch(&tx, &ty)) { int8_t touchedOpt = -1; - if (ty >= 36 && ty < 102) touchedOpt = 0; - else if (ty >= 102 && ty < 166) touchedOpt = 1; - else if (ty >= 166 && ty <= 235) touchedOpt = 2; + if (ty >= 28 && ty < 80) touchedOpt = 0; + else if (ty >= 80 && ty < 130) touchedOpt = 1; + else if (ty >= 130 && ty < 180) touchedOpt = 2; + else if (ty >= 180 && ty <= 235) touchedOpt = 3; if (touchedOpt >= 0) { select = touchedOpt; @@ -285,7 +446,7 @@ void configMenu() { if (executeSelect >= 0) { select = executeSelect; - // Option 0: Warning setting (Thresholds & ON/OFF) + // Option 0: Activacion de Alarmas (ON/OFF) if (select == 0) { warningMenu(); tft.fillScreen(TFT_BLACK); @@ -294,8 +455,17 @@ void configMenu() { tft.drawCentreString("[--- Configuration Menu ---]", 159, 0, 4); listMenu(select); } - // Option 1: Volume setting (Buzzer level & live touch) + // Option 1: Sonido de Alarma (Tipo de Sonido y Volumen) else if (select == 1) { + alarmSoundMenu(); + tft.fillScreen(TFT_BLACK); + tft.fillRectVGradient(0, 0, 320, 26, TFT_ORANGE, 0xfc00); + tft.setTextColor(TFT_BLACK); + tft.drawCentreString("[--- Configuration Menu ---]", 159, 0, 4); + listMenu(select); + } + // Option 2: Volumen de Bip (Toques de pantalla) + else if (select == 2) { volumeMenu(); tft.fillScreen(TFT_BLACK); tft.fillRectVGradient(0, 0, 320, 26, TFT_ORANGE, 0xfc00); @@ -303,8 +473,8 @@ void configMenu() { tft.drawCentreString("[--- Configuration Menu ---]", 159, 0, 4); listMenu(select); } - // Option 2: Exit - else if (select == 2) { + // Option 3: Exit + else if (select == 3) { break; } diff --git a/sketch/obd2_gauge/meter.h b/sketch/obd2_gauge/meter.h index 9379c7e..5ee599d 100644 --- a/sketch/obd2_gauge/meter.h +++ b/sketch/obd2_gauge/meter.h @@ -1365,13 +1365,13 @@ void updateMeter(uint8_t pidNo, String response) { // update parameter on screen } /*-------------------------------*/ -// Non-blocking alarm sound generator: classic Casio double-chirp [bip-bip] every 1 second +// Non-blocking alarm sound generator: 4 selectable patterns uint32_t lastAlarmSoundCycle = 0; uint8_t alarmSoundStep = 0; uint32_t alarmSoundStepTime = 0; -void handleAlarmSound(bool anyAlarmActive) { - if (!anyAlarmActive || buzzerVolumeLevel == 0) { +void handleAlarmSound(bool anyAlarmActive, bool flashTick = false) { + if (!anyAlarmActive || alarmVolumeLevel == 0) { if (alarmSoundStep != 0) { ledcWrite(buzzerChannel, 0); alarmSoundStep = 0; @@ -1380,35 +1380,119 @@ void handleAlarmSound(bool anyAlarmActive) { } uint32_t now = millis(); - uint16_t duty = VOL_DUTY_TABLE[buzzerVolumeLevel > 5 ? 3 : buzzerVolumeLevel]; + uint16_t duty = VOL_DUTY_TABLE[alarmVolumeLevel > 5 ? 3 : alarmVolumeLevel]; - // Pattern: [25ms chirp] -> 35ms pause -> [25ms chirp] -> pause until 1000ms - if (alarmSoundStep == 0) { - if (now - lastAlarmSoundCycle >= 1000) { - lastAlarmSoundCycle = now; + if (alarmSoundType == 0) { + // Patrón 0: Casio Doble cada 1000ms + if (alarmSoundStep == 0) { + if (now - lastAlarmSoundCycle >= 1000) { + lastAlarmSoundCycle = now; + alarmSoundStep = 1; + alarmSoundStepTime = now; + ledcSetup(buzzerChannel, 4096, 10); + ledcWrite(buzzerChannel, duty); + } + } else if (alarmSoundStep == 1) { // 25ms chirp + if (now - alarmSoundStepTime >= 25) { + ledcWrite(buzzerChannel, 0); + alarmSoundStep = 2; + alarmSoundStepTime = now; + } + } else if (alarmSoundStep == 2) { // 35ms pausa + if (now - alarmSoundStepTime >= 35) { + ledcSetup(buzzerChannel, 4096, 10); + ledcWrite(buzzerChannel, duty); + alarmSoundStep = 3; + alarmSoundStepTime = now; + } + } else if (alarmSoundStep == 3) { // 25ms chirp + if (now - alarmSoundStepTime >= 25) { + ledcWrite(buzzerChannel, 0); + alarmSoundStep = 0; + } + } + } else if (alarmSoundType == 1) { + // Patrón 1: Sincronizado con el parpadeo del vúmetro + if (flashTick && arcFlashVisible) { + ledcSetup(buzzerChannel, 4096, 10); + ledcWrite(buzzerChannel, duty); alarmSoundStep = 1; alarmSoundStepTime = now; - ledcSetup(buzzerChannel, 4096, 10); - ledcWrite(buzzerChannel, duty); - } - } else if (alarmSoundStep == 1) { // 25ms first chirp - if (now - alarmSoundStepTime >= 25) { - ledcWrite(buzzerChannel, 0); - alarmSoundStep = 2; - alarmSoundStepTime = now; - } - } else if (alarmSoundStep == 2) { // 35ms gap - if (now - alarmSoundStepTime >= 35) { - ledcSetup(buzzerChannel, 4096, 10); - ledcWrite(buzzerChannel, duty); - alarmSoundStep = 3; - alarmSoundStepTime = now; - } - } else if (alarmSoundStep == 3) { // 25ms second chirp - if (now - alarmSoundStepTime >= 25) { + } else if (alarmSoundStep == 1 && (now - alarmSoundStepTime >= 30)) { ledcWrite(buzzerChannel, 0); alarmSoundStep = 0; } + } else if (alarmSoundType == 2) { + // Patrón 2: Bitonal Automotriz cada 1000ms (4096 Hz -> 3072 Hz) + if (alarmSoundStep == 0) { + if (now - lastAlarmSoundCycle >= 1000) { + lastAlarmSoundCycle = now; + alarmSoundStep = 1; + alarmSoundStepTime = now; + ledcSetup(buzzerChannel, 4096, 10); + ledcWrite(buzzerChannel, duty); + } + } else if (alarmSoundStep == 1) { // 40ms tono alto + if (now - alarmSoundStepTime >= 40) { + ledcWrite(buzzerChannel, 0); + alarmSoundStep = 2; + alarmSoundStepTime = now; + } + } else if (alarmSoundStep == 2) { // 20ms silencio + if (now - alarmSoundStepTime >= 20) { + ledcSetup(buzzerChannel, 3072, 10); + ledcWrite(buzzerChannel, duty); + alarmSoundStep = 3; + alarmSoundStepTime = now; + } + } else if (alarmSoundStep == 3) { // 40ms tono medio + if (now - alarmSoundStepTime >= 40) { + ledcWrite(buzzerChannel, 0); + alarmSoundStep = 0; + } + } + } else if (alarmSoundType == 3) { + // Patrón 3: Triple Urgente cada 1200ms + if (alarmSoundStep == 0) { + if (now - lastAlarmSoundCycle >= 1200) { + lastAlarmSoundCycle = now; + alarmSoundStep = 1; + alarmSoundStepTime = now; + ledcSetup(buzzerChannel, 4096, 10); + ledcWrite(buzzerChannel, duty); + } + } else if (alarmSoundStep == 1) { // 20ms pulso 1 + if (now - alarmSoundStepTime >= 20) { + ledcWrite(buzzerChannel, 0); + alarmSoundStep = 2; + alarmSoundStepTime = now; + } + } else if (alarmSoundStep == 2) { // 25ms pausa + if (now - alarmSoundStepTime >= 25) { + ledcSetup(buzzerChannel, 4096, 10); + ledcWrite(buzzerChannel, duty); + alarmSoundStep = 3; + alarmSoundStepTime = now; + } + } else if (alarmSoundStep == 3) { // 20ms pulso 2 + if (now - alarmSoundStepTime >= 20) { + ledcWrite(buzzerChannel, 0); + alarmSoundStep = 4; + alarmSoundStepTime = now; + } + } else if (alarmSoundStep == 4) { // 25ms pausa + if (now - alarmSoundStepTime >= 25) { + ledcSetup(buzzerChannel, 4096, 10); + ledcWrite(buzzerChannel, duty); + alarmSoundStep = 5; + alarmSoundStepTime = now; + } + } else if (alarmSoundStep == 5) { // 20ms pulso 3 + if (now - alarmSoundStepTime >= 20) { + ledcWrite(buzzerChannel, 0); + alarmSoundStep = 0; + } + } } } @@ -1595,7 +1679,7 @@ void handleArcAlarmFlash() { } } } - handleAlarmSound(anyInRedAlarm); + handleAlarmSound(anyInRedAlarm, toggleTick); } // Target and current brightness variables for smooth fade diff --git a/sketch/obd2_gauge/obd2_gauge.ino b/sketch/obd2_gauge/obd2_gauge.ino index 8c4add2..c89a9a5 100644 --- a/sketch/obd2_gauge/obd2_gauge.ino +++ b/sketch/obd2_gauge/obd2_gauge.ino @@ -97,6 +97,8 @@ bool alarmEnabled[4] = { true, true, true, true }; const uint16_t VOL_DUTY_TABLE[6] = { 0, 2, 8, 25, 80, 250 }; const char* const VOL_NAME_TABLE[6] = { "SILENCIO", "ULTRA BAJO", "MUY BAJO", "BAJO (CASIO)", "MEDIO", "ALTO" }; uint8_t buzzerVolumeLevel = 3; // Default 3: Bajo (Casio suave) +uint8_t alarmVolumeLevel = 3; // Default 3: Bajo/Casio (Volumen independiente de alarma) +uint8_t alarmSoundType = 0; // Default 0: Casio Doble (0: Casio Doble, 1: Sincronizado, 2: Bitonal, 3: Triple Urgente) /* 0 - Refrigerante @@ -286,8 +288,12 @@ recent_client_addr : {0x00,0x00,0x00,0x00,0x00,0x00} array of bytes[6] String almKey = "alm_" + String(i); alarmEnabled[i] = pref.getBool(almKey.c_str(), true); } - buzzerVolumeLevel = pref.getUChar("b_vol", 2); - if (buzzerVolumeLevel > 5) buzzerVolumeLevel = 2; + buzzerVolumeLevel = pref.getUChar("b_vol", 3); + if (buzzerVolumeLevel > 5) buzzerVolumeLevel = 3; + alarmVolumeLevel = pref.getUChar("alm_vol", 3); + if (alarmVolumeLevel > 5) alarmVolumeLevel = 3; + alarmSoundType = pref.getUChar("alm_type", 0); + if (alarmSoundType > 3) alarmSoundType = 0; pref.getBytes("recent_client", recent_client_addr, pref.getBytesLength("recent_client")); //read last bt address