Using BMP280 sensor to control servos. Need some guidance with coding

Hi @mikami2020

I've tweaked your code, so that it should work with the BMP280_DEV library:

/////////////////////////////////////////////////////////////////////////////////
// BMP280_DEV - I2C Communications, Default Configuration, Normal Conversion
/////////////////////////////////////////////////////////////////////////////////
#include <Servo.h>
#include <BMP280_DEV.h>                           // Include the BMP280_DEV.h library

int pushButton = 2;
int servoPin1 = 9;
int servoPin2 = 10;
int servoPin3 = 11;
int greenLed = 13;

Servo Myservo1; //define servos
Servo Myservo2;
Servo Myservo3;

int servoPos=0;
float temperature, pressure, altitude;            // Create the temperature, pressure and altitude variables
BMP280_DEV bmp280;                                // Instantiate (create) a BMP280_DEV object and set-up for I2C operation (address 0x77)

void setup() 
{
  Serial.begin(115200);                           // Initialise the serial port
  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_2000MS);     // Set the standby time to 2 seconds
  bmp280.startNormalConversion();                 // Start BMP280 continuous conversion in NORMAL_MODE  
  pinMode(greenLed,OUTPUT);
  Myservo1.attach(servoPin1);
  Myservo2.attach(servoPin2);
  Myservo3.attach(servoPin3);
}

void loop() 
{
  digitalWrite(greenLed, HIGH);
  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")); 
    
    if (altitude > -14.5)
    {
      Myservo1.write(90);
      Myservo2.write(90);
      Myservo3.write(90);
    }
    else
    {
      Myservo1.write(0);
      Myservo2.write(0);
      Myservo3.write(0);
    }
  }
}

This will cause the code to sample the barometer every 2 seconds without stopping in the loop(), in other words non-blocking.

The sample rate can be changed by amending the line in the setup() function:

bmp280.setTimeStandby(TIME_STANDBY_2000MS);     // Set the standby time to 2 seconds

It can be replaced by any of these intervals:

TIME_STANDBY_05MS 
TIME_STANDBY_62MS  
TIME_STANDBY_125MS    
TIME_STANDBY_250MS   
TIME_STANDBY_500MS    
TIME_STANDBY_1000MS   
TIME_STANDBY_2000MS   
TIME_STANDBY_4000MS   

This is great! Thanks for correcting my code :sweat_smile:
I think I will shorten the sampling intervals; maybe that would allow the airbrake to deploy more accurately.
I will try both the Adafruit and your library in my airbrake mechanism when I do my test flights.
Thanks again for your help! You're a great mentor to arduino newbies like me!

I have a suggestion for you:

Instead of using the value of 1013.25 hPa as the reference pressure that is used in all the altitude measurements mentioned in this topic, you could measure the pressure at the launch site near the beginning of the sketch, and use that figure as the reference pressure.

That way all your altitude measurements will be the actual height above the launch site.

Hi,

I agree, you could put a button on the pcb that you could press that measured the local barometric pressure before launch and stored it as the reference point.
OR.
If your board can sense launch, have it until launch, measuring the barometric pressure, then when launched it saves and starts calculating.

Just a thought.. Tom.. :smiley: :+1: :coffee: :australia:

This is a great suggestion! I will definitely calibrate the sensor before launch.
Thanks!

How do I do that? That is definitely a good idea cause that way I may not need to pull the entire device out of the rocket and connect to my laptop to calibrate before each launch. However, I am afraid that would require way more advance coding to achieve that; at this point, my coding skills are pretty basic :sweat_smile:

Hi @mikami2020

You just need to sample the altitude at your launch site and subtract this from your altitude measurements when the rocket is launched.

To represent the various phases of rocket flight in code, the concept of the state machine might come in handy:

//
// State machine example
//
enum StateMachine { DISARMED,
                    ARMED,
                    LAUNCH,
                    DEPLOY,
                    ABORT } stateMachine = DISARMED;

bool armedInput = false, lauchInput = false, abortInput = false; 
bool servosActivated = false;
float altitude, launchSiteAltitude;
float threshold = 500.0f;   // Altitude above launch site in meters

void setup() 
{  
  initialiseBarometer();
  initialiseServos();
}

void loop() 
{
  switch (stateMachine)
  {
    case DISARMED:
      armedInput = readArmedInput();
      if (armedInput == true)
      {
        stateMachine = ARMED;
      }
      break;
    case ARMED:
      launchInput = readLaunchInput();
      launchSiteAltitude = readBarometerAltitude();
      if (launchInput == true)
      {        
        stateMachine == LAUNCH;
      }
      break;
    case LAUNCH:
      altitude = readBarometerAltitude() - 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;
  }
}

Hi Martin,

Wow! Thanks for introducing this State Machine idea to me. It looks so neat! I like how each phrase is completed and then move to the next phrase, and I would like to use this for my project if I could. However, as a new learner in Arduino programming, I have encountered some questions while trying it out, like for example:
How do I turn this whole chunk

Serial.begin(115200);                           // Initialise the serial port
  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_125MS);     // Set the standby time to 125 milliseconds
  bmp280.startNormalConversion();                 // Start BMP280 continuous conversion in NORMAL_MODE

into a simple function initialiseBarometer(); ?

Also, in these two missions below for instance, is it the right way to interpret how the State Machine works (please see the comments that I made) ?
The example code that you provided above is so clean and nice, and I am trying to work toward that.

case DISARMED:
     armedInput = (digitalRead(pushButton)==LOW);  //to initiate the system by pressing a push button?
     if (armedInput == true)
{
stateMachine = ARMED;
}
break;
case ARMED:
      launchInput = readLaunchInput();  //What is this supposed to be?
      launchSiteAltitude = bmp280 (); //is this how to readBarometerAltitude?
      if (launchInput == true)
      {        
        stateMachine == LAUNCH;
      }
      break;

Thanks again for the information :raised_hands: :smiley:

Hi @mikami2020

To wrap the barometer initialisation into a single function (with no parameters or return value):

void initialiseBarometer()
{
  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_125MS);     // Set the standby time to 125 milliseconds
  bmp280.startNormalConversion();                 // Start BMP280 continuous conversion in NORMAL_MODE
}

In this line I imagined a readLaunchInput() function that's monitoring a (debounced) launch switch. It returns a bool (true or false) value stored in the launchInput variable.

When the lauch switch is thrown, the microcontroller then knows that the rocket has been launched. It can then move on to the LAUNCH phase of the state machine and start calculating the altitude above the launch site.

I used this line to simplify the state machine code, however if you're using the BMP280_DEV library then it works slightly differently. In this case, it's necessary to declare a floating point global variable before the setup() function:

float launchSiteAltitude;

Then pass it through to the library's getAltitude() function in the state machine:

bmp280.getAltitude(launchSiteAltitude);

The launchSiteAltitude variable is updated each time the function is called and a new barometer measurement is ready, otherwise the variable remains unchanged:

case ARMED:
  launchInput = readLaunchInput();
  bmp280.getAltitude(launchSiteAltitude);
  if (launchInput == true)
  {        
    stateMachine == LAUNCH;
  }
  break;
1 Like

Thanks again Martin for taking the time to help me out and providing all the very useful information. I am sorry that I am going to bug you again for a little bit of help.
I found some code online to create a debounce button, and I attempted to incorporate it to the State Machine code hoping that it would function.
After some trials and errors, I managed to clear the errors and allowed the codes to compile. However, unfortunately, I must have done something wrong here because when I uploaded the codes, the servos were activated immediately without me pushing the button. And the servo also didn't return to the original position after being activated. I couldn't figure out what's wrong and I hope you can help me find out.
Anyways, I would like to say thank you again; because of your help, I have felt more comfortable in trying out Arduino coding for the project and (at least I feel like) my skills are slowly improving.
I hope people that are building similar projects and are new to Arduino will find this post and the responses helpful too.

#include <Servo.h>
#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

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


int servoPin1 = 9;
int servoPin2 = 10;
int servoPin3 = 11;
int servoPos = 0;

Servo Myservo1;                                   //define servos
Servo Myservo2;
Servo Myservo3;

float temperature, pressure, altitude;            // Create the temperature, pressure and altitude variables
BMP280_DEV bmp280;                               // Instantiate (create) a BMP280_DEV object and set-up for I2C operation (address 0x76)

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

bool armedInput = false, launchInput = false, abortInput = false;
bool servosActivated = false;

float threshold = 5.0f;   // Altitude above launch site in meters
float launchSiteAltitude;


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);                     // Connect Servos to arduino pins
  Myservo2.attach(servoPin2);
  Myservo3.attach(servoPin3);
}

void debounceButtonSetup() {
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);

  // set initial LED state
  digitalWrite(ledPin, ledState);
}

void setup()
{
  Serial.begin(115200);                           // Initialise the serial port
  initializeBarometer();
  initializeServos();
  debounceButtonSetup();
  pinMode(buttonPin, INPUT);


}

void airbrakeGo()
{
  Myservo1.write(90);
  Myservo2.write(90);
  Myservo3.write(90);
}

void airbrakeClose()
{
  Myservo1.write(0);
  Myservo2.write(0);
  Myservo3.write(0);
}

float debounceButtonPush() {

  // 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 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:
      armedInput = digitalRead(buttonPin);
      if (armedInput == true)
      {
        stateMachine = ARMED;
      }
      break;
    case ARMED:
      launchInput = debounceButtonPush();
      bmp280.getAltitude(launchSiteAltitude);
      if (launchInput == true)
      {
        stateMachine == LAUNCH;
      }
      break;
    case LAUNCH:
      altitude = bmp280.getAltitude(altitude) - launchSiteAltitude;
      if (altitude > threshold)
      {
        stateMachine = DEPLOY;
      }
      break;
    case DEPLOY:
      if (servosActivated == false)
      {
        airbrakeGo();
        servosActivated = true;
      }
      break;
    case ABORT:
      // Add abort code here...
      break;
    default:
      break;
  }
 

}

Thanks again :pray:

Hi,
Does your button operate between 5V and the digital input pin?
If so you will need a 10K resistor between the digital input pin and gnd.
This so the input will be pulled low when the button is open.

A circuit diagram might be a good idea at this stage.

Thanks.. Tom... :grinning: :+1: :coffee: :australia: