ELMduino icon indicating copy to clipboard operation
ELMduino copied to clipboard

Watchdog Triggered Cheap Yellow Display

Open brickhockey3 opened this issue 1 year ago • 7 comments

I am currently having an issue when trying to connect to SAE1850 devices but Ido not encounter the error with OBDII devices. I am trying to use LVGL on a cheap yellow display to make a simple multigauge.

#include <lvgl.h>
#include <TFT_eSPI.h>
#include <XPT2046_Touchscreen.h>
#include <BluetoothSerial.h>
#include <ELMduino.h>
#include <atomic>

#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320
#define XPT2046_IRQ 36
#define XPT2046_MOSI 32
#define XPT2046_MISO 39
#define XPT2046_CLK 25
#define XPT2046_CS 33

#define LCD_BACK_LIGHT_PIN 21
#define LEDC_CHANNEL_0     0
#define LEDC_TIMER_12_BIT  12
#define LEDC_BASE_FREQ     5000

SPIClass touchscreenSPI(VSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);
TFT_eSPI tft = TFT_eSPI();
uint32_t draw_buf[SCREEN_WIDTH * SCREEN_HEIGHT / 10];

BluetoothSerial SerialBT;
ELM327 obd;
std::atomic<uint32_t> rpm{0};
std::atomic<uint32_t> coolant_temp{0};
std::atomic<uint32_t> voltage{0};
std::atomic<uint32_t> tpsval{0};
uint32_t last_displayed_rpm = -1;
uint32_t last_displayed_temp = -1;
uint32_t last_displayed_voltage = -1;
uint32_t last_displayed_tpsval = -1;

SemaphoreHandle_t lvgl_mutex;
TaskHandle_t lvgl_task_handle;
TaskHandle_t obd_task_handle;
TaskHandle_t bt_icon_task_handle;

static lv_obj_t *status_label;
static lv_obj_t *rpm_label;
static lv_obj_t *rpm_val;
static lv_obj_t *coolant_temp_label;
static lv_obj_t *coolant_temp_val;
static lv_obj_t *voltage_label;
static lv_obj_t *voltage_unit_label;
static lv_obj_t *voltage_val;
static lv_obj_t *TPS_label;
static lv_obj_t *TPS_unit_label;
static lv_obj_t *TPS_val;
static lv_obj_t *bt_icon_label;
static lv_obj_t *toggle_btn;
static lv_obj_t *settings_btn;
static lv_obj_t *settings_scr;
static lv_obj_t *main_scr;
static lv_obj_t *brightness_slider;

bool use_celsius = false;
uint8_t current_brightness = 128;

void lvgl_task(void *parameters);
void obd_task(void *parameters);
void bt_icon_task(void *parameters);
void show_main_screen();
void show_settings_screen();

void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {
  // calculate duty, 4095 from 2 ^ 12 - 1
  uint32_t duty = (4095 * min(value, valueMax)) / valueMax;

  // write duty to LEDC
  ledcWrite(channel, duty);
  Serial.printf("duty value: %d\n", duty);
  Serial.printf("brightness value: %d\n", current_brightness);
}

void brightness_slider_event_handler(lv_event_t *e) {
  lv_obj_t *slider = (lv_obj_t *)lv_event_get_target(e);  // Explicit cast added here
  int value = lv_slider_get_value(slider);
  current_brightness = value;  // Update the global brightness value
  Serial.printf("Brightness Slider Value: %d\n", value);  // Log to Serial Monitor
  ledcAnalogWrite(LEDC_CHANNEL_0, value);
  // ⚡️ Later: Add brightness control logic here
}

void toggle_temp_unit_event_handler(lv_event_t *e) {
  use_celsius = !use_celsius;
  lv_obj_t *btn = (lv_obj_t *)lv_event_get_current_target(e);
  lv_label_set_text(lv_obj_get_child(btn, 0), use_celsius ? "Units: Metric" : "Units: Standard");
  update_coolant_temp_val(coolant_temp.load());
  Serial.printf("Temperature unit changed to: %s\n", use_celsius ? "metric" : "standard");
}

void back_btn_event_handler(lv_event_t *e) {
  show_main_screen();
}

void settings_btn_event_handler(lv_event_t *e) {
  show_settings_screen();
}

void lv_create_settings_gui() {
  settings_scr = lv_obj_create(NULL);
  lv_obj_t *label = lv_label_create(settings_scr);
  lv_label_set_text(label, "Settings");
  lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 10);

  lv_obj_t *temp_unit_btn = lv_btn_create(settings_scr);
  lv_obj_add_flag(temp_unit_btn, LV_OBJ_FLAG_CHECKABLE);
  lv_obj_set_height(temp_unit_btn, LV_SIZE_CONTENT);
  lv_obj_align(temp_unit_btn, LV_ALIGN_CENTER, 0, 30);
  lv_obj_add_event_cb(temp_unit_btn, toggle_temp_unit_event_handler, LV_EVENT_CLICKED, NULL);
  lv_obj_t *btn_label = lv_label_create(temp_unit_btn);
  lv_label_set_text(btn_label, "Units: Standard");

  lv_obj_t *brightness_label = lv_label_create(settings_scr);
  lv_label_set_text(brightness_label, "Brightness");
  lv_obj_align(brightness_label, LV_ALIGN_CENTER, 0, -60);

  brightness_slider = lv_slider_create(settings_scr);
  lv_slider_set_range(brightness_slider, 0, 255);
  lv_slider_set_value(brightness_slider, current_brightness, LV_ANIM_OFF);
  lv_obj_set_size(brightness_slider, 180, 10);
  lv_obj_align(brightness_slider, LV_ALIGN_CENTER, 0, -40);
  lv_obj_add_event_cb(brightness_slider, brightness_slider_event_handler, LV_EVENT_VALUE_CHANGED, NULL);

  lv_obj_t *back_btn = lv_btn_create(settings_scr);
  lv_obj_set_height(back_btn, LV_SIZE_CONTENT);
  lv_obj_align(back_btn, LV_ALIGN_TOP_RIGHT, -10, 10);
  lv_obj_add_event_cb(back_btn, back_btn_event_handler, LV_EVENT_CLICKED, NULL);
  lv_obj_t *back_label = lv_label_create(back_btn);
  lv_label_set_text(back_label, "Back");
}

void lv_create_main_gui() {
  static lv_style_t style_large_text;
lv_style_init(&style_large_text);
lv_style_set_text_font(&style_large_text, &lv_font_montserrat_48); // Change to desired font
static lv_style_t style_medium_text;
lv_style_init(&style_medium_text);
lv_style_set_text_font(&style_medium_text, &lv_font_montserrat_32); // Change to desired font
static lv_style_t style_mm_text;
lv_style_init(&style_mm_text);
lv_style_set_text_font(&style_mm_text, &lv_font_montserrat_22); // Change to desired font
static lv_style_t style_small_text;
lv_style_init(&style_small_text);
lv_style_set_text_font(&style_small_text, &lv_font_montserrat_18); // Change to desired font

  main_scr = lv_scr_act();
  status_label = lv_label_create(main_scr);
  lv_label_set_text(status_label, "Status: Initializing...");
  lv_obj_add_style(status_label, &style_small_text, 0);
  lv_obj_align(status_label, LV_ALIGN_TOP_MID, 0, 10);

  rpm_val = lv_label_create(main_scr);
  lv_label_set_text(rpm_val, "----");
  lv_obj_add_style(rpm_val, &style_large_text, 0);
  lv_obj_align(rpm_val, LV_ALIGN_CENTER, 80, -20);

  rpm_label = lv_label_create(main_scr);
  lv_label_set_text(rpm_label, "RPM:");
  lv_obj_add_style(rpm_label, &style_medium_text, 0);
  lv_obj_align_to(rpm_label, rpm_val, LV_ALIGN_CENTER, 0, -30);

  voltage_val = lv_label_create(main_scr);
  lv_label_set_text(voltage_val, "--.-");
  lv_obj_add_style(voltage_val, &style_large_text, 0);
  lv_obj_align(voltage_val, LV_ALIGN_CENTER, 80, 70);

  voltage_label = lv_label_create(main_scr);
  lv_label_set_text(voltage_label, "Volts:");
  lv_obj_add_style(voltage_label, &style_medium_text, 0);
  lv_obj_align_to(voltage_label, voltage_val, LV_ALIGN_CENTER, 0, -40);
  voltage_unit_label = lv_label_create(main_scr);
  lv_label_set_text(voltage_unit_label, "V");
  lv_obj_add_style(voltage_unit_label, &style_large_text, 0);
  lv_obj_align_to(voltage_unit_label, voltage_val, LV_ALIGN_CENTER, 40, 0);

  TPS_val = lv_label_create(main_scr); 
  lv_label_set_text(TPS_val, "---");
  lv_obj_add_style(TPS_val, &style_large_text, 0);
  lv_obj_align(TPS_val, LV_ALIGN_CENTER, -80, -20);

  TPS_label = lv_label_create(main_scr); 
  lv_label_set_text(TPS_label, "Throttle Position:");
  lv_obj_add_style(TPS_label, &style_small_text, 0);
  lv_obj_align_to(TPS_label, TPS_val, LV_ALIGN_CENTER, 0, -30);
  TPS_unit_label = lv_label_create(main_scr); 
  lv_label_set_text(TPS_unit_label, "%");
  lv_obj_add_style(TPS_unit_label, &style_large_text, 0);
  lv_obj_align_to(TPS_unit_label, TPS_val, LV_ALIGN_CENTER, 60, 0);

  coolant_temp_val = lv_label_create(main_scr);
  update_coolant_temp_val(coolant_temp.load());
  lv_obj_add_style(coolant_temp_val, &style_large_text, 0);
  lv_obj_align(coolant_temp_val, LV_ALIGN_CENTER, -80, 70);

  coolant_temp_label = lv_label_create(main_scr);
  lv_label_set_text(coolant_temp_label, "Coolant Temp:");
  lv_obj_add_style(coolant_temp_label, &style_small_text, 0);
  lv_obj_align_to(coolant_temp_label, coolant_temp_val, LV_ALIGN_CENTER, 0, -40);

  bt_icon_label = lv_label_create(main_scr);
  lv_label_set_text(bt_icon_label, "");
  lv_obj_add_style(bt_icon_label, &style_small_text, 0);
  lv_obj_align(bt_icon_label, LV_ALIGN_TOP_LEFT, 10, 10);

  settings_btn = lv_btn_create(main_scr);
  lv_obj_set_height(settings_btn, LV_SIZE_CONTENT);
  lv_obj_align(settings_btn, LV_ALIGN_TOP_RIGHT, -5, 5);
  lv_obj_add_event_cb(settings_btn, settings_btn_event_handler, LV_EVENT_CLICKED, NULL);
  lv_obj_t *settings_label = lv_label_create(settings_btn);
  lv_label_set_text(settings_label, LV_SYMBOL_SETTINGS);
}

void show_main_screen() {
  lv_scr_load(main_scr);
}

void show_settings_screen() {
  lv_scr_load(settings_scr);
}

void setup() {
  Serial.begin(115200);
  lv_init();
  lvgl_mutex = xSemaphoreCreateMutex();

  touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
  touchscreen.begin(touchscreenSPI);
  touchscreen.setRotation(2);
  tft.init();
  tft.begin();
  
  // Initialize LEDC for backlight control
  ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT);
  ledcAttachPin(LCD_BACK_LIGHT_PIN, LEDC_CHANNEL_0);
  ledcAnalogWrite(LEDC_CHANNEL_0, current_brightness); // Set initial brightness
  delay(1000);
    Serial.println("Setting initial brightness to 25...");
    ledcAnalogWrite(LEDC_CHANNEL_0, 25); // Set to mid-high brightness
  tft.setRotation(1);
  tft.fillScreen(TFT_BLACK);

  lv_display_t *disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
  lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);

  lv_indev_t *indev = lv_indev_create();
  lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
  lv_indev_set_read_cb(indev, touchscreen_read);

  lv_create_main_gui();
  lv_create_settings_gui();

  xTaskCreatePinnedToCore(lvgl_task, "LVGL Task", 12000, NULL, 3, &lvgl_task_handle, 1);
  xTaskCreatePinnedToCore(obd_task, "OBD Task", 10000, NULL, 2, &obd_task_handle, 0);
  xTaskCreatePinnedToCore(bt_icon_task, "BT Icon Task", 2048, NULL, 1, &bt_icon_task_handle, 1);
}

void loop() {
  vTaskDelete(NULL);
}

void touchscreen_read(lv_indev_t *indev, lv_indev_data_t *data) {
  if (touchscreen.tirqTouched() && touchscreen.touched()) {
    TS_Point p = touchscreen.getPoint();
    data->point.x = map(p.x, 200, 3700, 1, SCREEN_WIDTH);
    data->point.y = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);
    data->state = LV_INDEV_STATE_PRESSED;
  } else {
    data->state = LV_INDEV_STATE_RELEASED;
  }
}

void lv_safe_call(std::function<void()> func) {
  if (xSemaphoreTake(lvgl_mutex, pdMS_TO_TICKS(50))) {
    func();
    xSemaphoreGive(lvgl_mutex);
  }
}

void update_status_label(const char *text) {
  lv_safe_call([&]() { lv_label_set_text(status_label, text); });
}

void update_rpm_val(uint32_t current_rpm) {
  if (current_rpm > 0 && current_rpm != last_displayed_rpm) {
    lv_safe_call([&]() {
      char buf[16];
      snprintf(buf, sizeof(buf), "%d", current_rpm);
      lv_label_set_text(rpm_val, buf);
    });
    last_displayed_rpm = current_rpm;
  }
}

void update_coolant_temp_val(uint32_t temp) {
  lv_safe_call([&]() {
    char buf[16];  // Adjust buffer size since we removed extra text
    if (temp == 0 && last_displayed_temp == -1) {
      snprintf(buf, sizeof(buf), "--%s", use_celsius ? "°C" : "°F");
    } else {
      uint32_t display_temp = use_celsius ? temp : static_cast<uint32_t>((temp * 9.0 / 5.0) + 32);
      snprintf(buf, sizeof(buf), "%d%s", display_temp, use_celsius ? "°C" : "°F");
    }
    lv_label_set_text(coolant_temp_val, buf);
    last_displayed_temp = temp;
  });
}

void update_voltage_val(uint32_t voltage) {
  if (voltage != last_displayed_voltage) {
    lv_safe_call([&]() {
      char buf[32];  // Increase buffer size to accommodate decimal points
      snprintf(buf, sizeof(buf), "%.1f", static_cast<float>(voltage)); // Use float directly
      lv_label_set_text(voltage_val, buf);
    });
    last_displayed_voltage = voltage;
  }
}

void update_TPS_val(uint32_t tpsval) {
  if (tpsval != last_displayed_tpsval) {
    lv_safe_call([&]() {
      char buf[32];  // Increase buffer size to accommodate decimal points
      snprintf(buf, sizeof(buf), "%d", tpsval);
      lv_label_set_text(TPS_val, buf);
    });
    last_displayed_tpsval = tpsval;
  }
}

void connect_to_obd() {
  update_status_label("Connecting to OBD...");
  SerialBT.begin("ESP32_OBD", true);
  //SerialBT.setTimeout(50);

  if (!SerialBT.connect("OBDII")) {
    update_status_label("Retrying OBD connection...");
    //vTaskDelay(pdMS_TO_TICKS(1000));
  }

  SerialBT.println("AT Z");     // Reset
  vTaskDelay(pdMS_TO_TICKS(1000));

  if (!obd.begin(SerialBT, true, 2000)) {
    update_status_label("Reinitializing ELM327...");
   // vTaskDelay(pdMS_TO_TICKS(200));
  }
  update_status_label("Connected to ELM327");
}

void lvgl_task(void *parameters) {
  while (true) {
    if (xSemaphoreTake(lvgl_mutex, portMAX_DELAY)) {
      lv_task_handler();
      lv_tick_inc(5);
      xSemaphoreGive(lvgl_mutex);
    }
    vTaskDelay(pdMS_TO_TICKS(2));
  }
}

typedef enum {
  ENG_RPM,
  COOLANT_TEMP,
  VOLTAGE,
  TPS
} obd_pid_states;

void obd_task(void *parameters) {
  connect_to_obd();  // Initial connection
  obd_pid_states obd_state = ENG_RPM;

  while (true) {
    if (!SerialBT.hasClient()) { 
      // If Bluetooth is disconnected, attempt to reconnect
      update_status_label("OBD Disconnected! Reconnecting...");

      while (!SerialBT.hasClient()) {  // Keep trying until connected
        Serial.println("Attempting to reconnect...");
        SerialBT.end();  // Reset Bluetooth
        vTaskDelay(pdMS_TO_TICKS(500)); // Short delay
        SerialBT.begin("ESP32_OBD", true);
        if (SerialBT.connect("OBDII")) {
          connect_to_obd();  // Reinitialize OBD connection
          update_status_label("Reconnected to OBD");
        }
        vTaskDelay(pdMS_TO_TICKS(2000)); // Wait before retrying
      }
    }

    // Continue polling OBD if connected
    switch (obd_state) {
      case ENG_RPM: {
        float tempRPM = obd.rpm();
        if (obd.nb_rx_state == ELM_SUCCESS) {
          rpm = static_cast<uint32_t>(tempRPM);
          update_rpm_val(rpm.load());
          obd_state = COOLANT_TEMP;
        } else if (obd.nb_rx_state != ELM_GETTING_MSG) {
          obd.printError();
          obd_state = COOLANT_TEMP;
        }
        break;
      }

      case COOLANT_TEMP: {
        float tempC = obd.engineCoolantTemp();
        if (obd.nb_rx_state == ELM_SUCCESS) {
          coolant_temp = static_cast<uint32_t>(tempC);
          update_coolant_temp_val(coolant_temp.load());
          obd_state = VOLTAGE;
        } else if (obd.nb_rx_state != ELM_GETTING_MSG) {
          obd.printError();
          obd_state = VOLTAGE;
        }
        break;
      }

      case VOLTAGE: {
        float voltageC = obd.ctrlModVoltage();
        if (obd.nb_rx_state == ELM_SUCCESS) {
          voltage = static_cast<uint32_t>(voltageC);
          update_voltage_val(voltage.load());
          obd_state = TPS;
        } else if (obd.nb_rx_state != ELM_GETTING_MSG) {
          obd.printError();
          obd_state = TPS;
        }
        break;
      }

      case TPS: {
        float throttle = obd.throttle();
        if (obd.nb_rx_state == ELM_SUCCESS) {
          tpsval = static_cast<uint32_t>(throttle);
          update_TPS_val(tpsval.load());
          obd_state = ENG_RPM;
        } else if (obd.nb_rx_state != ELM_GETTING_MSG) {
          obd.printError();
          obd_state = ENG_RPM;
        }
        break;
      }
    }

    vTaskDelay(pdMS_TO_TICKS(12));  // Small delay between readings
  }
}

void bt_icon_task(void *parameters) {
  bool visible = true;
  while (true) {
    lv_safe_call([&]() {
      lv_label_set_text(bt_icon_label, SerialBT.hasClient() ? LV_SYMBOL_BLUETOOTH : (visible ? LV_SYMBOL_BLUETOOTH : ""));
    // Update TPS label when Bluetooth is disconnected
      if (!SerialBT.hasClient()) {
        lv_label_set_text(rpm_val, "----");
        lv_label_set_text(coolant_temp_val, "---");
        lv_label_set_text(voltage_val, "--");
        lv_label_set_text(TPS_val, "---");
        update_status_label("Retrying connection...");
      }
    });

    visible = !visible;
    vTaskDelay(pdMS_TO_TICKS(SerialBT.hasClient() ? 500 : 250));
  }
}

Serial monitor:

Delimiter found.
All chars received: OK
Clearing input serial buffer
Sending the following command/query: 0100
	Received char: S
	Received char: E
	Received char: A
	Received char: R
	Received char: C
	Received char: H
	Received char: I
	Received char: N
	Received char: G
	Received char: .
	Received char: .
	Received char: .
	Received char: \r
E (47170) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (47170) task_wdt:  - IDLE (CPU 0)
E (47170) task_wdt: Tasks currently running:
E (47170) task_wdt: CPU 0: OBD Task
E (47170) task_wdt: CPU 1: IDLE
E (47170) task_wdt: Aborting.

When I use the Bluetooth_multiple_pids example my serial monitor returns:

All chars received: OK
Clearing input serial buffer
Sending the following command/query: 0100
	Received char: S
	Received char: E
	Received char: A
	Received char: R
	Received char: C
	Received char: H
	Received char: I
	Received char: N
	Received char: G
	Received char: .
	Received char: .
	Received char: .
	Received char: \r
	Received char: U
	Received char: N
	Received char: A
	Received char: B
	Received char: L
	Received char: E
	Received char: _
	Received char: T
	Received char: O
	Received char: _
	Received char: C
	Received char: O
Received char: N
	Received char: N
	Received char: E
	Received char: C
	Received char: T
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: SEARCHING...UNABLETOCONNECT
ELM responded with error "UNABLE TO CONNECT"
Setting protocol via AT TP A%c did not work - trying via AT SP %c
Clearing input serial buffer
Sending the following command/query: AT SP 0
	Received char: O
	Received char: K
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: OK

brickhockey3 avatar Mar 13 '25 18:03 brickhockey3

Since you know the protocol you want to connect with, try specifying that in obd.begin(). That will avoid the delay incurred with searching for the correct protocol.

jimwhitelaw avatar Mar 13 '25 19:03 jimwhitelaw

Since you know the protocol you want to connect with, try specifying that in obd.begin(). That will avoid the delay incurred with searching for the correct protocol.

Well my car uses OBDII and my truck uses SAEJ1850, it would be great to make the device universal in also if I wanted to give them to friends.

brickhockey3 avatar Mar 13 '25 20:03 brickhockey3

In both cases, the failures are occurring at the initialization step, where ELM327 is doing the auto search for the correct protocol. The second failure isn't a timeout per se, it's a notice that the ELM327 device simply can't find a compatible ECU/protocol. It's possible the first example would return the same, if it weren't killed by the watchdog first. Are you certain that your ELM device works with the vehicles you're working with? Does it work with other apps or one of the simple sketches like examples/ESP32_Bluetooth_Serial?

jimwhitelaw avatar Mar 14 '25 04:03 jimwhitelaw

In both cases, the failures are occurring at the initialization step, where ELM327 is doing the auto search for the correct protocol. The second failure isn't a timeout per se, it's a notice that the ELM327 device simply can't find a compatible ECU/protocol. It's possible the first example would return the same, if it weren't killed by the watchdog first. Are you certain that your ELM device works with the vehicles you're working with? Does it work with other apps or one of the simple sketches like examples/ESP32_Bluetooth_Serial?

 So ESP_Bluetooth_multiple_pids  connects to both OBDII and SAEj1850 without problems. I will include the serial output from that now with my SAEj1850 since that is what is causing problems for me. 
Clearing input serial buffer
Sending the following command/query: AT D
	Received char: A
	Received char: T
	Received char: _
	Received char: D
	Received char: \r
	Received char: \r
	Received char: O
	Received char: K
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: ATDOK
Clearing input serial buffer
Sending the following command/query: AT Z
	Received char: A
	Received char: T
	Received char: _
	Received char: Z
	Received char: \r
	Received char: \r
	Received char: \r
	Received char: E
	Received char: L
	Received char: M
	Received char: 3
	Received char: 2
	Received char: 7
	Received char: _
	Received char: v
	Received char: 1
	Received char: .
	Received char: 5
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: ATZELM327v1.5
Clearing input serial buffer
Sending the following command/query: AT E0
	Received char: A
	Received char: T
	Received char: _
	Received char: E
	Received char: 0
	Received char: \r
	Received char: O
	Received char: K
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: ATE0OK
Clearing input serial buffer
Sending the following command/query: AT S0
	Received char: O
	Received char: K
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: OK
Clearing input serial buffer
Sending the following command/query: AT AL
	Received char: O
	Received char: K
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: OK
Clearing input serial buffer
Sending the following command/query: AT ST 00
	Received char: O
	Received char: K
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: OK
Clearing input serial buffer
Sending the following command/query: AT SP A0
	Received char: O
	Received char: K
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: OK
Clearing input serial buffer
Sending the following command/query: 0100
	Received char: S
	Received char: E
	Received char: A
	Received char: R
	Received char: C
	Received char: H
	Received char: I
	Received char: N
	Received char: G
	Received char: .
	Received char: .
	Received char: .
	Received char: \r
	Received char: U
	Received char: N
	Received char: A
	Received char: B
	Received char: L
	Received char: E
	Received char: _
	Received char: T
	Received char: O
	Received char: _
	Received char: C
	Received char: O
	Received char: N
	Received char: N
	Received char: E
	Received char: C
	Received char: T
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: SEARCHING...UNABLETOCONNECT
ELM responded with error "UNABLE TO CONNECT"
Setting protocol via AT TP A%c did not work - trying via AT SP %c
Clearing input serial buffer
Sending the following command/query: AT SP 0
	Received char: O
	Received char: K
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: OK
Connected to ELM327
Service: 1
PID: 12
Normal length query detected
Query string: 010C1
Clearing input serial buffer
Connected to ELM327
Service: 1
PID: 12
Normal length query detected
Query string: 010C1
Clearing input serial buffer
Sending the following command/query: 010C1
	Received char: S
	Received char: E
	Received char: A
	Received char: R
	Received char: C
	Received char: H
	Received char: I
	Received char: N
	Received char: G
	Received char: .
	Received char: .
	Received char: .
	Received char: \r
Timeout detected with overflow of 0ms
Received: SEARCHING...
ERROR: ELM_TIMEOUT
Service: 1
PID: 13
Normal length query detected
Query string: 010D1
Clearing input serial buffer
Sending the following command/query: 010D1
	Received char: S
	Received char: T
	Received char: O
	Received char: P
	Received char: P
	Received char: E
	Received char: D
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: STOPPED
ELM responded with error "STOPPED"
Received: STOPPED
ERROR: ELM_STOPPED
Service: 1
PID: 12
Normal length query detected
Query string: 010C1
Clearing input serial buffer
Sending the following command/query: 010C1
	Received char: S
	Received char: E
	Received char: A
	Received char: R
	Received char: C
	Received char: H
	Received char: I
	Received char: N
	Received char: G
	Received char: .
	Received char: .
	Received char: .
	Received char: \r
	Received char: 4
	Received char: 1
	Received char: 0
	Received char: C
	Received char: 0
	Received char: 0
	Received char: 0
	Received char: 0
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: SEARCHING...410C0000
Expected response header: 410C
Single response detected
	Processing hex nibble: 0
	Processing hex nibble: 0
	Processing hex nibble: 0
	Processing hex nibble: 0
64-bit response: 
	responseByte_0: 0
	responseByte_1: 0
	responseByte_2: 0
	responseByte_3: 0
	responseByte_4: 0
	responseByte_5: 0
	responseByte_6: 0
	responseByte_7: 0
rpm: 0.00
Service: 1
PID: 13
Normal length query detected
Query string: 010D1
Clearing input serial buffer
Sending the following command/query: 010D1
	Received char: 4
	Received char: 1
	Received char: 0
	Received char: D
	Received char: 0
	Received char: 0
	Received char: \r
	Received char: \r
	Received char: >
Delimiter found.
All chars received: 410D00
Expected response header: 410D
Single response detected
	Processing hex nibble: 0
	Processing hex nibble: 0
64-bit response: 
	responseByte_0: 0
	responseByte_1: 0
	responseByte_2: 0
	responseByte_3: 0
	responseByte_4: 0
	responseByte_5: 0
	responseByte_6: 0
	responseByte_7: 0
mph: 0.00

brickhockey3 avatar Mar 14 '25 13:03 brickhockey3

So after a couple failed attempts to connect, it does finally sync up and return some results. The rpm and mph values are 0 presumably because the engine is off and vehicle is not moving. The way the protocol search works in an auto search is that after receiving the SP0 command, the ELM327 does not actually initiate the search immediately; instead, it initiates the search on the next query sent. The library tries to manage this for you and even extends the default timeout during this search. In this case, you get an "Unable to connect" message follow by a valid connection a few queries later. I honestly don't know what's happening there. Do you have more than one device you can test with?

jimwhitelaw avatar Mar 14 '25 16:03 jimwhitelaw

So after a couple failed attempts to connect, it does finally sync up and return some results. The rpm and mph values are 0 presumably because the engine is off and vehicle is not moving. The way the protocol search works in an auto search is that after receiving the SP0 command, the ELM327 does not actually initiate the search immediately; instead, it initiates the search on the next query sent. The library tries to manage this for you and even extends the default timeout during this search. In this case, you get an "Unable to connect" message follow by a valid connection a few queries later. I honestly don't know what's happening there. Do you have more than one device you can test with?

The only other ELM device I have is WIFI. Are you able to tell me how to extend the watchdog timer for that function so it has time to fail, then default to the other protocols ? If I am comprehending the CPP in the library correctly it seems that when the connection protocol is not specified it will call an AT Z to reset an AT E0 to disable echo, then a AT SP0, after that it sends 0100 to check for supported PIDS, if that returns a timeout it will move onto the next protocol and then send the 0100 again. That is provided I am comprehending the CPP file and how it operates in a basic sketch without protocol specification.

brickhockey3 avatar Mar 14 '25 16:03 brickhockey3

https://github.com/PowerBroker2/ELMduino/blob/286023d3fd0b0fd525ea7669fa64dc0465645854/src/ELMduino.cpp#L122-L125

You can change the timeout for the protocol search here. I don't think that's the issue with the "unable to connect" issue, but it might help.

jimwhitelaw avatar Mar 15 '25 02:03 jimwhitelaw