I am working on a Motorbike Dashboard with a clock and at the moment I am trying to get the editmode for my clock to work so that I can change the time. The Clock keeps the time well but when i try to enter editmode. the hours section just blinks and the buttons don't increment or decrement the values, then when i hold down the buttons, it doesn't move on to editing the minutes and the blinking just stops for a couple seconds.
sorry, am very much a newb to this in case my issue is very obvious -_-'
#include <Wire.h>
#include <LiquidCrystal.h>
#include <driver/twai.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <esp_sleep.h>
#include <time.h>
#define LCD_RS 13
#define LCD_EN 10
#define LCD_D4 4
#define LCD_D5 2
#define LCD_D6 21
#define LCD_D7 17
#define BTN1_PIN 1
#define BTN2_PIN 12
#define ACC_PIN 15
#define CAN_TX 18
#define CAN_RX 9
#define DS18B20_PIN 11 // or 14
OneWire oneWire(DS18B20_PIN);
DallasTemperature sensors(&oneWire);
static bool bothButtonsHeld = false;
static unsigned long bothButtonsHeldTime = 0;
static bool editModeEntered = false;
bool inClockEditMode = false;
bool editingHours = true;
int currenthour = 0, currentminute = 0;
bool blinkState = true;
unsigned long lastBlinkTime = 0;
const unsigned long blinkInterval = 500;
unsigned long lastAccCheck = 0;
const unsigned long accDebounceTime = 1000; //milliseconds
int editHour = 0;
int editMinute = 0;
int gearValue = -1;
int rpmValue = 0;
int coolantTemp = 0;
RTC_DATA_ATTR int bootCount = 0;
bool lastAccState = HIGH;
unsigned long accLastChangeTime = 0;
bool isAccStableLow() {
bool reading = digitalRead(ACC_PIN);
if (reading != lastAccState) {
accLastChangeTime = millis();
lastAccState = reading;
}
return (reading == LOW && (millis() - accLastChangeTime) > 1000);
}
struct Button {
uint8_t pin;
bool lastState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;
};
Button btn1;
Button btn2;
bool checkButton(Button& btn);
bool checkButton(Button& btn) {
bool reading = digitalRead(btn.pin);
if (reading != btn.lastState) {
btn.lastDebounceTime = millis();
}
if ((millis() - btn.lastDebounceTime) > btn.debounceDelay) {
if (reading == HIGH && btn.lastState == LOW) {
btn.lastState = reading;
return true;
}
}
btn.lastState = reading;
return false;
}
void iniTime() {
configTime(0, 0, NULL);
}
LiquidCrystal lcd(LCD_RS, LCD_EN, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
void initCAN() {
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT((gpio_num_t)CAN_RX, (gpio_num_t)CAN_TX, TWAI_MODE_NORMAL);
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS();
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
if (twai_driver_install(&g_config, &t_config, &f_config) == ESP_OK) {
twai_start();
}
else {
lcd.setCursor(0, 1);
lcd.print("CAN init failed");
}
}
void prepareForSleep() {
esp_sleep_enable_ext0_wakeup((gpio_num_t)ACC_PIN, 1);
esp_deep_sleep_start();
}
void setClock(int hour, int minute) {
struct tm t;
getLocalTime(&t);
t.tm_hour = hour;
t.tm_min = minute;
t.tm_sec = 0;
time_t newTime = mktime(&t);
struct timeval now = { .tv_sec = newTime };
settimeofday(&now, NULL);
}
void displayClockEdit() {
lcd.setCursor(0, 1);
if (editingHours) {
if (blinkState) lcd.print(" ");
else lcd.print((editHour < 10 ? "0" : "") + String(editHour));
lcd.print(":");
lcd.print((editMinute < 10 ? "0" : "") + String(editMinute));
}
else {
lcd.print((editHour < 10 ? "0" : "") + String(editHour));
lcd.print(":");
if (blinkState) lcd.print(" ");
else lcd.print((editMinute < 10 ? "0" : "") + String(editMinute));
}
}
void enterClockEditMode() {
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
editHour = timeinfo.tm_hour;
editMinute = timeinfo.tm_min;
}
inClockEditMode = true;
editingHours = true;
blinkState = true;
lastBlinkTime = millis();
}
void handleClockEdit() {
if (millis() - lastBlinkTime > blinkInterval) {
blinkState = !blinkState;
lastBlinkTime = millis();
}
bool b1Pressed = (digitalRead(btn1.pin) == HIGH);
bool b2Pressed = (digitalRead(btn2.pin) == HIGH);
// Debounced single button presses
bool b1 = checkButton(btn1);
bool b2 = checkButton(btn2);
// Handle increment/decrement
if (b1 && !b2Pressed) {
if (editingHours) editHour = (editHour + 1) % 24;
else editMinute = (editMinute + 1) % 60;
}
if (b2 && !b1Pressed) {
if (editingHours) editHour = (editHour + 23) % 24;
else editMinute = (editMinute + 59) % 60;
}
// Handle both buttons held for >500ms
if (b1Pressed && b2Pressed) {
if (!bothButtonsHeld) {
bothButtonsHeld = true;
bothButtonsHeldTime = millis();
}
else if (millis() - bothButtonsHeldTime > 500) {
if (editingHours) {
editingHours = false;
}
else {
setClock(editHour, editMinute);
inClockEditMode = false;
}
bothButtonsHeld = false; // Prevent repeated triggers
delay(300); // Prevent bouncing back into edit mode
}
}
else {
bothButtonsHeld = false;
}
displayClockEdit();
}
/*void checkCANMessages() {
twai_message_t message;
while (twai_receive(&message, 0) == ESP_OK) {
if (message.identifier == 0x312 && message.data_length_code >=4 && message.data[0] == 0x00 && message.data[1] == 0x00) {
gearValue = message.data[3];
}
rpmValue = 1234;
coolantTemp = 85;
}
}*/
/*void readCAN() {
twai_message_t message;
while(twai_receive(&message, 0) == ESP_OK) {
if (message.identifier == 0x312 && message.data_length_code >= 3) {
uint8_t pid = message.data[2];
if (pid == 0x05) {
coolantTemp = message.data[3] - 40;
} else if (pid == 0x0C && message.data_length_code >= 5) {
rpmValue = ((message.data[3] << 8) | message.data[4]) / 4;
}
}
}
}*/
void readCAN() {
twai_message_t message;
while (twai_receive(&message, 0) == ESP_OK) {
if (message.identifier == 0x312) {
if (message.data_length_code >= 4 && message.data[0] == 0x00 && message.data[1] == 0x00) {
gearValue = message.data[3];
}
if (message.data_length_code >= 3) {
uint8_t pid = message.data[2];
if (pid == 0x05 && message.data_length_code >= 4) {
coolantTemp = message.data[3] - 40;
}
else if (pid == 0x0C && message.data_length_code >= 5) {
rpmValue = ((message.data[3] << 8) | message.data[4]) / 4;
}
}
}
}
}
void updateLCD(int hour, int minute) {
// Line 1: Gear and RPM
lcd.setCursor(0, 0);
lcd.print("G:");
if (gearValue >= 0 && gearValue <= 9) lcd.print(gearValue);
else lcd.print("-");
lcd.print(" RPM:");
if (rpmValue >= 0 && rpmValue <= 9999) lcd.print(rpmValue);
else lcd.print("----");
// Coolant Temp at (12, 0) as "C:--"
lcd.setCursor(12, 0);
lcd.print("C:");
if (coolantTemp >= -40 && coolantTemp <= 150) {
if (coolantTemp < 10 && coolantTemp > -10) lcd.print(" "); // Padding for 1-digit temps
lcd.print(coolantTemp);
}
else {
lcd.print("--");
}
// Line 2: Time and DS18B20 Temp
lcd.setCursor(0, 1);
if (hour < 10) lcd.print("0");
lcd.print(hour);
lcd.print(":");
if (minute < 10) lcd.print("0");
lcd.print(minute);
// Read and display DS18B20 temperature
sensors.requestTemperatures();
float tempC = sensors.getTempCByIndex(0);
lcd.setCursor(7, 1);
if (tempC == DEVICE_DISCONNECTED_C) {
lcd.print("Tmp:Err ");
}
else {
lcd.print(" Tmp:");
lcd.print(tempC, 1);
lcd.print((char)223);
lcd.print("C");
}
}
void setup() {
Serial.begin(115200); //for debugging into the serial display
sensors.begin(); //for the DS18B20 temperature sensor
//initializing the LCD screen.
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print("Initizializing...");
// set up the pin modes for the buttons.
pinMode(BTN1_PIN, INPUT_PULLDOWN);
pinMode(BTN2_PIN, INPUT_PULLDOWN);
pinMode(ACC_PIN, INPUT);
btn1.pin = BTN1_PIN;
btn2.pin = BTN2_PIN;
//setting up initial button states.
btn1.lastState = digitalRead(BTN1_PIN);
btn2.lastState = digitalRead(BTN2_PIN);
// initialize the RTC with the inbuilt RTC ram
configTime(0, 0, NULL);
//boot counter
bootCount++;
//check ACC state
if (isAccStableLow()) {
prepareForSleep(); //sends the controller to sleep if the ACC pin is LOW
}
initCAN();
lastAccState = digitalRead(ACC_PIN);
delay(1000);
lcd.clear();
}
void loop() {
if (isAccStableLow()) {
prepareForSleep();
}
/*if (checkButton(btn1) && checkButton(btn2)) {
enterClockEditMode();
}*/
if (!editModeEntered && digitalRead(BTN1_PIN) == HIGH && digitalRead(BTN2_PIN) == HIGH) {
enterClockEditMode();
editModeEntered = true;
}
else if (digitalRead(BTN1_PIN) == LOW || digitalRead(BTN2_PIN) == LOW) {
editModeEntered = false;
}
if (inClockEditMode) {
handleClockEdit();
return;
}
struct tm timeinfo;
getLocalTime(&timeinfo);
currenthour = timeinfo.tm_hour;
currentminute = timeinfo.tm_min;
//checkCANMessages();
readCAN();
updateLCD(currenthour, currentminute);
}