Stuck counting and using millis()

So I am making a drag tree using an old stop light and my arduino uno. Red light comes on for 3 seconds, then yellow light for 5 seconds, and then go on green light. I started just using outputs and delay() and it worked fine. But now I am trying to integrate photoresistors to use as trip wires to see which truck jumps the line. Because I needed to continually monitor the resistors I had to switch to using millis() and counting to run the light. This is running well. But I cannot figure out how to integrate the starting button and the photo resistors. Here is a short run down of how I want it to work and my code. If anyone can give me some help or ideas that would be great.

Press button to start
Check to see if laser is still lit
Start red light (keep checking for laser)
Start yellow light (keep checking for laser)
Start green light

I dont understand how to have it loop to look for the button push, and then once it sees the button loop to keep looking for the laser without looking for the button anymore.

Keep in mind I have almost no programming experience.

Here's my code:

int ledRed =8;
int ledStateRed = LOW;
unsigned long previousMillisRed = 0;
long OnTimeRed = 0;
long OffTimeRed = 3000;
int ledYellow = 9;
int ledStateYellow = LOW;
unsigned long previousMillisYellow = 0;
long OnTimeYellow = 3000;
long OffTimeYellow = 8000;
int ledGreen = 10;
int ledStateGreen = LOW;
unsigned long previousMillisGreen = 0;
long OnTimeGreen = 8000;
long OffTimeGreen = 10000;
int Buzzer = 11;
int BuzzerState = LOW;
unsigned long previousBuzzer = 0;
long OnTimeBuzzer = 8000;
long OffTimeBuzzer  = 9000;
int voltage = 0;


void setup()	{
  Serial.begin(9600);
  pinMode(ledRed, OUTPUT);
  pinMode(ledYellow, OUTPUT);
  pinMode(ledGreen, OUTPUT);
  pinMode(Buzzer, OUTPUT);
}
void loop()	{
  voltage = analogRead(0) * .004888;
  Serial.println(voltage);
  
  if(voltage >= 4){
  unsigned long currentMillis = millis();
  
  if((ledStateRed == LOW)  && (currentMillis >= OnTimeRed))	{
    ledStateRed = HIGH;
    previousMillisRed = currentMillis;
    digitalWrite(ledRed, ledStateRed);
  }
  if ((ledStateRed = HIGH) && (currentMillis >= OffTimeRed))	{
    ledStateRed = LOW;
    previousMillisRed = currentMillis;
    digitalWrite(ledRed, LOW);
  }
  if((ledStateYellow == LOW) && (currentMillis >= OnTimeYellow))	{
    ledStateYellow = HIGH;
    previousMillisYellow = currentMillis;
    digitalWrite(ledYellow, ledStateYellow);
  }  
  if((ledStateYellow == HIGH) && (currentMillis >= OffTimeYellow))	{
    ledStateYellow = LOW;
    previousMillisYellow = currentMillis;
    digitalWrite(ledYellow, ledStateYellow);
  }
  if((ledStateGreen == LOW) && (currentMillis >= OnTimeGreen))	{
    ledStateGreen = HIGH;
    previousMillisGreen = currentMillis;
    digitalWrite(ledGreen, ledStateGreen);
  }  
  if((ledStateGreen == HIGH) && (currentMillis >= OffTimeGreen))	{
    ledStateGreen = LOW;
    previousMillisGreen = currentMillis;
    digitalWrite(ledGreen, ledStateGreen);
  }
  if((BuzzerState == LOW) && (currentMillis >= OnTimeBuzzer))	{
    BuzzerState = HIGH;
    previousBuzzer = currentMillis;
    digitalWrite(Buzzer, BuzzerState);
  }
  if((BuzzerState == HIGH) && (currentMillis >= OffTimeBuzzer))	{
    BuzzerState = LOW;
    previousBuzzer = currentMillis;
    digitalWrite(Buzzer, BuzzerState);
  }
}
}

the example for mills timer is

    unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > interval) {
 //do something here
       previousMillis = currentMillis;}

remember that millis is not being reset so it counts up to some huge number like 4 million

if(voltage >= 4){
unsigned long currentMillis = millis();

if((ledStateRed == LOW) && (currentMillis >= OnTimeRed))

current millis is equal to millis which might be 2 million then you check to see if its bigger than OnTimeRed

the correct way is to look at the difference between current and previous before checking against OnTimeRed

I dont understand how to have it loop to look for the button push, and then once it sees the button loop to keep looking

You could put a while(buttonNotPressed) loop in the setup(). Then when the button is pressed, it will continue out of setup() and into the loop().
You could also turn your red light on in the setup() just after the button is pressed.
Then in your loop(), check for laser break over and over, while waiting for milli time to turn on the next light. If a laser break is detected, call a function, that will not return to the loop(), therefore no further lights will come on, and you can process your alert as desired in that function.

You have a buzzer in the code. What does the buzzer do? You need to describe how you want your device to interact with the real world. Presumably people will place vehicles on a starting line? What information would you like output? You need to describe things including when and how people place the trucks.

I'd cycle through the lights while monitoring the photoresistors. I need to capture the time that three separate events occur, when the light turns green and when each truck leaves.

Truck leave time - green light time == reaction time

A negative reaction time means the truck left early.

Your state machine is mostly sequential, so it’s easier to use a single state machine using a switch statement, then transition to the next state in the sequence.

If someone jumps the gun, you can then just change the state machine directly before each run of the state machine in the loop.

I think you might want to do something like this (untested)

// Constants
const int ledRed = 8;
const int ledYellow = 9;
const int ledGreen = 10;
const int Buzzer = 11;

const long OnTimeRed = 3000;
const long OnTimeYellow = 5000;
const long OnTimeGreen = 2000;
const long OnTimeBuzzer = 1000;
const long FalseStartTime = 3000;

// state machine states
enum {IDLE, RED, YELLOW, GREEN, FALSE_START}

// global variables
int lightState = IDLE;
unsigned long previousMillis = 0;

void setup() {
	Serial.begin(9600);
	pinMode(ledRed, OUTPUT);
	pinMode(ledYellow, OUTPUT);
	pinMode(ledGreen, OUTPUT);
	pinMode(Buzzer, OUTPUT);
}
void loop() {
	unsigned long currentMillis = millis();
	int voltage = analogRead(0) * .004888;
	Serial.println(voltage);
	
	if (truck_jumps_gun) { // this line is pseudocode. You need to modify to read your IR or whatever
		// signal false start
		ledState = FALSE_START;
		digitalWrite(ledRed, HIGH);
		digitalWrite(ledYellow, HIGH);
		digitalWrite(ledGreen, HIGH);
		digitalWrite(buzzer, HIGH);
	}

	// look for a button push (voltage transition edge)
	if (voltage >= 4 && lastVoltage <= 4) {
		ledState = RED;
		previousMillis = currentMillis;
		digitalWrite(ledRed, HIGH);
	}
	lastVoltage = voltage;

	// run the state machine
	switch (lightState) {
	case IDLE:
		break;
	case RED:
		if (currentMillis - previousMillis > OnTimeRed)	{
			ledState = YELLOW;
			previousMillis = currentMillis;
			digitalWrite(ledRed, LOW);
			digitalWrite(ledYellow, HIGH);
		}
		break;
	case YELLOW:
		if (currentMillis - previousMillis > OnTimeYellow) {
			ledState = GREEN;
			previousMillis = currentMillis;
			digitalWrite(ledYellow, LOW);
			digitalWrite(ledGreen, HIGH);
			digitalWrite(Buzzer, HIGH);
		}  
		break;
	case GREEN:
		if (currentMillis - previousMillis > OnTimeGreen) {
			ledState = IDLE;
			digitalWrite(ledGreen, LOW);
		}  
		if (currrentMillis - previousMillis > OnTimeBuzzer) {
			digitalWrite(Buzzer, LOW);
		}
		break;
	case FALSE_START:
		if (currentMillis - previousMillis > FalseStartTime) {
			ledState = IDLE;
			digitalWrite(ledRed, LOW);
			digitalWrite(ledYellow, LOW);
			digitalWrite(ledGreen, LOW);
			digitalWrite(buzzer, LOW);
		} 
		break;
	}
}

Have a look at how the code is organized in several things at a time. Notice how there is very little in loop().

The same approach is in planning and implementing a program

If you organize your code into functions it will be easy to make it do what you want - and more especially, to see clearly how to get it to do that. The key thing is to separate the control logic from the details of the actions.

...R

DOOM:
if (currentmillis - previousmillis >= time) {
previousmillis += time;

if (currentmillis - previousmillis >= time) {
previousmillis = currentmillis

use simply

The first of these examples is the better one. It avoids accumulating small timing errors. There is a long discussion about it in several things at a time.

...R

More than one way to do soft delays. I prefer a simpler to read version.

In Loop()
if(nextMilliLedToggle<nowMilli) toggleLed();

Then in the function toggleLed() I set the next time to run this function (can be based on led state)
nextMilliLedToggle = nowMilli+99;
if (digitalRead(led) == LOW){
nextMilliLedToggle = nowMilli+4999;
}

     if(nextMilliLedToggle<nowMilli) toggleLed();

Is not used because it won’t work when the counters rollover!

Mark

Thanks Mark, I can see your point.

In that case
if (currentmillis - previousmillis >= time) {
previousmillis += time;

Would also have the same problem wouldn't it?

How many days running without a reset would it take for unsigned millis to rollover ? I seem to remember something about two weeks ?

In the case of this thread, I think the OP will reset every few minutes tho. In fact, the OP may want to use the button to initiate a reset.

What do you recommend for a processor that runs for a really long time without a reset?

Thanks, Jack
I hope we are not to far off topic.

No it wont.
The math is done with unsigned longs.

See:
[url=http://http://www.gammon.com.au/forum/?id=12127]http://www.gammon.com.au/forum/?id=12127[/url]

No that works properly when millis() rollsover thanks to the magic of binary integer maths. Think about it using byte values.
237 - 187 = 50
32 - 237 = 50

…R
(fingers crossed that I have it right)

Ok guys, I think I got it now. Thanks.

BTW thanks for the URL Larry, but it had a problem. I think the one you were suggestion is Gammon Forum : Electronics : Microprocessors : millis() overflow ... a bad thing?
for anyone else as interested as I am to see it.

I am having huge problems with URLs and this new 8.4 iPad iOS. >:(

"new 8.4 iPad iOS"
Did you mean "new 8.4 iPad POS" ? 8)

8.4 Sh*t

Thanks for the input everyone. I ended up just doing the currentMillis - millis() and just set it so every time I push the start button it updates currentMillis, the code worked flawlessly. But the actual set up of the light was a total bust. The photo resistors were too finnicky with sun and shade and smoke and dust. Thanks again!