Buttons not working as intended

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);
	
}


So… where did you get all that somewhat non-noonish code?

Are you sure the buttons are working? The first thing I woukd do is get rid of everything that isn't related to handling buttons, and make sure your code is producing plausible events or callbacks or flags or whatever.

Write a sketch that counts button presses, and uses the serial monitor to report the count each time it goes up.

First for one, and the for more if you have more then one button.

a7

have been working on smaller projects up until this for roughly a month. newb as in self taught for a short period of time

OK, good.

You should have no trouble, then, stepping back and restructuring your code so that the input section is separate, and so separated made perfect before the button actions mean anything to the functionality of the sketch as a hole.

You might like the IPO model

The IPO Model

but the general idea for fixing this is "divide and conquer". If the button logic is not good, what it controls will be harder to fix.

If that doesn't work for you, at least you could add serial printing to check the values of key variabkes and that they are properly informing the flow through your code.

The button logic as it is is tnaglked uo with the actions they are meant to cause, and furthermore it looks a bit scattered, redundant and is to me at my current state confusing.

HTH

a7

cool thank you. I had set up if statements in my loop to send a message into the serial monitor to check if they were receiving signals, which they do. so probably as you said something to do with my logic.

shall attempt the fix o7

I haven't read all of your code in detail, but it seems that you may need some debouncing code here.

@skalmold_9 - I have spent some time working with the code.

A problem with self-teaching yourself is that you end up with creative solutions which may indeed be correct, but are not quite how problems like this are often solved and coded. So helpers (I will speak for myself) look for patterns that are conventional to the point of being idiomatic; failing to see such patterns, at least partly because the button logic is intertwined with the effects the buttons are supposed to have, and maybe partly because your method is unique makes understanding the sketch difficult. Even with the rest of the functionality weed-wacked out of the picture.

As a first step, please describe how this is supposed to function, sticking with just buttons 1 and 2.

Like if you were telling your grandmother how to set this clock using the two buttons you provide.

I'm asking for something more than just do the buttons send signals. Strew serial printing about liberally so where, for example, the minutes get incremented, print "I am adding to minutes".

And where you can identify that modes are being entered or left. And where the numerous booleans controlling the logic get set and cleared.

Make your sketch talk to you and tell it what it is doing. Since it isn't doing what you want, these statements will tell you what it is doing.

It's your first, easiest and cheapest way to see what is really happening.

At least. A semi-noncasual perusal makes me wonder how any of the editing modes gets reliably entered to assume responsibility for reacting to the next button actions.

a7

Hi, @skalmold_9

I think you will find there are no built in pull-down resistors.
What is t he controller you are using?

Did you write your code in stages?
If so;
Then you should have code that JUST reads your buttons and proves you are able to reliably read input levels.

Can you please post a copy of your circuit, a picture of a hand drawn circuit in jpg, png?
Hand drawn and photographed is perfectly acceptable.
Please include ALL hardware, power supplies, component names and pin labels.

Tom.... :smiley: :+1: :coffee: :australia:

Yeth. Then write a bit more to show you can do whatever it is with pressing and releasing combinations.

But why make an obtuse fiddly you-can't-get-it-to-work UI? Buttons and switches are cheap. Just think how easy this would be with three or four buttons, or two buttons and a slide switch, or…

Maybe a commercial product needs to skimp; maybe you are out of inputs pins. Maybe you like frustrating puzzles WRT coding and use of buttons to control things.

a7

2 Likes