Time measurement with millis() function

Have you ever lost your umbrella while inside a public building?
My group is working on a theft-prevention umbrella module. It detects the acceleration of the earth's gravity, and if the value falls below a certain threshold, we can say it was turned over.
Everyone wishing to use the umbrella must enter the preset passcode known only to the user immediately within 20 seconds of the Z-axis register recording -8.

The issue is that I'm not sure how to embed this time measurement logic.
In this case, how should time be measured?
If the time after the Z-axis value falls below -8 exceeds 20 seconds, we must exit the loop.

My code employs the delay function, and I am aware that it has an impact on the millis function; therefore, we must carefully consider delay.

Any answer will be greatly appreciated.

Edit: Okay, I have just completed my project code.

Here is the final version. I think some students can refer to it.

Note:
-pitches.h is a standard in-built library. It's available on Arduino IDE -> File -> Example -> Digital -> toneMelody.
-millis() function worked just fine when I simply subtracted the amount of time the delay() function took.

//Play tone melody
#include "pitches.h"
int melody[] = {
  NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4
};
int noteDurations[] = {
  4, 8, 8, 4, 4, 4, 4, 4
};

//Variables
int buttonPin1 = 13;
int buttonPin2 = 12;
int buttonPin3 = 11;
int LEDGreen = 2;
int LEDRed = 3;
int vibrator = 7;
int buzzer = 8;
int dt = 100;
float ms2 = 9.80665;
float offset_voltage = 2500.0;
float z;
float zAcceleration;
unsigned long startTime;
unsigned long nowTime;
boolean stopTimer = false;

int button1Old = 1;
int button2Old = 1;
int button3Old = 1;
int button1New;
int button2New;
int button3New;
String passcode = "ABCBA";
String userInput = "";
int num_of_push = 0;
int attempt;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(buttonPin1, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);
  pinMode(buttonPin3, INPUT_PULLUP);
  pinMode(LEDGreen, OUTPUT);
  pinMode(LEDRed, OUTPUT);
  pinMode(vibrator, OUTPUT);
  pinMode(buzzer, OUTPUT);
}

void loop() {
  attempt = 3;
  stopTimer = false;
  z =  (analogRead(A2) / 1024.0) * 5.0 * 1000;
  z = z - offset_voltage;
  zAcceleration = (z / 1000.0) * ms2;
  Serial.print(" Z : ");
  Serial.print(zAcceleration);
  Serial.println(" m/s^2");
  delay(dt);
  if (zAcceleration < -8) {
    Serial.println("It seems like the umbrella was turned over.");
    startTime = millis();

    for (int j = 0; j < 25; j = j + 1) {
      digitalWrite(vibrator, HIGH);
      delay(dt);
      digitalWrite(vibrator, LOW);
      delay(dt);
    }

    while (1) {
      if (!stopTimer) {
        nowTime = millis();
        if (nowTime > startTime + 20000)
        {
          Serial.println("Time's up");
          digitalWrite(buzzer, HIGH);
          delay(15 * dt);
          digitalWrite(buzzer, LOW);
          delay(10 * dt);
          break;
        }
      } else {
        break;
      }

      button1New = digitalRead(buttonPin1);
      button2New = digitalRead(buttonPin2);
      button3New = digitalRead(buttonPin3);
      Serial.print("Button 1 = ");
      Serial.print(button1Old);
      Serial.print(", ");
      Serial.print("Button 2 = ");
      Serial.print(button2Old);
      Serial.print(", ");
      Serial.print("Button 3 = ");
      Serial.print(button3Old);
      Serial.println();
      delay(dt);
      if (button1Old == 0 && button1New == 1) {
        userInput = userInput + "A";
        num_of_push = num_of_push + 1;
      };
      button1Old = button1New;
      if (button2Old == 0 && button2New == 1) {
        userInput = userInput + "B";
        num_of_push = num_of_push + 1;
      };
      button2Old = button2New;
      if (button3Old == 0 && button3New == 1) {
        userInput = userInput + "C";
        num_of_push = num_of_push + 1;
      };
      button3Old = button3New;
      Serial.println(userInput);

      if (num_of_push == 5) {
        if (userInput == passcode) {
          stopTimer = true;
          for (int i = 0; i < 5; i = i + 1) {
            digitalWrite(LEDGreen, HIGH);
            delay(dt);
            digitalWrite(LEDGreen, LOW);
            delay(dt);
          }
          for (int thisNote = 0; thisNote < 8; thisNote++) {
            int noteDuration = 1000 / noteDurations[thisNote];
            tone(8, melody[thisNote], noteDuration);
            int pauseBetweenNotes = noteDuration * 1.30;
            delay(pauseBetweenNotes);
            noTone(8);
          }
          userInput = "";
          num_of_push = 0;
          while (zAcceleration < 8) {
            z =  (analogRead(A2) / 1024.0) * 5.0 * 1000;
            z = z - offset_voltage;
            zAcceleration = (z / 1000.0) * ms2;
          }
        } else {
          for (int i = 0; i < 5; i = i + 1) {
            digitalWrite(LEDRed, HIGH);
            digitalWrite(buzzer, HIGH);
            delay(dt);
            digitalWrite(LEDRed, LOW);
            digitalWrite(buzzer, LOW);
            delay(dt);
          }
          userInput = "";
          num_of_push = 0;
          attempt = attempt - 1;

        }
        if (attempt == 0) {
          while (zAcceleration < 8) {
            z =  (analogRead(A2) / 1024.0) * 5.0 * 1000;
            z = z - offset_voltage;
            zAcceleration = (z / 1000.0) * ms2;
            digitalWrite(buzzer, HIGH);
          }
          digitalWrite(buzzer, LOW);
          break;
        }
      }
    }
  }
}

Set a time stamp for future comparison with "now", by saving the value of millis() to an unsigned long variable when the 20 second period begins. Check for expiry by subtracting the time stamp from the current value of millis().

1 Like

BTW, "security? there's something like a bomb in my umbrella... " :slight_smile:

Can you post your full program code... need to see where this code above runs in relation to the main loop.

You don't need delay() and millis().

delay() blocks your code for a given number of milliseconds, and then continues. Probably not what you want.

millis() on the other hand gives you the number of milliseconds since startup (so is effectively a relative time). So you need to capture the time the umbrella was turned over... and then keep checking millis() until it is 20000 milliseconds larger than the turnover time.

1 Like

Yeah, that is what I want. How do I achieve that?

Could you please give me a code example?

Haha, I know. It is for a school project.

I have added the full program code. Please, take a look.

if(umbrellaTurnedOver == true)
{
  startTime = millis();
}

then in your key checking loop...

nowTime = millis();
if (nowTime - startTime > 20000)
{
  Serial.println("Time's up");
}

(edited as noted to avoid potential rollover issues)

1 Like

I didn't know the solution was so simple :relaxed: Thank you so much. Have a great day!!

But you have to change

if(umbrellaTurnedOver == true)
{
  startTime = millis();
}

to something like:

if(umbrellaTurnedOver == true)
{
  umbrellaTurnedOver = false;
  startTime = millis();
}

unless 'umbrellaTurnedOver' is the result of state change detection on the actual umbrella position. Otherwise the time stamp will be continually reset, and the alarm will not go off until the umbrella is put down somewhere.

1 Like

Yeah, I realized boolean was necessary. Thanks anyway :relaxed:.
I have just completed my code.

In future, never, ever update your original post to revise a sketch. Just post it in a new message. Doing so, introduces confusion about the replies, and makes it impossible for readers to see exactly what changes were made.

1 Like

To avoil rollover problems, there is only one way to use millis (with a few variations), and that is how it is done in the Blink Without Delay example.

This is also what aarg was talking about in reply #2.

Example:

unsigned long previousMillis;

void loop()
{
  // get a timestamp
  previousMillis = millis();

  ...

  // calculate if the time has passed
  if( currentMillis - previousMillis >= 20000)
  {
    ...
  }
}

Good: currentMillis - previousMillis
Bad: startTime + 20000

We prefer not to use a while-loop in the loop(). Let the loop() function itself run over and over again and do something when needed.

You should be friends with the compiler. You can make the compiler happy by using 'const' for values that don't change.

const int melody[] = ...
const int noteDurations[] = ...
const int buttonPin1 = 13;

and so on.

I personally prefer that variables contain real units. For example "Volt" and not "milliVolt".
You could have the variable 'z' use "Volt".

The digitalRead() function returns HIGH or LOW. However, you compare that with 1 or 0. It returns HIGH or LOW as you can read in the reference: https://www.arduino.cc/reference/en/language/functions/digital-io/digitalread/

A if-statement is not followed by a ;
You can remove those.

if (button1Old ... {
  };                                  // <-- just a '}' will do
1 Like

Okay, my apologies.
I will keep that in mind from now on.
This was my very first post on this forum.

I appreciate your practical advice.