Crosswalk Project

ECE301 - Intro. to Circuits, Summer 2019, EiL Written by Ben Sergent V
ECE301 Crosswalk | Stage 2
Back

Stage 2 - Blinking

Basic Setup

Review the previous stage's sections on using the Arduino IDE before continuing on if you're fuzzy on the topic.

From the last group's stage, you should have a breadboard with three resistors and two LEDs wired to the Arduino that turn on when the Arduino is powered.

Blinking Synchronously

Now that you have both LEDs lighting up, you need to have them blink one after the other. This can be done fairly simply with the delay(milliseconds) function. Just turn one LED on and the other off, delay for an amount of time, switch which LED is on, and then delay some more. This should be done in the loop() function since this should happened over and over. Code for this approach may be found below. However, you'll be writing something slightly more sophisticated.

void setup() {
	pinMode(PIN_LED_RED, OUTPUT);
	pinMode(PIN_LED_GREEN, OUTPUT);
}
void loop() {
	// Show wait state for 10 seconds
	digitalWrite(PIN_LED_RED, HIGH);
	digitalWrite(PIN_LED_GREEN, LOW);
	delay(10000);
	// Show walk state for 5 seconds
	digitalWrite(PIN_LED_RED, LOW);
	digitalWrite(PIN_LED_GREEN, HIGH);
	delay(5000);
}

Blinking Asynchronously

The problem with the approach in the previous section is that nothing can occur while delay() is executing (because it is a synchronous, blocking function). This means that button presses could only be processed once each loop, which will take slightly longer than the sum of the two previous delay() calls.

One solution is to save the current state of the crosswalk (walk or wait) as well as the last time the lights flipped. Then, constantly check if the current time is past the sum of the last switch time and a delay for how loong that state should last. This will ensure any button presses or other events may be detected very quickly. The downside to this new approach is that the time between states may be larger than expected, but only by a few milliseconds (the time it takes for the loop() function to execute). This should be indiscernable in regular use.

Implementing a State Machine

Walk state with arrow to wait after a delay of five seconds and wait state with arrow to walk after a delay of ten seconds

The core concept behind this new approach is a state machine consisting of walk and wait states with transitions of having elapsed a certain amount of time. Just so you have some numbers to work with, let's say the wait state lasts ten (10) seconds and the walk state lasts five (5) seconds. An abstract diagram of the state machine is shown to the right. You'll need to define a global variable to maintain the current state and another one to maintain the last state transition time. Within the loop() function, you'll need to check both the current state and if enough time has elapsed to transition to the next state. An if-else statement should suffice for this, although a switch statement would be cleaner if you had more than two states. You'll need to use either millis() or a time library to implement this stage. I recommend using millis() despite its short-comings due to the simplicity of this stage. It will return the number of elapsed milliseconds since the Arduino was last powered on or reset. The main short-coming of the function is that millis() will rollover and start back from zero (0) after about eight (8) minutes due to the size of the variable used by the Arduino, but I have handled that for you in the skeleton code.

Speaking of tidiness, some constants should be defined: walk state, wait state, walk delay, and wait delay. These aren't entirely necessary, but will make for less work in the future stages when things need to be changed and reduces the number of "magic numbers" in your program. Some skeleton code may be found below to work off of, or you may write your own from scratch.

Skeleton Code

// Crosswalk Project - Stage 2
// ECE301 - Intro. to Circuits

// Pins
// TODO Replace the -1s with the pin numbers wired by the previous group
const int PIN_LED_RED = -1;
const int PIN_LED_GREEN = -1;

// State
const bool STATE_WAIT = false;
const bool STATE_WALK = true;
const unsigned long DELAY_WAIT = 10000;
const unsigned long DELAY_WALK = 5000;
bool state_current = STATE_WAIT;
unsigned long transition_last = 0;

void setup() {
  pinMode(PIN_LED_RED, OUTPUT);
  pinMode(PIN_LED_GREEN, OUTPUT);
}

void loop() {
  // Handle millis() rolling back over to 0 (with some error here, but it's negligible)
  if (millis() < transition_last)
    transition_last = 0;
	
  if (state_current == STATE_WAIT && /* TODO Add state change condition */) {
    // Switch to walk state
    state_current = STATE_WALK;
    // TODO Save the last state change time
  	// millis() returns number of milliseconds since last power on or reset
    // TODO Update the power to the LEDs
  	// digitalWrite(pin, HIGH | LOW)
  } else if (state_current == STATE_WALK && /* TODO Add state change condition */) {
    // Switch to wait state
    state_current = STATE_WAIT;
    // TODO Save the last state change time
    // TODO Update the power to the LEDs
  }
}

Adding a Fade Effect

At this point, I was going to have you wire a capacitor in parallel with each LED so that it would fade in and out (capacitors try to maintain the same voltage). However, what I thought were capacitors in the starter kit are apparently tilt/vibration sensors? So you get a freebie and don't have to worry about that.