Push Button Loop Data Reading From GY-87 (Gyroscope)

Hey fellas, need a bit of a help here :slight_smile: Im currently trying to build my own flight computer (obviously simple one). I have attached 2 leds 1 buzzer and 1 button to my skech in order to change from mode to mode and see what's happening throw LED's and buzzer.

My Idea is that one push will put it into flight mode (or modeOne():wink: and this supposed to print new line of a data into Serial and SD card to be recorded and monitored. This is where the problem occurs: Instead of constantly repeating the same thing over and over again, prints only one line of DATA.

Two pushes will trigger Test Mode, here all okay :slight_smile: Holding the push button will switch all LED's off and do nothing e.g. sleep mode

Here is my complete code:

#include "I2Cdev.h"
#include "MPU6050.h"
#include "HMC5883L.h"
#include "BMP085.h"
#include "Wire.h"
#include <Servo.h>
#include "ServoEasing.hpp"
#include <PinButton.h>

#define GREEN_LED 12 // GREEN LED
#define RED_LED 31 // RED LED 
#define LEFT_WING_PIN 41 // WING
#define RIGHT_WING_PIN 40 // WING

const int buzzer = 27; // Buzzer
static const float ACCEL_SENS = 16384.0; // Accel Sensitivity with default +/- 2g scale
static const float GYRO_SENS = 131.0; // Gyro Sensitivity with default +/- 250 deg/s scale

ServoEasing Left_Wing;
ServoEasing Right_Wing;
PinButton myButton(9);

// Magnetometer class default I2C address is 0x1E
// specific I2C addresses may be passed as a parameter here
// this device only supports one I2C address (0x1E)
HMC5883L mag;
int16_t mx, my, mz;

// Accel/Gyro class default I2C address is 0x68 (can be 0x69 if AD0 is high)
// specific I2C addresses may be passed as a parameter here
MPU6050 accelgyro;

int16_t ax, ay, az;
int16_t gx, gy, gz;

// Barometer class default I2C address is 0x77
// specific I2C addresses may be passed as a parameter here
// (though the BMP085 supports only one address)
BMP085 barometer;

float temperature;
float pressure;
int32_t lastMicros;

void setup() {
  
  boolean state = HIGH;
  unsigned int count = 0;

  /********************************************************
   * Attach servo to pin and set servo to start position.
   *******************************************************/
  pinMode(RED_LED, OUTPUT);
  pinMode(GREEN_LED, OUTPUT);
  pinMode(buzzer, OUTPUT); // Set buzzer - pin 9 as an output

  Left_Wing.attach(LEFT_WING_PIN, 4);
  Right_Wing.attach(RIGHT_WING_PIN, 3);
  Left_Wing.setSpeed(140); // This speed is taken if no further speed argument is given.
  Right_Wing.setSpeed(140); // This speed is taken if no further speed argument is given.
  delay(500); // Wait for servo to reach start position.

  Serial.begin(9600);
  while (!Serial && (count < 30)) {
    delay(200); // Wait for serial port to connect with timeout. Needed for native USB
    digitalWrite(GREEN_LED, state);
    state = !state;
    count++;
  }
  digitalWrite(GREEN_LED, HIGH);
 // join I2C bus (I2Cdev library doesn't do this automatically)
  Wire.begin();

  // ==================== MPU6050 ============================
  accelgyro.initialize();
  Serial.print("Testing Accel/Gyro... ");
  Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
  // Starts up with accel +/- 2 g and gyro +/- 250 deg/s scale
  accelgyro.setI2CBypassEnabled(true); // set bypass mode
  // Now we can talk to the HMC5883l

  // ==================== HMC5883L ============================
  mag.initialize();
  Serial.print("Testing Mag...  ");
  Serial.println(mag.testConnection() ? "HMC5883L connection successful" : "HMC5883L connection failed");

  // ==================== BMP085 ============================
  barometer.initialize();
  Serial.print("Testing Pressure...  ");
  Serial.println(barometer.testConnection() ? "BMP085 connection successful" : "BMP085 connection failed");

  Serial.println("====// Setup Complete //====");
  //CLOSE BOTH WINGS
  Left_Wing.write(95); // Left Wing CLOSED
  Right_Wing.write(0); // Right Wing CLOSED
}

void modeOne() {
  //CLOSE BOTH WINGS
  Left_Wing.write(95); // Left Wing CLOSED
  Right_Wing.write(0); // Right Wing CLOSED
  
  static unsigned long ms = 0;
  static boolean state = HIGH;

  // Serial Output Format
  // === Accel === | === Gyro === | ======= Mag ======= | === Barometer === |
  //   X   Y   Z   |  X   Y   Z   |  X   Y   Z  Heading |  Temp   Pressure  |

  if (millis() - ms > 100) {
    // read raw accel/gyro measurements
    accelgyro.getMotion6( & ax, & ay, & az, & gx, & gy, & gz);

    // display tab-separated accel/gyro x/y/z values
    Serial.print(ax / ACCEL_SENS);
    Serial.print("\t");
    Serial.print(ay / ACCEL_SENS);
    Serial.print("\t");
    Serial.print(az / ACCEL_SENS);
    Serial.print("\t");
    Serial.print(gx / GYRO_SENS);
    Serial.print("\t");
    Serial.print(gy / GYRO_SENS);
    Serial.print("\t");
    Serial.print(gz / GYRO_SENS);
    Serial.print("\t");

    // read raw heading measurements
    mag.getHeading( & mx, & my, & mz);

    // display tab-separated mag x/y/z values
    Serial.print(mx);
    Serial.print("\t");
    Serial.print(my);
    Serial.print("\t");
    Serial.print(mz);
    Serial.print("\t");

    // To calculate heading in degrees. 0 degree indicates North
    float heading = atan2(my, mx);
    if (heading < 0) heading += 2 * M_PI;
    Serial.print(heading * 180 / M_PI);
    Serial.print("\t");

    // request temperature
    barometer.setControl(BMP085_MODE_TEMPERATURE);

    // wait appropriate time for conversion (4.5ms delay)
    lastMicros = micros();
    while (micros() - lastMicros < barometer.getMeasureDelayMicroseconds());

    // read calibrated temperature value in degrees Celsius
    temperature = barometer.getTemperatureC();

    // request pressure (3x oversampling mode, high detail, 23.5ms delay)
    barometer.setControl(BMP085_MODE_PRESSURE_3);
    while (micros() - lastMicros < barometer.getMeasureDelayMicroseconds());

    // read calibrated pressure value in Pascals (Pa)
    pressure = barometer.getPressure();

    // display measured values if appropriate
    Serial.print(temperature);
    Serial.print("\t");
    Serial.print(pressure / 100);
    Serial.println("\t");

    ms = millis();

  }
}

void modeTwo() {
  // START TESTING MODE:
  Serial.println("TEST MODE STARTED");
  digitalWrite(RED_LED, HIGH);
  digitalWrite(GREEN_LED, LOW);

  tone(buzzer, 1000); // 
  delay(200);
  tone(buzzer, 500); // 
  delay(250);
  tone(buzzer, 1000); // 
  delay(10);
  noTone(buzzer); // Stop sound...

  //OPEN BOTH WINGS
  Left_Wing.write(0); // Left Wing OPEN
  Right_Wing.write(105); // Right Wing OPEN

  delay(2000);

  //TEST LEFT WING
  Left_Wing.write(0); // Left Wing OPEN
  delay(500);
  Left_Wing.write(95); // Left Wing CLOSED
  delay(500);
  Left_Wing.write(0); // Left Wing OPEN
  delay(1000);

  //TEST RIGHT WING
  Right_Wing.write(105); // Right Wing OPEN
  delay(500);
  Right_Wing.write(0); // Right  Wing CLOSED
  delay(500);
  Right_Wing.write(105); // Right Wing OPEN
  delay(500);

  //CLOSE BOTH WINGS
  Left_Wing.write(95); // Left Wing CLOSED
  Right_Wing.write(0); // Right Wing CLOSED
  
  tone(buzzer, 1000); // 
  delay(10);
  tone(buzzer, 500); // 
  delay(250);
  tone(buzzer, 1000); // 
  delay(250);
  noTone(buzzer); // Stop sound...
  Serial.println("TEST MODE FINISHED");
  digitalWrite(RED_LED, LOW);

}

void modeOff() {
  Left_Wing.write(95); // Left Wing CLOSED
  Right_Wing.write(0); // Right Wing CLOSED
  digitalWrite(GREEN_LED, LOW);
  digitalWrite(RED_LED, LOW);
  tone(buzzer, 200); // 
  delay(750);
  noTone(buzzer); // Stop sound...  
}


void loop() {
  myButton.update();
  if (myButton.isSingleClick()) {

    modeOne();

  }

  if (myButton.isDoubleClick()) {
    modeTwo();
  }

  if (myButton.isLongClick()) {

    modeOff();

  }

} // close void loop

Now the code I want to run constantly when single push is made works fine on it's own, but for some reason doesn't when push button is involved.

Here is the code from the original example of GY-87

/*
  GY-87 Test
  Tests basic functionality of the GY-87 sensor board.
  
  Connections:
  |== GY-87 ==|== Arduino ==|
  |  VCC_IN   |     VCC     |
  |   GND     |     GND     |
  |   SDA     |     SDA     |
  |   SCL     |     SCL     |
  |=========================|
 
  Requires the I2CDevLib library, which can be found here: https://github.com/jrowberg/i2cdevlib
  
  by Tom Kuehn
  26/06/2016
*/

#include "I2Cdev.h"
#include "MPU6050.h"
#include "HMC5883L.h"
#include "BMP085.h"
#include "Wire.h"

static const char LED = 12;
static const float ACCEL_SENS = 16384.0; // Accel Sensitivity with default +/- 2g scale
static const float GYRO_SENS  = 131.0;   // Gyro Sensitivity with default +/- 250 deg/s scale

// Magnetometer class default I2C address is 0x1E
// specific I2C addresses may be passed as a parameter here
// this device only supports one I2C address (0x1E)
HMC5883L mag;
int16_t mx, my, mz;

// Accel/Gyro class default I2C address is 0x68 (can be 0x69 if AD0 is high)
// specific I2C addresses may be passed as a parameter here
MPU6050 accelgyro;

int16_t ax, ay, az;
int16_t gx, gy, gz;

// Barometer class default I2C address is 0x77
// specific I2C addresses may be passed as a parameter here
// (though the BMP085 supports only one address)
BMP085 barometer;

float temperature;
float pressure;
int32_t lastMicros;

void setup()
{
  boolean state = HIGH;
  unsigned int count = 0;
  
  pinMode(LED, OUTPUT);
  
  Serial.begin(9600);
  while (!Serial && (count < 30) )
  {
    delay(200); // Wait for serial port to connect with timeout. Needed for native USB
    digitalWrite(LED, state);
    state = !state;
    count++;
  }

  digitalWrite(LED, HIGH);

  // join I2C bus (I2Cdev library doesn't do this automatically)
  Wire.begin();

  // ==================== MPU6050 ============================
  accelgyro.initialize();
  Serial.print("Testing Accel/Gyro... ");
  Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");

  // Starts up with accel +/- 2 g and gyro +/- 250 deg/s scale
  accelgyro.setI2CBypassEnabled(true); // set bypass mode
  // Now we can talk to the HMC5883l

  // ==================== HMC5883L ============================
  mag.initialize();
  Serial.print("Testing Mag...  ");
  Serial.println(mag.testConnection() ? "HMC5883L connection successful" : "HMC5883L connection failed");

  // ==================== BMP085 ============================
  barometer.initialize();
  Serial.print("Testing Pressure...  ");
  Serial.println(barometer.testConnection() ? "BMP085 connection successful" : "BMP085 connection failed");

  Serial.println("Setup Complete");
}

void loop()
{
   
  static unsigned long ms = 0;
  static boolean state = HIGH;

  // Serial Output Format
  // === Accel === | === Gyro === | ======= Mag ======= | === Barometer === |
  //   X   Y   Z   |  X   Y   Z   |  X   Y   Z  Heading |  Temp   Pressure  |

  if (millis() - ms > 100)
  {
    // read raw accel/gyro measurements
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
    

  
    // display tab-separated accel/gyro x/y/z values
    Serial.print(ax/ACCEL_SENS); Serial.print("\t");
    Serial.print(ay/ACCEL_SENS); Serial.print("\t");
    Serial.print(az/ACCEL_SENS); Serial.print("\t");
    Serial.print(gx/GYRO_SENS); Serial.print("\t");
    Serial.print(gy/GYRO_SENS); Serial.print("\t");
    Serial.print(gz/GYRO_SENS); Serial.print("\t");

    // read raw heading measurements
    mag.getHeading(&mx, &my, &mz);

    // display tab-separated mag x/y/z values
    Serial.print(mx); Serial.print("\t");
    Serial.print(my); Serial.print("\t");
    Serial.print(mz); Serial.print("\t");
    
    // To calculate heading in degrees. 0 degree indicates North
    float heading = atan2(my, mx);
    if(heading < 0) heading += 2 * M_PI;
    Serial.print(heading * 180/M_PI); Serial.print("\t");

    // request temperature
    barometer.setControl(BMP085_MODE_TEMPERATURE);
    
    // wait appropriate time for conversion (4.5ms delay)
    lastMicros = micros();
    while (micros() - lastMicros < barometer.getMeasureDelayMicroseconds());

    // read calibrated temperature value in degrees Celsius
    temperature = barometer.getTemperatureC();

    // request pressure (3x oversampling mode, high detail, 23.5ms delay)
    barometer.setControl(BMP085_MODE_PRESSURE_3);
    while (micros() - lastMicros < barometer.getMeasureDelayMicroseconds());

    // read calibrated pressure value in Pascals (Pa)
    pressure = barometer.getPressure();

    // display measured values if appropriate
    Serial.print(temperature); Serial.print("\t");
    Serial.print(pressure/100); Serial.println("\t");

    ms = millis();
    digitalWrite(LED, state);
    state = !state;
  }
}

Any help is much appreciated

I'm not familiar with the PinButton library. Based on the names of the methods (e.g. isSingleClick()), your code will react on clicks and not on continously being pressed.

You can separate the actions from the functions as shown below; it uses an additional variable.

void loop()
{
  // remember the selected mode; 255 indicates no mode is active
  static uint8_t mode = 255;

  myButton.update();
  if (myButton.isSingleClick())
  {
    mode = 1;
  }

  if (myButton.isDoubleClick())
  {
    mode = 2;
  }

  if (myButton.isLongClick())
  {
    mode = 0;
  }

  // honour the mode
  switch (mode)
  {
    case 0:
      modeOff();
      // if you want to prevent modeOff to be repeated, set mode to 255
      mode = 255;
      break;
    case 1:
      modeOne();
      break;
    case 2:
      modeTwo();
      break;
    default:
      break;      
  }

}  // close void loop

Note:
not compiled

1 Like

This is great solution, thank you so much! The only problem I experience is that when modeTwo(); or DubleClick it goes to Test mode and it doesn't stop (looping in test mode) I tried with while(1){} but this stops the whole loop

If you only want to run modeTwo() once, have a look how the presented code prevents modeOff() from being called continuously in 'case 0`.

1 Like

Make sense, thank you so much for pointing the obvious for me :slightly_smiling_face:, but why 255? What this number mean

Thank you for you time
Best regards :sparkles:

The number does not mean much. It's just a number that is not seperately handled in the switch/case.

I could have choosen 3 but if you want to expand with a third mode etc, you will have to change it. 255 (0xFF) is at the end of the range of an uint8_t (byte) and hence I choose that so I still have the option to expand with up to 254 modes.

If I would have used a signed integer for the mode variable, I would have taken the value -1 as the default to do nothing in the switch / case.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.