If statement only run once in Loop(), and second if statement never run

Hello everybody,
Relatively new to C++ and Arduino, but have experience in Java. Helping my partner with a uni project that involves coding and as I have some experience, I'm helping her out. I'm working with an Arduino Uno R3 with two vibration sensors and a 24-LED NeoPixel in TinkerCAD.

The code below (as far as I am aware) checks every 100ms to detect if either one of two buttons, vibSensorL or vibSensorR (buttons are a substitute for vibration sensors) is pressed. If pressed, two lights on a 24-LED NeoPixel will illuminate. With each button press, the next two lights will illuminate in a clockwise fashion. However, the problem arises in that the button can be pressed and held down, causing two lights to be illuminated clockwise every 100ms. I'm attempting to make it so that no matter how long the button is pressed down, it will only light up two lights, and to illuminate the next two lights in the sequence, the button has to be depressed and repressed again. I've tried to make a boolean and change it to true after pressing the button, but the problem is it needs to be set to false again after the button is released (read the part below to see why I can't change the variable to false in an else statement).

In addition to this problem, it seems that the bottom if statement (or else if/else) does not run on its own after 10 seconds, however, this code is executed only when one of the buttons is pressed, even though it's not in the if statement that detects when a button is pressed?? This is starting to drive me insane, from my understanding, the bottom if statement should be run regardless of whether or not the button is pressed as it is in the loop() method and being checked every 100ms if the conditions are true?

#include <Adafruit_NeoPixel.h>
#define PIN 9
#define NUMPIXELS 24
Adafruit_NeoPixel strip(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int vibSensorLPin = 2;
int vibSensorRPin = 4;

int pressCount = -2;

uint32_t white = strip.Color(254, 254, 254);
uint32_t red = strip.Color(255, 0, 0);
uint32_t green = strip.Color(0, 255, 0);

unsigned long dangerTime = (10 * 1000); //max time that can elapse before NeoPixel turns red.
unsigned long timerCount = 0; //timer starts from 0ms.

void setup()
{
  strip.begin();
  strip.show();
  pinMode(vibSensorLPin, INPUT);
  pinMode(vibSensorRPin, INPUT);
  
  Serial.begin(9600);
}

void loop()
{
  delay(100);
  
  timerCount = millis();
  bool vibration = digitalRead(vibSensorLPin) || digitalRead(vibSensorRPin);

  if(vibration)
  {
    //DETECTS EITHER BUTTON BEING PRESSED
    if(pressCount <= 23)
    {
      pressCount = pressCount + 2;
    }

    if(pressCount >= 0 && pressCount <= 15)
    {
      strip.setPixelColor(pressCount, red);
      strip.setPixelColor(pressCount + 1, red);
    }

    if(pressCount >= 16 && pressCount <= 23)
    {
      strip.setPixelColor(pressCount, green);
      strip.setPixelColor(pressCount + 1, green);
    }
    strip.show();
  }
  
  else if((timerCount >= dangerTime) && pressCount <=15)
  {
    //MAKES ALL LEDs RED IF NOT ENOUGH VIBRATIONS ARE DETECTED AFTER 10 SECONDS.
    strip.fill(red, 0, 24);
  }
  
}

Any insight at all is appreciated since I have been struggling with this all evening.

Hi calwads,

the most simply method to analyse this is to add debugout to the serial monitor that prints the values of the uses variables or if a condition is true
This will guide you quickly to the place where your code does other things than you expect.

millis() starts counting up right on power-on pressing the reset-button or right after uploading the code
Leaving...
Hard resetting via RTS pin...
so after 10 seconds millis() is greater than 10000 and will count up for the next 49 days to values greater and greater

is this exactly what you want to do
Power up the microcontroller and within 10 seconds enough button-presses must have occured and if not all LEDs should turn red?
best regards Stefan

I believe you need to compare current versus previous vibration state along with checking the current time against your 100 ms and 10 s requirements. Is something like the following what you are looking for?

// Add global variables
int previousVibration = false
int currentVibration = false

void loop() {
   static unsigned long previousCheckTime = millis();
   static unsigned long previousDangerTime = millis();
   unsigned long currentTime = millis();
   if (currentTime - previousDangerTime >= 10 * 1000) {  // check every 10 s
      previousDangerTime = currentTime;
      previousCheckTime = currentTime;
      // make all pixels red
   } else if (currentTime - previousCheckTime >= 100) {  // check every 100 ms
      previousCheckTime = currentTime;
      currentVibration = digitalRead(vibSensorLPin) || digitalRead(vibSensorRPin);
      if (currentVibration != previousVibration) {  // state change
         if (currentVibration) {
            // do vibration code
         }
         previousVibration = currentVibration;
      }
   }
}

Also, you may need to learn from the State Change example in the Arduino IDE because it appears that you want to know when a button BECOMES pressed (or released) and not when the button IS pressed (or released). You may need to learn about debouncing as well.

You have not shown your wiring. Your code implies that you have pullup or pulldown resistors. If not, the code in the setup() function appears wrong.

StefanL38:
Hi calwads,

the most simply method to analyse this is to add debugout to the serial monitor that prints the values of the uses variables or if a condition is true
This will guide you quickly to the place where your code does other things than you expect.

millis() starts counting up right on power-on pressing the reset-button or right after uploading the code
Leaving...
Hard resetting via RTS pin...
so after 10 seconds millis() is greater than 10000 and will count up for the next 49 days to values greater and greater

is this exactly what you want to do
Power up the microcontroller and within 10 seconds enough button-presses must have occured and if not all LEDs should turn red?
best regards Stefan

Thanks for your reply Stefan,
Correct, I want to countdown from 10 seconds (will eventually be 2 hours, using 10 seconds as a test) after the unit is powered on, then at the end of the 10 seconds, if enough vibrations have been detected, the timer will reset to zero and start over. If not enough vibrations have been detected after 10 seconds, all of the LEDs turn red. Should the millis() be put in setup() to start the timer when powered up, then called again after the timer has reset after two hours?
Cheers,
Cal

woolsey:
I believe you need to compare current versus previous vibration state along with checking the current time against your 100 ms and 10 s requirements. Is something like the following what you are looking for?

// Add global variables

int previousVibration = false
int currentVibration = false

void loop() {
  static unsigned long previousCheckTime = millis();
  static unsigned long previousDangerTime = millis();
  unsigned long currentTime = millis();
  if (currentTime - previousDangerTime >= 10 * 1000) {  // check every 10 s
      previousDangerTime = currentTime;
      previousCheckTime = currentTime;
      // make all pixels red
  } else if (currentTime - previousCheckTime >= 100) {  // check every 100 ms
      previousCheckTime = currentTime;
      currentVibration = digitalRead(vibSensorLPin) || digitalRead(vibSensorRPin);
      if (currentVibration != previousVibration) {  // state change
        if (currentVibration) {
            // do vibration code
        }
        previousVibration = currentVibration;
      }
  }
}

Hi woolsey, thanks for your reply.
I've rewritten the code and included two state boolean variables for comparison in an if statement. Since I made this post I've also hooked a proper Arduino up as opposed to using a simulator. Using the state checks, only the first four lights illuminate (the first two illuminate on startup without vibration input, yet to figure that out), and the 3rd+4th lights also light up when a vibration is detected from the vibration sensors. Any more vibrations applied to the sensor do not illuminate sequential lights, so I assume that either the currentState or previousState variables are stuck to a value that will never allow the code to execute the if statement again? I'll include my updated code below (some variable names are updated).
Cheers,
Cal

#include <Adafruit_NeoPixel.h>
#define PIN 11
#define NUMPIXELS 24
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

//PINS
const int vibSensor1Pin = 4;
const int vibSensor2Pin = 7;

//PIN STATES
int vibState1 = 0;
int vibState2 = 0;

int counter1 = 0;
int counter2 = 0;

int currentState = 0;
int previousState = 0;

//VIBRATION COUNTER
int vibCount = -2;

//COLOUR DECLARATION
uint32_t white = pixels.Color(254, 254, 254);
uint32_t red = pixels.Color(255, 0, 0);
uint32_t green = pixels.Color(0, 255, 0);

//TIMER SETTINGS
unsigned long dangerTime = (10 * 1000);
unsigned long timerCount = 0;

void setup()
{
  delay(50);
  pixels.begin();
  pixels.show();
  pinMode(vibSensor1Pin, INPUT);
  pinMode(vibSensor2Pin, INPUT);
  pinMode(NUMPIXELS, OUTPUT);
  
  Serial.begin(9600);
}

void loop()
{
  //pixels.clear();
  timerCount = millis();

  vibState1 = digitalRead(vibSensor1Pin);
  vibState2 = digitalRead(vibSensor2Pin);
  
  if(vibState1 == 1 || vibState2 == 1)
  {
    currentState = 1;
  }
  else
  {
    currentState = 0;
  }

  if(currentState != previousState)
  {
    if(vibCount <= 23)
    {
      vibCount = vibCount + 2;
    }

    if(vibCount >= 0 && vibCount <= 15)
    {
      pixels.setPixelColor(vibCount, white);
      pixels.setPixelColor(vibCount + 1, white);
    }

    if(vibCount >= 16 && vibCount <= 23)
    {
      pixels.setPixelColor(vibCount, green);
      pixels.setPixelColor(vibCount + 1, green);
    }
    pixels.show();
    Serial.println(currentState);
  }
  
  previousState = currentState;
  
}

Thanks for your reply, vaj4088.
My wiring (reconstructed in TinkerCAD). Note that I have had to replace the vibration sensor with an IR sensor in TinkerCAD, as they do not have vibration sensors available. For some reason activating the IR sensors in TinkerCAD does not activate the NeoPixel, even though the code and wiring is the exact same.

I've read the IDE here and altered my code to be similar as in the IDE, however a vibration of either sensor no longer illuminates any lights at all. Here is my altered code:

#include <Adafruit_NeoPixel.h>
#define PIN 11
#define NUMPIXELS 24
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

//PINS
const int vibSensor1Pin = 4;
const int vibSensor2Pin = 7;

//PIN STATES
int vibState1 = LOW;
int vibState2 = LOW;

int currentState = LOW;
int previousState = LOW;

//VIBRATION COUNTER
int vibCount = -2;

//COLOUR DECLARATION
uint32_t white = pixels.Color(254, 254, 254);
uint32_t red = pixels.Color(255, 0, 0);
uint32_t green = pixels.Color(0, 255, 0);

//TIMER SETTINGS
unsigned long dangerTime = (10 * 1000);
unsigned long timerCount = 0;

void setup()
{
  delay(50);
  pixels.begin();
  pixels.show();
  pinMode(vibSensor1Pin, INPUT);
  pinMode(vibSensor2Pin, INPUT);
  pinMode(NUMPIXELS, OUTPUT);
  
  Serial.begin(9600);
}

void loop()
{
  //pixels.clear();
  timerCount = millis();

  vibState1 = digitalRead(vibSensor1Pin);
  vibState2 = digitalRead(vibSensor2Pin);
  
  if(vibState1 == HIGH || vibState2 == HIGH)
  {
    currentState = HIGH;
  }
  else
  {
    currentState = LOW;
  }

  if(currentState != previousState)
  {
    if(currentState == HIGH)
    {
      if(vibCount <= 23)
      {
        vibCount = vibCount + 2;
      }
  
      if(vibCount >= 0 && vibCount <= 15)
      {
        pixels.setPixelColor(vibCount, white);
        pixels.setPixelColor(vibCount + 1, white);
      }
  
      if(vibCount >= 16 && vibCount <= 23)
      {
        pixels.setPixelColor(vibCount, green);
        pixels.setPixelColor(vibCount + 1, green);
      }
      pixels.show();
    }
    
    else
    {
    
    }

    delay(50);
   
  }
  
  previousState = currentState;
  
}

Thanks for the help,
Cal

Seems to me you probably don’t have your buttons wired correctly.

Do you have pull ups or pull down resistors?

You will need some if you are not using internal pull-ups...

pinMode(vibSensor1Pin, INPUT);
pinMode(vibSensor2Pin, INPUT);

Also what is this doing...

pinMode(NUMPIXELS, OUTPUT);

Did you perhaps mean PIN and not NUMPIXELS?
I suspect the neopixel library sets the CORRECT pin to output when you do...

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

So doing it yourself (even if it was on the correct pin) is superfluous.

Thanks for the reply pcbbc,
Unfortunately, my already limited knowledge of coding dwarfs my knowledge of electronics, so I was hoping I wouldn't have to do anything too complex (meaning anything) on the electronics side of things :slight_smile:
A quick Google and a guess says my wiring has a single 100 ohm resistor positioned in a pull-down configuration (if that is the correct terminology!). If you check out my reply to a post directly above your post, I have a screenshot of my wiring that I recreated in TinkerCAD.

pinMode(NUMPIXELS, OUTPUT);

Whoops, this was a remnant of some code my partner's tutor emailed to her.

Thanks,
Cal