State Change with momentary contact button

Total newbie to Arduino and programming, not electronics. I have the starter kit and I'm learning fast.

End goal is to build a Watt meter converting volts to Watts at a specific load.

I was able to put together a simple program to convert a small analog test voltage to Watts and have it print out on the serial monitor with the choice of loads. However, this being Arduino, I would like to have the momentary button switch between the two loads, possibly three loads. I assembled some code and I have issues. The serial data no longer runs continuous and the state does not stay locked. I'm hoping to post the code and gain some knowledge of some group members.

I think I am having issues understanding where things are placed for the if / else. Any details and help would be appreciated.

Thanks,

Dave

const int sensorPin = A0; // analog input
const int buttonPin = 2; // the pin that the pushbutton is attached to (digital 2)
int buttonState = 0; // current state of the button
int lastButtonState = 0; // previous state of the button

void setup() {
// put your setup code here, to run once:
Serial.begin(9600); //opens the serial port
pinMode(buttonPin, INPUT); // initialize the button pin as a input

}

void loop() {
int sensorVal = analogRead(sensorPin); // defines sensorVal for formula (analog pin A0 called sensorPin)
buttonState = digitalRead(buttonPin); // read digital pin 2
if (buttonState != lastButtonState) {

if (buttonState == 1) {

int sensorVal = analogRead(sensorPin);
Serial.print("Watts @ 8 Ohms:");

//convert the ADC reding to voltage
float watts = ((sq((sensorVal/1024.0) * 20.0)) / 8); //ADC conversion, sq of volts and divide by 8(ohms). 20.0 is just a value to make #s large enough
//'voltage' is just the name for the value out
Serial.print(" Watts: "); //This identifies the field name of watts
Serial.println(watts, 4); //display of watts answer / calculation. The 4 after watts is for number of decimal points.

}

else

int sensorVal = analogRead(sensorPin);
Serial.print("Watts @ 4 Ohms:");

//convert the ADC reding to voltage
float watts = ((sq((sensorVal/1024.0) * 20.0)) / 4); //ADC conversion, sq of volts and divide by 8(ohms). 20.0 is just a value to make #s large enough
//'voltage' is just the name for the value out
Serial.print(" Watts: "); //This identifies the field name of watts
Serial.println(watts, 4); //display of watts answer / calculation. The 4 after watts is for number of decimal points.
}
lastButtonState = buttonState;

delay(250);

}

else
      int sensorVal = analogRead(sensorPin);
    Serial.print("Watts @ 4 Ohms:");

Your code seems to be missing curly brackets ({ }) to enclose the else block so the only instruction that executes, conditionally, in the else is the analog read. And the lastButtonState = buttonState is outside the if block so it executes every time through loop(), not just when it was changed. How is your switch wired? You aren't using INPUT_PULLUP so do you have a pullup or pulldown in the switch input?

Maybe like this:

  void loop() 
{
  int sensorVal = analogRead(sensorPin);  // defines sensorVal for formula (analog pin A0 called sensorPin)
  buttonState = digitalRead(buttonPin);   // read digital pin 2
  if (buttonState != lastButtonState)
  {
    if (buttonState == 1)
    {
      int sensorVal = analogRead(sensorPin);
      Serial.print("Watts @ 8 Ohms:");

      //convert the ADC reding to voltage
      float watts = ((sq((sensorVal / 1024.0) * 20.0)) / 8); //ADC conversion, sq of volts and divide by 8(ohms).  20.0 is just a value to make #s large enough
      //'voltage' is just the name for the value out
      Serial.print(" Watts: ");                           //This identifies the field name of watts
      Serial.println(watts, 4);                          //display of watts answer / calculation.  The 4 after watts is for number of decimal points.

    }
    else
    {
      int sensorVal = analogRead(sensorPin);
      Serial.print("Watts @ 4 Ohms:");

      //convert the ADC reding to voltage
      float watts = ((sq((sensorVal / 1024.0) * 20.0)) / 4); //ADC conversion, sq of volts and divide by 8(ohms).  20.0 is just a value to make #s large enough
      //'voltage' is just the name for the value out
      Serial.print(" Watts: ");                           //This identifies the field name of watts
      Serial.println(watts, 4);                          //display of watts answer / calculation.  The 4 after watts is for number of decimal points.
    }
    lastButtonState = buttonState;
  }
  delay(250);
}

Please read the "how to use the forum" stickies to see how to properly format and post code.

Thank you groundfungus.

That helped with some issues. I'm using pull down.

I have a lot to learn and go through some more learning. I will post again once I get to the next challenge.

Thanks for helping the newbie. I re-read about how to post code.

I made some more progress and have gotten comfortable with some additional programming. I’m still struggling with the push button state change code.

The problem is when I push the push button to change from one state to another, it does not stay in the new state. I push the button, I get a momentary change, but then back to the original state. I’m trying to create a Watt meter to measure voltage across an 8 ohm load and 4 ohm load.

Here is my latest code:

const int sensorPin = A0;                     // analog input pin A0
const int buttonPin = 6;                      // button input D6
int buttonState = 0;                         // current state of the button
int lastButtonState  = 0;                     // previous state of the button
#include <LiquidCrystal.h>                    // include the library code

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);       // initialize the library with the numbers of the interface pins


void setup() {
  
  pinMode(buttonPin, INPUT);                 // initialize the button pin as a input
  pinMode(6, INPUT);                        // D6 input
  
  lcd.begin(16, 2);                         // set up the number of columns and rows on the LCD

  lcd.setCursor(3, 0);
 
  lcd.print("Watt Meter");                 // Print a message to the LCD.
 
  delay(5000);                             // 5 second delay
  
  lcd.clear();                             // clear the LCD display
}

void loop() {
  int sensorVal = analogRead(sensorPin);
  buttonState = digitalRead(6);            // read pin 6
  
  
  if (buttonState != lastButtonState)
  {
      if (buttonState == 0)
  int sensorVal = analogRead(sensorPin);
 
  
  
  float watts = ((sq((sensorVal/1024.0) * 20.0)) / 8);  //ADC conversion, sq of volts and divide by 8(ohms).  20.0 is just a value to make test #s large enough
                                                                                         
            
      lcd.setCursor(0, 0);                // set the cursor to column 0, line 0
     
      lcd.print("Watts @ 8 Ohms");        // print text 'Watts'
           
      lcd.setCursor(0, 1);                // move the cursor to the second line
     
      lcd.print(watts);                   // print 'value of watts'
 
      delay(500);                         // .5 second delay
      
      lcd.clear();                        // clear the LCD before the loop starts over

}
   else {

      
  int sensorVal = analogRead(sensorPin);
  
  
  float watts = ((sq((sensorVal/1024.0) * 20.0)) / 4);  //ADC conversion, sq of volts and divide by 8(ohms).  20.0 is just a value to make test #s large enough
                                                                                        
            
      lcd.setCursor(0, 0);                // set the cursor to column 0, line 0
     
      lcd.print("Watts @ 4 Ohms");        // print text 'Watts'
           
      lcd.setCursor(0, 1);                // move the cursor to the second line
     
      lcd.print(watts);                   // print 'value of watts'
 
      delay(500);                         // .5 second delay
      
      lcd.clear();                        // clear the LCD before the loop starts over
  
}
lastButtonState = buttonState;
}

I bookmarked an article about state changes with Arduino code, but it seems to work with LEDs, but not my math formula. In the article, it even goes on to talk about multiple presses of the same button to enact different functions. I’d ultimately like to switch between 8, 4, and 2 ohm calculations. BUT, one step at a time!

Thank you.

As previously asked, how is your switch wired ?

By the way, why give a pin a meaningful name as in

const int buttonPin = 6;

Then not use the name in the program

  buttonState = digitalRead(6);

The switch is a pull down. 10K ohm to ground on the switch keeping the input low.

As for the pin name and not using it properly--lack of knowledge and still learing :o

Thank you

The switch is a pull down. 10K ohm to ground on the switch keeping the input low.

Is the pin connected to 5V when it the button is pressed ?

  buttonState = digitalRead(6);            // read pin 6
  
  
  if (buttonState != lastButtonState)
  {
      if (buttonState == 0)

This looks like you are testing whether the button has just been released

[/quote]

UKHeliBob:
Is the pin connected to 5V when it the button is pressed ?

  buttonState = digitalRead(6);            // read pin 6

if (buttonState != lastButtonState)
  {
      if (buttonState == 0)



This looks like you are testing whether the button has just been released

Yes, the button is connected to 5V when the button is pressed. If you take the button state code out of the loop, the code works properly. When the switch pin is 0, the code calculates at 8 ohms and when the button is pressed (and held) the code calculates at 4 ohms.

Again, the goal is to get it to toggle between the two options with just a button press. Maybe even get to a point where a third option is available by the button press.

I understand the if / else functions. I'm not getting the button press and how to count the button press amounts to make the code do the proper function.

Thanks again.

. I’m not getting the button press and how to count the button press amounts to make the code do the proper function.

The state change detection example in the ide (file>examples.02digital>StateChangeDetection) shows how to increment a counter with a button press.

You can use the counter value in a switch case statement. https://www.arduino.cc/en/Reference/SwitchCase

So I experimented with this code and it works as it should. LED illuminates with button press; next time button is pressed LED no longer illuminates.

I tried changing the code to have it do something other than LED illumination without success. I tried to write to the LCD two different statements based on button operation. Not great results.

const int buttonPin  = 6;     // the pin that the pushbutton is attached to
const int ledPin     = 13;    // the pin that the LED is attached to
 
int buttonState      = 0;     // current state of the button
int lastButtonState  = 0;     // previous state of the button
int ledState         = 0;     // remember current led state
 
void setup() {
  pinMode(buttonPin, INPUT);  // initialize the button pin as a input
  pinMode(ledPin, OUTPUT);    // initialize the button pin as a output

}
 
void loop() {
  // read the pushbutton input pin
  buttonState = digitalRead(buttonPin);
 
  // check if the button is pressed or released
  // by comparing the buttonState to its previous state 
  if (buttonState != lastButtonState) {
    
    // change the state of the led when someone pressed the button
    if (buttonState == 1) { 
      if(ledState==1) ledState=0;
      else            ledState=1;         
    }
    
    // remember the current state of the button
    lastButtonState = buttonState;
  }
  
  // turns LED on if the ledState=1 or off if the ledState=0
  digitalWrite(ledPin, ledState);
  
  // adding a small delay prevents reading the buttonState to fast
  // ( debouncing )
  delay(20);
}

Please post the LCD code and explain what "not great results" means if you want help with that.

const int buttonPin  = 6;     // the pin that the pushbutton is attached to
const int ledPin     = 13;    // the pin that the LED is attached to
#include <LiquidCrystal.h>                    // include the library code

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);       // initialize the library with the numbers of the interface pins 
int buttonState      = 0;     // current state of the button
int lastButtonState  = 0;     // previous state of the button
int ledState         = 0;     // remember current led state
 
void setup() {
  pinMode(buttonPin, INPUT);  // initialize the button pin as a input
  pinMode(ledPin, OUTPUT);    // initialize the button pin as a output
  lcd.begin(16, 2);                         // set up the number of columns and rows on the LCD
  


}
 
void loop() {
  
  buttonState = digitalRead(buttonPin);     // read the pushbutton input pin
 
                                            // check if the button is pressed or released by comparing the buttonState to its previous state

  if (buttonState != lastButtonState) {
    
                                            // change the state of the led when someone pressed the button
    if (buttonState == 1) { 
      if(ledState==1) ledState=0;           // turn LED on
        lcd.setCursor(0, 0);                // set cursor position
        lcd.print("TEST");                  // diplay 'TEST'
      }
       
  }
      else     {       ledState=1;         
    
    
                                            // remember the current state of the button
    lastButtonState = buttonState;
  }
  
                                            // turns LED on if the ledState=1 or off if the ledState=0
  digitalWrite(ledPin, ledState);
  
                                            // adding a small delay prevents reading the buttonState too fast
  
  delay(20);
}

So I adjusted the code a bit to add the word TEST for the button press is high. I loose the LED latching on (and staying on until the next button press) and off. The word TEST does show up, but never leaves.

I’m having a hard time understanding what makes the state stay. I guess it stays because the ‘else’ has not occurred.

Also, why did I loose the latching of the LED staying on until the next button press. In addition, I’m sure I’m missing LCD clear somewhere, but not sure where to put that.

Thank you.

Which if does the else relate to ?

      if (ledState == 1) ledState = 0;      // turn LED on
      lcd.setCursor(0, 0);                // set cursor position
      lcd.print("TEST");                  // diplay 'TEST'

Which line(s) of code should be executed if the test returns true and which should always be executed? My advice would be to put curly braces round it/them so that is obvious what you intend to happen.

I think this will do, but no guarantees. 3 different states.

const int buttonPin  = 6;     // the pin that the pushbutton is attached to
const int ledPin     = 13;    // the pin that the LED is attached to
#include <LiquidCrystal.h>                    // include the library code

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);       // initialize the library with the numbers of the interface pins
int buttonState      = 0;     // current state of the button
int lastButtonState  = 0;     // previous state of the button
int ledState         = 0;     // remember current led state
int loadState = 1;

void setup() {
  pinMode(buttonPin, INPUT);  // initialize the button pin as a input
  pinMode(ledPin, OUTPUT);    // initialize the button pin as a output
  lcd.begin(16, 2);                         // set up the number of columns and rows on the LCD
}

void loop() {
  buttonState = digitalRead(buttonPin);     // read the pushbutton input pin
  if (buttonState == 1 && lastButtonState == 0) {
    loadState++;
    if (loadState > 3) loadState = 1;  // 3 states as it is now
    ledState = 1 - ledState;           //ledState will alternate between 1 and 0
    digitalWrite(ledPin, ledState);
  }
  delay(20);
  lastButtonState = buttonState;

// Common stuff goes here like analogRead

  if (loadState == 1) {
    lcd.setCursor(0, 0);
    lcd.print("State=1");
    // Stuff depending state goes here

  }
  if (loadState == 2) {
    // Do your stuff here
    lcd.setCursor(0, 0);
    lcd.print("State=2");
    // and here

  }
  if (loadState == 3) {
    lcd.setCursor(0, 0);
    lcd.print("State=3");
    // and here

  }
}

Thank you UKHeliBob and Gabriel_swe. I brought my Arduino book home for the weekend, but left the board at work. I needed a break. I’ll pickup on Monday and post results next week.

Thanks!

Gabriel_swe, thank you for helping with that code. It worked well. I could follow along and add the rest of my code where you put pointers. I'm going to finish some more pieces of the code. I'm sure I will have questions about delays and placement of other small functions.

I appreciate your help.

OK, I made a lot of progress with what I would like to do. I have 3 states for my measurement. Press the single momentary button and it toggles between the 3 states. :smiley:

I added a push and hold (same single momentary button) for 2 seconds and I’m trying to get a 4th state. I would like the forth state to remain active until you push and hold for 2 seconds to go back to normal operation. I can get this push and hold function to illuminate the LED on until the push and hold is activated again. But not with the LCD displaying values. It is just momentary. I see how I was able to get the LED to toggle with the LED1State = !LED1State line. I’m assuming I can’t do this with the display :o

Issues I would like to review are 1. getting LCD to display values after push and hold state is activated. 2. After adding the push and hold function, there is an extra key press required occasionally to toggle through the normal 3 states.

I have LEDs next to some of the math functions because it helps me identify the state change with the buttons. I don’t plan to have any LED activity in the final code.

If there is a better approach, please let me know.

Thanks to all who are helping.

const int sensorPin = A0;                     // analog input pin A0
const int buttonPin  = 6;                     // the pin that the pushbutton is attached to
/*const int ledPin     = 13;                    // the pin that the LED is attached to*/  //removed
int LED1 = 8;                    //added
int LED2 = 13;                   //added
boolean LED1State = false;       //added
boolean LED2State = false;       //added
long buttonTimer = 0;            //added
long longPressTime = 2000;       //added
boolean buttonActive = false;    //added
boolean longPressActive = false; //added
#include <LiquidCrystal.h>                    // include the library code

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);        // initialize the library with the numbers of the interface pins

int buttonState      = 0;                     // current state of the button
int lastButtonState  = 0;                     // previous state of the button
int ledState         = 0;                     // remember current led state
int loadState = 1;

void setup() {
  pinMode(buttonPin, INPUT);                 // initialize the button pin as a input
  /*pinMode(ledPin, OUTPUT);                   // initialize the button pin as a output*/ //removed
  pinMode(LED1, OUTPUT);    //added
  pinMode(LED2, OUTPUT);    //added
 
}

void loop() {
  buttonState = digitalRead(buttonPin);     // read the pushbutton input pin
  if (buttonState == 1 && lastButtonState == 0) {
    loadState++;
    if (loadState > 3) loadState = 1;       // 3 states as it is now
    ledState = 1 - ledState;                //ledState will alternate between 1 and 0
    digitalWrite(LED2, ledState);
  }
  delay(20);
  lastButtonState = buttonState;

// Common stuff goes here like analogRead

int sensorVal = analogRead(sensorPin);

  if (loadState == 1) {
    float watts = ((sq((sensorVal/1024.0) * 20.0)) / 8);  //ADC conversion, sq of volts and divide by 8(ohms).  20.0 is just a value to make test #s large enough
    lcd.setCursor(0, 0);
    lcd.print("Watts @ 8 Ohms");            // print text 'Watts'
    lcd.setCursor(0, 1);                    // move the cursor to the second line
    lcd.print(watts);
    
    
    // Stuff depending state goes here

  }
  if (loadState == 2) {
    float watts = ((sq((sensorVal/1024.0) * 20.0)) / 4);  //ADC conversion, sq of volts and divide by 8(ohms).  20.0 is just a value to make test #s large enough
    lcd.setCursor(0, 0);
    lcd.print("Watts @ 4 Ohms");           // print text 'Watts'
    lcd.setCursor(0, 1);                   // move the cursor to the second line
    lcd.print(watts);
  
    // Do your stuff here
    
    // and here

  }
  if (loadState == 3) {
    float watts = ((sq((sensorVal/1024.0) * 20.0)) / 2);  //ADC conversion, sq of volts and divide by 8(ohms).  20.0 is just a value to make test #s large enough
    lcd.setCursor(0, 0);
    lcd.print("Watts @ 2 Ohms");          // print text 'Watts'
    lcd.setCursor(0, 1);                  // move the cursor to the second line
    lcd.print(watts);
    // and here

  }
  if (digitalRead(buttonPin) == HIGH) {

    if (buttonActive == false) {

      buttonActive = true;
      buttonTimer = millis();

    }

    if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) {

      longPressActive = true;
      LED1State = !LED1State;
      
      digitalWrite(LED1, LED1State);
      float watts = ((sq((sensorVal/1024.0) * 20.0)) / 16);  //ADC conversion, sq of volts and divide by 8(ohms).  20.0 is just a value to make test #s large enough
    lcd.setCursor(0, 0);
    lcd.print("Watts @ 16 Ohms");           // print text 'Watts'
    lcd.setCursor(0, 1);                   // move the cursor to the second line
    lcd.print(watts);

    }

  } else {

    if (buttonActive == true) {

      if (longPressActive == true) {

        longPressActive = false;

      } 

      buttonActive = false;

    }

  }

  
delay(250);
  
}

From the long code posted above, I clipped out this code:

    if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) {

      
      longPressActive = true;
      LED1State = !LED1State;
      
      digitalWrite(LED1, LED1State);
      float watts = ((sq((sensorVal/1024.0) * 20.0)) / 16);  //ADC conversion, sq of volts and divide by 16(ohms).  20.0 is just a value to make test #s large enough
    lcd.setCursor(0, 0);
    lcd.print("Watts @ 16 Ohms");           // print text 'Watts'
    lcd.setCursor(0, 1);                   // move the cursor to the second line
    lcd.print(watts);
    

    }

Is there a way to make this repeat until another button press occurs? This is where I struggle with understanding the grouped operators and structure.

I have tried my best to put all button detecting in one place. This code isn’t tested. It is supposed to change mode when releasing the button. Holding the button activate/deactivate longpress.
A fourth loadstate would have been much easier :wink:

It is faster to write 0 instead of false or low, and 1 instead of true or high.

const int sensorPin = A0;                     // analog input pin A0
const int buttonPin  = 6;                     // the pin that the pushbutton is attached to
/*const int ledPin     = 13;                    // the pin that the LED is attached to*/  //removed
int LED1 = 8;                    //added
int LED2 = 13;                   //added
boolean LED1State = false;       //added
boolean LED2State = false;       //added
unsigned long buttonTime = 0;            //added
unsigned long lastButtonTime = 0;
unsigned long longPressTime = 2000;       //added
boolean buttonActive = false;    //added
boolean longPressActive = false; //added
#include <LiquidCrystal.h>                    // include the library code

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);        // initialize the library with the numbers of the interface pins

int buttonState      = 0;                     // current state of the button
int lastButtonState  = 0;                     // previous state of the button
int ledState         = 0;                     // remember current led state
int loadState = 1;

void setup() {
  pinMode(buttonPin, INPUT);                 // initialize the button pin as a input
  /*pinMode(ledPin, OUTPUT);                   // initialize the button pin as a output*/ //removed
  pinMode(LED1, OUTPUT);    //added
  pinMode(LED2, OUTPUT);    //added

}

void loop() {
  buttonState = digitalRead(buttonPin);     // read the pushbutton input pin
  if (buttonState != lastButtonState) {
    buttonTime = millis();
    if (buttonState == 1) {
      buttonActive = 1;
    } else if (buttonTime - lastButtonTime < longPressTime) {
      buttonActive = 0;
      loadState++;
    }
    lastButtonTime = buttonTime;
  }
  lastButtonState = buttonState;
  delay(20);

  if (buttonActive == 1 && millis() - lastButtonTime > longPressTime) {
    longPressActive = !longPressActive;
    LED1State = !LED1State;
    buttonActive = 0;
  }

  if (loadState > 3) loadState = 1;       // 3 states as it is now
  // Common stuff goes here like analogRead

  int sensorVal = analogRead(sensorPin);


  if (loadState == 1 && longPressActive == 0) {
    float watts = ((sq((sensorVal / 1024.0) * 20.0)) / 8); //ADC conversion, sq of volts and divide by 8(ohms).  20.0 is just a value to make test #s large enough
    lcd.setCursor(0, 0);
    lcd.print("Watts @ 8 Ohms");            // print text 'Watts'
    lcd.setCursor(0, 1);                    // move the cursor to the second line
    lcd.print(watts);


    // Stuff depending state goes here

  }
  if (loadState == 2 && longPressActive == 0) {
    float watts = ((sq((sensorVal / 1024.0) * 20.0)) / 4); //ADC conversion, sq of volts and divide by 8(ohms).  20.0 is just a value to make test #s large enough
    lcd.setCursor(0, 0);
    lcd.print("Watts @ 4 Ohms");           // print text 'Watts'
    lcd.setCursor(0, 1);                   // move the cursor to the second line
    lcd.print(watts);

    // Do your stuff here

    // and here

  }
  if (loadState == 3 && longPressActive == 0) {
    float watts = ((sq((sensorVal / 1024.0) * 20.0)) / 2); //ADC conversion, sq of volts and divide by 8(ohms).  20.0 is just a value to make test #s large enough
    lcd.setCursor(0, 0);
    lcd.print("Watts @ 2 Ohms");          // print text 'Watts'
    lcd.setCursor(0, 1);                  // move the cursor to the second line
    lcd.print(watts);
    // and here

  }
  digitalWrite(LED1, LED1State);

  if (longPressActive == true) {

    float watts = ((sq((sensorVal / 1024.0) * 20.0)) / 16); //ADC conversion, sq of volts and divide by 8(ohms).  20.0 is just a value to make test #s large enough
    lcd.setCursor(0, 0);
    lcd.print("Watts @ 16 Ohms");           // print text 'Watts'
    lcd.setCursor(0, 1);                   // move the cursor to the second line
    lcd.print(watts);

  }
  delay(250);
}

Thank you again. I'm traveling today. I'll test it tomorrow.