Using debounce button as on/off button to trigger BMP280 to read altitude


Hello all,

I am fairly new to programming and Arduino, and I would like to ask for some help with my current project.
The project that I am building is an airbrake mechanism for a model rocket. Flaps are to deployed at a preset altitude to slow the rocket down before reaching a specific altitude. BMP280 is used to measure altitude, and three servos will be triggered to pull open the flaps when the preset altitude is reached.
I would like to use a debounce button as an on/off button to trigger the BMP280 to start reading the altitude.
In my experiment with the code, I set the threshold to one meter, so if I move the sensor one meter higher than initial altitude measured (launchSiteAltitude), the flaps should be deployed.
I utilized a state machine to organize the code. In the "ARMED" phrase, I plan to press the button once to switch on an LED light, and when the LED light is on, it triggers the launch input and then the BMP280 will read and save the altitude reading as the starting altitude. I added a Serial.println to indicate "launch" in the serial monitor if the launch phrase is triggered.
The things is that I pressed the button but nothing really happened. I wonder if the code that I used in the "ARMED" phrase is incorrect.
If you can see where the mistake is, please let me know. Attached is the codes and a hand drawn schematic.
P.S. the debounce button code worked when the debounce button codes were execute alone. It just didn't do what I hope it would do when I incorporated it to my airbrake codes.
Thanks!

#include <Servo.h>                                // Include the Servo.h library
#include <BMP280_DEV.h>                           // Include the BMP280_DEV.h library


const int buttonPin = 2;    // the number of the pushbutton pin
const int ledPin = 13;      // the number of the LED pin
int ledState = HIGH;         // the current state of the output pin
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin

int servoPin1 = 9;            // the number of servo pin 1
int servoPin2 = 10;           // the number of servo pin 2
int servoPin3 = 11;           // the number of servo pin 3
int servoPos = 0;             //the starting servo position
Servo Myservo1;               //define servo 1
Servo Myservo2;               //define servo 2
Servo Myservo3;               //define servo 3


float temperature, pressure, altitude;            // Create the temperature, pressure and altitude variables
BMP280_DEV bmp280;


unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

enum StateMachine { DISARMED,
                    ARMED,
                    LAUNCH,
                    DEPLOY,
                    ABORT
                  } stateMachine = DISARMED;

bool armedInput = false, launchInput = false, abortInput = false;
bool servosActivated = false;
float currentAltitude, launchSiteAltitude;
float threshold = 1.0f;   // Altitude above launch site in meters

void initializeBarometer()
{
  bmp280.begin();                                 // Default initialisation, place the BMP280 into SLEEP_MODE
  //bmp280.setPresOversampling(OVERSAMPLING_X4);    // Set the pressure oversampling to X4
  //bmp280.setTempOversampling(OVERSAMPLING_X1);    // Set the temperature oversampling to X1
  //bmp280.setIIRFilter(IIR_FILTER_4);              // Set the IIR filter to setting 4
  bmp280.setTimeStandby(TIME_STANDBY_1000MS);     // Set the standby time to 1 seconds
  bmp280.startNormalConversion();   // Start BMP280 continuous conversion in NORMAL_MODE

}

void initializeServos()
{
  Myservo1.attach(servoPin1);
  Myservo2.attach(servoPin2);
  Myservo3.attach(servoPin3);

}

void initializeDebounceButton()
{
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, ledState);       // set initial LED state
}

void debounceButton()
{

  // read the state of the switch into a local variable:
  int reading = digitalRead(buttonPin);

  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH), and you've waited long enough
  // since the last press to ignore any noise:

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // only toggle the LED if the new button state is HIGH
      if (buttonState == HIGH) {
        ledState = !ledState;
      }
    }
  }

  // set the LED:
  digitalWrite(ledPin, ledState);

  // save the reading. Next time through the loop, it'll be the lastButtonState:
  lastButtonState = reading;
}

void activateServos ()
{
  Myservo1.write(90);
  Myservo2.write(90);
  Myservo3.write(90);

}




void setup()
{
  initializeBarometer();      //done
  initializeServos();         //done
  Myservo1.write(0);
  Myservo2.write(0);
  Myservo3.write(0);
  initializeDebounceButton(); //done

  Serial.begin(115200);       // Initialise the serial port
}

void loop()
{


  if (bmp280.getMeasurements(temperature, pressure, altitude))    // Check if the measurement is complete
  {
    Serial.print(temperature);                    // Display the results
    Serial.print(F("*C   "));
    Serial.print(pressure);
    Serial.print(F("hPa   "));
    Serial.print(altitude);
    Serial.println(F("m"));
  }



  switch (stateMachine)
  {
    case DISARMED:

      stateMachine = ARMED;

      break;



    case ARMED:
      debounceButton ();
      launchInput =  digitalRead(ledPin);
      bmp280.getAltitude(launchSiteAltitude);
      if (launchInput == true)
      {
        stateMachine == LAUNCH;
      }
      if (stateMachine == LAUNCH)

        Serial.println("launch");

      break;




    case LAUNCH:
      altitude = bmp280.getAltitude(currentAltitude) - launchSiteAltitude;
      if (altitude > threshold)
      {
        stateMachine = DEPLOY;
      }
      break;
    case DEPLOY:
      if (servosActivated == false)
      {
        activateServos();
        servosActivated = true;
      }
      break;
    case ABORT:
      // Add abort code here...
      break;
    default:
      break;
  }
}
![IMG_3469.HEIC|666x500](upload://40D7jJGDF1mjFUPn0TxFX7WVBRb.jpeg)

Hello

There is this line doing nothing useful : stateMachine == LAUNCH;

So how do I use the debounce button to trigger the launchInput and sample the starting altitude before moving to the next phrase (Launch)?

I'd check that the pressure sensor provides the expected values when you lift it for 1 meter. In the ascending rocket the static/dynamic pressure differences will cause more trouble.

Your setting and usage of ledState is inconsistent, reading the ledPin is bogus. Why do you toggle the ledState whenever buttonState goes high?

Hello
Make some flight tests with BMP280 data logging only.

This line stateMachine == LAUNCH; compares stateMachine with LAUNCH, and do nothing with the result of the comparison. And how can it be == to LAUNCH when it is already == to ARMED ? This line is discarded by the compiler, because it's useless

You want to assign a new state : stateMachine = LAUNCH;

Hello DrDiettrich,
I actually found the debounce button codes online. And I also thought turning the LED on when the button is pressed would be a good indication that the lauchInput was executed and the sensor sampled a starting altitude, but i guess it wouldn't work that way :sweat_smile:
Can you give me some guidance on how to achieve what I hope my code would do?
And as for the pressure difference in the ascending rocket. I guess I would need to do test flights to see how that goes, if it's bad, then may have to change plan.

For the button see Debounce and StateChangeDetection IDE examples.

Hello guix,

OH yes, you're right! I totally missed that. Thanks for catching this!

You've used an amplifier symbol to represent a LED. Also it's worth learning the schematic symbol for a switch - for obvious reasons, it was one of the first symbols, and it's very self explanatory. A round circle with 3 wires coming out of it, gives no clue how they are internally connected.

That´s a tube :nerd_face:

What about the cathode heater? :slight_smile:

Virtuel virtuel

"left as an exercise for the student"

Hi @mikami2020

Here's some code containing a function that returns (true) whenever a button has been depressed and is then released. In this instance, it requires one side of the button to be connected to the Arduino pin and the other to ground (GND). The Arduino's internal pull-up resistor on the pin pulls it to 5V. This means that you don't require the external 10K pull-down resistor that you're currently using.

Here's the test code (on digital pin D2):

void setup()
{
  Serial.begin(115200);                       // Activate the Serial port at 115200
  //while (!Serial);                            // Wait for the console to open

  pinMode(2, INPUT_PULLUP);                   // Configure digital pin 2 as an input with pull-up resistor activated
}

void loop()
{
  if (debounce(2))                            // Poll the button
  {
    Serial.println(F("Button Activated"));    // If the button has been depressed and released display string
  }
}

// Debounce the pin
bool debounce(int pin) 
{  
  const unsigned long intervalMillis = 40ul;          // Set the debounce time interval to 40 milliseconds
  static unsigned long previousMillis = millis();     
  static bool state = true;

  bool newState = digitalRead(pin);                   // Read the state of the pin
  if (state != newState)                              // If the state has changed since last time...
  {
    if (millis() - previousMillis >= intervalMillis)  // Check if the 40ms debounce interval has been exceeded
    {
      previousMillis = millis();                      // Update the previous millis variable
      state = newState;                               // Update the state to the new state
      if (state) { return true; }                     // If the state has transitioned to HIGH/button release, return true
    } 
  }
  return false;                                       // Otherwise return false
}

Just some minutae - timing intervals don't have to be unsigned long unless the interval won't fit in anything smaller. If it's declared const, though, the compiler will optimize it anyway... only complete timestamps require unsigned long representation.

This is an interesting case, because the timing interval is so short, the timestamp 'previousMillis' could be an unsigned int, saving two bytes. It will also execute faster. But you need to implicitly cast it first, like

const unsigned char intervalMillis = 40;
static unsigned int previousMillis;
...
    unsigned int currentMillis = millis();
    if (currentMillis - previousMillis >= intervalMillis)  // Check if the 40ms debounce interval has been exceeded

Also, this is the "slow" button debounce. It only returns a value after the debounce period has elapsed. If the switch has a lot of bounce and the interval has to be extended, this will result in a sluggish human interface.

In most cases, it's better to report a valid state change immediately, then ignore any further changes until the interval has elapsed.

The stated application does not require any debounce. It's a "dynamite plunger" app, the state of the switch contacts after an actuation is irrelevant because there will be only one activation.

This code introduces a 40ms delay, in which time, a model rocket can travel quite far.

The button is checked only before launch, the timing is irrelevant.

Direct heating, probably a VFD display :slight_smile:

If your using a Mega, how big is this 'model' rocket and how high does it go ?

Thanks @anon57585045 all valid points, hadn't thought shortening the comparison of currentMillis and previousMillis like that before.

But for the same reason, a debounce is not required. :slight_smile: