help needed to remove while(1)

Hi All

I have been struggling with this for days now and can't solve it, I need some help.

I have working LCD sketch and part of it uses a while (1) to block the sketch while the user adjusts either the time or date. I have rewritten this to a state machine

i want to make this non-blocking purely for my own learning to write non-blocking code. However I cannot work out how to use flags to prevent the original calling function continuing to call and therefore overwrite the number the user is trying to change. I have tried hundreds or iterations and google seems to be no help.

The first part of the code is called directly by the state machine (state changed by button push)

void set_time() {

// set up initial LCD display
if(inputTimeFlag == false){
  
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Set Time        ");
  show_time(SECOND_LINE);  

  setHoursFlag = true;
  setMinutesFlag = true;
  setSecondsFlag = true;

  hoursTmp = tm.Hour;
  minutesTmp = tm.Minute;
  secondsTmp = tm.Second;
  
  inputTimeFlag = true;
  previousMillis = millis();
} // end initial LCD set if
  
else if (inputTimeFlag == true){
    //get_button_input(initial value, x_position, min value, max value)

that sets up flags to set hrs, min secs and sets the temporary variables so the RTC doesn't keep overwriting them.

then each part of the time (hr.min.sec) or date (dd.MM.YYYY) is passed, using hours as an example

if(setHoursFlag == true){

               hours = get_button_input(hoursTmp, 8, 0, 23); //hour ranged from 0 to 23
              // need to add code here to set hoursflag to false
              setHoursFlag = btnUPDATE // button update set to false in the get_button_input 
}

This is passed to the get_button_input function. This is where my problem is. There use to be a while statement at the beginning
previousMillis = millis(); // reset timeout so that the timeout will not occur
while (1) { // loop forver
if (isTimeout()) { //exit loop if timeout occurred
return -1;
}
which is what I want to remove. but every time I do the calling function just keeps updating the data plus a few other issues such as my local variables keep being reinitialized to 0 unless I make them global and then the case btnRIGHT: which should confirm the value isn't setting val or returning the value to the original calling function. I think my preblem is things are looping back through and I'm not catching them in the right place.

Should I be looking a for statements to do counts ? Any other suggestions are welcome.

//////////////////////////////////////////////////////////////////////////
// read input from button switches
//////////////////////////////////////////////////////////////////////////
int get_button_input(int val, byte pos_x, int min, int max) {  // Takes for example int hourstmp = tm.hours and the LCD position plus min and max allowed
                                                               // eg 13 9 0 23 for 13:00hrs 9th column min 0hrs max 23 hrs 

int tmpVal;  // initialises variables to use within this function - Issue is they keep being reinitialised back to 0 unless they are global. 
int result;  // as the original call function keeps calling 

previousMillis = millis(); // reset timeout so that the timeout will not occur
  
if(getInputFlag == true){       // flag for the first read through
    tmpVal = val;               // sets the tmpVal to match the val passed
    getInputFlag = false;       // sets the flag so the next run to allow function to continue
  }

if (getInputFlag == false){     
                
    lcd_key = key_press();   // read the buttons
     
       switch (lcd_key) {
        
              case btnUP:
              tmpVal++;                            // if we increase tmpVal to 15 for 1500 hrs 
              if (tmpVal > max) tmpVal = min;
              break;
              
              case btnDOWN:
              tmpVal--;
              if (tmpVal < min) tmpVal = max;
              break;
              
              case btnRIGHT:
              val = tmpVal;                      // this should set val to tmp value ie val now equals 15
              lcd.setCursor(pos_x, SECOND_LINE); // set curser to the hours position
              lcd.print(int2str(val));           // writes the new value ie 15
              getInputFlag = true;               // resets the flag
              btnUPDATE = false              // passed back to original calling function
              result = val;                      // unnessary but there  
              return result;                     // should retun 15 to the original passing function 
                        
              break;
              }// end switch

              blink_text(pos_x, int2str(tmpVal));  // makes the value being edit blink
            
         if (lcd_key != btnNONE) { //if a button is pressed
         previousMillis = millis(); // reset timeout so that the timeout will not occur
          }
}

} // end get_button_input

Cheers
Al

You’ve said you are using a state machine model in your program design, so you probably have to solve this, what looks like a ‘timeout during user input’ problem, at a higher level in the state machine.
For example, you have a state IN_TIME_SWITCH_MODE and a state IN_TIME_SET_MODE and you are currently in IN_TIME_SET_MODE. If you discover you have been in IN_TIME_SET_MODE for too long, then you have to exit the current mode, in this example, maybe by changing the state back to IN_TIME_SWITCH_MODE, or maybe an intermediate state CLEAN_UP_FAILED_TIME_SET_ATTEMPT.

Anyway, you should post the whole code , to make it easier to demonstrate what you are doing.

Thanks

I have attached two codes
code #1 - LCD_Clock_Display_Working // this is the code that works but has the blocking while
Code #2 - LCD_Clock_Display_State_Machine // changed to a different structure but doesn't work it does compile and run but hangs in one of the if statements.

I had originally asked in another post a while ago if I should have a "InEdit" state but was advised I should be able to sole my problem using flags and another state was not achieving anything.

(Just for clarity I am very new to this and only through the help of Larryd on the forum a few weeks ago do I know what a state machine is)

I know there is still a lot to do in code 2 but it is getting rid of that while (1) that has me stumped.

EDIT - Currently there is no timeout on the get_button_Input function as i was trying to make it exit cleanly without a timeout. I intend to add that back in once it works just via a clean exit with the btnRIGHT

In Code #2 I have added in a few Serial.Println("**text"); so i can follow whats happening and I see it gets into the if (getInputFlag == false){ loop but never comes out. So its not passing the value back to the calling function, quite possibly because its being recalled so quickly. Since the first post I have also tried added in some extra if's in the calling function on line 437.

Note the Date and time functions are the same (relatively) but I'm only working on the time one until I solve that.

This problem has frustrated me for days and usually I only want a nudge in a direction and i can google from there but I am really looking for some help before loose my mind.

Cheers
Al

LCD_Clock_Display_Working.ino (15 KB)

LCD_Clock_Display_State_Machine.ino (21.9 KB)

Add file with standard LiquidCrystal_I2C.h library, just uncomment to use

LCD_Clock_Display_State_Machine.ino (22.2 KB)

If anyone is already looking at helping with the previous file just ignore this post, what I have changed is inconsequential and still doesn't work.

I have managed to make the the btnRIGHT work and cycle through with a clean exit back to state "update" and then "running"

Issue now is the the get_button_input doesn't actually update the values, but I can see the switch case recognizing the button inputs, so the tmpVal isn't working or displaying on the LCD.

If you have only just come across this post and can give me a bit of help probably use this code as it is marginally better although still doesn't work.

Tea time here now and I'm on cooking duty.

Cheers
Al

LCD_Clock_Display_State_Machine.ino (22.7 KB)

There are a couple of ways to handle this...

  1. Is create separate states in the main state machine for doing the input of each value

That’s a lot of work because you need to call the get_button_input from lots of places, so it requires multiple duplicate states. So...
2. Have a separate independent state machine for the edit routine

Option 2 seems to be where you are going with the getInputFlag, but you logic is hard to follow because the “states” are scattered across multiple different variables (getInputFlag being one of them). Try creating a separate state machine just for that input logic.

Also I feel you main state machine really needs states not just for “edit time” and “edit date”, but for each individual step within that process. So states “edit hours”, “edit minutes”... and “edit year”, “edit month”....

You kind of have that with the hour minute flags being -1 and checking them in your superstate, but I think you’ll find it a lot easier if you break those out into individual states and name them. Then you just need to move to the next edit field once input of each is complete...

I’ll try and write you some pseudo code which might make it easier to follow. For the edit state machine...

bool editing_value(int val, .....)
{
    switch (mEditState)
    {
        case StartEdit:
            editVal = val;
            mEditState = PerformEdit;
            return true; // still editing
        case PerformEdit:
            // add code to support editing editVal
            // you may need additional states, for example waiting for debounce or release of keys, etc
            // return true to indicate you are still performing the active edit
            // then, when you have finished editing...
            edit_state = StartEdit;
            return false;
    }
}

Then your main state machine has...

case EditHours:
  if (editing_value(hours, ....)) break; // stay in edit hours state
  hours = editVal;
  mState = EditMinutes;
  break;

case EditMinutes:
  if editing_value(minutes, ....) break; // stay in edit minutes state
  minutes = editVal;
  mState = EditSeconds;
  break;

 // etc

If you want to have a timeout, or way to quit editing you can do that by having editing_value return different return codes to indicate: still editing, edit complete, cancel. Then switch on the result and adjust the next state of the main state machine accordingly.

OK. In principle, a state machine solution to this sort of problem is a good idea. However, if you start encrusting the states with lots of global flags you are getting away from the clean state machine concept.

You have anyway a complex project and you've said that you are relatively new to this, so there is plenty of scope for learning and you'll probably go through many iterations of trying things, cleaning up and optimising. A couple of things you should look at in the future are getting it into a clean state machine model, clean separation of different tasks and replacing Strings with character arrays. But at the beginning, I guess you just want something to work so you can build on that.

So from the code you have named ....Working.ino, I've found his which you have mentioned:

int get_button_input(int val, byte pos_x, int min, int max) {
  previousMillis = millis(); // reset timeout so that the timeout will not occur
  while (1) { // loop forver
    if (isTimeout()) { //exit loop if timeout occurred
      return -1;
    }

    byte lcd_key = key_press();   // read the buttons
    switch (lcd_key) {
      case btnUP:
        val++;
        if (val > max) val = min;
        break;
      case btnDOWN:
        val--;
        if (val < min) val = max;
        break;
      case btnRIGHT:
        lcd.setCursor(pos_x, SECOND_LINE);
        lcd.print(int2str(val));
        return val;
        break;
    }
    blink_text(pos_x, int2str(val));

    if (lcd_key != btnNONE) { //if a button is pressed
      previousMillis = millis(); // reset timeout so that the timeout will not occur
    }
  }
}

It is clearly going to get stuck in the while loop until it times out and returns to the calling routine. The code following to read the buttons is never going to be executed.

OK. So this looks like the call chain:

loop() --> LCD_Clock_Function() --> button_input() --> set_time() --> get_button_input()

So at some point, you detect a use has pressed a button and want to give him some time to set a time etc. before throwing him out and reverting to displaying the current time on the display.

If that is the case, you set a timer on the first button press, probably in button_input() and set a flag which stops LCD_Clock_Function() displaying a time. During the period this timer is active you can be getting button inputs and updating the display. On time out, or when the user has finished his updating, you unset the flag stops LCD_Clock_Function() displaying a time.

OK. In the meantime, as I was typing this, @pcbbc has also answered so you now have quite a number of interesting options.

Thanks for your replies (both of you)

I certainly can break the state machine into more defined states.
Edit hrs, edit minutes, edit seconds, update etc. I'm still learning, how many sates are too many? (rhetorical) this is the issue learning complex subjects without a formal setting. just asking questions as I go, but I do really appreciate the help.

I think my fundamental learning block is the part where it takes the item to be edited, lets say hours, and then returns a value after being manipulated by a function that can handle any value that it is given (so it can be used by multiple parts of the program). I can muddle through a time out if the user doesn't do anything.

Currently I cannot fathom how to take the hours (currently here it is 8pm) so taking 20 plus the two LCD fields it occupies and a min and max value.

Sending that to a function that then holds the value 20 allowing the user to val++ or val-- (this then updates the display and nicely blinks) and once selecting the "right" button sends the modified value back to the set time function ie a value of 19 or 21 etc. In this case there would need to be an intermediate section to capture hr min and sec before sending it to the set time function, if I can solve the first bit of just one value the rest will follow (I hope).

I just can't work out how to stop the code constantly sending a value (20 plus the pos, min and max) to the "get input" as it always called because it is non blocking. Even if I enter a new state, the new state is going to be looping on the get hours and sending the hours, position min and mix to the function manipulating it.

My early code you cold use the val going from 20 to 21 with an up button and immediately resetting to 20. Using Serial.print to inspect

you can see I tried different methods of flags and hours = -1, tmp hours etc, I just look at it and think there must be a neater way to achieve this.

Lastly, yes the code needs a huge amount of work to make it efficient, current it uses 42% of program storage and 53% of dynamic memory which sounds hideous for a 2 line LCD clock. Removing String and add string objects will be my next project. But currently it works with the "while(1)". I want to remove that before I start breaking other stuff. I have looked into removing the String() but for a newbie it is a bit complicated, however there is a wealth of information out there. One thing at a time.

Cheers
Al

For setting time etc. I would have taken a slightly simpler approach.

define a section of display to hold the time to be set.
have a character array that maps 1 to 1 to that display section.
have navigation buttons right, left, up, down which select a digit position and change its value (in the array)
Every button press causes the array to be written to the display to refresh it.

On start of the set time function, copy the current time into the array (or a default time). It is automatically displayed.

When the user confirms that the time in the display is OK, validate the time held in the array, store it and revert to the normal time display.

This way you are not constantly validating every button press and it is immaterial whether it is currently forms part of an hour, minute or second.

Thanks, its late here so will keep working on it tomorrow.

define a section of display to hold the time to be set.
have a character array that maps 1 to 1 to that display section.
have navigation buttons right, left, up, down which select a digit position and change its value (in the array)
Every button press causes the array to be written to the display to refresh it.

How do you stop the program constantly updating the array that is passed to the LCD with the original value, eg seconds will keep ticking over.

I do feel I'm asking a blindingly obvious question, and i feel like an idiot, but I have spent 3 days in the forest and can no longer see the trees. Hopefully stupid but savable :slight_smile:

Do you possible have example?

As a thought, do I, at the time you select "edit time" it saves the values to a set of variables HRS, MIN, SEC which become the character array. then edit those one at a time, and when you confirm it sends them back to the set_time ?

Allan_Pritchard:
How do you stop the program constantly updating the array that is passed to the LCD with the original value, eg seconds will keep ticking over.

You have a temporary array for performing the edits. At the point you move to the "editing time" state...

editValue = time;
mState = EditTime;

Only when you leave the "editing time" state and return to the "display time" state do you copy from the temporary variable back to the real time variable(s):

time = editValue;
mState = DisplayTime;

Edit: Oh, and you only display the "time" in the "display time" state. And you display the "editValue" in the "edit time" state.

As a thought, do I, at the time you select "edit time" it saves the values to a set of variables HRS, MIN, SEC which become the character array. then edit those one at a time, and when you confirm it sends them back to the set_time ?

That's about it. In fact this is a fairly key component of a state machine. Before you move to the next state, usually it involves some data being saved or updated. That data then gets manipulated by the new state, or a subsequent one.

Thanks I'm sort of getting the concept of a state machine but without working (relevant) examples its learn by google theory.

So I would have
Startup - Just to get everything going - in this case its not much except checking the RTC is going
Running - displaying the time and date
edit time - a state to do any pre edit work
edit hours
edit minutes
edit seconds
save time - a state to save the time value back ( or do I segment this further?)
edit date - a state to do any pre edit work
edit years
edit months
edit days
save date. (save date and time are currently one function with flags but separated here)

loop checks the "proofofLife" checks the buttons and what state I'm in.

Its very late here and before I head of to bed are State Machines typically flat in structure in programming of switch cases (I'm talking about relatively basic programs) or are they nested in some form.? In this example its a pretty simple program, it doesn't do much, but if it was part of a larger program (still simple) would you nest the setting the clock if, for example, it was part of a program to turn the garden watering on/off in different zones at different times.

Your replies have given me hope I can solve this, hopefully tomorrow. I think my function worked OK as a while(1) but changing it to non-blocking requires it to be segmented a lot more so it is just dealing with one part of the problem at a time. the how to eat an elephant example.

I'll post hopefully a working clock tomorrow.

Cheers
Al

Allan_Pritchard:
Its very late here and before I head of to bed are State Machines typically flat in structure in programming of switch cases (I'm talking about relatively basic programs) or are they nested in some form.? In this example its a pretty simple program, it doesn't do much, but if it was part of a larger program (still simple) would you nest the setting the clock if, for example, it was part of a program to turn the garden watering on/off in different zones at different times.

It varies. There's no reason you can't have several state machines in the same program. You might for example have one controlling the house heating and another monitoring the fridge and freezer.

It's usually desirable to build your system in separate modules if possible so that once the heating controller is working, you don't break it trying to add unrelated matters like the freezer. It's the OO principle of information hiding.

You could nest them too. It would be one way to deal with your edit the time problem although I think it would be overkill.

Your garden example is tricky. The gardening controller needs to know the time. Nonetheless I would keep all the time/date setting and display functions in a separate state machine and provide a function that other parts of the code can use to ask what time it is.

It's actually worse than that, because when you edit the time or date, the garden controller probably needs to know about it but I still wouldn't entangle them tightly together because the clock doesn't and shouldn't care about the garden or the freezer or the burglar alarm etc.

I'd probably let other components register with the clock to be informed if the time has been changed, but the clock still needn't know anything about them. MQTT would be a trendy solution to this, but personally I find it a bit silly to involve another computer to pass messages within a single program.

Allan_Pritchard:
So I would have:
Startup - Just to get everything going - in this case its not much except checking the RTC is going
Running - displaying the time and date
edit time - a state to do any pre edit work
edit hours
edit minutes
edit seconds
save time - a state to save the time value back ( or do I segment this further?)
edit date - a state to do any pre edit work
edit years
edit months
edit days
save date. (save date and time are currently one function with flags but separated here)

That's a good start.

Nothing wrong with the edit time/edit date states, but they probably won't be waiting for anything.

You certainly can have states that don't wait, and just do pre-cursor work and then move automatically to the next state. In fact that can be useful if:
a) You need to enter that state from multiple other states. So you don't duplicating the initialization code (although you could put it in a function).
b) You have a lot of work to do, and you want to break a long running task down into several steps so you don't "block" other things from happening.

Similarly the save time/save date states, although if you are waiting for a "Confirm" save button press you will need a state.

Are State Machines typically flat in structure in programming of switch cases (I'm talking about relatively basic programs) or are they nested in some form.?

There are no hard and fast rules. For more complex applications you may see many separate state machines running at the same time; or one state machine that calls another to perform a sub-process.

A switch statement is not uncommon, but not required. If statements can also be used, or a combination of the two. Consider a task which must be performed in multiple states. For example if a led that must be flashed in all but the standby state. There you might see:

if (state != standby && led_timeout)
{
    toggle_led();
}
switch (state)
{
  case standby:
    led_off();
    break;
  case ...:
  case ...:
}

I think the key thing to try to get away from is having too many variables/flags which say what you are doing. For example your hours/minutes = -1 flags. Those constitute part of the state and it gets complex to see what is going on when they are separated out.

For more complex system it might help to draw one or more state diagrams which show the states you can be in and the events/actions which cause the transitions between states.

Good luck. :slight_smile:

Well I have hit the same brick wall as yesterday :o just from a different angle

I redid the state machine and managed to use strings to display the edit time. (which i was happy about using sprintf) I still need to remove Strings() from the rest.

If I run the "edit hours" within the state I can make the int editHours go up and down including displaying on the LCD screen using button pushes. But then I would have to duplicate that code in every edit state.

However as soon as I pull that out to a "function" so it can be used again by other parts (ie minutes or seconds) and pass the values I want edited, then because the state is constantly calling the function it constantly updates the int editHours. This is what the While(1) blocked. Full code attached but snipets below.

Please help I'm desperate to move on from this bewildering puzzle.

this bit works

     case EditTime:
      {
      editHours = tm.Hour;      // sets the the time to be edited so the time can change during edit
      editMinutes = tm.Minute;
      editSeconds = tm.Second;

      lcd.setCursor(0,0);
      lcd.print(LCDDisp[0]);
      lcd.setCursor(0,1);
      // prints the time to be edited to the LCD
      sprintf(st_Time, ("Time    %02d:%02d:%02d"),editHours,editMinutes,editSeconds);
      lcd.print(st_Time);
      delay(100);             //delay added to settle things down 
      mState = EditHRS;

      }
      break;

This bit works only if all the code is in the state (commented out) it doesn't work it it calls a function to do the same thing, function is not commented out.

      case EditHRS:
      
     {
  
      if(isTimeout()) {        // timeout function
        mState = lastMstate;
        break;
      }

editValue(editHours, 8, 0, 23); // if calling from state machine it simply keeps calling with the same value
      
/*   below code works to adjust the hours if use within the state machine
     lcd_key = key_press(); 

      lcd.setCursor(8,1);
      sprintf(st_EditValue, ("%02d"),editHours);
      lcd.print(st_EditValue);                            // Makes the numbers go up and down on the LCD
      
     if(lcd_key == btnUP){
      if(editHours < 23) editHours++;
      Serial.println(editHours);
     }
     else
     if(lcd_key == btnDOWN){
      if(editHours > 0) editHours--;
     Serial.println(editHours);
     }
     else
     if(lcd_key == btnRIGHT){
      Serial.println(editHours);
      delay(1000);
      mState = lastMstate;
     }
   
      if(lcd_key > 0){
        previousMillis = millis(); // reset timeout
      }

 */     

    //  delay(1000);
      }
      break;

this is the function which I made a void in a desperate attempt rather than an int.

void editValue(int val, byte pos_x, int min, int max){

lcd_key = key_press(); 

      lcd.setCursor(pos_x,1);
      sprintf(st_EditValue, ("%02d"),val);
      lcd.print(st_EditValue);
      
     if(lcd_key == btnUP){
      if(val < 23) val++;
      else if (val > max) val = min;
      Serial.println(val);
     }
     else
     if(lcd_key == btnDOWN){
      if(val > 0) val--;
     Serial.println(val);
     }
     else
     if(lcd_key == btnRIGHT){
      returnValue = val;     // return value back via a global variable. 
      Serial.println(editHours);
      delay(1000);
      mState = lastMstate;
     }

      if(lcd_key > 0){
        previousMillis = millis(); // reset timeout
      }

}

LCD_Clock_Display_State_Machine_v2.ino (26.8 KB)

I think I solved it

I added in to each key press to update the global variable returnValue

     if(lcd_key == btnUP){
      if(val < 23) {
      val++;
      returnValue = val; }
      else if (val > max) val = min;
      Serial.println(val);

then in the switch case of the state machine added in

if(returnValue != editHours){ 
  editHours = returnValue;
}

this catches the updated value before calling the function again to adjust the value.

Now I just need to make sure returnValue is set before entering the next state by

      returnValue = editMinutes;
      mState = EditMINS;

Feel free to tell me if there is a better way I'm still a hack at this.

Cheers
Al

Before you enter the state EditHrs:
Set the global variable that is holding the value being edited
When you leave state EditHrs:
Save the global variable of the value being edited

Or even better not using a globals at all...
Pass in a pointer to One of the values editHours, editMinutes, editSeconds
Then you can update it directly.

The edit function doesn’t need to return the value, in fact it’s probably better that it doesn’t.
Return something that indicates the state of the edit. In progress, completed, cancelled, Timeout, etc.

Then you can use that in the main state machine to desired what to do:
In progress: stay on current state
Completed: Move to next state (e.g. edit minutes)
Cancelled/timeout: Go back to displaying unedited time

I think I'm doing what you say in paragraph 1 . Saving a global, in this case editHours is saved in the "editTime" state initially and then used in the editHours state, and then saving as global at the end. Eventually they will all end up in the Update State to be saved to the RTC.

I haven't used pointers before (not intentionally), an example would be great.

The edit function doesn't need to return the value, in fact it's probably better that it doesn't.
Return something that indicates the state of the edit. In progress, completed, cancelled, Timeout, etc.

.

I don't understand what you mean here, how can the function editing the value not return the finished value.?

I would be very greatful if you could possible hash out an example (unchecked/tested) just so i know what you mean and I can google key terms. Google has been my friend in lockdown here in NZ and I spent a lot of the day on sprintf, sprintf_p atoi, itoa and why %0f doesn't work :slight_smile:

Chees
Al

If you need editvalue to have a lasting effect, you must return the value or pass a pointer to it or in C++ world, a reference. Try changing the function to this:

void editValue(int &val, byte pos_x, int min, int max)