Built-in project · Arduino Uno R3

Crosswalk on an Arduino Uno

A traffic light module runs green, yellow and red on its own, and a button asks for the walk signal sooner; a piezo beeps while it is safe to cross. The whole build, an Arduino Uno and 3 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Beginner About 15 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $31. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • Driving three outputs as one traffic light state machine
  • Reading a button with an internal pull-up resistor
  • Timing a loop with millis while still watching an input
  • Making beeps with tone on a piezo
  • Difficulty: Beginner
  • Time: About 15 minutes
  • Board: Arduino Uno R3
  • Parts: 5

How it works

The traffic light module's red, yellow and green inputs go to pins 13, 12 and 11. The sketch turns them on in a fixed cycle: green for cars, then yellow, then red while walkers cross.

During the green the sketch watches the button on pin 2. The button uses the built-in pull-up, so pressing it reads LOW. A press shortens the green to 1.5 seconds more. Without a press the green lasts 8 seconds.

On red, the piezo on pin 8 beeps 16 times at a steady pace, then 12 faster, higher beeps as a hurry warning. Then the cycle starts over.

Parts list

5 parts, plus the jumper wires. They retail for about $31; on Mokxi they're free.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Traffic light module pin RArduino Uno R3 pin 13
Traffic light module pin YArduino Uno R3 pin 12
Traffic light module pin GArduino Uno R3 pin 11
Piezo speaker pin 1Arduino Uno R3 pin 8
Pushbutton pin 1aArduino Uno R3 pin 2
Traffic light module pin GNDArduino Uno R3 pin GND
Pushbutton pin 2aArduino Uno R3 pin GND
Piezo speaker pin 2Arduino Uno R3 pin GND

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Crosswalk: a traffic light module, a button for walkers and a piezo that
// beeps while it is safe to cross.
//
//   pins 13, 12, 11  the module's R, Y and G (its resistors are on the board)
//   GND              the module's GND, to a ground symbol
//   pin 2            the button, to ground (INPUT_PULLUP: pressed reads LOW)
//   pin 8            the piezo; its other leg to ground
//
// The light runs on its own: green for the cars, yellow, then red while the
// walkers cross with the piezo beeping, faster at the end. A press of the
// button during the green cuts it short, so nobody waits the whole cycle.

const int RED_PIN = 13;
const int YELLOW_PIN = 12;
const int GREEN_PIN = 11;
const int BUTTON = 2;
const int PIEZO = 8;

const unsigned long GREEN_MS = 8000;
const unsigned long SHORT_GREEN_MS = 1500;
const unsigned long YELLOW_MS = 2000;
const int WALK_BEEPS = 16;
const int HURRY_BEEPS = 12;

void lights(bool red, bool yellow, bool green) {
  digitalWrite(RED_PIN, red ? HIGH : LOW);
  digitalWrite(YELLOW_PIN, yellow ? HIGH : LOW);
  digitalWrite(GREEN_PIN, green ? HIGH : LOW);
}

void setup() {
  Serial.begin(115200);
  Serial.println("Mokxi Arduino Uno: crosswalk");
  pinMode(RED_PIN, OUTPUT);
  pinMode(YELLOW_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(PIEZO, OUTPUT);
}

// Green for the cars until the time is up, or a walker presses the button.
void green() {
  lights(false, false, true);
  Serial.println("cars: green");
  unsigned long start = millis();
  unsigned long limit = GREEN_MS;
  while (millis() - start < limit) {
    if (digitalRead(BUTTON) == LOW && limit == GREEN_MS) {
      Serial.println("button: walk soon");
      limit = millis() - start + SHORT_GREEN_MS;
    }
    delay(10);
  }
}

void loop() {
  green();
  lights(false, true, false);
  Serial.println("cars: yellow");
  delay(YELLOW_MS);

  lights(true, false, false);
  Serial.println("cars: red, walk");
  for (int i = 0; i < WALK_BEEPS; i++) {
    tone(PIEZO, 2000, 50);
    delay(250);
  }
  Serial.println("hurry");
  for (int i = 0; i < HURRY_BEEPS; i++) {
    tone(PIEZO, 2500, 30);
    delay(100);
  }
}

Code walkthrough

The key functions of the sketch, and what each one does.

lights sketch.ino

A small helper that sets all three lights at once. You pass true or false for red, yellow and green, and it writes HIGH or LOW to each pin. It keeps the rest of the code short and stops two lights being left on by accident.

void lights(bool red, bool yellow, bool green) {
  digitalWrite(RED_PIN, red ? HIGH : LOW);
  digitalWrite(YELLOW_PIN, yellow ? HIGH : LOW);
  digitalWrite(GREEN_PIN, green ? HIGH : LOW);
}

setup sketch.ino

Starts the serial port, sets the three light pins and the piezo pin as outputs, and sets the button pin as INPUT_PULLUP. The pull-up means the button needs only one wire to ground, and pressed reads LOW.

void setup() {
  Serial.begin(115200);
  Serial.println("Mokxi Arduino Uno: crosswalk");
  pinMode(RED_PIN, OUTPUT);
  pinMode(YELLOW_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(PIEZO, OUTPUT);
}

green sketch.ino

Turns on the green light and waits. It checks the button every 10 ms. On the first press it changes the limit to the time already passed plus SHORT_GREEN_MS. The check limit == GREEN_MS makes sure only the first press counts.

// Green for the cars until the time is up, or a walker presses the button.
void green() {
  lights(false, false, true);
  Serial.println("cars: green");
  unsigned long start = millis();
  unsigned long limit = GREEN_MS;
  while (millis() - start < limit) {
    if (digitalRead(BUTTON) == LOW && limit == GREEN_MS) {
      Serial.println("button: walk soon");
      limit = millis() - start + SHORT_GREEN_MS;
    }
    delay(10);
  }
}

loop sketch.ino

Runs the whole cycle: call green, show yellow for YELLOW_MS, then red. On red it beeps WALK_BEEPS times, then HURRY_BEEPS faster, higher beeps before it repeats.

void loop() {
  green();
  lights(false, true, false);
  Serial.println("cars: yellow");
  delay(YELLOW_MS);

  lights(true, false, false);
  Serial.println("cars: red, walk");
  for (int i = 0; i < WALK_BEEPS; i++) {
    tone(PIEZO, 2000, 50);
    delay(250);
  }
  Serial.println("hurry");
  for (int i = 0; i < HURRY_BEEPS; i++) {
    tone(PIEZO, 2500, 30);
    delay(100);
  }
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • Pressing the button does nothing. This project
    Wire pin 2 to one side of the button and the other side to ground. The button only works during the green light.
  • The lights are in the wrong order. This project
    Check that pin 13 goes to R, pin 12 to Y and pin 11 to G on the module.
  • No beeping. This project
    The piezo beeps only during red. Check pin 8 on one leg and ground on the other.
  • One color never lights, or all three light together. Traffic light module
    Check each wire goes to its own pin labeled R, Y, G, and that GND is connected. A pin driven high lights its LED. Resistors are already on the board, so do not add more.
  • Lights seem dim or the wrong color comes on in a sequence. Traffic light module
    Confirm the pins in your code match the wires, and that the module gets the right supply voltage. Keep each state's delay long enough to see, and turn the old color off before the next.
  • The input reads random HIGH and LOW when the button is not pressed. Pushbutton
    A floating pin picks up noise. Add a 10 k pull-down or pull-up resistor, or use INPUT_PULLUP and wire the button between the pin and GND. Then pressed reads LOW.
  • The button seems always pressed, or never changes. Pushbutton
    A 4-leg tactile switch has legs joined in pairs. Place it across the breadboard center gap so the pairs sit on different rows. Wired along one side, the legs are already connected.
  • A passive piezo stays silent when you just set the pin HIGH. Piezo speaker
    A passive piezo needs a changing signal. Use tone(pin, frequency) or a PWM wave. Around 2 to 4 kHz is loudest. A buzzer with a built in oscillator makes sound from plain DC instead.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.