Problem with logic

In the following snippet, pause and reset are bool types and keyPressed returns a character.

When I press '#' the outer look is paused because the inner loop runs until '#' is pressed again. If '*' is pressed while in the inner loop, the inter look is exits as is the outer one.

From the Serial.print command I can see the the correct characters are been returned. but rest and pause are not being set as per the code. I must has a flaw in my thinking.

I am wondering if all the the if statements are always all checked i.e. if "#' is pressed then the first to will always be checked as well?

   do{
       //some other code    
        do {
              ctrlKey=keyPressed();
              if ((ctrlKey=='#') && pause) {
                  ctrlKey=' ';
                  pause=false;
                  startClock=millis();
                  //adjust clock by +(startClock-stopClock);
              }
              else if (ctrlKey=='#') {
                  ctrlKey=' ';
                  pause=true;
                  stopClock=millis();
              }
              else if ((ctrlKey=='*') && pause) reset=true;
          } while (pause && !reset);

          Serial.print(ctrlKey); Serial.print(":"); Serial.print(pause); Serial.print(":"); Serial.println(reset);

     } while (!reset);

I must has a flaw in my thinking.

Yes. It is in thinking that you can post just a snippet of code, and get meaningful answers.

We can't see what types the variables are, or what the functions do.

I am wondering if all the the if statements are always all checked i.e. if "#' is pressed then the first to will always be checked as well?

No because you have an else

What is pause initialized with when entering the outer while?

The inner loop repeats until 'pause' is false or 'reset' is true. What does your output show?

Hi

Here is the complete code. Everything works except the following:

Once a number is entered, and the timer started, I wish to pause the timer when '#' is pressed. If '*' is pressed while paused then the whole program restarts i.e. the last internal loop exits and the main program loop starts again.


I still have to finish the bit that adjusts the timer while it is paused but I will work on that one I can get this problem sorted.

Both bool variables are set to false when initialised.

Tracing the variables, when "#" is pressed, even though pause==false, the if statement with ((ctrlKey=='#') && pause) is still triggered which should not be the case.

Here is the complete code

If you want help THERE, post your question THERE.

If you want help HERE, post your code HERE.

Build a state machine

you push the key, the pause is set, key is still pushed, pause ends, loop ends

OK I have tried to simplify things but still have two issues - one is a new one and the other the old logic issue

  1. readTime() returns a double entry e.g. when I press 1 I get 11
  2. in the main loop, the nested "if (pause)" statement is always triggered, even after I have reset ctrlKey to 'b' and therefore the previous if statement "(ctrlKey=='#')"should not have been triggered.

I think both issues are related i.e. something to do with how keyPressed() works. It.s like keyPressed keeps returning the last key returned by the function instead of resetting it. In short I am confused.

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3F,16,2); 
#include <Keypad.h>

  const byte ROWS = 4; //four rows
  const byte COLS = 3; //four columns
  //define the cymbols on the buttons of the keypads
  
  char hexaKeys[ROWS][COLS] = {
    {'1','2','3'},
    {'4','5','6'},
    {'7','8','9'},
    {'*','0','#'}
  };
  byte rowPins[ROWS] = {7,6, 8, 5}; //connect to the row pinouts of the keypad
  byte colPins[COLS] = {4, 3, 2}; //connect to the column pinouts of the keypad
  //initialize an instance of class NewKeypad
  Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
  
  int minutes, seconds, targetTime, timer, lghts[3];
  
  unsigned long startTime, checkTime, startClock,stopClock;
  
  char ctrlKey;
  
  bool pause, reset;

void setup(){
  Serial.begin(9600);

  double integer;
  
  lcd.init();
  lcd.backlight();  
  Serial.begin(115200);
}

char keyPressed() {

  char customKey = ' ';
 
  customKey = customKeypad.getKey();
    
  if (customKey){
      return customKey;   
  }
}

String readTime(int noDigits) {

  String numStr ="";
  char keyPrsd;
 
  do {
      keyPrsd = keyPressed();
    
      if ((keyPrsd>='0' && keyPrsd<='9') && (numStr.length()<2)){
        numStr=numStr+keyPrsd;
        lcd.print(keyPrsd);
      }
      if ((keyPrsd=='*') &&(numStr.length()>=1)){
        numStr.remove(numStr.length()-1);
        lcd.setCursor(0,1);
        lcd.print("00");
        lcd.setCursor(0,1);
        lcd.print(numStr);
      }

  } while ((keyPrsd!='#') || (numStr.length()==0));
  if (numStr.length()<2) {numStr=numStr+"0";}
   return numStr;
}

void initialise() {
  lcd.setCursor(0,0);
  lcd.print("Enter (#) Time");
  lcd.setCursor(0,1);
  lcd.print("00:00");
  lcd.setCursor(0,1);
  targetTime=readTime(2).toInt(); //gets times in minutes as a string and converts to integer
  
  getTimings(targetTime*60); //converts minutes to seconds
  lcd.clear();

  lcd.setCursor(9,0);
  lcd.print("(");
  if (targetTime<10) {lcd.print("0");}
  lcd.print(targetTime);
  lcd.print(":00)");


  /* Check number entered */
  Serial.print("Target time: ");
  Serial.println(targetTime);
  Serial.print("0:");Serial.print(lghts[0]);Serial.print(" - ");
  Serial.print("1:");Serial.print(lghts[1]);Serial.print(" - ");
  Serial.print("2:");Serial.print(lghts[2]);Serial.print(" - ");
  Serial.print("3:");Serial.println(lghts[3]);

  startTime = millis();
  pause = false;
  reset = false;
  ctrlKey = 'a';
}

int getMinutes(unsigned long currentTime,unsigned long startTime) {
  return ((currentTime-startTime)/1000) /60;
}

int getSeconds(unsigned long currentTime,unsigned long startTime) {
  return ((currentTime-startTime)/1000) % 60;
}

void getTimings(int n)
{

    lghts[2] = n;
    if (n == 60)
    {
        lghts[1] = 50;
        lghts[0] = 40;
    } else if (n==120) {
        lghts[1] = 90;
        lghts[0] = 60;
    } else {
        lghts[1] = n-60;
        lghts[0] = n-120;
    } 
}

void loop()
{
  initialise();
   Serial.print(ctrlKey); Serial.print(":"); Serial.print(pause); Serial.print(":"); Serial.println(reset);
  do {   
        checkTime=millis();

        seconds=getSeconds(checkTime,startTime);
        minutes=getMinutes(checkTime,startTime);
      
        timer=minutes*60+seconds; 
      
        if (timer>=lghts[2]) {
           lcd.setCursor(0,1);
           lcd.print("Red     ");
         } else if (timer>=lghts[1]){
              lcd.setCursor(0,1);
              lcd.print("Orange");   
         } else if (timer>=lghts[0]){
              lcd.setCursor(0,1);
              lcd.print("Green");   
         }
         
         lcd.setCursor(0,0);
         if (minutes>10) {
           lcd.print(minutes);
         }
         else {
          lcd.print(0);lcd.print(minutes);
         }
         lcd.print(":");
         if (seconds>9) {
           lcd.print(seconds);
          }
          else {
              lcd.print(0);lcd.print(seconds);
          }

          ctrlKey=keyPressed();
          if (ctrlKey=='#')  {
            if (pause) {
              pause=false;
              ctrlKey='c'; //reset ctrlKey so no longer =='#'
              Serial.print(ctrlKey); Serial.print(":"); Serial.print(pause); Serial.print(":"); Serial.println(reset);
              //code to "stop" clock coming still
            } else {
              pause = true;
              ctrlKey='b';
              Serial.print(ctrlKey); Serial.print(":"); Serial.print(pause); Serial.print(":"); Serial.println(reset);
            }
          } else if (ctrlKey=='*')  reset=true;
          
 //         Serial.print(ctrlKey); Serial.print(":"); Serial.print(pause); Serial.print(":"); Serial.println(reset);
  
       } while (!reset); 
       lcd.clear();

}

Aren’t you getting warnings at compile time?

For your question 1, why do you think you get back 11 when you press 1#? (you actually return a String (see notes below) “10”)

For your question 2, why do you think so?

Some notes:

  1. Using the String class is a recipe for failure. Get rid of it, use c-string instead.

  2. In char keyPressed() { what do you return if No key was pressed?
    BTW note There is no need to initialize customKey if the first thing you do the line after is put another value in that variable

char customKey = ' ';
customKey = customKeypad.getKey();
  1. In String readTime(int noDigits) { why don’t you use the parameter noDigits and test against a hardwired length of 2 all the time? You could have an else in between the two if in the do {...}

(You also return by value an object that was dynamically created locally. What happens is that the copy constructor gets called to make a copy of the local object. Whilst the compiler may eliminate the copy in a process called copy elision (at its discretion), you don't have control over it. never tried if the String class had an effective copy constructor (because of point 1, never use that class))

  1. What’s going on in void getTimings(int n) ? You test against 60 and 120 (ie if user hade entered 1 or 2 as you call it with getTimings(targetTime*60);. If I enter 0 for example your if will store a negative value in the array (Also Have not check how much math you do with those but using int as a type for int minutes, seconds, targetTime, timer, lghts[3]; might lead to overflow issues. May be those need to be unsigned )

  2. When you do

ctrlKey=keyPressed();
          if (ctrlKey=='#')  {

the previous value (eg your ‘b’ or ‘c’) is always erased and replaced by no key if nothing was entered or the key pressed.

Thanks so much. I did not notice that nothing was returned when no key was pressed

Changed keyPressed() to

char keyPressed() {

  char customKey = ' ';
 
  customKey = customKeypad.getKey();
    
  if (customKey){
      return customKey;   
  } else return 'n';
}

And everything came right. I will none the less address the string issue.

Just to answer your first question, there were no compile warning.

The reason set ctrlKey to n, a, b, or c is purely for debugging purposes so that I can see the outputs in Serial.print.

Regarding an entry of 0, I never tested for; always assumed it would be a non-zero number :slight_smile: Will fix that.

The largest time that can be entered is 99 ie 5940 so INT should be fine.

Lastly, I never use lghts[3] but when I only had lgths[2] defines my code did not work properly (can't remember what the issue was but it just didn't work until I changed the definition to lghts[3];

I will post my complete code once done. Thanks again.

I am a little confused about c-string. Done some googling but it's still not clear.

Does this mean I need to use char num[] and const char * myFunction()?

Why Serial.begin(9600) AND Serial.begin(115200) in your setup?

you could define a global buffer for the value entered.

That being said, since you want to read a number, I would suggest that the function should return a number directly

here is a piece of code I had written a while back.

// ****** HANDLE KEYPAD ******
#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns

//define the symbols on the buttons of the keypads
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

// Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
byte rowPins[ROWS] = { 34, 36, 38, 40 };
// Connect keypad COL0, COL1, COL2 and COL3 to these Arduino pins.
byte colPins[COLS] = {41, 39, 37, 35};

//initialize an instance of class Keypad
Keypad membraneKeypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS);


// ****** HANDLE LCD ******
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd( 0x3F, 20, 4); // my LCD is at I2C address 0x3F, 20 cols, 4 rows


// **********************************************************************************
// getNumber()
// if prepare is true, we initialize everything to receive data in a specific field on the LCD
// otherwise it handles the data input
// returns true when user has pressed the # key
// Note: defined defaults value to simplify calling the function when not in prepare stage (see below)
// **********************************************************************************
boolean getNumber(long *dataEntry, const boolean prepare = false, const uint8_t x = 0, const uint8_t y = 0, const uint8_t fieldSize = 2, const char asciiChar = '_')
{
  boolean entryCompleted = false;
  static uint8_t _x0, _y0, _x, _w;
  static char _p; //padding Char
  static boolean _fullEntry = false;

  if (prepare) { // initialize the capture field
    const byte maxLongLength = 12; // enough for -2147483648 so 11 characters + trailing null
    char dataBuffer[maxLongLength];
    _x0 = _x = x;
    _y0 = y;
    _w = fieldSize;
    _p = asciiChar;

    lcd.setCursor(_x0, _y0);
    for (uint8_t i = 0; i < _w; i++) lcd.write(_p);
    lcd.setCursor(_x0, _y0);

    if (dataEntry) { // if we gave an initial value, display the part that fits into the area
      ltoa(*dataEntry, dataBuffer, 10); // convert value into c-string in base 10
      if (strlen(dataBuffer) < _w) { // do we fit in the reserved area?
        _fullEntry = false;
        lcd.print(dataBuffer);
        _x = _x0 + strlen(dataBuffer);
      } else {
        dataBuffer[_w] = '\0';
        *dataEntry = atol(dataBuffer);
        _fullEntry = true;
        lcd.print(dataBuffer);
        _x = _x0 + strlen(dataBuffer) - 1;
        lcd.setCursor(_x, _y0); // and don't go further than the reserved space
      }
    }
    lcd.blink();
  } else {
    char entry = membraneKeypad.getKey();

    switch (entry) // don't deal with case 'A'...'D':
    {
      case '0'...'9': // add this digit
        lcd.print(entry);
        if (_fullEntry) {
          *dataEntry = (*dataEntry / 10) * 10 + entry - '0'; // overwrite the last digit
          lcd.setCursor(_x, _y0); // and don't go further than the reserved space
        } else {
          *dataEntry = *dataEntry * 10 + entry - '0';
          _x++;
          if (_x >= _x0 + _w) {
            _x--;
            lcd.setCursor(_x, _y0);
            _fullEntry = true;
          }
        }
        break;

      case '*': // erase last digit
        *dataEntry = *dataEntry / 10;
        if (_fullEntry) {
          _fullEntry = false;
        } else {
          if (_x > _x0) _x--;
        }
        lcd.setCursor(_x, _y0);
        lcd.write(_p);
        lcd.setCursor(_x, _y0);
        break;

      case '#':
        lcd.noBlink();
        if (!_fullEntry)
          for (uint8_t i = _x; i < _x0 + _w; i++) lcd.write(' '); // replace padding with white spaces
        entryCompleted = true;
        break;
    }
  }
  return entryCompleted;
}


void setup() {
  long dataEntry = 0;

  Serial.begin(115200);
  lcd.begin();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0); lcd.print(F("Min=")); // to see the area
  getNumber(NULL, true, 4, 0, 5, '_'); // prepare the data entry. initial value, prepare mode, start col for field entry, start line for field entry, padding number during entry
  while (!getNumber(&dataEntry)); // active loop until we get an entry (validated by #)
  lcd.clear();
  lcd.setCursor(0, 0); lcd.print(F("Min set @ ")); lcd.print(dataEntry);
}

void loop() {}

Thanks all for your help. I finally have completed the coding aspect. As you will see my programming experience dates back from my Pascal days in the 90's so I have some gaps with respect to objects and c data types. This said it's been good to dust the cobwebs back at start with some up-skilling :slight_smile: Now to package it all up in a case.

So what does this do? It is a timing light for Toastmasters. Basically a Toastmaster speech has a set min (Green) and max (Red) time. Except for 1 & 2 minute options, the green, amber and red lights are set to come on 2min, 1min ,0min from the Max time.

Time can be paused (it's an approximate pause) by pressing "#" and then reset by pressing "*" while paused.

Final code attached as too long to include in post.

Toastmaster_Timer_V5.ino (9.5 KB)

I thought I had it sussed but ran into one more problem.

My initialize function call setTime which in turn calls getNumber

The getNumber function works fine the first time that I call it but if I call it a second time (and I have entered a two digit entry previously) it will not permit any more 2 digit numbers. For example if for lghts[2] = setTime("Red") * 60 I enter a two digit number then when I execute lghts[1] = setTime("Orange") * 60 getNumber will no longer permit a two digit entry.

I think it might have something to do with the pointer but not sure.

The code for getNumber I am borrowing from above.

Toastmaster_Timer_V6.ino (8.76 KB)

try this example which demonstrates how to call my function multiple times

--> I moved the code from the setup() into the loop() and use it in a non blocking way
--> input data on first line of the LCD, display what has been typed on second line
--> option in the code (commented out lines) to either allow user to change his mind and type a new number starting from the one already entered (using '*' on the keypad to erase existing digits) or if you comment out the existing line and un-comment the 2 other one, alternative is to start from a blank entry again.

give it a try

// ****** HANDLE KEYPAD ******
#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns

//define the symbols on the buttons of the keypads
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

// Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
byte rowPins[ROWS] = { 34, 36, 38, 40 };
// Connect keypad COL0, COL1, COL2 and COL3 to these Arduino pins.
byte colPins[COLS] = {41, 39, 37, 35};

//initialize an instance of class Keypad
Keypad membraneKeypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS);


// ****** HANDLE LCD ******
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
const byte nbCols = 20;
const byte nbLines = 4;
LiquidCrystal_I2C lcd( 0x3F, nbCols, nbLines); // my LCD is at I2C address 0x3F, 20 cols, 4 rows


// **********************************************************************************
// getNumber()
// if prepare is true, we initialize everything to receive data in a specific field on the LCD
// otherwise it handles the data input
// returns true when user has pressed the # key
// Note: defined defaults value to simplify calling the function when not in prepare stage (see below)
// **********************************************************************************
boolean getNumber(long *dataEntry, const boolean prepare = false, const uint8_t x = 0, const uint8_t y = 0, const uint8_t fieldSize = 2, const char asciiChar = '_')
{
  boolean entryCompleted = false;
  static uint8_t _x0, _y0, _x, _w;
  static char _p; //padding Char
  static boolean _fullEntry = false;

  if (prepare) { // initialize the capture field
    const byte maxLongLength = 12; // enough for -2147483648 so 11 characters + trailing null
    char dataBuffer[maxLongLength];
    _x0 = _x = x;
    _y0 = y;
    _w = fieldSize;
    _p = asciiChar;

    lcd.setCursor(_x0, _y0);
    for (uint8_t i = 0; i < _w; i++) lcd.write(_p);
    lcd.setCursor(_x0, _y0);

    if (dataEntry) { // if we gave an initial value, display the part that fits into the area
      ltoa(*dataEntry, dataBuffer, 10); // convert value into c-string in base 10
      if (strlen(dataBuffer) < _w) { // do we fit in the reserved area?
        _fullEntry = false;
        lcd.print(dataBuffer);
        _x = _x0 + strlen(dataBuffer);
      } else {
        dataBuffer[_w] = '\0';
        *dataEntry = atol(dataBuffer);
        _fullEntry = true;
        lcd.print(dataBuffer);
        _x = _x0 + strlen(dataBuffer) - 1;
        lcd.setCursor(_x, _y0); // and don't go further than the reserved space
      }
    }
    lcd.blink();
  } else {
    char entry = membraneKeypad.getKey();

    switch (entry) // don't deal with case 'A'...'D':
    {
      case '0'...'9': // add this digit
        lcd.print(entry);
        if (_fullEntry) {
          *dataEntry = (*dataEntry / 10) * 10 + entry - '0'; // overwrite the last digit
          lcd.setCursor(_x, _y0); // and don't go further than the reserved space
        } else {
          *dataEntry = *dataEntry * 10 + entry - '0';
          _x++;
          if (_x >= _x0 + _w) {
            _x--;
            lcd.setCursor(_x, _y0);
            _fullEntry = true;
          }
        }
        break;

      case '*': // erase last digit
        *dataEntry = *dataEntry / 10;
        if (_fullEntry) {
          _fullEntry = false;
        } else {
          if (_x > _x0) _x--;
        }
        lcd.setCursor(_x, _y0);
        lcd.write(_p);
        lcd.setCursor(_x, _y0);
        break;

      case '#':
        lcd.noBlink();
        if (!_fullEntry)
          for (uint8_t i = _x; i < _x0 + _w; i++) lcd.write(' '); // replace padding with white spaces
        entryCompleted = true;
        break;
    }
  }
  return entryCompleted;
}


void setup() {
  Serial.begin(115200);
  lcd.begin();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Min=")); // to see the area
  
  // prepare the data entry. initial value in empty, prepare mode, start col for field entry, start line for field entry, padding number during entry
  getNumber(NULL, true, 4, 0, 5, '_');
}

void loop() {
  static long dataEntry = 0; // important to be static to survive across loops

  if (getNumber(&dataEntry)) { // wait until we get an entry validated by #

    // erase the second line
    lcd.setCursor(0, 1);
    for (byte i = 0; i < nbCols; i++) lcd.print(F(" "));
    lcd.setCursor(0, 1);

    // print the data we got
    lcd.print(F("Min set @ ")); 
    lcd.print(dataEntry);

    // prepare the next data entry. initial value is what has been entered
    getNumber(&dataEntry, true, 4, 0, 5, '_'); // we start from the existing value, use * for example on the pad to erase digits if necessary 

    // if you want to restart from an empty value you would do that instead of the line above
    // dataEntry = 0;
    // getNumber(NULL, true, 4, 0, 5, '_'); // start with no value
  
  }

  // here you could do other stuff
}

Many thanks @J-M-L for your patient help. I have modified your code to accommodate my 3x4 keypad and my line placement but yet it still does the same thing i.e. the 2nd, 3rd etc entries only allow one digit to be entered, keeping the first digits of the initial entry (only changes the last entry e.g. Enter "123" in first entry and then all other entries can only be "12?"

// ****** HANDLE KEYPAD ******
#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns

const byte NUMLENG = 3;

//define the symbols on the buttons of the keypads
char keys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};

byte rowPins[ROWS] = {8, 7, 6, 5}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {4, 3, 2}; //connect to the column pinouts of the keypad


//initialize an instance of class Keypad
Keypad membraneKeypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS);


// ****** HANDLE LCD ******
#include <LiquidCrystal_I2C.h>
const byte nbCols = 16;
const byte nbLines = 2;
LiquidCrystal_I2C lcd( 0x3F, nbCols, nbLines); // my LCD is at I2C address 0x3F, 20 cols, 4 rows


// **********************************************************************************
// getNumber()
// if prepare is true, we initialize everything to receive data in a specific field on the LCD
// otherwise it handles the data input
// returns true when user has pressed the # key
// Note: defined defaults value to simplify calling the function when not in prepare stage (see below)
// **********************************************************************************
boolean getNumber(long *dataEntry, const boolean prepare = false, const uint8_t x = 0, const uint8_t y = 0, const uint8_t fieldSize = 2, const char asciiChar = '_')
{

  boolean entryCompleted = false;
  static uint8_t _x0, _y0, _x, _w;
  static char _p; //padding Char
  static boolean _fullEntry = false;

  if (prepare) { // initialize the capture field
    const byte maxLongLength = 12; // enough for -2147483648 so 11 characters + trailing null
    char dataBuffer[maxLongLength];
    _x0 = _x = x;
    _y0 = y;
    _w = fieldSize;
    _p = asciiChar;

    lcd.setCursor(_x0, _y0);
    for (uint8_t i = 0; i < _w; i++) lcd.write(_p);
    lcd.setCursor(_x0, _y0);

    if (dataEntry) { // if we gave an initial value, display the part that fits into the area
      ltoa(*dataEntry, dataBuffer, 10); // convert value into c-string in base 10
      if (strlen(dataBuffer) < _w) { // do we fit in the reserved area?
        _fullEntry = false;
        lcd.print(dataBuffer);
        _x = _x0 + strlen(dataBuffer);
      } else {
        dataBuffer[_w] = '\0';
        *dataEntry = atol(dataBuffer);
        _fullEntry = true;
        lcd.print(dataBuffer);
        _x = _x0 + strlen(dataBuffer) - 1;
        lcd.setCursor(_x, _y0); // and don't go further than the reserved space
      }
    }
    lcd.blink();
  } else {
    char entry = membraneKeypad.getKey();

    switch (entry) // don't deal with case 'A'...'D':
    {
      case '0'...'9': // add this digit
        lcd.print(entry);
        if (_fullEntry) {
          *dataEntry = (*dataEntry / 10) * 10 + entry - '0'; // overwrite the last digit
          lcd.setCursor(_x, _y0); // and don't go further than the reserved space
        } else {
          *dataEntry = *dataEntry * 10 + entry - '0';
          _x++;
          if (_x >= _x0 + _w) {
            _x--;
            lcd.setCursor(_x, _y0);
            _fullEntry = true;
          }
        }
        break;

      case '*': // erase last digit
        *dataEntry = *dataEntry / 10;
        if (_fullEntry) {
          _fullEntry = false;
        } else {
          if (_x > _x0) _x--;
        }
        lcd.setCursor(_x, _y0);
        lcd.write(_p);
        lcd.setCursor(_x, _y0);
        break;

      case '#':
        lcd.noBlink();
        if (!_fullEntry)
          for (uint8_t i = _x; i < _x0 + _w; i++) lcd.write(' '); // replace padding with white spaces
        entryCompleted = true;
        break;
    }
  }
  return entryCompleted;
}


void setup() {
  Serial.begin(115200);
  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(NUMLENG+1, 1);
  lcd.print(F("Min")); // to see the area
 
  // prepare the data entry. initial value in empty, prepare mode, start col for field entry, start line for field entry, padding number during entry
  getNumber(NULL, true, 0, 1, NUMLENG, ' ');
}

void loop() {
  static long dataEntry = 0; // important to be static to survive across loops

  if (getNumber(&dataEntry)) { // wait until we get an entry validated by #

    // erase the second line
    lcd.setCursor(0, 1);
    for (byte i = 0; i < nbCols; i++) lcd.print(F(" "));
    lcd.setCursor(0, 0);

    // print the data we got
    lcd.print(F("Min set @ "));
    lcd.print(dataEntry);
    Serial.println(dataEntry);
    // prepare the next data entry. initial value is what has been entered
    //getNumber(&dataEntry, true, 0, 1, NUMLENG, ' '); // we start from the existing value, use * for example on the pad to erase digits if necessary

    // if you want to restart from an empty value you would do that instead of the line above
    // dataEntry = 0;
    getNumber(NULL, true, 0, 1, NUMLENG, ' '); // start with no value
 
  }

  // here you could do other stuff
}

You did not uncomment
dataEntry = 0;...

The following works i.e. it allows me to erase the previous numbers and then enter what I want.

    // prepare the next data entry. initial value is what has been entered
    getNumber(&dataEntry, true, 0, 1, NUMLENG, ' '); // we start from the existing value, use * for example on the pad to erase digits if necessary

    // if you want to restart from an empty value you would do that instead of the line above
    //dataEntry = 0;
    //getNumber(NULL, true, 0, 1, NUMLENG, ' '); // start with no value

but the following which now resets the value to 0 (as a result of uncommenting the dataEntry=0 line ) but still only lets me enter one digit.

    // prepare the next data entry. initial value is what has been entered
    //getNumber(&dataEntry, true, 0, 1, NUMLENG, ' '); // we start from the existing value, use * for example on the pad to erase digits if necessary

    // if you want to restart from an empty value you would do that instead of the line above
    dataEntry = 0;
    getNumber(NULL, true, 0, 1, NUMLENG, ' '); // start with no value

Making the following change gets me closer

    // prepare the next data entry. initial value is what has been entered
    //getNumber(&dataEntry, true, 0, 1, NUMLENG, ' '); // we start from the existing value, use * for example on the pad to erase digits if necessary

    // if you want to restart from an empty value you would do that instead of the line above
    dataEntry = 0;
    getNumber(&dataEntry, true, 0, 1, NUMLENG, ' '); // start with no value

But now I am presented with a leading zero which I now need to delete first.

It looks like I need to "clear" dataEntry" totally but I am an not sure how to do that. As a guess I tried dataEntry=NULL but that does not work.