Go Down

Topic: Smart traffic light project (Read 477 times) previous topic - next topic

opportunejaw

Im working on a project for school and im wondering if this would be possible to do on arduino and if so how would i approach it. Ive included a flow chart on how i want the project to run. Thanks!

Delta_G

Yes, that is appropriate for an Arduino.  You would begin your approach by learning the basics of how to code with Arduino.  This probably means that this particular project goes on the back burner for a little bit while you learn to make leds light up and how to handle timing without blocking execution and stuff like that.  Either way, the first step is for *YOU* to do *something* more than just post your assignment to an internet forum. 
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

opportunejaw

Yes, that is appropriate for an Arduino.  You would begin your approach by learning the basics of how to code with Arduino.  This probably means that this particular project goes on the back burner for a little bit while you learn to make leds light up and how to handle timing without blocking execution and stuff like that.  Either way, the first step is for *YOU* to do *something* more than just post your assignment to an internet forum.  
I have started it actually, i just wanted to see if anyone else would approach it differently. Im currently stuck on the car waiting decision block, right now i just want the first sensor to recognize something in front of it when green2 light is on. Heres my code

Delta_G

OP's code:

Code: [Select]
int red1 = 7;
int yellow1 = 6;
int green1 = 5;
int red2 = 4;
int yellow2 = 3;
int green2 = 2;

const int trigPin = 12;
const int echoPin = 11;

long duration;
int distance;

long previousMillis = 0;
long interval = 1000;

void setup() {
  pinMode(red1, OUTPUT);
  pinMode(yellow1, OUTPUT);
  pinMode(green1, OUTPUT);
  pinMode(red2, OUTPUT);
  pinMode(yellow2, OUTPUT);
  pinMode(green2, OUTPUT);

  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}
void loop() {
  unsigned long currentMillis = millis();
  static bool carInRangeLast;

  bool carInRange = distance < 10;
 
  //set-up to read distance at sensor 1
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = duration * 0.034 / 2;

  carInRangeLast = carInRange;

  bool changeLights = (carInRange == true) && (currentMillis - previousMillis > interval);

    //neutral period
    digitalWrite(red1, HIGH);
    digitalWrite(yellow1, LOW);
    digitalWrite(green1, LOW);
    digitalWrite(red2, HIGH);
    digitalWrite(yellow2, LOW);
    digitalWrite(green2, LOW);
    delay(3000);
   
  if(changeLights == true){
    digitalWrite(red1, HIGH);
    digitalWrite(yellow1, LOW);
    digitalWrite(green1, LOW);
    digitalWrite(red2, LOW);
    digitalWrite(yellow2, HIGH);
    digitalWrite(green2, LOW);
    delay(3000);
  } else {
    digitalWrite(red1, HIGH);
    digitalWrite(yellow1, LOW);
    digitalWrite(green1, LOW);
    digitalWrite(red2, LOW);
    digitalWrite(yellow2, LOW);
    digitalWrite(green2, HIGH);
    delay(5000);
  }
}
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

Delta_G

You seem to have combined some non-blocking techniques:

Code: [Select]
bool changeLights = (carInRange == true) && (currentMillis - previousMillis > interval);


With prodigious use of the delay function sprinkled in.  The delay function tells the processor to bury its head in the sand for a length of time.  During that time none of the rest of your code, save any interrupts, is being run.  So your code can't look for cars or read sensors or anything during that time. 

This isn't going to work until you get all the delay calls out.  There are a couple of short delayMicroseconds calls when you read the ping sensor and those can stay.  But all the ones that wait for several seconds at a time need to go. 

Go have a look at the Blink Without Delay example or any of the thousands of fine tutorials on the topic all over the net and learn how to handle timing with non-blocking code.  Here's a good one.  https://www.gammon.com.au/blink
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

opportunejaw

You seem to have combined some non-blocking techniques:

Code: [Select]
bool changeLights = (carInRange == true) && (currentMillis - previousMillis > interval);


With prodigious use of the delay function sprinkled in.  The delay function tells the processor to bury its head in the sand for a length of time.  During that time none of the rest of your code, save any interrupts, is being run.  So your code can't look for cars or read sensors or anything during that time. 

This isn't going to work until you get all the delay calls out.  There are a couple of short delayMicroseconds calls when you read the ping sensor and those can stay.  But all the ones that wait for several seconds at a time need to go. 

Go have a look at the Blink Without Delay example or any of the thousands of fine tutorials on the topic all over the net and learn how to handle timing with non-blocking code.  Here's a good one.  https://www.gammon.com.au/blink
Im kind of confused on the millis() command, where would i put the code to have the green2 light on for a certain amount of time?

Delta_G

#6
Feb 21, 2018, 03:10 pm Last Edit: Feb 21, 2018, 03:11 pm by Delta_G
where would i put the code
It's not about magical placement.  Code is like a list of instructions.  You put any line of code at the point in that list where you want that command to be done. 

People let the millis thing confuse them and I just don't understand it.  This is something you know how to do.  If you look at the clock and it is 2:05 right now and I say to meet me in 15 minutes you could handle that right?  Working with millis is exactly the same except the clock runs in milliseconds instead of minutes and hours.  You still take the time now and subtract the time then to find out how long it has been. 

Moving the millis line you have around isn't going to help anything.  You have to learn to understand how it is working so you can apply the same technique to the other parts of your code that need timing.  This will not work until you have got rid of every single line with delay in it.
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

opportunejaw

It's not about magical placement.  Code is like a list of instructions.  You put any line of code at the point in that list where you want that command to be done. 

People let the millis thing confuse them and I just don't understand it.  This is something you know how to do.  If you look at the clock and it is 2:05 right now and I say to meet me in 15 minutes you could handle that right?  Working with millis is exactly the same except the clock runs in milliseconds instead of minutes and hours.  You still take the time now and subtract the time then to find out how long it has been. 

Moving the millis line you have around isn't going to help anything.  You have to learn to understand how it is working so you can apply the same technique to the other parts of your code that need timing.  This will not work until you have got rid of every single line with delay in it.
So I worked on my code for a bit and now its doing what i want, sort of.
Code: [Select]

int red1 = 7;
int yellow1 = 6;
int green1 = 5;
int red2 = 4;
int yellow2 = 3;
int green2 = 2;

const int trigPin = 12;
const int echoPin = 11;

long duration;
int distance;

unsigned long currentMillis;
unsigned long currentMillis2;
long interval = 5000;
long pause = 3000;

void setup() {
  pinMode(red1, OUTPUT);
  pinMode(yellow1, OUTPUT);
  pinMode(green1, OUTPUT);
  pinMode(red2, OUTPUT);
  pinMode(yellow2, OUTPUT);
  pinMode(green2, OUTPUT);

  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  currentMillis = millis();

}
void loop() {
 
  //set-up to read distance at sensor 1
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = duration * 0.034 / 2;

  bool carInRange = distance <= 10;
  bool changeLights = (carInRange == true);

    //neutral period
    if(millis() - currentMillis >= pause){
    digitalWrite(red1, HIGH);
    digitalWrite(yellow1, LOW);
    digitalWrite(green1, LOW);
    digitalWrite(red2, HIGH);
    digitalWrite(yellow2, LOW);
    digitalWrite(green2, LOW);
    }

currentMillis2 = millis();
   
    if(millis() - currentMillis2 >= interval){
      if(changeLights){
    digitalWrite(red1, HIGH);
    digitalWrite(yellow1, LOW);
    digitalWrite(green1, LOW);
    digitalWrite(red2, LOW);
    digitalWrite(yellow2, HIGH);
    digitalWrite(green2, LOW);
  } else {
    digitalWrite(red1, HIGH);
    digitalWrite(yellow1, LOW);
    digitalWrite(green1, LOW);
    digitalWrite(red2, LOW);
    digitalWrite(yellow2, LOW);
    digitalWrite(green2, HIGH);
    }
  }
}


So now my issue is that its not going in a loop, i have my interval for my green2 light to be on for 5 seconds then go back to the neutral period (both red lights are on). Any idea how i can fix this?

wvmarle

#8
Feb 27, 2018, 05:26 am Last Edit: Feb 27, 2018, 06:27 am by wvmarle
You're going to have to build a state machine out of it, as you have different states.

Each traffic light goes through three stages: green, amber, red. The whole intersection has seven states: the three states for each of the two directions plus a "neutral" where all lights are red.

Switching between states is based on time and whether there are cars waiting.

So your code would look like this:

Code: [Select]

// The states (order doesn't matter as long as the numbers are unique).
#define EW_GREEN 1
#define EW_AMBER 2
#define EW_RED 3
#define NEUTRAL 4

byte intersectionState = NEUTRAL;
unsigned long lastMillis;

setup() {
  lastMillis = millis();
}

loop() {
  currentMillis = millis();
  switch (intersectionState) {
    case EW_GREEN:
      if (currentMillis - lastMillis > greenInterval) {
        intersectionState = EW_AMBER;
        lastMillis() = millis();
      }
      break;

    case EW_AMBER {
      // etc.
      break;

    }
    case NEUTRAL {
      // Do your decision making on next green direction.
      break;
    }
  }

}

(edit: code corrected as per comments #9/#10)

This would produce the basic timing of your traffic lights. Neutral would be reached say 1 second after the lights in any direction are switched to red, and neutral is required for any other direction to be allowed to turn to green.

Now you have your distance sensing, based on it (and the direction that last had green - use a bool flag for that - if it's set to true it was NS last, if set to false it was EW last) you decide which direction can get a green light, set the state to that, and let the timing loop to it's job.
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

allanhurst

wvmarle...



I'd quibble some of your code - for example you can't say

bool intersectionState and assign it a value of 4.


But in general a finite state machine is the correct approach to this type of problem, and once you've got the concept, leads to much more legible code.


OP -  you've got lots to learn,  and understanding FSM's is a great step forward to more complex programming, leading onto eg operating systems .


Keep at it!

Good luck.


Allan


wvmarle

#10
Feb 27, 2018, 06:26 am Last Edit: Feb 27, 2018, 06:29 am by wvmarle
Oh, that should be a byte of course. Not a bool.
That happens when you quickly type in some code without any testing whatsoever :-) It's definitely not perfect but meant as starting point for OP. Understand what's going on and it can be turned into properly working traffic light handling.

Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

opportunejaw

understanding FSM's is a great step forward to more complex programming
Whats FSM?

wvmarle

Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

GoForSmoke

Whats FSM?
It's where your code keeps track of what it is doing as one process state, call it step, in whatever job it is doing.

When waiting for a car it would run the wait for car code and when it finishes doing that the wait for car code switches the state value to the next step/state and when void loop() runs again, the code for the switched to state runs.

The idea is simple, the technique takes a bit more working out and like there are 26 alphabet letters that can be arranged into whole books... you can make state machines inside of state machines to very complex tasks if desired but keep it simple until you know it well.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

Go Up