Using a button to change LCD Screen

So, I need help. My code uses an MPU6050 and a filter to smooth out the angles, it then uses an HC-SR04 to measure the height from the ground. It displays both the angles and the height from the ground to the LCD Screen, but I'm trying to find a way to change the LCD screen through a button push to show the Distance of the observer to the object and the height of the object. In my code I've been trying for the past few hours to change this but the closest I've ever gotten was just a flickering between the 2 modes of the screen (which is this code.) This uses an ESP32-WROOM

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <NewPing.h>

#define TRIG_PIN 5
#define ECHO_PIN 18
#define MAX_DISTANCE 400 // Maximum distance for HC-SR04 in cm
#define BUTTON_PIN 4 // Pin connected to the button

// Variables for Kalman filter
float RateRoll, RatePitch;
float AccX, AccY, AccZ;
float AngleRoll, AnglePitch;
float KalmanAngleRoll = 0, KalmanUncertaintyAngleRoll = 2 * 2;
float KalmanAnglePitch = 0, KalmanUncertaintyAnglePitch = 2 * 2;
uint32_t LoopTimer;
float Kalman1DOutput[] = {0, 0};

// Variables for height and distance calculation
float userHeight = 0; // User height from HC-SR04
float partialHeight = 0; // Partial height of the object
float distanceToObject = 0; // Distance to the object
bool calculateDistance = false; // Flag to toggle display mode
unsigned long lastButtonPress = 0; // Last button press time
const unsigned long debounceDelay = 200; // Debounce delay

LiquidCrystal_I2C lcd(0x27, 16, 2); 
NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE); 

void kalman_1d(float &KalmanState, float &KalmanUncertainty, float KalmanInput, float KalmanMeasurement) {
  KalmanState = KalmanState + 0.004 * KalmanInput;
  KalmanUncertainty = KalmanUncertainty + 0.004 * 0.004 * 4 * 4;
  float KalmanGain = KalmanUncertainty / (KalmanUncertainty + 3 * 3);
  KalmanState = KalmanState + KalmanGain * (KalmanMeasurement - KalmanState);
  KalmanUncertainty = (1 - KalmanGain) * KalmanUncertainty;
  Kalman1DOutput[0] = KalmanState;
  Kalman1DOutput[1] = KalmanUncertainty;
}

void gyro_signals() {
  Wire.beginTransmission(0x68); 
  Wire.write(0x3B); 
  Wire.endTransmission();
  Wire.requestFrom(0x68, 6);

  int16_t AccXLSB = Wire.read() << 8 | Wire.read();
  int16_t AccYLSB = Wire.read() << 8 | Wire.read();
  int16_t AccZLSB = Wire.read() << 8 | Wire.read();

  AccX = (float)AccXLSB / 4096;
  AccY = (float)AccYLSB / 4096;
  AccZ = (float)AccZLSB / 4096;

  AngleRoll = atan(AccY / sqrt(AccX * AccX + AccZ * AccZ)) * 180 / PI;
  AnglePitch = -atan(AccX / sqrt(AccY * AccY + AccZ * AccZ)) * 180 / PI;
}

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

  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Initializing...");

  delay(1000);

  Wire.beginTransmission(0x68);
  Wire.write(0x6B);
  Wire.write(0x00); 
  Wire.endTransmission();

  pinMode(BUTTON_PIN, INPUT); 
  LoopTimer = micros();
}

void loop() {
  gyro_signals();
  kalman_1d(KalmanAngleRoll, KalmanUncertaintyAngleRoll, 0, AngleRoll);
  kalman_1d(KalmanAnglePitch, KalmanUncertaintyAnglePitch, 0, AnglePitch);

  userHeight = sonar.ping_cm();

  // Check for button press
  if (digitalRead(BUTTON_PIN) == HIGH) {
    if (millis() - lastButtonPress > debounceDelay) { // Check for debounce
      calculateDistance = !calculateDistance; // Toggle between modes
      lastButtonPress = millis(); // Update last button press time
    }
  }

  if (calculateDistance) {
    partialHeight = tan(abs(KalmanAngleRoll * PI / 180)) * userHeight; 
    float totalHeight = userHeight + partialHeight; 

    lcd.setCursor(0, 0);
    lcd.print("Height: ");
    lcd.print(totalHeight);
    lcd.print(" cm");

    lcd.setCursor(0, 1);
    lcd.print("Distance: ");
    lcd.print(userHeight / tan(abs(KalmanAngleRoll * PI / 180))); 
    lcd.print(" cm");
  } else {
    lcd.setCursor(0, 0);
    lcd.print("Roll: ");
    lcd.print(KalmanAngleRoll);
    lcd.print(" deg");

    lcd.setCursor(0, 1);
    lcd.print("Height: ");
    lcd.print(userHeight);
    lcd.print(" cm");
  }

  while (micros() - LoopTimer < 4000);
  LoopTimer = micros();
}


[quote="HaroldTech, post:1, topic:278543, full:true"]
Hi, I need help with the programming of my latest project. So I have a 16 x 2 LCD display and I had it displaying Something. So every time a button is pressed, I want the screen to change its text. the button is connected to port 2. Thanks for your help!
[/quote]


Hi! Thanks for replying, after doing the fixes you said, whenever I press the button it still doesn't change the LCD

#include <LiquidCrystal_I2C.h>
#include <NewPing.h>

#define TRIG_PIN 5
#define ECHO_PIN 18
#define MAX_DISTANCE 400 // Maximum distance for HC-SR04 in cm
#define BUTTON_PIN 4 // Pin connected to the button

// Variables for Kalman filter
float RateRoll, RatePitch;
float AccX, AccY, AccZ;
float AngleRoll, AnglePitch;
float KalmanAngleRoll = 0, KalmanUncertaintyAngleRoll = 2 * 2;
float KalmanAnglePitch = 0, KalmanUncertaintyAnglePitch = 2 * 2;
uint32_t LoopTimer;
float Kalman1DOutput[] = {0, 0};

// Variables for height and distance calculation
float userHeight = 0; // User height from HC-SR04
float partialHeight = 0; // Partial height of the object
float distanceToObject = 0; // Distance to the object
bool calculateDistance = false; // Flag to toggle display mode
unsigned long lastButtonPress = 0; // Last button press time
const unsigned long debounceDelay = 200; // Debounce delay

// Variables to handle button state
int lastButtonState = LOW; // Store the previous state of the button
int currentButtonState = LOW; // Store the current state of the button

// Timing variables for non-blocking control (Blink Without Delay style)
unsigned long previousMillis = 0;  // Stores the last time a loop cycle started
const long interval = 4; // 4ms interval (250Hz)

// LCD initialization
LiquidCrystal_I2C lcd(0x27, 16, 2); // Assuming address 0x27
NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE); // HC-SR04 initialization

void kalman_1d(float &KalmanState, float &KalmanUncertainty, float KalmanInput, float KalmanMeasurement) {
  KalmanState = KalmanState + 0.004 * KalmanInput;
  KalmanUncertainty = KalmanUncertainty + 0.004 * 0.004 * 4 * 4;
  float KalmanGain = KalmanUncertainty / (KalmanUncertainty + 3 * 3);
  KalmanState = KalmanState + KalmanGain * (KalmanMeasurement - KalmanState);
  KalmanUncertainty = (1 - KalmanGain) * KalmanUncertainty;
  Kalman1DOutput[0] = KalmanState;
  Kalman1DOutput[1] = KalmanUncertainty;
}

void gyro_signals() {
  Wire.beginTransmission(0x68); // Assuming MPU6050 is at 0x68
  Wire.write(0x3B); // Start reading from accelerometer
  Wire.endTransmission();
  Wire.requestFrom(0x68, 6); // Read 6 bytes

  int16_t AccXLSB = Wire.read() << 8 | Wire.read();
  int16_t AccYLSB = Wire.read() << 8 | Wire.read();
  int16_t AccZLSB = Wire.read() << 8 | Wire.read();

  // Convert to g-forces
  AccX = (float)AccXLSB / 4096;
  AccY = (float)AccYLSB / 4096;
  AccZ = (float)AccZLSB / 4096;

  AngleRoll = atan(AccY / sqrt(AccX * AccX + AccZ * AccZ)) * 180 / PI;
  AnglePitch = -atan(AccX / sqrt(AccY * AccY + AccZ * AccZ)) * 180 / PI;
}

void setup() {
  Serial.begin(115200);
  Wire.begin(); // Initialize I2C for MPU6050

  // Initialize LCD
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Initializing...");

  delay(1000); // Give time to initialize the devices

  // Initialize MPU6050 (at 0x68)
  Wire.beginTransmission(0x68);
  Wire.write(0x6B);
  Wire.write(0x00); // Wake up MPU6050
  Wire.endTransmission();

  // Initialize button pin
  pinMode(BUTTON_PIN, INPUT); // Set button pin as input
}

void loop() {
  // Check if it's time to execute the next loop cycle (Blink Without Delay)
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    // Kalman filtering of roll and pitch
    gyro_signals();
    kalman_1d(KalmanAngleRoll, KalmanUncertaintyAngleRoll, 0, AngleRoll);
    kalman_1d(KalmanAnglePitch, KalmanUncertaintyAnglePitch, 0, AnglePitch);

    // Read distance from the HC-SR04
    userHeight = sonar.ping_cm();

    // Read the current button state
    currentButtonState = digitalRead(BUTTON_PIN);

    // Check if the button was pressed (HIGH now and LOW last time)
    if (currentButtonState == HIGH && lastButtonState == LOW && millis() - lastButtonPress > debounceDelay) {
      calculateDistance = !calculateDistance; // Toggle between modes
      lastButtonPress = millis(); // Update last button press time
    }

    // Save the current button state as the last state for the next loop
    lastButtonState = currentButtonState;

    // Update the LCD based on mode
    lcd.clear(); // Clear LCD for new data

    if (calculateDistance) {
      // Calculate height and distance
      partialHeight = tan(abs(KalmanAngleRoll * PI / 180)) * userHeight; // Use userHeight for distance calculation
      float totalHeight = userHeight + partialHeight; // Total height of the object

      // Display results
      lcd.setCursor(0, 0);
      lcd.print("Height: ");
      lcd.print(totalHeight);
      lcd.print(" cm");

      lcd.setCursor(0, 1);
      lcd.print("Distance: ");
      lcd.print(userHeight / tan(abs(KalmanAngleRoll * PI / 180))); // Calculate distance
      lcd.print(" cm");
    } else {
      // Display roll and user height
      lcd.setCursor(0, 0);
      lcd.print("Roll: ");
      lcd.print(KalmanAngleRoll);
      lcd.print(" deg");

      lcd.setCursor(0, 1);
      lcd.print("Height: ");
      lcd.print(userHeight);
      lcd.print(" cm");
    }
  }
}

Hi @zadkiel_01,

You have to ensure to void the button from the moment it's read as pushed once, and "rearm" it for a new use after it's released.

You can see the solution implemented in (or just directly use) the ButtonToSwitch library, using the SnglSrvcVdblMPBttn class. Use the methods to set a function to execute when the button enters the isOn state, using the setFnWhnTrnOnPtr() method, and set the function to do the calculateDistance value swapping.

I believe the 13_SnglSrvcVdblMPBttn_1e.ino example file shows how to implement that pretty simple, about 6 lines of code...

Good Luck!
Gaby.//

Hi @Delta_G ,

Given Supercalifragilisticespialidosus name was taken (but hey, you've got a point, everybody remembers it after 50 or 60 year!!), SingleServiceVoidableMomentaryPushButton seemed to me the same as the name without a vowel and it is: An awful name everybody will copy instead of trying to remember and transcrip so the shortest copy... :crazy_face:

BTW Thank you very much for your fork of the TimerOne library to include UNO-R4, I was about to drop TimerOne dependency as an option to my library!! Great Job!

Gaby.//

Hi @Delta_G ,

Last check it was stucked:

But I believe he has enough ideas to start pushing out of the mud!

Gaby.//

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