modifying multi-digit numbers, one digit at a time by using buttons?

Hello everybody!
Im working on a system that requires some variables to be editable by the end-user, using a display and momentary push buttons.

As an example; the project includes a speedometer that is dependent on a wheel circumference variable (cirq) to be set correctly.

I therefore have a "configuration menu" implemented to select the various variables and change their values.
The problem is; the circumference can differ quite a bit and it would be tedious to increase a value from "1408" to "2114" for example, if i keep the current "one button click increase/decrease"-routine.

So now im trying to figure out how to select a variable and edit only one digit at a time.

I would prefer to be able to select what digit to edit, but a sequential edit would suffice too.
I think i could pull this off if the number was binary, but i really dont understand how i can select a single digit, edit it and store it in a base10 value, without changing the other digits in the value.

Does anybody have ideas or suggestions?

Maybe I didn't catch your problem correctly, but wouldn't that just be adding/subtracting powers of 10?

The nth digit in a decimal number (counting from the point to the left) represents 10^(n-1). Want to edit the third digit? 10^(3-1)=10^2=100. So 1408+100 = 1508, you just added 1 to the third digit.

Tim, you managed to catch my problem correctly and you are indeed correct. :slight_smile:

And i really dont need to fetch>modify>store the value digit in question, i just need to add/subtract a 1/10/100/1000 to/from the value.

I will just need to mark WHAT digit im modifying on the display and modify the button behaviour accordingly. :slight_smile:

modifying multi-digit numbers, one digit at a time by using buttons?

That's how the time and date is adjusted on my digital watch and it's a real PITA

Try to find a more user-friendly way.

...R

xarvox:
Tim, you managed to catch my problem correctly and you are indeed correct. :slight_smile:

And i really dont need to fetch>modify>store the value digit in question, i just need to add/subtract a 1/10/100/1000 to/from the value.

I will just need to mark WHAT digit im modifying on the display and modify the button behaviour accordingly. :slight_smile:

Glad you found your solution. Indeed you will need to allow to select a digit to modify, and then e.g. have that blink on the display.

As Robin said, it's maybe not the most user friendly way. For example, if I want to switch from 99 to 100, I need to adjust 3 digits. But it all depends on the range of values you expect, and how often you need to change them.

Another option might be to have the value increment by larger steps of you hold the button longer

Ive worked up a neat solution, pretty much adding/subtracting a 1/10/100/1000 depending on what "digit" ive selected.
This works just great, lowering 2000 by a 1 gives a 1999, lowering 999 with 1000 returns 0 since i compare the value being modified with the value i want to subtract and return 0 if subtracted value is greater.

I now have a new dilemma tho..

Most of my data variables are uint16_t, one is uint32_t and a few are uint8_t.

But if i were to try to store a larger value than the variable can hold, the result would be troublesome..

So is there a way to check the type of variable in the program?

That is, to be able to return a error if im trying to store a value of 500 into a byte?
Or will i have to hard-code a limit to each variable?

And finally, in case somebody else is interested in this; here´s part of the code im using. :slight_smile:

// SOME of the global variables:
bool selected = false;
bool selectedVal = false;
bool selectedValPart = false;
uint8_t numSelections = 5;
uint8_t currentSel = 0;
uint32_t currentVal = 0;
uint8_t currentValPart = 0;
uint16_t valMod[4] = {
    1,10,100,1000
};

void loop() {
  NOW = millis();
  if (checkElapsed(lastBtnCheck,btnCheckInterval)) {
      readInputs();
  }
  uint8_t thisBtnState = 0;
  thisBtnState = getBtnState(0);
  if (thisBtnState >= 2) { // "enter"-button clicked
    if (!selected) {
      if (currentSel) {
        currentVal = getConfig(currentSel);
        selected = true;
      }
    }
    else {
      if (currentVal) {
        putConfig(currentSel,currentVal);
        selected = false;
      }
    }
  }
  
  thisBtnState = getBtnState(1);
  if (thisBtnState == 2) { // "+"-button, shortclicked (increase value)
    writeSerial("btn 1 shortclick","");
    if (!selected) {
      currentSel++;
      if (currentSel > numSelections) {
        currentSel = 0;
      }
    }
    else {
      currentVal = currentVal + valMod[currentValPart];
    }
  }
  if (thisBtnState == 3) { // "+"-button, longclicked (increase valPart)
    writeSerial("btn 1 longclick","");
    currentValPart++;
    if (currentValPart >= 4) {
      currentValPart = 0;
    }
  }

  thisBtnState = getBtnState(2);
  if (thisBtnState == 2) { // "-"-button, shortclicked (increase value)
    writeSerial("btn 2 shortclick","");
    if (!selected) {
      if (!currentSel) {
        currentSel = numSelections;
      }
      else {
        currentSel--;
      }
    }
    else {
      if (currentVal <= valMod[currentValPart]) {
        currentVal = 0;
      }
      else {
        currentVal = currentVal - valMod[currentValPart];
      }
    }
  }
  if (thisBtnState == 3) { // "-"-button, longclicked (decrease valPart)
    writeSerial("btn 2 longclick","");
    if (!currentValPart) {
      currentValPart = 3;
    }
    else {
      currentValPart--;
    }
  }
  // display portion
  if(checkElapsed(lastDisplayUpdate,displayUpdateFreq)) {
    lastDisplayUpdate = NOW;
    lcd.clear();
    // printLCD(12,0,currentSel);
    // printLCD(14,0,currentValPart);
    switch(currentSel) {
      case 1: {
        printLCD(0,0,"BatteryLimit");
      } break;
      case 2: {
        printLCD(0,0,"BatteryWarn");
      } break;
      case 3: {
        printLCD(0,0,"blinkInterval");
      } break;
      case 4: {
        printLCD(0,0,"SysTimeout");
      } break;
      case 5: {
        printLCD(0,0,"wheelCirq");
      } break;
      case 0:
      default: {
        printLCD(0,0,"none");
      }
    }
    if (selected) {
      printLCD(7,1,"val: ");
      uint8_t base_offset = 11;
      uint8_t offset = 0;
      if (currentVal > 999) {
        offset = base_offset + 1;
      }
      else if (currentVal > 99) {
        offset = base_offset + 2;
      }
      else if (currentVal > 9) {
        offset = base_offset + 3;
      }
      else {
        offset = base_offset + 4;
      }
      
      printLCD(offset,1,currentVal);
      offset = ((base_offset + 4) - currentValPart);
      printLCD(offset,0,"V");
    }
}

Im using a function to read all buttons at once, then populate a struct with each button state.
I then use a separate function to poll the button struct to get the clicktype of a specific button.
getBtnState(2) polls button 2 and returns 0 if not clicked, 1 if pressed, 2 if shortclicked and 3 if longclicked.
short/long-clicked gets reset to 0 in the getBtnState function.

xarvox:
So is there a way to check the type of variable in the program?

I'm unsure if this is all of the functionality you are looking for, but sizeof() can tell you the size of a variable (in bytes). I think this should suffice, especially since of the types you mentioned are unsigned.

I realized that the intermediate variable (used to copy original variables into) is always a specific size, so i cant make a comparison with the original value size, instead im forced to hard-code each variable size limitation.. :confused:
But it works out allright, the program refuses to store anything larger than set value, warns the end-user about it and displays the maximum value along with current value.

Its not the prettiest code ever, but its mine :slight_smile:

Thanks TimMJN for the assistance!