317 lines
9.2 KiB
C
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);
|
|
}
|