Single button to manage 2 analog values inputs

Hi all,

I'm looking for some advice how to interface with a linear actuator,

Thanks for any help/guidance.

Required inputs are:

Travel distance in mm
Travel speed in mm/min
Move Up
Move Down

The code to control the stepper motor works, reading the analog input, and reading the state of the buttons work in individual scripts. I'm stuck on how to put it all together and read the buttons when they are pressed while the program takes care of other things.

I am using a Arduino UNO to control a stepper driver to motorize the bed of my drill press.
I have (1) 10K pot and (3) buttons to use as user inputs.
LCD 16x2 is used to display travel distance (mm) on row1 and travel speed (mm/min) on row 2.

I need 2 buttons for the UP/DOWN controls, and that only leaves me with the POT and one button.

I think this should be done using a interrupt routine, but I have never used one.
My thought was to use the single button to cycle through the three possible states
Update Travel value
Update Speed value
Enter/Exit input mode

the logic would look like this:
Initial button click
Highlight LCD Row-1
Update Row-1 with Pot value
Second button click
Keep current vale on row-1
Save current value in variable1
Remove highlight from Row-1
Highlight Row-2
Update Row-2 with Pot value
Third button click
Keep current value on Row2
Save current value in variable2
Remove highlight from Row-2
Return control to loop()

This is the code I have so far, but I don't know where to put the interrupt routine and can the interrupt routine update the global variable "SelectButtonIndex" so it can be used in the main body code?

/*
Based on  https://dronebotworkshop.com/big-stepper-motors/

Status:
7/24/2024 > Test LCD display, completed
7/24/2014 > Read Potentiometer on pin A0, completed.
7/25/2024 > Display pot value on LCD, completed.
7/26/2024 > Display value of Select button in serial monitor, completed

TODO: Use select button to switch between 3 states (update row-1, update row-2, exit)

Pin Notes:
  Potentiometer wiper pin 2 to analog A0
  Potentiometer pin 3 to 5V
  Potentiometer pin 1 to Gnd
  
  Button "Select" to digital pin 4
  Button "Up"     to digital pin 8
  Button "Down"   to digital pin 9
  
  Stepper driver Pul- on digital pin 7
  Stepper driver Dir- on digital pin 6

LCD Display output
  Row1:Colmun1 > active field symbol ">", Stored value symbol "#"
  Row1:Colmun3 > Tavel distance value + "mm"
  Row2:Colmun1 > active field symbol ">", Stored value symbol "#"
  Row2:Colmun3 > Travel speed value + "mm/min"
*/


#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <OneButton.h>

// Constant's
const int SelectPin = 4;

// Global variables
int PotValue = 0;
int SelectButtonState = 0;
int SelectButtonIndex = 0;

// Identify I2C devices
LiquidCrystal_I2C lcd(0x27,16,2);


?
? Interrupt 
? cycle the SelectButtonIndex variable [0,1,2] when SelectButtonState is triggered
?

void setup() {
  // Configure LCD
  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.home();

  // Initialize serial port
  Serial.begin(9600);

  // configure push button's
  pinMode(SelectPin, INPUT);
}

void loop() {
    // Get potentiomer raw value (0-1024)
    PotValue = analogRead(A0);

    // display values  for testing
    Serial.println(PotValue);
    
    if (SelectButtonIndex = 1) {
      // ? highlight row-1 ?
      TravelDistance = PotValue;
      lcd.setCursor(3,0);
      lcd.print(TravelDistance);
      Serial.print("Distance: ");
      Serial.println(TravelDistance);
      // ? remove highlight row-1 ?
    }
    
    if (SelectButtonIndex = 2) {
      // ? highlight row-2 ?
      TravelSpeed = PotValue;
      lcd.setCursor(3,0);
      lcd.print(TravelSpeed);
      Serial.print("Speed: ");
      Serial.println(TravelDistance);
      // ? remove highlight row-2 ?
    }
     
    delay(500);
}
  • Let's see your schematic ?

  • You are updating the LCD at loop speed, just do it every 250ms or so.

if (SelectButtonIndex = 1)
  • Did you mean:
    :wink:
if (SelectButtonIndex == 1)

delay(500);
  • Do not use delay( ) in your sketches unless you know why you shouldn't.
1 Like

Something you should keep in mind…

When you adjust the pot in one context(A), then switch to another (B), the current (A) position of the pot will be used in the second context until you move it. Probably not what you want,

This can be overcome, but you’ll need to choose when, why and how… or maybe use an encoder or buttons instead of a pot.

There is no good reason to use an interrupt routine. Your program should just poll the buttons, and take appropriate action when a button becomes pressed (or released).

See the Arduino state change detection example for how to do this, which also shows you why delay() statements should be used only when absolutely required.

Files>Examples>02.Digital>StateChangeDetection

1 Like

Here is an example of a stepper motor running with button polling using event timing.

1 Like

output.pdf (50.0 KB)

Thanks for catching the "=", bad habit of mine.

So we don’t need to download the PDF.

Yes, I expect to adjust the value o the pot and then use the select button to save the value.

I thought of using buttons to increase / decrease the values, but distance ranges from 0 to 152.4, that's allot of button presses. With the pot I can map the min max values and get pretty accurate input

I agree this is not ideal, in the future I would like to replace it with a numeric keypad (till then this is a workaround).

Thanks

Thank you, new to forums.

Thanks for the link, I'm still new at this so the example is greatly appreciated.

Sal

  • A push button switch can be monitored for a short push or long push.
    Example, short might be < 500ms, long might be > 1000ms.

  • A short push might mean advance/increment something, a long push might mean enter new potentiometer value . . .

  • Switches can be polled every ~50ms to see if there is a change in state, take appropriate action as needed when we see a change.
    No interrupts needed.

  • Write the sketch so execution has no blocking sections of code.

I noticed on your schematic, you don't have pulldown resistors on the limit switch pins.

Oversight on my part, thanks for pointing it out.

Update,

I looked at all the code resources that were mentioned in the replies, and honestly I got more than a bit confused. So I went back to a simple script with no button libraries and it almost works.

The script below, works with one major flaw. in the switch statement case 1 and 3 need to update the display as the pot is turned, because there is no loop it just grabs the initial reading.

Can the code in case 1 & 3 be put into a loop to update the display? or am I completely off base?

PS: I updated the wiring schematic and I included it, just in case it can help some one.

Thanks for your help.

#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// Constant's
#define SELECT_PIN 4
#define POT_Pin A0
#define debounceTimeOut 100

// Global variables
int potValue = 0;
int selectButtonRead = 0;
int selectButtonState = LOW;
int selectButtonPreviousState = LOW;
long int lastDebounceTime;
int menuIndex = 0;
int travelDistance = 0;
int travelSpeed = 0;

// Identify I2C devices
LiquidCrystal_I2C lcd(0x27,16,2);

void setup() {
  // Configure LCD
  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.home();

  Serial.begin(9600);

  // configure push button's
  pinMode(SELECT_PIN, INPUT);
}

void loop() {
  
  selectButtonRead = digitalRead(SELECT_PIN);
    
  // button state changed, either by press or noise
  if (selectButtonState != selectButtonPreviousState){
    lastDebounceTime = millis();
  }

  // If button press was pressed longer than debounce time
  if((millis() - lastDebounceTime) > debounceTimeOut){
      
    // Check if the button state has changed
    if(selectButtonRead != selectButtonState){
      selectButtonState = selectButtonRead;

      // Process if button state went from LOW to HIGH
      if(selectButtonState == HIGH){
        menuIndex += 1;
        if(menuIndex > 4){
          menuIndex = 0;
        }
        Serial.print("button has been pressed, menuIndex = ");
        Serial.println(menuIndex);

        switch (menuIndex){
          case 1:
            // get distance input
            potValue = analogRead(A0);
            lcd.setCursor(0, 0);
            lcd.print("Dist> ");
            lcd.print(potValue);
            lcd.print("mm");
            break;
          case 2:
            // process distance value
            travelDistance = potValue;
            lcd.setCursor(0, 0);
            lcd.print("Dist# ");
            lcd.print(potValue);
            lcd.print("mm");
            break;
          case 3:
            // get speed value
            potValue = analogRead(A0);
            lcd.setCursor(0, 1);
            lcd.print("Speed> ");
            lcd.print(potValue);
            lcd.print("mm/min");
            break;
          case 4:
            // process speed value
            travelSpeed = potValue;
            lcd.setCursor(0, 1);
            lcd.print("Speed# ");
            lcd.print(potValue);
            lcd.print("mm/min");
            break;
        }
      }
    }
  }

  selectButtonPreviousState = selectButtonRead;
}

  • I would have done this using a State Machine, however, in your example, you can just read the pot in cases 1 and 3.

  • In your sketches, always recommend you let loop( ) continue running in a non-blocking looping fashion.

  • Different sections of code in loop( ) can be run using a Flag and possibly adding a non-blocking TIMER.

1 Like

LarryD, thanks for the advise. Trying to use the Pot to do this is just too awkward, I broke down and ordered some keypads. That should make things much easier.