Pushbutton not working on selecting

hello everyone.

I am trying to do a system which has a main menu with 3 options and inside of each option there will be things that will have to be selected using a potentiometer and a push button. While selecting in the menu, everything is working well. However when I try to select inside of estatico() it sometimes doesn't select or sometimes it selects 2 things at the same time ("currente inicial" and "RPM minimas").

Below you can find the code. appreciate your help on this.

#include "HX711.h"
#define seconds() (millis()/1000)

const int potPin = A0; // Potenciômetro conectado ao A0

// Load Cell with HX711
const int LOADCELL_DOUT_PIN = 3;
const int LOADCELL_SCK_PIN = 4;
HX711 scale;

// Hall Sensor
const int hallSensorPin = 2;
int sensorState = 0;
volatile unsigned int rpmCount = 0;
unsigned long previousMillis = 0;
const unsigned long interval = 1000; // Update RPM every second

// Pushbutton
const int buttonPin = 7; // Pino digital 7
int lastButtonState = LOW;
int buttonState = LOW;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 30;

int menuIndex = 0;
const int numMenuItems = 3;
int currente_inicial = 0;
int rpm_min = 0;
int rpm_adjusted = 0;
float PrevTime = 0;
float Duration = 0;
int rpm = 0;

// Declaração antecipada de funções
void estatico();
void manual();
void countRpm();
void currente();
void rpm_selecter();
void RPM();

void setup() {
  Serial.begin(9600);
  pinMode(potPin, INPUT);

  // Initialize the HX711
  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  scale.set_scale(431.311f); // Calibration factor - adjust as needed
  scale.tare(); // Reset the scale to 0

  // Initialize the Hall sensor pin
  pinMode(hallSensorPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(hallSensorPin), countRpm, RISING);

  // Initialize the button pin
  pinMode(buttonPin, INPUT_PULLUP);

  updateMenu();
}

void loop() {
  int potValue = analogRead(potPin);
  int newMenuIndex = map(potValue, 1023, 0, 0, numMenuItems); // Mapeamento ajustado

  if (newMenuIndex >= numMenuItems) {
    newMenuIndex = numMenuItems - 1;
  }

  if (newMenuIndex != menuIndex) {
    menuIndex = newMenuIndex;
    updateMenu();
  }

  // Read the state of the pushbutton
  int reading = digitalRead(buttonPin);

  // Check for button press with debouncing
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;
      if (buttonState == LOW) { // Button pressed
        selectMenuItem();
        
        if (menuIndex == 0) {
          estatico();
        } else if (menuIndex == 1) {
          Serial.println("Ainda não feito.");
        } else {
          manual();
        }
      }
    }
  }

  lastButtonState = reading;
}

void updateMenu() {
  Serial.print("Menu: ");
  switch (menuIndex) {
    case 0:
      Serial.println("1- Estático");
      break;
    case 1:
      Serial.println("2- Transiente");
      break;
    case 2:
      Serial.println("3- Manual");
      break;
  }
}

void selectMenuItem() {
  // Placeholder para a ação quando um item de menu é selecionado
  Serial.println("Selecionado!");
  delay(1000);
  updateMenu();
}

void estatico() {
  while (true) {
    int cond = 1;
    
    while(cond == 1){
      
      int new_currente_inicial = map(analogRead(potPin), 1023, 0, 0, 30);
      
      if(new_currente_inicial != currente_inicial){
        currente_inicial = new_currente_inicial;
        currente();
      }

      int reading = digitalRead(buttonPin);

      if (reading != lastButtonState) {
        lastDebounceTime = millis();
      }

      if ((millis() - lastDebounceTime) > debounceDelay) {
        if (reading != buttonState) {
          buttonState = reading;
          if (buttonState == LOW) { // Button pressed
            cond = 0;
          }
        }
      }

      lastButtonState = reading;
      delay(1000);
    }
    delay(1000);

    cond = 1;
    int new_rpm_min = 0;

    while(cond == 1){
      
      new_rpm_min = map(analogRead(potPin), 1023, 0, 0, 2000);
            
      if(rpm_min - new_rpm_min > 10 || new_rpm_min - rpm_min > 10) {
        rpm_min = new_rpm_min;
        rpm_selecter();
      }

      int reading = digitalRead(buttonPin);
      if (reading != lastButtonState) {
        lastDebounceTime = millis();
      }

      if ((millis() - lastDebounceTime) > debounceDelay) {
        if (reading != buttonState) {
          buttonState = reading;
          if (buttonState == LOW) { // Button pressed
            cond = 0;
          }
        }
      }

      lastButtonState = reading;
      delay(1000);
    }

    Serial.println("O teste vai ser começar!");
    Serial.println("Garante que o acelerador está na posição a testar");
    
    //Falta aqui a parte do potenciometro digital
    cond = 1;

    while(cond == 1){
      int tempo = seconds();
      while(seconds() - tempo < 60){
        RPM();

        // Load Cell Code
        // Read the weight
        float weight = scale.get_units(10); // Average of 10 readings

        // Print the RPM and weight on the Serial Monitor
        long tempo_atual =(millis() * 1e-3 -tempo);
        Serial.print(tempo_atual);
        Serial.print(",");
        Serial.print(rpm);
        Serial.print(",");
        Serial.println(weight);
        sensorState = digitalRead(hallSensorPin); // Lê o estado do sensor
          
     }

      //Falta parte do potenciometro digital
      delay(5000);
      
      RPM();
      
      if (rpm < rpm_adjusted){
        //Falta tirar corrente do potenciometro

        Serial.println("Teste parado, atingiu rpm minimas");
        cond=0;
      }
    }
    return;
  }    
}


void manual() {
  while(true){
    int cond = 0;
    Serial.println("Atenção, neste teste não há limite!");
    Serial.println("Cuidado para não danificar o motor!");

    unsigned long tempo = seconds();

    while(cond==0){
      unsigned long currentMillis = millis();
          
      if (currentMillis - previousMillis >= interval) {
        previousMillis = currentMillis;
    
        // Calculate RPM (rpmCount is the number of pulses in the last interval)
        unsigned int rpm = rpmCount * 60; // Convert to RPM
        rpmCount = 0; // Reset count

        // Load Cell Code
        // Read the weight
        float weight = scale.get_units(10); // Average of 10 readings

        // Print the RPM and weight on the Serial Monitor
        float tempo_atual =(seconds()-tempo);
        Serial.print(tempo_atual);
        Serial.print(",");
        Serial.print(rpm);
        Serial.print(",");
        Serial.println(weight);
        sensorState = digitalRead(hallSensorPin); // Lê o estado do sensor
      }

      int reading = digitalRead(buttonPin);
      if (reading != lastButtonState) {
        lastDebounceTime = millis();
      }

      if ((millis() - lastDebounceTime) > debounceDelay) {
        if (reading != buttonState) {
          buttonState = reading;
          if (buttonState == LOW) { // Button pressed
            cond = 1;
          }
        }
      }

      lastButtonState = reading;
    }
    return;
  }
}

void countRpm() {
  Duration = micros() - PrevTime;                       // Calculates time difference between revs in microsecond
  PrevTime = micros();
}

void currente() {
  Serial.print("Corrente inicial = ");
  Serial.println(currente_inicial);
}

void RPM()
{
  rpm = 60000000 / Duration;                                 // rpm = (1/ time millis)*1000*1000*60;
  if (micros() - PrevTime  > 2*1000000)                       // Check if motor stopped - unchanged after 2s
  {
    rpm = 0;
  }
}

void rpm_selecter() {
  Serial.print("RPM mínimas = ");
  rpm_adjusted = round(rpm_min/50)*50;
  Serial.println(rpm_adjusted);
}

Once this "while" condition is entered, you never leave... so "estatico()" will always be the problem area. Add some "print" statements into your code to tell you where, inside "estatico()" the errors are located.

Also, re-write your code to remove "while()"... you already have a continuous loop called "loop()"

yes i know i have loop() but i was trying to separate estatico(), transiente() and manual() into different functions. However theese functions had to be looping always.

I can't your code just now, but typically functions that need to be "looping always" are simply called frequently from your loop() function.

In much the way loop() itself be looping always, as it is just called repeatedly from the main.c function Arduino hides away.

These frequently called functions can decide for themselves what to do, based kn time and other conditions.

a7

For example, function01() is called often from loop(), while function02() is called seldom from loop().

unsigned long timer01, timer02, interval01 = 250, interval02 = 2000;

void setup() {
  Serial.begin(115200);
}

void loop() {
  if (millis() - timer01 > interval01) {
    timer01 = millis();
    function01();
  }
  if (millis() - timer02 > interval02) {
    timer02 = millis();
    function02();
  }
}

void function01() {
  Serial.print("1");
}

void function02() {
  Serial.println("\n2");
}
1 Like

And function three is called hella often, thousands of times a second, but prints only very infrequently:

void function03()
{
  const unsigned long interval03 = 17500;
  static unsigned long timer03;

  if (millis() - timer03 < interval03) return;  // not time for me to do it!

  Serial.println("\n\nTHREE!");

  timer03 = millis();
}

Also self contained, just add a call to function03 to you loop.

a7

1 Like

I like this forum for the chance to see other styles and angles of programming.

ok, understood, will restructure the whole code and try it again, thank you @xfpd and @alto777

I had not looked at the code, just tossed some gasoline on @xfpd's demo of juggling multiple,e things that all need looping.

Now I have glanced at esatatisc() function, and I will only say that another very useful tool for structuring code like this is the IPO model. Read about it in the abstract here, your eyes may become heavy but it's good stuff and makes sense when you get your brain around it:

The IPO Model.

Just because I see you reading the button in several places, and maybe doing the same kind of handling in several places.

In the IPO model, all inputs are read once per loop, and all timing is considered.

Next, based on where the various things going are, the inputs are used to figure out what to do next.

Lastly, given the new inputs and an examination of what shoukd happen next, any outputs are performed, be it lighting up LEDs or turning on or off pumps or moving servos. Whatever.

This is the loop, I, P and O over and over, with any of those steps taking relatively little time.

The flow will not "live" off in some function, rather the function will get frequent chances to advance its mission, if indeed it is even currently on some kinda mission.

I'll try to find a simple illustration of that. Most of what I do fits that pattern, but is fairly well tgangled up and none are the best sketches to learn from.

I'll see if your function alone can be turned inside out to become a good citizen in this kind of coding. Little steps, or no step, neither taking any significant time and certainly not waiting around for anyone to press a button.

a7

1 Like

look over this approach

correct: add debounce and limit range

const byte ButPin = 7;       // Capitalize Constants
const byte PotPin = A0;

byte butState;

// -----------------------------------------------------------------------------
void estatico   () { Serial.println (__func__); };
void transiente () { Serial.println (__func__); };
void manual     () { Serial.println (__func__); };

typedef void (*func_t)();
struct Menu_t {
    func_t      func;
    const char *label;
};

Menu_t menus [] = {
    { estatico ,   "Estático"   },
    { transiente , "Transiente" },
    { manual,      "Manual"     },
};

const int Nmenu   = sizeof(menus)/sizeof(Menu_t);
      int menuIdx = 0;

// -----------------------------------------------------------------------------
void loop () {
    int pot = analogRead (PotPin);
    int idx = map (pot, 1023, 0, 0, Nmenu-1);

    // update menu if changed
    if (menuIdx != idx) {
        menuIdx = idx;
#if 1
        Serial.print   (pot);
        Serial.print   ("  ");
        Serial.print   (idx);
        Serial.print   ("  ");
        Serial.print   (menuIdx);
        Serial.print   ("  ");
#endif
        Serial.println (menus [menuIdx].label);
    }

    // process button
    int but = digitalRead (ButPin);

    if (butState != but)  {
        butState  = but;
        delay (20);             // debounce

        if (butState == LOW) { // Button pressed
            menus [menuIdx].func ();
        }
    }
}

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

    pinMode (ButPin, INPUT_PULLUP);
    butState = digitalRead (ButPin);

    for (int n = 0; n < Nmenu; n++)
        Serial.println (menus [n].label);
}

Fix the bouncing the way you usually do.

a7

thanks

@timoneiro, is this a fair description of the estatico() function? I'm trying to work it and would like to know if this is correct.

void estatico() {
  
// 1. adjusting current until the button press
    
// 2. changing rpm until the button press

//3. do something for a minute, repeat if not satisfactory
}

The odd part is where you do something for a minute, and then repeat that if a exit condition is not arrived.

What means estatico? It translates as ecstatic, which is not meaningful. The function certainly does not create happiness or joyful excitement… just curious.

a7

Estatico means static, i am trying to do a static test to an engine, so basically the first part selects a load current, then you select the minimum rpm which the engine can operate safely. then for a minute the there's data being collected and afterwards the load will be increased with the help of a digital potentiometer. in case the the engine's RPM get lower than the minimum the test should stop. My problem was that the button was that the pushbutton wasn't being detected. I have done the code again and I right now i have solved the problem, and yes it had to do with the fact that the code was always in estatico() and so the

lastButtonState = reading;

in the loop didn't happen, right now I am using a different methodology with a a series of ifs and a variable called "step" which is incremented everytime a process is concluded as you can see below. I should say that i suspect this isn't the most efficient way to do it however it was the way that I could make it work, feel free to make any suggestions, I will be welcoming them!

#include "HX711.h"
#define seconds() (millis()/1000)

const int potPin = A0;                                                    // Pin connnected to potentiometer

// Load Cell with HX711
const int LOADCELL_DOUT_PIN = 3;                                          // Pin connected to DOUT of HX711
const int LOADCELL_SCK_PIN = 4;                                           // Pin connected to SCK of HX711
HX711 scale;                                                              // Start link with HX711
float Calibration = 218.5;                                                // Calibration for load cell
const int Distance = 500;                                                 // Distance from the load cell to the axle
float Mass;                                                               // Mass measured in grams
float Weight;                                                             // Weight measured in Newtons
float Torque;                                                             // Engine torque in N.m

// RPM measurements
const int rpmPin = 2;                                                     // Pin 2 connected to hall sensor signal
int rpm;                                                                  // RPM value to be displayed
float PrevTime = 0;                                                       // Time of previous magnet detection in microseconds
float Duration = 0;                                                       // Time elapsed between magnet detection in microseconds
double time = 0;

// Pushbutton
const int buttonPin = 7;                                                  // Pin 7 connected to pushbutton
int lastButtonState = LOW;
int buttonState = LOW;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 30;

//Selectors
int menuIndex = 0;
const int numMenuItems = 3;
int initial_current = 0;
int rpm_min = 0;
int rpm_adjusted = 0;
int level = 1;
int step = 1;

//Variables for easier selection of the minimum RPM on automatic test.
int minInput = 1023;
int maxInput = 0;
int minOutput = 0;
int maxOutput = 4000;
int increment = 100;

//Current Sensor variables
const int current_pin = A1;                                             // Pin used to read OUT of the current sensor
const float sensitivity = 0.066;                                        // Sensitivity of the sensor
const float vOffset = 2.5;                                              // Voltage offset at 0 A of current
float current_load = 0;

// Data collection
float time_now = 0;

int mapToIncrements(int value, int inMin, int inMax, int outMin, int outMax, int increment) {
    int range = (outMax - outMin) / increment;
    int mappedValue = map(value, inMin, inMax, 0, range);
    return outMin + (mappedValue * increment);
}

void setup() {
  pinMode(potPin, INPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  //pinMode(ResetPin, INPUT_PULLUP);                                        // Activate internal resistor and connect to pin 2
  
  pinMode(rpmPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(rpmPin), countRpm, RISING);
  
  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);                       // Start link with HX711
  scale.tare();                                                           // Tare scale to zero
  scale.set_scale(Calibration);                                           // Adjust scale to calibration factor

  Serial.begin(9600);
  
  //lcd.init();                                                   // Start I2C link with LCD
  //lcd.backlight();                                              // Turn on LCD backlight
  //lcd.setCursor(0, 0);
  //lcd.print(" The Dyno Bench ");                                      // Delay routine to stabilize scale.tare
  //lcd.setCursor(0, 1);
  //lcd.print("   C-MAST UBI   ");
  //delay(1500);
  updateMenu();

}

void loop() {
  if (level==1)
  {
    int potValue = analogRead(potPin);
    int newMenuIndex = map(potValue, 1023, 0, 0, numMenuItems); // Mapeamento ajustado

    if (newMenuIndex >= numMenuItems) {
      newMenuIndex = numMenuItems - 1;
    }

    if (newMenuIndex != menuIndex) {
      menuIndex = newMenuIndex;
      updateMenu();
    }

    // Read the state of the pushbutton
    int reading = digitalRead(buttonPin);

    // Check for button press with debouncing
    if (reading != lastButtonState) {
      lastDebounceTime = millis();
    }

    if ((millis() - lastDebounceTime) > debounceDelay) {
      if (reading != buttonState) {
        buttonState = reading;
        if (buttonState == LOW) { // Button pressed
          selectMenuItem();
          level = 2;

          
        }
      }
    }
    lastButtonState = reading;
  }

  if (level == 2)
  {
    if (menuIndex == 0) {
      estatico();
    } else if (menuIndex == 1) {
      Serial.println("Ainda não feito.");
    } else {
      //manual();
    }
  }

}

void updateMenu() {
  Serial.print("Menu: ");
  switch (menuIndex) {
    case 0:
      Serial.println("1- Estático");
      break;
    case 1:
      Serial.println("2- Transiente");
      break;
    case 2:
      Serial.println("3- Manual");
      break;
  }
}

void selectMenuItem() {
  //Actions for when the pushbutton is pressed
  Serial.println("Selecionado!");
  delay(1000);
}

void estatico()
{
  if (step == 1)
  {
    int new_initial_current = map(analogRead(potPin), 1023, 0, 0, 30);
    
    if(new_initial_current != initial_current){
      initial_current = new_initial_current;
      current();
    }

    // Read the state of the pushbutton
    int reading = digitalRead(buttonPin);

    // Check for button press with debouncing
    if (reading != lastButtonState) {
      lastDebounceTime = millis();
    }

    if ((millis() - lastDebounceTime) > debounceDelay) {
      if (reading != buttonState) {
        buttonState = reading;
        if (buttonState == LOW) { // Button pressed
          step = 2;
          Serial.println("Selecionado!");
        }
      }
    }
    lastButtonState = reading;
    delay(250);
  }
  else if (step == 2)
  {
    int new_rpm_min = mapToIncrements(analogRead(potPin), minInput, maxInput, minOutput, maxOutput, increment);
            
    if(rpm_min - new_rpm_min > 10 || new_rpm_min - rpm_min > 10) {
      rpm_min = new_rpm_min;
      rpm_selecter();
    }

    // Read the state of the pushbutton
    int reading = digitalRead(buttonPin);

    // Check for button press with debouncing
    if (reading != lastButtonState) {
      lastDebounceTime = millis();
    }

    if ((millis() - lastDebounceTime) > debounceDelay) {
      if (reading != buttonState) {
        buttonState = reading;
        if (buttonState == LOW) { // Button pressed
          step = 3;
          Serial.println("Selecionado!");
        }
      }
    }
    lastButtonState = reading;
    delay(250);
  }
  else if(step == 3)
  {
    Serial.println("O teste vai ser começar!");
    Serial.println("Garante que o acelerador está na posição a testar");
    step = 4;
    delay(2000);
  }
  else if(step == 4)
  {
    //INICIALIZAÇÃO DA FONTE DE ALIMENTAÇÃO
    step = 5;
    time = millis(); 
  }
  else if(step == 5)
  {
    if (millis() - time < 60000)
    {
      Collect_Data();
    }
    else
    {
      step = 6;
    }
  }
  else if(step == 6)
  {
    int load_increment = 1;
    // processo de Aumento de corrente
    step = 7;
  }
  else if(step == 7)
  {
    delay(2500);
    Serial.println("A testar limite.");

    RPM();
    int rpm_test = 1000;
    Serial.println(rpm_test);
    Serial.println(rpm_min);

    if (rpm_min < rpm_test)
    {
      step = 5;
    }
    else
    {
      level = 1;
      step = 1;
      Serial.println("Atingido limite mínimo, teste parado!");
      updateMenu();
      return;
    }
    Serial.println(step); 
  }

}

void current()
{
  Serial.print("Corrente inicial = ");
  Serial.println(initial_current);
}

void rpm_selecter() {
  Serial.print("RPM mínimas = ");
  rpm_adjusted = round(rpm_min/50)*50;
  Serial.println(rpm_adjusted);
}

void countRpm() {
  Duration = micros() - PrevTime;                       // Calculates time difference between revs in microsecond
  PrevTime = micros();
}

void RPM()
{
  rpm = 60000000 / Duration;                                 // rpm = (1/ time millis)*1000*1000*60;
  if (micros() - PrevTime  > 2*1000000)                       // Check if motor stopped - unchanged after 2s
  {
    rpm = 0;
  }
}

void Collect_Data()
{
  time_now = millis() - time;
  // Load Cell Code
  // Read the weight
  Mass = scale.get_units(10); // Average of 10 readings
      
  int sensorValue = analogRead(current_pin);                        // Reads the OUT pin from the current sensor
  float voltage = sensorValue * (5.0 / 1023.0);                     // Convert the analog reading to voltage
  current_load = (voltage - vOffset) / sensitivity;                // Calculate the current

  RPM();                                                             // Calculate the engine RPM 


  // Print the RPM and weight on the Serial Monitor
  Serial.print(time_now);
  Serial.print(",");
  Serial.print(rpm);
  Serial.print(",");
  Serial.print(Mass);
  Serial.print(",Load,");
  Serial.println(current_load);
}