Crosswalk Project

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

Stage 5 - Request

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 third stage's writeup.

Buttons and Pullup Resistors

Input pin connected directly to 5V via switch Input pin connected to the node between a pull-up resistor and a switch

When working with buttons and Arduinos for the very first time, it is extremely tempting to try to connect the 5V pin to the input pin via a button/switch. However, there is a problem with this approach. When the button is unpressed, there is no voltage applied to the input pin at all, not 5V or 0V. We call this pin floating as it can be read as any random value (high or low). Obviously, this is not ideal.

The correct and slightly less intuitive approach is to use a pull-up resistor and effectively ground the input pin when the button is not depressed. This is done by connecting the node between a pull-up resistor (connected to ground) and a switch (connected to 5V, shown in the second figure). When the switch is closed, the pull-up resistor is pulled up to 5V. When the switch is open, there is no current flowing and the input pin is directly connected to ground across the resistor (since the resistor has no voltage drop according to Ohm's Law with zero current).

Reading Input Pins

The input pins may be read with the digitalRead(pin) function, which effectively reads the nodal voltage at the pin. In this case, that is the voltage across the resistor which will vary between 0V (digital low, returned as false) and 5V (digital high, returned as true). Don't forget to set the pin mode for the button's pin to input.

Changing State on Request

Add additional transition path from wait straight to walk

Modify the state machine transitions in the loop() function. This will only require editing a single if statement as we're only worried about transitioning to walk from wait, not from warn. Below is some skeleton code to get you started. You should only need to focus on the sections with TODO in the comments.

Skeleton Code

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

// Pins
const int PIN_LED_RED = -1; // TODO Replace the -1s with the correct pins
const int PIN_LED_GREEN = -1;
const int PIN_PIEZO = -1;
const int PIN_BUTTON = -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);
  // TODO Set the pin number for the button
}

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
  // TODO Modify this first if-statement to also evalutate to true when the button is pressed
  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
  unsigned long period_beep_walk = 1 / FREQ_BEEP * 1000;
  unsigned long period_beep_warn = period_beep_walk / 2;
  unsigned long period_beep = state_current == STATE_WARN ? period_beep_warn : period_beep_walk;
  if (millis() > beep_last + (period_beep / 2)) {
    beep_current = !beep_current;
    beep_last = millis();
  }
  // Flash LED during warning
  if (state_current == STATE_WARN)
    digitalWrite(PIN_LED_RED, beep_current ? HIGH : LOW);

  // Tone
  unsigned long period_a4 = 1 / FREQ_A4 * 1000 * 1000;
  if ((state_current == STATE_WALK || state_current == STATE_WARN) && beep_current) {
    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.