12-OBD-Speed: battery voltage arc and idle/screen-off mode

- ATRV every 2 s; voltage arc in the bottom gap (11-15 V) + number,
  colored by state (low / weak / resting / charging / overcharge)
- idle after Off/2/5/10 min without speed data (settings page, NVS):
  screen off, GIF paused, OBD polling stopped, ATRV every 30 s;
  wakes on touch (swallowed by a top-layer overlay) or > 13.2 V
- status pill moved up to make room for the voltage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
patriandClaude Opus 5.5 committed 2026-10-23 18:32:56 +02:00
1 parent 0777db3498
commit be69bda4f1
5 files changed
+258 -16

No files matched your search

+78 -2
View File
@@ -4,12 +4,20 @@
#include <Preferences.h>
#define RETRY_DELAY_MS 3000
#define IDLE_RETRY_DELAY_MS 30000
#define POLL_FAIL_LIMIT 3
#define VOLTAGE_INTERVAL_MS 2000 // ATRV while driving
#define IDLE_VOLTAGE_INTERVAL_MS 30000 // ATRV while idle (adapter-internal, doesn't wake the car's bus)
#define WAKE_VOLTAGE 13.2f // above this the alternator is charging: engine running
// ---------- shared state (protected by `mtx`) ----------
static SemaphoreHandle_t mtx;
static volatile ObdState state = ObdState::NoDevice;
static volatile int speed_kmh = -1;
static volatile float voltage = -1;
static volatile bool idle = false;
static volatile uint32_t idle_timeout_ms = 2 * 60 * 1000;
static volatile uint32_t last_data_ms = 0; // last valid speed (or wake-up)
static String status_text = "starting";
static String saved_addr, saved_name;
static uint8_t saved_type = 0;
@@ -18,7 +26,7 @@ static uint32_t devices_version = 1;
static String elm_rx; // bytes received from the ELM (notifications)
// ---------- requests from the UI ----------
static volatile bool req_select = false, req_forget = false;
static volatile bool req_select = false, req_forget = false, req_wake = false;
static volatile bool scan_wanted = false;
static ObdDevInfo req_dev;
@@ -155,6 +163,23 @@ static int parse_speed(String reply)
return (int)strtol(reply.substring(i + 4, i + 6).c_str(), nullptr, 16);
}
// "12.6V" -> 12.6, -1 if not a voltage
static float parse_voltage(const String &reply)
{
int v = reply.indexOf('V');
if (v <= 0)
return -1;
float f = reply.substring(0, v).toFloat();
return f > 0 ? f : -1;
}
static void read_voltage()
{
float v = parse_voltage(elm_command("ATRV", 1500));
if (v > 0)
voltage = v;
}
// ---------- connection ----------
static void disconnect()
{
@@ -259,9 +284,28 @@ static void load_saved()
static void worker_task(void *)
{
uint32_t next_attempt = 0;
uint32_t next_voltage = 0;
int fails = 0;
last_data_ms = millis();
for (;;)
{
if (req_wake)
{
req_wake = false;
if (idle)
Serial.println("[obd] wake (touch)");
idle = false;
last_data_ms = millis();
next_attempt = 0;
}
// no speed data for a while (ignition off / adapter gone): go idle
if (!idle && idle_timeout_ms && millis() - last_data_ms > idle_timeout_ms)
{
idle = true;
speed_kmh = -1;
Serial.println("[obd] idle");
}
// requests from the UI
if (req_forget)
{
@@ -296,6 +340,8 @@ static void worker_task(void *)
if (saved_addr.length() == 0)
{
state = ObdState::NoDevice;
idle = false;
last_data_ms = millis(); // no idle while no adapter is chosen (user is setting up)
set_status("No adapter - swipe left");
vTaskDelay(pdMS_TO_TICKS(100));
continue;
@@ -317,11 +363,36 @@ static void worker_task(void *)
{
disconnect();
state = ObdState::Waiting;
next_attempt = millis() + RETRY_DELAY_MS;
next_attempt = millis() + (idle ? IDLE_RETRY_DELAY_MS : RETRY_DELAY_MS);
}
next_voltage = 0;
continue;
}
if (idle)
{
// only the battery voltage now and then; an engine start (charging voltage) wakes us up
if ((int32_t)(millis() - next_voltage) >= 0)
{
next_voltage = millis() + IDLE_VOLTAGE_INTERVAL_MS;
read_voltage();
if (voltage > WAKE_VOLTAGE)
{
idle = false;
last_data_ms = millis();
Serial.printf("[obd] wake (%.1f V)\n", voltage);
}
}
vTaskDelay(pdMS_TO_TICKS(100));
continue;
}
if ((int32_t)(millis() - next_voltage) >= 0)
{
next_voltage = millis() + VOLTAGE_INTERVAL_MS;
read_voltage();
}
// connected: poll the vehicle speed
String reply = elm_command("010D", 1500);
int v = parse_speed(reply);
@@ -329,6 +400,7 @@ static void worker_task(void *)
if (v >= 0)
{
fails = 0;
last_data_ms = millis();
set_status(saved_name.length() ? saved_name : saved_addr);
}
else if (reply.length() == 0)
@@ -365,6 +437,10 @@ void obd_begin()
ObdState obd_state() { return state; }
int obd_speed() { return speed_kmh; }
float obd_voltage() { return voltage; }
bool obd_is_idle() { return idle; }
void obd_wake() { req_wake = true; }
void obd_set_idle_timeout(uint32_t minutes) { idle_timeout_ms = minutes * 60 * 1000; }
String obd_status_text()
{