Noob, FSM timings help

Hey guys,

I spent the last few days really examining closely the FSM code for the traffic light project i'm using as a learning tool and I think I have got most of it down. I am running into a problem in my code where i'm having trouble setting the time for a particular function and on one of the other functions I don't understand why it is creating the desired effect.

In normFunction the timing is supposed to go greenlight for 5 seconds, yellow light for three, red light 5 and then repeat indefinitely (or until the failFunction is called for) currently the code only has greenLight written into it but read on about the failFunction redLight and you'll see why i'm confused.

I'm not sure how to seperate each timed item in the void normFunction to achieve the desired effect! Also, the last line of code in the normFunction is identical to the timing line in failFunction... failFunction works as desired (red light on 1 second, off 1 second, and repeats indefinitely or until normal is called for, but in the current code the greenLight does not do the same. Any explanation?

Help on how to code the timing in normFunction would be awesome!

Here is the code I have written up

int redLight = 5;
int yellowLight = 4;
int greenLight = 3;
int failButton = 6;
int normButton = 7;
int failLed = 8;
int normLed = 12;
int normState;
int failState;
int state;
unsigned long startTime;

void setup()
{
  Serial.begin(9600);
  pinMode(redLight, OUTPUT);
  pinMode(yellowLight, OUTPUT);
  pinMode(greenLight, OUTPUT);
  pinMode(normButton, INPUT_PULLUP);
  pinMode(failButton, INPUT_PULLUP);
  pinMode(failLed, OUTPUT);
  pinMode(normLed, OUTPUT);
}

void loop()
{
  int normButtonState = digitalRead(normButton);
  {
    if (normButtonState)
    {
      if (normButtonState != normState)
      {
        state=0;
        startTime = millis();
      }
    }
  }
  normState = normButtonState;
  //above section of code defines normal state or state "0"
  // it does not define what happens in state 0
  
  int failButtonState = digitalRead(failButton);
  {
    if (failButtonState)
    {
      if (failButtonState != failState)
      {
        state = 1;
        startTime = millis();
      }
    }
  }
  failState = failButtonState;
  //above section of code defines state 1 aka fail mode
  //not what fail mode actually does
  
  if (state == 0)
  {
    normFunction();
  }
  else if (state == 1)
  {
    failFunction();
  }
}

//above section of code sets which function to run depending upon
//current state in which board is running

void normFunction()
{
  Serial.println("Normal Mode Selected");
  digitalWrite(normLed, HIGH);
  digitalWrite(failLed, LOW);
  if (millis() - startTime >= 3000UL);
  {
    digitalWrite(greenLight, !digitalRead(greenLight));
    startTime = millis();
    }
}
void failFunction()
{
  Serial.println("Fail Mode Selected");
  digitalWrite(failLed, HIGH);
  digitalWrite(normLed, LOW);
  digitalWrite(greenLight, LOW);
  digitalWrite(yellowLight, LOW);
  if (millis() - startTime >= 1000UL)
  {
    digitalWrite(redLight, !digitalRead(redLight));
    startTime = millis();
  }
}

Any help from you guys help me learn more! Trying to do it on my own but sometimes all the reading in the world just can't help me until I see it.

Mike

This is my third attempt to write a reply.

I guess your code is a classic case of "if I was going there I wouldn't start from here". I don't see a simple one or two line change that will sort the problem.

I think you need to simplify the reading of the buttons and saving of states. It probably just needs two lines of code. The state of the buttons and the states of the lights are not the same thing.

Then you could put into your function normFunction() the code to check the saved state and decide whether it should activate .

The real problem is that when I glance through your code I don't get a sense of how it is supposed to work.

You probably need a few state variables to keep track of the progression through the light sequence - redState, orangeState, greenState and corresponding intervals (numbers of millisecs) for the time keeping.

A good way to design a program for this sort of project (or for any project) is to jot down all the steps/actions that need to happen for the project to work. Then create a small function for each action and call all the functions from loop(). Try to keep all the decision making code out of loop() to make the code easier to follow.

...R

you were almost there.

in the normal mode, you are cycling the three lights sequentially. Use your millis() timer to switch the lights (LEDs)

something like:

void normFunction()
{
  Serial.println("Normal Mode Selected");
  digitalWrite(normLed, HIGH);
  digitalWrite(failLed, LOW);
  unsigned long elapsedTime = millis() - startTime;
  if (elapsedTime < 5000UL)
  {
    digitalWrite(greenLight, HIGH);
    digitalWrite(yellowLight, LOW);
    digitalWrite(redLight, LOW);
  }
  else if (elapsedTime < 8000UL)
  {
    digitalWrite(greenLight, LOW);
    digitalWrite(yellowLight, HIGH);
    digitalWrite(redLight, LOW);
  }
  else if (elapsedTime < 13000UL)
  {
    digitalWrite(greenLight, LOW);
    digitalWrite(yellowLight, LOW);
    digitalWrite(redLight, HIGH);
  }
  else
  {
    startTime = millis();
  }
}

As Robin 2 hinted, half an hour with a pencil and paper writing a list of all the states your system can be in will save you from 'spaghetti code' and much associated angst. Also look up the switch-case construct. Within each state, you set the lights for that state and wait until the time has elapsed, before moving on to the next state.

BulldogLowell:
you were almost there.

in the normal mode, you are cycling the three lights sequentially. Use your millis() timer to switch the lights (LEDs)

something like:

void normFunction()

{
  Serial.println(“Normal Mode Selected”);
  digitalWrite(normLed, HIGH);
  digitalWrite(failLed, LOW);
  unsigned long elapsedTime = millis() - startTime;
  if (elapsedTime < 5000UL)
  {
    digitalWrite(greenLight, HIGH);
    digitalWrite(yellowLight, LOW);
    digitalWrite(redLight, LOW);
  }
  else if (elapsedTime < 8000UL)
  {
    digitalWrite(greenLight, LOW);
    digitalWrite(yellowLight, HIGH);
    digitalWrite(redLight, LOW);
  }
  else if (elapsedTime < 13000UL)
  {
    digitalWrite(greenLight, LOW);
    digitalWrite(yellowLight, LOW);
    digitalWrite(redLight, HIGH);
  }
  else
  {
    startTime = millis();
  }
}

Thank you so much BulldogLowell!!! Now that I see it broken down it makes a ton of sense! This is fantastic! I’m going to continue to study this section of the code to understand what and why its doing what is doing.

I hope to keep learning from you!

I find it easier to think of millis() as a timestamp.

this assigns the current time (millis()) to your startTime:

startTime = millis();

then you look at how much time has expired since that timestamp:

  unsigned long elapsedTime = millis() - startTime;

millis() returns the current time, remember while you fixed startTime when you started the counter:

void normFunction()
{
  Serial.println("Normal Mode Selected");
  digitalWrite(normLed, HIGH);
  digitalWrite(failLed, LOW);
  unsigned long elapsedTime = millis() - startTime;  //<<<<<How much time has elapsed?
  if (elapsedTime < 5000UL) //  <<<<<< if the total elapsed time is under 5 seconds....
  {
    digitalWrite(greenLight, HIGH);
    digitalWrite(yellowLight, LOW);
    digitalWrite(redLight, LOW);
  }
  else if (elapsedTime < 8000UL)//<<<<<<<<<<<< else if the total elapsed time is under 8 seconds....
  {
    digitalWrite(greenLight, LOW);
    digitalWrite(yellowLight, HIGH);
    digitalWrite(redLight, LOW);
  }
  else if (elapsedTime < 13000UL)//<<<<<<<<<<<<< else if the total elapsed time is under 13 seconds....
  {
    digitalWrite(greenLight, LOW);
    digitalWrite(yellowLight, LOW);
    digitalWrite(redLight, HIGH);
  }
  else
  {
    startTime = millis(); //<<<<<<<<<<< once timer reaches the full time, reset the timer... it will start again at the top the next time normFunction() is called
  }
}

look at if / else if function to better understand how they evaluate if you don’t already know.

Amazing how timing works! I had already taken the next step and changed the timing in the code to make a more realistic traffic light.

I.E. One minute green, ten second yellow, one minute red.

Once I saw the code and understood how the timining "stacks", it was easy. 60000 for one minute, add 10000 for ten seconds for the yellow, and add 60000 for the red, total time start to finish, 130000 millis and back to repeat.

I'm so happy I learned this, and I can't thank you enough for showing me how. Your quote was "I was almost there" I just needed that extra step!!

Im certainly going to expand on this project to continue learning,

I.E. adding a photo sensor to trigger a light change (using a pen light to simulate a car approaching the intersection), adding a pedestrian protocol, etc.

All in the interest of learning,

Cant thank you enough!

Mike

It's great to learn isn't it....

In either this or another thread of yours I mentioned this write-up on FSMs. Not sure if you'vre read it yet, but if you are....

certainly going to expand on this project to continue learning,

.... I think you'll need to embrace the FSM as a formal thing or your project's states will get too complex to keep track of.

Visual Paradigm is one of a number of tools which helps in drawing up state diagrams.

I think I mentioned my turnstile thread before, not sure.

mgttrottier:
All in the interest of learning,

I have yet to see a really thorough example of a traffic signal here on the site. That is, both sides of the intersection and with a pedestrian pushbutton ‘walk’ sequence.

If you think about your states (and you did a good job identifying your two states here) you can add another state for the white led… the walk sign, for one or both sides of the intersection.

fun stuff, yeah!

That is, both sides of the intersection and with a pedestrian pushbutton 'walk' sequence.

Not to mention a phase for turning across the traffic (right-turn for me, UK and Aussie/NZ(?), we drive on the left; left-turn to most of the world). And sensors in the road to determine if the phasing should be changed under certain traffic conditions).

I have an old Lego intersection board. I’ll build on that and keep Bulldog and Jimbo on my list for when I need help.

I’ll toss it out there, here is my challenge to myself…

Four way intersection, with pedestrian crossing, and directional turning on green arrow. (In the US thats a left on green arrow)

all LEDs must be present (not a mock up), and has to be written in a state machine without any delay(xx)

Starting Monday 8/18/2014 and has to be complete by 8/22/14

Sounds good to you fellas?

mgttrottier: I'll toss it out there, here is my challenge to myself...

what, is that glove on the floor?

BulldogLowell:

mgttrottier:
I’ll toss it out there, here is my challenge to myself…

what, is that glove on the floor?

It might as well be. But i’m up for it. I’ll make it the prettiest f****n FSM traffic light with a Lego set that operates on true US traffic rules.

Of course based on every I have learned, so sounding cocky sounds good right now, but wait until I get myself stuck in a corner.

wait until I get myself stuck in a corner

Draw up a state diagram and / or table showing every possible Current state / Event / Transition / New state, as in my turnstile thread before you think of code. Then you won’t get stuck in a corner: the coding is almost trivial compared to the thinking through of the states.

JimboZA:

wait until I get myself stuck in a corner

Draw up a state diagram and / or table showing every possible Current state / Event / Transition / New state, as in my turnstile thread before you think of code. Then you won't get stuck in a corner: the coding is almost trivial compared to the thinking through of the states.

And once you've got the states sorted, you've virtually written your code.

By the way, 'turn lights', here are called 'filter lights' and can be for either left or right turning traffic. I know of one set of lights with 3 green aspects on the same pole, although two of them always come on at the same time.