Programmable switch timer

I have beginner skills on arduino, and I kind of jumped the gun into a project for school, to build a programmable switch timer, it has 4 buttons The switch is to be able to program on and off times of a bulb, it should be able to set 2 programs daily for each day of the week(i.e. i can manually program 2 on/off times for each day Monday through sunday).This also implies that date, time are variables that can be programmed. It should also display the room temperature. I WANT IT TO PERFORM CERTAIN FUNCTIONS USING 4 PUSHBUTTONS,

  1. Button 1 (Manual Control): This button will allow manual control over the relay module, overriding the programmed settings.
    When Button 1 is pressed, it should toggle the state of the relay module (ON if it's currently OFF, and OFF if it's currently ON). This button can be used for manual control regardless of the programmed settings.

  2. Button 2 (Set Button): This button will be used to set the ON and OFF times for a specific day of the week.
    When Button 2 is pressed, it should enter the programming mode for setting the ON and OFF times for each day. The LCD display shows all adjustable variables(i.e. Day on the second row, ON time on the 3rd row, OFF time on the fourth row) indicating which variable is being adjusted. The user can then use Button 3 to adjust the variable, the user presses Button 4 to go to the next variable(i.e The user presses Button 2 to go into programming mode, then uses Button 3 to select the day that is to be set, then uses Button 4 to move to the next variable which is the Hour variable of the ON time, then uses button 3 to adjust the value, then uses button 4 again to move to the next variable and so on... ), pressing Button 2 after adjusting all desired variables should save the on and off time of the programmed day

  3. Button 3 (Adjust Variable):This button will be used to adjust the value of any variable during the programming mode. When Button 3 is pressed, it should increment the chosen variable's value by one. If the maximum variable value is reached, the value should wrap around to the minimum value.

  4. Button 4 (Next Variable): This button will be used to move to the next variable to be adjusted during the programming mode. When Button 4 is pressed, it should move to the next variable's tab to let it be adjusted by Button 3

I have a code, but it doesn't work as intended, can someone help me analyze and find what i did wrong
I have attached a link to the wokwi simulation site with the code and simulated connections

Welcom to the forum.

I look later. And thanks for using the wokwi, it really helps helpers.

Who wrote or where did you find inspiration (!) for this code you have?

If the manual override turns on the relay when it was off, what happens when the next "on" timed event arrives? Stay on?

Unanswered questions will be whatever happens that you didn't plan.

a7

OK, I looked briefly.

Perhaps you could save some time by elaborating. What does it not do,that it should, or do that it should not?

You may benefit from using a button library, I like ezButton, it gets my highest praise for button libraries "it doesn't suck".

That would make the hole thing more responsive, you could get rid of some of the dealy() calls.

a7

First off, no need for those pull-down resistors on your buttons. You have configured them as INPUT_PULLUP.

When you press the Manual button, mode goes from 0 to 1. The next time through loop(), you check if mode == 0 else mode == 2 and since neither of those tests are true, nothing gets done and there is no way to change the mode.

Much better to recode as a finite state machine.

Yes, it should stay on as long as the manual override is On

From my simulation, when I press the manual override button, it just stays on, I try pressing the button again to turn it off, but no results, and I am completely unable to perform any function with the other buttons

Alright, thank you, I would take out those resistors now
the code is sourced/ inspired from various "alarm clock" projects, and the 'loop display' was from me, refined by Ai

That makes no sense. A button press did the override. How does it remain overridden?

Or do,you mean to have a switch?

Or do you mean that override obtains until the same button gets you out of the manual override relay condition?

OK, so what kind of sketch can you write, and do you understand what you have in front of you?

The nice thing about code is that it does what you wrote. Use some serial printing at various points in your code to "see" the flow and check on the values of variables that inform it.


Those 10K resistors are perfectly harmless, and in some circumstances advisable.

Old fashioned ppl, or just old ppl, cough! use pullup resistors even though an internal pullup is available. I would just leave them there.

if I was making a million copies, I might think about saving what you would by omitting them.

a7

Hi, thank you for responding continually, so yes, the Override button is basically supposed to act as a manual switch, it keeps the system on until it is turned off again using the same button, then which it can go back to coming on and going off at the programmed times.
YES, i understand the code in front of me, i basically did my void loop and setup sketch myself.

OK, I just lost a lengthy post, the main point of which was that you should learn about finite state machines.

I also suggested that trying AI but describing your problem knowing about FSMs might generate some better results.

Indeed feeding

worked well but each time it made code, I saw some way to help it along.

I had to ask it to use C, not whatever it picked at random.

I told it to use switch/case instead of if/else chains.

I suggested using an integer to hold the state instead of the whizzy struct it was using, which involved an enum and pointers. (For this kind of code, an integer and # defined state names is entirely fine, as in not underkill).

It switched to an integer instead of an enum type, but left it in the struct. It cheerfully took my suggestion to just drop the struct and pointer stuff:

sorry posted accidentally so I am finishing this now

alto777: you don't need to use a struct, it's just one integer. this will make things simpler

ChatGPT: Absolutely, we can simplify the implementation further by directly using an int variable to represent the state of the FSM without encapsulating it in a struct.

And closer and closer. So meet this wherever you want - learn how to code, or learn how to tell an AI what you want - I have no doubt that sooner or later "we" could have gotten something plausible working.

In any case, no time spent looking into finite state machines will be wasted.

a7

I tried again.

With the above, and a bit of back and forth (the code was always plausible, but unnecessarily complicate) this pops out, the state machine disappeared as unnecessary.

// Simulated button function (replace with actual button reading function)
bool button(int buttonNumber);

// Function to handle button presses
void handleButtonPresses(void) {
    // Check button presses using the button function
    bool button1Pressed = button(1);
    bool button2Pressed = button(2);
    bool button3Pressed = button(3);
    bool button4Pressed = button(4);

    // Handle button presses
    if (button1Pressed) {
        allDone();
        resetMachine();
    }
    if (button2Pressed) {
        selectNextParameter();
    }
    if (button3Pressed) {
        incrementCurrentParameter();
    }
    if (button4Pressed) {
        decrementCurrentParameter();
    }
}

Explanation:

  1. Button Functionality:

    • button(int buttonNumber) is assumed to correctly detect the rising edge (when the button is pressed) and returns true only once per press.
  2. Reading Button States:

    • At the beginning of handleButtonPresses(), the function reads the state of each button (button1Pressed, button2Pressed, etc.) using the button() function.
  3. Handling Button Presses:

    • Based on the state of each button, the corresponding function (allDone, resetMachine, selectNextParameter, incrementCurrentParameter, decrementCurrentParameter) is called.

Notes:

  • Ensure that button(int buttonNumber) correctly detects button presses and returns true only once per press (rising edge detection). Replace the simulated function with actual hardware or software-specific implementation as needed.
  • This implementation efficiently handles button presses by first reading the state of each button and then executing the corresponding action, adhering to your request to place the button reading at the top of the function.

So when you are in adjustment mode, just keep calling the function and it will update the parameters. A button is provided to clean up and exit the mode.

Then I thought it could write the higher level mode switching code, why not?

At the end of that we got

// Simulated button function (replace with actual button reading function)
bool button(int buttonNumber) {
    // Replace this with actual code to read button state
    // For simulation purposes, function body is left empty
    // Return value is placeholder
    return false;  // Replace with actual button reading logic
}

// Define modes
#define MODE1 1
#define MODE2 2
#define MODE3 3
#define MODE4 4
#define MODE5 5

// Function prototypes
void handleModeSwitch(void);

// Global variable to store current mode
int currentMode = MODE1;

// Function to handle mode switching
void handleModeSwitch(void) {
    // Check if the mode switch button (button 1) is pressed
    if (button(1)) {
        // Increment the current mode
        currentMode++;
        
        // Wrap around if exceeding the maximum mode
        if (currentMode > MODE5) {
            currentMode = MODE1;
        }
    }
}

Explanation:

  • Simulated button function:

    • button(int buttonNumber) is declared with an empty body, as requested.
    • This function is intended to be replaced with actual code that reads the state of the specified button (buttonNumber).
  • Mode Switching Logic:

    • The handleModeSwitch() function remains the same as previously explained.
    • It checks if button 1 is pressed (using button(1)), increments currentMode, and wraps around if it exceeds MODE5.
  • Define Modes and Function Prototypes:

    • Modes (MODE1 through MODE5) and function prototypes (handleModeSwitch()) are defined and declared respectively.

Notes:

  • Ensure to replace the empty body of button(int buttonNumber) with actual code that reads the state of the specified button in your hardware or software environment.
  • This structure allows you to integrate the simulated button() function with actual button reading logic seamlessly once implemented.

Here's my side of the conversation. chatGPT is indefatigavle and patient, each suggestion resulted in a full code and explanation.


The fake *button*() function can be replaced with ezButton's
   bool button1Pressed = button1.isPressed();

I see I should have just asked chatGPT to not bother reading the switches, the button() function could be used directly in the if statements.

Too much fun.

I lost track of states and modes. If I started again and took it a bit more seriously I think "we" could have done better.

But the idea is that at a high level you switch modes, and depending on the mode, send any buttons presses to functions that handle them one at a time.

a7

Hello david_femi

Welcome to the best Arduino forum ever :smiley:

Check and test this button manager for your timer project. You have to modify this sketch to your project needs.

//https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
#define ProjectName "Basic Button Manager"
#define NotesOnRelease "Arduino MEGA tested"
// -- some useful text replacements used because I'm lazy with typing --
#define equ ==
#define nequ !=
#define view(x) Serial.print(x)
#define viewLn(x) Serial.println(x)
// make names
enum ButtonState {Released, Pressed};
enum TimerEvent {NotExpired, Expired};
enum ButtonNames {One, Two, Three, Four};
// make variables
constexpr uint8_t ButtonPins[] {A0, A1, A2, A3};
// forward declarations
void manualControl();
void setButton();
void adjustVariable();
void nextVariable();
void (*task[])() {
  manualControl, setButton, adjustVariable, nextVariable
};
// make structures
struct TIMER
{
  uint32_t interval;
  uint32_t now;
  uint8_t expired(uint32_t currentMillis)
  {
    uint8_t timerEvent = currentMillis - now >= interval;
    if (timerEvent equ Expired) now = currentMillis;
    return timerEvent;
  }
};
struct BUTTON
{
  const uint8_t Name;
  const uint8_t Button;
  void  (*task)();
  uint8_t stateOld;
  TIMER debounce;
  void check(uint32_t currentMillis)
  {
    if (debounce.expired(currentMillis) equ Expired)
    {
      uint8_t stateNew = digitalRead(Button) ? LOW : HIGH;
      if (stateOld nequ stateNew)
      {
        stateOld = stateNew;
        view ("button "), view(Name), view(" becomes ");
        viewLn (stateNew ? "pressed" : "released");
        if (stateNew equ Pressed)
        {
          Serial.print("calling task: ");
          task();
        }
      }
    }
  }
};
BUTTON buttons[]
{
  {One, ButtonPins[One], task[One], LOW, 20, 0},
  {Two, ButtonPins[Two], task[Two], LOW, 20, 0},
  {Three, ButtonPins[Three], task[Three], LOW, 20, 0},
  {Four, ButtonPins[Four], task[Four], LOW, 20, 0},
};
// make support
void heartBeat(const uint8_t LedPin, uint32_t currentMillis)
{
  static bool setUp = false;
  if (setUp == false) pinMode (LedPin, OUTPUT), setUp = true;
  digitalWrite(LedPin, (currentMillis / 500) % 2);
}
//--
//---------------------------- make applications ----------------------------------------------
//
void manualControl()
{
  Serial.println(__func__);
}
void setButton()
{
  Serial.println(__func__);
}
void adjustVariable()
{
  Serial.println(__func__);
}
void nextVariable()
{
  Serial.println(__func__);
}
//---
//----------------------------------------------------------------------------------
void setup()
{
  Serial.begin(115200);
  Serial.print("Source: "), Serial.println(__FILE__);
  Serial.print(ProjectName), Serial.print(" - "), Serial.println(NotesOnRelease);
  for (auto ButtonPin : ButtonPins) pinMode (ButtonPin, INPUT_PULLUP);
  Serial.println(" =-> and off we go\n");
}
void loop()
{
  uint32_t currentMillis = millis();
  heartBeat(LED_BUILTIN, currentMillis);
  for (auto &button : buttons) button.check(currentMillis);
}

Have a nice day and enjoy coding in C++.