1

I have no clue why this is happening to me but the buttons (in this case, buttonAbort and buttonStage) are not working whatsoever.

Edit: Basically the LEDs are placed right next to the button,(e.g. ledStage -> placed next to the buttonStage) they start to blink when the timer hits zero with a buzzer noise, and if the user presses one of the buttons, the buzzer noise stops and the LED placed next to the button they pressed turns on whereas the other turns off. I also connected the buttons without resistors. (directly to the pins)

/*
   Jebediah's Launch Control System for Kerbal Space Program
   Alpha Build 2.00
   An Open-Source Project by John Seong
*/

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

const byte ROWS = 4;
const byte COLS = 3;

const int buzzer = A3;
const int ledAbort = 5;
const int ledStage = A5;
const int buttonAbort = 4;
const int buttonStage = A4;

char keys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};

byte rowPins[ROWS] = {12, 11, 10, 9};
byte colPins[COLS] = {8, 7, 6};

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

// Show a two-line message on the LCD.
void lcdShow(const char *line0, const char *line1) {
  lcd.clear();
  lcd.print(line0);
  lcd.setCursor(0, 1);
  lcd.print(line1);
}

void lcdShowResult(float result) {
  lcd.print(result);
}

void setup() {
  pinMode(buzzer, OUTPUT);
  pinMode(ledAbort, OUTPUT);
  pinMode(ledStage, OUTPUT);
  pinMode(buttonAbort, INPUT_PULLUP);
  pinMode(buttonStage, INPUT_PULLUP);

  Serial.begin(9600);
  lcd.begin(16, 2);
  while (!keypad.getKey()) ;  // wait for key press
}

void loop() {
  static enum {START, MENU, SET_COUNT, COUNT_DOWN, LAUNCH, SET_THRUST, SET_WEIGHT} state = START;
  static uint32_t last_second;  // millis() value on last full second
  static int count;
  static float thrust, weight, ratio;

  int stageConfirm = digitalRead(buttonStage);
  int abortConfirm = digitalRead(buttonAbort);

  char key = keypad.getKey();

  switch (state) {
    case START:  // transitional state
      lcdShow("1. LAUNCH SEQ", "2. TWR CALC");
      state = MENU;
    /* fallthrough */

    case MENU:
      if (key == '1') {  // Countdown
        lcdShow("COUNTDOWN TIMER", "SECONDS: ");
        count = 0;
        state = SET_COUNT;
      } else if (key == '2') {  // TWR
        lcdShow("TWR CALCULATOR", "THRUST: ");
        thrust = 0, weight = 0, ratio = 0;
        state = SET_THRUST;
      }
      break;


    case SET_COUNT:
      if (key >= '0' && key <= '9' && count <= 99) {
        lcd.print(key);
        count = 10 * count + (key - '0');
      } else if (key == '#') {
        lcdShow("    T-MINUS", "     SECONDS");
        // Force a refresh on entering COUNT_DOWN:
        last_second = millis() - 1000;
        count++;
        state = COUNT_DOWN;
      } else if (key == '*') {
        state = START;
      }
      break;

    case COUNT_DOWN:
      if (millis() - last_second >= 1000) {
        last_second += 1000;
        count--;
        if (count <= 10) {
          tone(buzzer, 500);
          delay(500);
          noTone(buzzer);
        }
        if (count == 0) {
          Serial.println("Lift off!");
        } else if (count < 0) {
          state = LAUNCH;
          break;
        }
        lcd.setCursor(1, 1);
        lcd.print(count < 10 ? "  " : count < 100 ? " " : ""); // pad
        lcd.print(count);
      } else if (key == '*') {
        state = START;
      }
      break;

    case LAUNCH:
      tone(buzzer, 3000);
      digitalWrite(ledAbort, HIGH);
      digitalWrite(ledStage, HIGH);
      lcdShow(" T-ZERO WARNING", "CONFIRM IGNITION");
      delay(1000);
      lcd.clear();
      digitalWrite(ledAbort, LOW);
      digitalWrite(ledStage, LOW);
      delay(500);

      stageConfirm = digitalRead(buttonStage);
      abortConfirm = digitalRead(buttonAbort);
      
      if (stageConfirm == LOW) {
        noTone(buzzer);
        digitalWrite(ledStage, HIGH);
        digitalWrite(ledAbort, LOW);
        lcdShow("  LIFT OFF", "HAVE A SAFE FLIGHT");
        break;
        
      } else if (abortConfirm == LOW) {

        break;
      }
      Serial.println(stageConfirm);
      Serial.println(abortConfirm);

      break;

    case SET_THRUST:
      if (key >= '0' && key <= '9' && thrust <= 9999) {
        lcd.print(key);
        thrust = 10 * thrust + (key - '0');
      } else if (key == '#') {
        lcdShow("TWR CALCULATOR", "WEIGHT: ");
        state = SET_WEIGHT;
      } else if (key == '*') {
        state = START;
      }
      break;

    case SET_WEIGHT:
      if (key >= '0' && key <= '9' && weight <= 9999) {
        lcd.print(key);
        weight = 10 * weight + (key - '0');
      } else if (key == '#') {
        lcdShow("THRUST-TO-WEIGHT", "RATIO: ");
        if (thrust != 0 || weight != 0) {
          ratio = thrust / weight;
          lcdShowResult(ratio);
        }
      } else if (key == '*') {
        state = START;
      }
      break;
  }
}
  • Try printing the values of stageConfirm and abortConfirm right after you read the buttons and see if they read what you think they should. Your logic in your if statements seems weird to me, if the stageConfirm button is not pressed OR if the abort button IS pressed then say "Have a safe flight". Seems to me like pressing the abort button should not lead to a liftoff. – Delta_G Jul 12 '20 at 4:49
  • Show your wiring as well. – Delta_G Jul 12 '20 at 4:50
  • That commented /* ... */ music stuff is distracting. Why do you keep it? Would you mind creating a copy of your program and remove everything that is not related to the button problem, e.g. the LCD intro in setup(). That would help as well. – Thomas Weller Jul 12 '20 at 6:28
  • Oh, it's supposed to be && not ||. Sorry for the confusion! – John Seong Jul 12 '20 at 7:04
  • you have to explain what you expect the buttons to do – jsotola Jul 12 '20 at 7:05
1

This code is reading the buttons at two places in loop(), but the first time the results are ignored. Only the readings done in the LAUNCH state can have any effect. The problem is that the reading is done only at one very specific time, 1.5 seconds after displaying “CONFIRM IGNITION”. If you press the button before or after this moment, it has no effect.

Reading only once per loop iteration is fine, as long as the loop turns fast enough, without getting stuck in a delay(). As a general rule, you try to avoid delay() whenever you want to write a program that should react to external events such as button presses. See the Arduino example Blink without delay for a technique for achieving that. A finite state machine is a good way to program without delays, and fortunately you are already working with one. To detect a button press, you typically use two distinct states:

  • a “wait for button press” state that is sensitive to the button
  • a “button has been pressed” state that is reached when the user presses the button.

There can be multiple “button pressed” states if there are multiple buttons to wait for. The transition from the “waiting” state to the others is then conditioned on the button presses:

case WAIT_FOR_BUTTON_PRESS:
    if (digitalRead(button_1_pin) == LOW) {
        do_the_action_relevant_to_button_1();
        state = BUTTON_1_HAS_BEEN_PRESSED;
    } else if (digitalRead(button_2_pin) == LOW) {
        do_the_action_relevant_to_button_2();
        state = BUTTON_2_HAS_BEEN_PRESSED;
    }
    break;

Obviously you would choose state names that are more relevant for your specific project.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.