How to advance to next integer after button push

I'll start off by saying I'm not very good at coding but I am trying my best.

I'm working on a bench top power supply and I want to use rotary encoders to set voltage and current limits. I think I have a good grasp on the general code I need to write, but where I'm drawing a blank is, I want to be able to set the voltage integers as 4 values (10s digit, 1s digit, tenths digit, and hundredths digit)... so for example, if i want to set 12.00V, I want to turn the rotary encoder to set the first int to 1, press the encoder switch, automatically advance to the next int and turn the rotary encoder to set the next value and so on... So pushing the encoder switch would cycle through the 4 digits to change them. I want to do the same thing for current limit.

Hopefully this makes sense and I appreciate any help I can get, but try to speak my language, which is not a experienced programmer.

Thanks!

That's not that hard... That's one of the simpler parts of the supply...

First understand the difference between a pushed button and a button becoming pushed. That's called state change detection (Google it :wink: ). But you can make life easy and handle button debounce as well by grabbing a library for that like Bounce2 :slight_smile: No need to reinvent the wheel.

Next, you just need a variable to hold at which digit you are. And simply increment that every time the buttons BECOMES pressed

With Bounce2 that will look like

byte cursorLocation;

rotaryButton.update();
if(rotaryButton.fell()){
  cursorLocation++;
  if(cursorLocation >= 4){
    cursorLocation = 0;
  }
}

Thank you very much, that makes sense and I actually already have Bounce2 incorporated in my sketch which should help.

My next question is, I'm really getting lost on how to combine all the things I want to do.

So I now know how to cycle through the digits, I think I know how to use the encoder library to cycle digits from 0 through 9, and I think I know how to do the math function to convert the final 4 digit voltage and current into numbers to program the output to the power supply transistor.

Now I'm trying to figure out how to make each digit only changeable when it has the cursor on the LCD under it. Then I want to do a long press on the rotary button to tell it to set the value of all 4 digits.

Hopefully this makes sense.

Thanks

That's just a matter of good program planning... There are good topics about that on the forum. The most basic rule, slice the program into smaller bits and functions.

And a question, do you really just want to cycle trough 0 to 9 for a digit? I think it's way more convenient that if you turn the know while on the 1s digit that if you pass 9 it does up the 10s digit. That feels way more natural.

So if the cursorLocation is 1 (10s digits) you increment or decrease the value in steps of 10 etc. No need to have combines etc.

I think I get what you're saying about the digits and it seems to make sense. I can try to do that instead.

About looking around. I've searched the forum quite a bit and found similar ideas but I'm such a newb that I'm struggling to figure it out.

Can any of you experts give me any suggestions or code examples that would pertain to what I'm after?

Now I'm trying to figure out how to make each digit only changeable when it has the cursor on the LCD under it. Then I want to do a long press on the rotary button to tell it to set the value of all 4 digits.

If you want to change the digits individually, one at a time with the encoder, but only save the value when complete (not four presses, one for each digit) you will need to keep track of the cursor position and what the encoder value is for each position. Construct a number by powers of 10 and save it when done with the button push.

That is exactly my plan and I have all the individual pieces (I think) in my code... I'm just really struggling on how to piece it together to get the button to "activate" a current value and save that value when I advance to the next value.

Do you only have the encoder button for input?

How do you enter the adjustment routine?

If you only have one input button, you may have to use a short and long press to do two different things. Bounce2 will make it easy to determine the duration of a button press. Maybe long press enters the adjustment routine where it sets the cursor at one digit and flashes and increments that digit with the encoder turns, then a short press will save the value and advance to the next digit.

I have an encoder for the voltage and one for the current but what you said is exactly what I want to do... just having a hard time wrapping my head around the code part.

Would it make sense to make that a switch case so it will ignore that stuff when the values aren't being changed?

..I'm not sure of what is confusing you, and what you already know how to do.

I think the best thing to do at this point would be to write down exactly what you want to do, and then we can work on how to turn each piece into coded

For example
1.Start adjustment routine--button press, serial input, time of day?
2. set cursor to location(x,y)
3. flash the digit? blank the digit?
4. display the digit currently selected by the encoder
5. accept the digit selected, store the value
6. repeat 2 through 5
7. when all digits are entered, created combined integer
8. do something with the new value

Using a switch case, or a state machine which advances to the next step, is not a bad way to do this, but first, please lay out in detail what you want to do. Think it through as if you were a computer. Then, take a stab a coding what you have and bring it back for comment.

Like I said, I would NOT make individual digits and combine them. Because that would make it harder to deal with overflow and underflow. I think it's way more natural that if you're on 10V with the 1s digit selected that the next voltage down would be 9V, NOT 19V which you have if you only look at individual pieces...

So I would

  • save which digit is selected
  • and depending on that, increment/decrease the total number accordingly

So if digit 0 (aka, 1s aka far right) is selected increment/decrease in steps of 1
If digit 1 (aka, 10s aka second of the right) is selected increment/decrease in steps of 10.

So I would

  • save which digit is selected
  • and depending on that, increment/decrease the total number accordingly

Yes, Septillion's approach looks good and will be easier to code. I would use that approach when you write down the exact steps you need to follow. Define in detail first, code later.

So I think I'm MAYBE getting somewhere. I have uploaded a copy of my code so far. Ive not finished it, and erased everything below the voltage setting portion of the loop which i think would be repeated for the current and everything else I think I can handle. Hopefully what I'm trying to do makes sense, but take it easy on me lol, I'm really not that good at this... :slight_smile:

Power_supply_code_rev1_partial.ino (5.5 KB)

jross827:
and erased everything below the voltage setting portion of the loop which i think would be repeated for the current

If you write code and you write nearly the same multipliple times there is in 99.9% of the cases a better way. In this case, why don't you make a function that handles it? That way you can use the function of both the voltage and the current setting :wink:

And starting at getting more efficient, the moment you want to number or otherwise orderly suffix variables it's time for arrays :wink:

    if(cursorVLocation >= 5){
      cursorVLocation = 1;
    }
[/quote]
Qhy not keep things easy and start at 0? 

And your making it hard on yourself by not doing what i sugested in my posts ;) NOT spilt the value, just use a different value (a multiple of 10) to increment the value based on the position. Wayyyyyyyy easier.

But I have no idea which encoder library you use and what it returns so bit hard to help.

But a more general advice, try to chop the program into smaller more manageable pieces and make functions of them. That way your loop might look as simple as
[code]loop(){
  updateInputs();
  updateDisplay();
  updateOutputs();
}
 if(cursorVLocation = 1)

all the conditionals require == instead of =
You need to compare not assign.

I'm not really sure I know what you mean or how to do it when you say

And your making it hard on yourself by not doing what i sugested in my posts :wink: NOT spilt the value, just use a different value (a multiple of 10) to increment the value based on the position. Wayyyyyyyy easier.

One other quick question.. any suggestions on how to determine a long button press with the bounce2 library?

Here is some code for a short and long button press with Bounce2. The long press is to a timeout value, rather than an actual release of the button. In your previously posted code, you do not have the syntax correct for using the library. You call functions by the object name, and not the pin which is attached to it.

#define buttonPin 5
#define shortTime 500
#define longTime 1500

#define noEvent    0
#define shortPress 1
#define longPress  2

// Instantiate a Bounce object called button :
Bounce button = Bounce();

unsigned long buttonPressStartTimeStamp;
unsigned long buttonPressDuration;
boolean startTimeout = false;
boolean endTimeout = false;
byte event;

void setup()
{
  Serial.begin(115200);
  Serial.println("Long/Short Button Press");

  // Setup the button with an internal pull-up
  pinMode(buttonPin, INPUT_PULLUP);
  button.attach(buttonPin);
  button.interval(20);//set debounce interval
}
void loop()
{
  event = checkButton();

  switch (event)
  {
    case shortPress:
      Serial.println("short press");
      //do short press code
      break;

    case longPress:
      Serial.println("long press");
      //do long press code
      break;
  }
}

byte checkButton()
{
  byte event = noEvent;
  // Update the Bounce instance, does digitalRead of button
  button.update();

  // Button press transition from HIGH to LOW)
  if (button.fell())
  {
    buttonPressStartTimeStamp = millis();
    startTimeout = true;
  }

  // Button release transition from LOW to HIGH) :
  if (button.rose())
  {
    buttonPressDuration = (millis() - buttonPressStartTimeStamp);
    startTimeout = false;
  }

  if (buttonPressDuration > 0 && buttonPressDuration <= shortTime)
  {
    event = shortPress;
    buttonPressDuration = 0;
  }

  if (startTimeout == true && (millis() - buttonPressStartTimeStamp) > longTime)
  {
    event = longPress;
    startTimeout = false;
    buttonPressDuration = 0;
  }
  return event;
}

Here is a more simple sketch for duration of the button press which can give you some ideas

// Include the Bounce2 library found here :
// https://github.com/thomasfredericks/Bounce2
#include <Bounce2.h>

#define buttonPin 5

// Instantiate a Bounce object called button 
Bounce button = Bounce();

unsigned long buttonPressStartTimeStamp;
unsigned long buttonPressDuration;

void setup() {
  Serial.begin(115200);
  Serial.println("Button Press Duration Example");
  // Setup the button with an internal pull-up
  pinMode(buttonPin, INPUT_PULLUP);
  button.attach(buttonPin);
  button.interval(20);//set debounce interval
}

void loop() {

  // Update the Bounce instance, does digitalRead of button
  button.update();

  // Button press transition from HIGH to LOW)
  if (button.fell())
  {
    buttonPressStartTimeStamp = millis();
  }

  // Button release transition from LOW to HIGH) :
  if (button.rose())
  {
    buttonPressDuration = (millis() - buttonPressStartTimeStamp);
    Serial.print("Button pressed for ");
    Serial.print(buttonPressDuration); 
    Serial.println(" milliseconds");   
  }
}

You now try to increment 4 different values you want to form the total number. But just leave it as one number but use different increments. Something like (pseudocode)

byte cursorLocation;
bool voltageSelected = true;

unsigned int voltage;
unsigned int current;

const byte MaxCursorPosition = 5;
const unsigned int Power10[MaxCursorPosition] = {1, 10, 100, 1000, 10000};

void loop(){
  updateInput();
}

void updateInput(){
  updateButtons();
  updateRotary();
}

void updateButtons(){
  rotaryButton.update();
  if(rotaryButton.fell(){
    cursorLocation++;
    
    if(cursorLocation >= MaxCursorPosition){
      cursorLocation = 0;
    }
  }
}

void updateRotary(){
  const int steps = getStepsFromRotary();
  
  if(voltageSelected){
    incrementValue(voltage, steps);
  }
  else{
    incrementValue(current, steps);
  }
}

void incrementValue(unsigned int &val, int steps){
  val += steps * Power10[cursorLocation];
  //in this function you might also want to check for over and undershoot of the value
}

And indeed, Bounce2 can't detect a long press by default. You can use it to detect it but that's up to you. You need two things:

  • Save the time when pressed so you can compare for a long pressed interval
  • DON'T act directly on the falling (.fell() ) of the button but on the rising (.rose() ). The moment the buttons becomes pressed you don't know if it's a short or long press :wink: