obd2_gauge/sketch/obd2_gauge/config.h

317 lines
9.2 KiB
C

/* "config.h"
Configuration menu for OBD2 Gauge with volume and per-instrument alarm controls
*/
#include <FS.h>
#include <SD.h>
#include <SPIFFS.h>
#define mypic_filename "/mypic.jpg"
#define FORMAT_SPIFFS_IF_FAILED true
/*------------ SOUND EFFECTS ----------*/
// Crisp High-Pitch Casio Chirp (4096 Hz, 25ms)
inline void casioBeep(uint16_t freq = 4096, uint8_t duration_ms = 25) {
uint16_t duty = VOL_DUTY_TABLE[buzzerVolumeLevel > 5 ? 3 : buzzerVolumeLevel];
if (duty == 0) return; // Mute mode
ledcSetup(buzzerChannel, freq, 10);
ledcWrite(buzzerChannel, duty);
delay(duration_ms);
ledcWrite(buzzerChannel, 0);
}
void beepbeep() {
casioBeep(4096, 25);
delay(30);
casioBeep(4096, 25);
}
void beep() {
casioBeep(4096, 40);
}
void clickSound() {
casioBeep(4096, 25);
}
// Configuration menu list (3 options)
const String menuList[3] = {
"Ajuste de Alarmas", // 0
"Volumen de Bip", // 1
"Salir" // 2
};
const uint8_t maxMenu = 3;
// Menu list for warning setting (4 vehicle gauges)
const String parameterList[4][2] = {
{"Temperatura", " `C"},
{"RPM", " rpm"},
{"Voltaje", " V"},
{"Velocidad", " km/h"}
};
// -------------------------------------------------------------
// Sub-screen 0: Ajuste de Alarmas (Activación Tactil ON/OFF)
// -------------------------------------------------------------
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);
auto drawAllRows = [&]() {
for (uint8_t i = 0; i < 4; i++) {
int y = 34 + i * 39;
tft.fillRoundRect(8, y, 304, 36, 5, 0x18E3);
tft.setTextColor(alarmEnabled[i] ? TFT_WHITE : 0x8410, 0x18E3);
tft.drawString(parameterList[i][0], 22, y + 6, 4);
// Large tactile ON / OFF badge
uint16_t badgeCol = alarmEnabled[i] ? TFT_GREEN : 0x8000;
tft.fillRoundRect(220, y + 4, 84, 28, 5, badgeCol);
tft.drawRoundRect(220, y + 4, 84, 28, 5, TFT_WHITE);
tft.setTextColor(TFT_WHITE, badgeCol);
tft.drawCentreString(alarmEnabled[i] ? "ON" : "OFF", 262, y + 6, 4);
}
};
drawAllRows();
// Bottom action button: [ GUARDAR Y SALIR ]
tft.fillRoundRect(25, 194, 270, 38, 5, 0x02EC); // Rich Ocean Blue
tft.setTextColor(TFT_WHITE);
tft.drawCentreString("GUARDAR Y SALIR", 160, 201, 4);
delay(200);
while (true) {
autoDim();
uint16_t tx = 0, ty = 0;
if (getTouch(&tx, &ty)) {
// 1. Direct touch on any parameter row to toggle ON/OFF
if (ty >= 32 && ty < 190) {
int8_t row = (ty - 34) / 39;
if (row >= 0 && row < 4) {
alarmEnabled[row] = !alarmEnabled[row];
drawAllRows();
casioBeep(4096, 25);
while (isScreenPressed()) { delay(10); yield(); }
delay(50);
}
}
// 2. Bottom button [ GUARDAR Y SALIR ] or top bar
else if ((ty >= 190 && ty <= 238 && tx >= 20 && tx <= 300) || ty < 30) {
casioBeep(4096, 25);
pref.begin("setting", false);
for (uint8_t i = 0; i < 4; i++) {
String almKey = "alm_" + String(i);
pref.putBool(almKey.c_str(), alarmEnabled[i]);
}
pref.end();
while (isScreenPressed()) { delay(10); yield(); }
break;
}
} else if (digitalRead(SELECTOR_PIN) == LOW) {
delay(200);
break;
}
yield();
}
}
// -------------------------------------------------------------
// Sub-screen 1: Volumen de Bip (Ajuste Tactil Directo)
// -------------------------------------------------------------
void volumeMenu() {
tft.fillScreen(TFT_BLACK);
tft.fillRectVGradient(0, 0, 320, 26, TFT_ORANGE, 0xfc00);
tft.setTextColor(TFT_BLACK);
tft.drawCentreString("[--- Volumen de Bip ---]", 159, 0, 4);
auto drawVolUI = []() {
tft.fillRect(0, 28, 320, 150, TFT_BLACK);
// Level number and title
tft.setTextColor(TFT_WHITE, TFT_BLACK);
String lvlStr = "Nivel " + String(buzzerVolumeLevel) + " de 5";
tft.drawCentreString(lvlStr.c_str(), 160, 34, 4);
tft.setTextColor(buzzerVolumeLevel == 0 ? TFT_RED : TFT_CYAN, TFT_BLACK);
tft.drawCentreString(VOL_NAME_TABLE[buzzerVolumeLevel], 160, 60, 4);
// 6 Direct Touch Tiles (0 to 5)
for (uint8_t i = 0; i <= 5; i++) {
int16_t bx = 7 + i * 52;
int16_t by = 94;
int16_t bw = 46;
int16_t bh = 68;
bool isSel = (i == buzzerVolumeLevel);
uint16_t bgCol;
if (isSel) {
bgCol = (i == 0) ? 0x8000 : (i >= 4 ? TFT_ORANGE : (i >= 2 ? TFT_GREEN : 0x03E0));
} else {
bgCol = 0x18E3; // Dark charcoal slate
}
tft.fillRoundRect(bx, by, bw, bh, 6, bgCol);
if (isSel) {
tft.drawRoundRect(bx, by, bw, bh, 6, TFT_WHITE);
tft.drawRoundRect(bx + 1, by + 1, bw - 2, bh - 2, 5, TFT_WHITE);
}
tft.setTextColor(isSel ? TFT_WHITE : 0xAD55, bgCol);
tft.drawCentreString(String(i).c_str(), bx + bw / 2, by + 14, 6);
}
};
drawVolUI();
// [ GUARDAR Y SALIR ]
tft.fillRoundRect(25, 186, 270, 42, 6, 0x02EC); // Rich Ocean Blue
tft.setTextColor(TFT_WHITE);
tft.drawCentreString("GUARDAR Y SALIR", 160, 196, 4);
delay(200);
while (true) {
autoDim();
uint16_t tx = 0, ty = 0;
if (getTouch(&tx, &ty)) {
// 1. Direct Touch on the 6 volume tiles (y: 85 to 175)
if (ty >= 85 && ty <= 175) {
int8_t touchedLvl = (tx - 7) / 52;
if (touchedLvl >= 0 && touchedLvl <= 5) {
buzzerVolumeLevel = touchedLvl;
drawVolUI();
casioBeep(4096, 25); // Immediate acoustic feedback at selected volume
}
while (isScreenPressed()) { delay(10); yield(); }
delay(50);
}
// 2. GUARDAR Y SALIR (y: 180 to 238) or top bar
else if ((ty >= 180 && ty <= 238 && tx >= 20 && tx <= 300) || ty < 30) {
casioBeep(4096, 25);
pref.begin("setting", false);
pref.putUChar("b_vol", buzzerVolumeLevel);
pref.end();
while (isScreenPressed()) { delay(10); yield(); }
break;
}
} else if (digitalRead(SELECTOR_PIN) == LOW) {
delay(200);
break;
}
yield();
}
}
// -------------------------------------------------------------
// -------------------------------------------------------------
// Menu Principal
// -------------------------------------------------------------
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;
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.setTextColor(TFT_WHITE, TFT_BLUE);
} else {
tft.fillRoundRect(10, y, 300, 48, 6, 0x18E3); // subtle dark card
tft.setTextColor(TFT_WHITE, 0x18E3);
}
tft.drawString(menuList[i], 25, y + 12, 4);
}
}
void configMenu() {
uint8_t select = 0;
bool pressed = false;
long holdtimer = 0;
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);
delay(300);
while (true) {
autoDim();
int8_t executeSelect = -1;
uint16_t tx = 0, ty = 0;
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 (touchedOpt >= 0) {
select = touchedOpt;
listMenu(select);
clickSound();
while (isScreenPressed()) {
delay(10);
yield();
}
delay(80);
executeSelect = select;
}
} else if (digitalRead(SELECTOR_PIN) == LOW) {
if (!pressed) {
pressed = true;
holdtimer = millis();
} else if (millis() - holdtimer > 1200) {
executeSelect = select;
pressed = false;
}
} else {
if (pressed) {
if (millis() - holdtimer >= 40 && millis() - holdtimer <= 1200) {
select++;
if (select >= maxMenu) select = 0;
listMenu(select);
clickSound();
}
pressed = false;
delay(150);
}
}
if (executeSelect >= 0) {
select = executeSelect;
// Option 0: Warning setting (Thresholds & ON/OFF)
if (select == 0) {
warningMenu();
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 1: Volume setting (Buzzer level & live touch)
else if (select == 1) {
volumeMenu();
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: Exit
else if (select == 2) {
break;
}
pressed = false;
prompt = true;
}
delay(30);
}
tft.fillScreen(TFT_BLACK);
}