Crosswalk Project

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

Stage 4 - Hurry Up

Basic Setup

Review the first 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, two LEDs, and a piezoelectric speaker (piezo) wired to the Arduino that turn on when the Arduino is powered. If you want to understand how the piezo works (which I recommend as you'll be working with it in this stage), refer to the previous stage's writeup.

Beeping Periodically

Two square waves with different periods

From the previous stage's description, we know sound may be generated by applying voltage across a piezo according to a square wave at the correct frequency. However, that only generates a single steady tone. To make that tone beep periodically, we want to apply another square wave at a lower frequency (say 2 Hz) on top of the first as a mask, only applying voltage when both waves are high/true. This will result in a wave that is low for a period and then rapidly oscillating between high and low for a period.

Programmatically, this is as simple as and'ing another condition into the tone section and saving the state necessary for asynchronous timing (like with the walk/wait state and tone generation). You may use millis() to track time since you don't need the accuracy of micros() for 2 Hz. For help with the state and timing, see how to drive the piezo pin in stage three.

Out of Time Warning

Walk state with arrow to wait after a delay of three seconds, warn state with arrow to wait after a delay of two seconds, and wait state with arrow to walk after a delay of ten seconds

Now we need to let our pedestrians know when they're about to run out of time to cross (so that they can all sprint across). To do this, we'll need to add another state to the state machine (review this section of stage two if you're unfamiliar with state machines). Our new warning state (maybe call it STATE_WARN) will take the place of part of the walk state, two out of the five seconds. So, the walk state's duration will be set to 3000 ms, and the new warning state will have a duration of 2000 ms. These values are pretty small, but it would be annoying to wait for cycles closer to the lengths used in actual crosswalks.

Now that you have this new state added to the cycle, you need to use it. During the warning state, make the piezo beep at 4 Hz (while maintaining the same A4 pitch frequency when enabled). You may do this by defining a new frequency to oscillate at during the warning phase, or just halve the period at which the piezo is toggling. Just halving is adding a "magic number" to your code and generally isn't good practice, but we can ignore that here.

Instead of including a new state, you could make the beep frequency increase linearly over the duration of the walk stage, but that would sound like a bomb countdown. If it sounds like a bomb is going off in the street, people are either going to panic and run, or they'll get desensitized to the sound and not run when a real bomb is about to go off (because real bombs actually beep, right?). Feel free to implement that if you want, but you'll still need the additional state for the next section (unless you want to make the LED blink increasingly quickly as well).

Visual Warning

Crosswalks need to be accessible to everyone, so what about people with a hearing disability? Let's make the red LED blink when there are only two seconds left to cross (and leave the green LED off) to give a visual indicator. You can just use the same 4 Hz frequency from the previous sction to drive the red LED during the warning state. Since this isn't a programming class, I've included skeleton code below for you to work with. Just focus on the TODO comments.

Skeleton Code

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

// Pins
const int PIN_LED_RED = -1; // TODO Replace the -1s with the correct pin numbers
const int PIN_LED_GREEN = -1;
const int PIN_PIEZO = -1;

// State
const int STATE_WAIT = 0;
const int STATE_WALK = 1;
const int STATE_WARN = 2;
const unsigned long DELAY_WAIT = 10000;
const unsigned long DELAY_WALK = 3000;
const unsigned long DELAY_WARN = 2000;
int state_current = STATE_WAIT;
unsigned long transition_last = 0;

// Tone
const float FREQ_A4 = 440; // Hz
bool tone_current = false;
unsigned long tone_last = 0;

// Beep
const float FREQ_BEEP = 2; // Hz
bool beep_current = false;
unsigned long beep_last = 0;

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

void loop() {
  // Handle millis() and micros() rolling back over to 0
  if (millis() < transition_last)
    transition_last = 0;
  if (micros() < tone_last)
    tone_last = 0;

  // State Machine
  if (state_current == STATE_WAIT && millis() >= transition_last + DELAY_WAIT) {
    // Switch to walk state
    state_current = STATE_WALK;
    transition_last = millis();
    digitalWrite(PIN_LED_RED, LOW);
    digitalWrite(PIN_LED_GREEN, HIGH);
  } else if (state_current == STATE_WALK && millis() >= transition_last + DELAY_WALK) {
    // Switch to warn state
    state_current = STATE_WARN;
    transition_last = millis();
    digitalWrite(PIN_LED_GREEN, LOW);
  } else if (state_current == STATE_WARN && millis() >= transition_last + DELAY_WARN) {
    // Switch to wait state
    state_current = STATE_WAIT;
    transition_last = millis();
    digitalWrite(PIN_LED_RED, HIGH);
    digitalWrite(PIN_LED_GREEN, LOW);
  }

  // Beep
  // TODO Replace 1 with an expression for the walk period (converted to milliseconds)
  unsigned long period_beep_walk = 1;
  unsigned long period_beep_warn = period_beep_walk / 2;
  unsigned long period_beep;
  // TODO Set period_beep to either period_beep_walk or period_beep_warn based on the state
  if (millis() > beep_last + (period_beep / 2)) {
    beep_current = !beep_current;
    beep_last = millis();
  }
  // Flash LED during warning
  // TODO Control the red LED during the warning state

  // Tone
  unsigned long period_a4 = 1 / FREQ_A4 * 1000 * 1000;
  // TODO Also generate a tone during the warning state
  if (state_current == STATE_WALK) {
    if (micros() > tone_last + (period_a4 / 2)) {
      tone_current = !tone_current;
      tone_last = micros();
    }
    digitalWrite(PIN_PIEZO, tone_current ? HIGH : LOW);
  } else digitalWrite(PIN_PIEZO, LOW);
}

Video

Once you're finished, you should have something like what's in the below video.