Skip to content

Watchdog Triggered Cheap Yellow Display #283

Description

@brickhockey3

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

questionFurther information is requested

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions