class: center, middle, inverse, title-slide .title[ # Reaction Duel: HIGH, LOW, and Pull-Ups ] .subtitle[ ## Programmable Electronics — Unit 3 ] .date[ ### Class 17 ] --- <style> .code-tight .remark-code { font-size: 14px; line-height: 1.2; } .code-tighter .remark-code { font-size: 12.5px; line-height: 1.15; } .vbar { display: flex; height: 54px; border-radius: 6px; overflow: hidden; margin: 10px 0 4px; font-weight: bold; } .vbar div { display: flex; align-items: center; justify-content: center; color: #fff; } .vbar .lo { background: #364F6B; flex: 1.5; } .vbar .mid { background: #bbbbbb; color: #333; flex: 1.5; } .vbar .hi { background: #C8102E; flex: 2; } .remark-slide-content table { width: 100%; } .vscale { display: flex; justify-content: space-between; font-size: 16px; color: #555; } </style> # Learning Targets .lt-box[ - Build a two-player game from LEDs, buttons, and the Serial Monitor. - Say what HIGH and LOW mean as **voltages**, and measure them on a pin. - Explain why an unconnected input reads garbage, and fix it with a pull-up or pull-down resistor. - Explain why a button on `INPUT_PULLUP` reads **LOW** when pressed. ] .eu-label[Essential Understanding:] A pin doesn't know about buttons. It only measures a voltage and sorts it into HIGH or LOW. If nothing sets that voltage, the pin guesses. A pull-up or pull-down resistor is how you make sure there is always an answer, and `INPUT_PULLUP` is that resistor, already inside the chip. --- # Agenda .agenda-box[ 1. Today's build, start to finish 2. **Build 1:** start lights 3. **Build 2:** the reaction duel 4. Play, then make it yours 5. Why is player 2 wired differently? Now we talk 6. HIGH and LOW, floating pins, pull-ups and pull-downs 7. What's next: pick a project ] .center[ **Build first again.** Follow the steps, get it working, and play. Player 2's button is wired differently from player 1's **on purpose**. Wire it exactly as shown, and we'll find out why after. ] --- class: small-font # Today's build, start to finish .pull-left.w48[ **Build 1: start lights** (keep last class's LED on pin 13) <ol start="1"><li>Add a <strong>yellow LED + 470 Ω</strong> on <strong>pin 12</strong> and a <strong>red LED + 470 Ω</strong> on <strong>pin 11</strong>.</li><li>Upload the start-lights code.</li><li>Open the <strong>🔌 Serial Monitor</strong>. Watch the lights and the messages together.</li></ol> **Build 2: the duel** <ol start="4"><li><strong>Player 1:</strong> last class's button, <strong>pin 2 → GND</strong>. No change.</li><li><strong>Player 2:</strong> a second button from <strong>5V → pin 3</strong>, and a <strong>10 kΩ</strong> from <strong>pin 3 → GND</strong>.</li><li>Upload the duel code. Play.</li></ol> ] .pull-right.w48[ **Then** <ol start="7"><li>Make it yours: fix the cheat, add a match winner.</li><li>We stop and talk: why is player 2 different?</li><li>Three small experiments on player 2's button.</li></ol> .font-small[ **You need:** three LEDs (red, yellow, green if you have them; any colours work), three 470 Ω, two pushbuttons, one 10 kΩ, jumper wires. One laptop, one Arduino, one USB cable per group. **Didn't finish the reaction timer last time?** No problem. Build 2 *is* the reaction timer, for two players. ] ] --- class: small-font # Build 1 (10 min): start lights .pull-left.w44[ <img src="assets/schematics/reaction-duel.svg" alt="Arduino Uno: pins 11, 12 and 13 each through 470 ohm and an LED (red, yellow, green) to ground. Pin 2 through the player 1 button to ground. Pin 3 to the player 2 button from 5 V, with a 10 kilohm pull-down from pin 3 to ground." style="width:100%;max-height:360px;"/> .font-small[Build 1 is just the three LEDs. The buttons come in Build 2.] ] .pull-right.w52.code-tight[ ```cpp #include <Arduino.h> void setup() { pinMode(11, OUTPUT); // red pinMode(12, OUTPUT); // yellow pinMode(13, OUTPUT); // green Serial.begin(9600); } void loop() { Serial.println("Ready..."); digitalWrite(11, HIGH); delay(1000); Serial.println("Set..."); digitalWrite(12, HIGH); delay(1000); Serial.println("GO!"); digitalWrite(11, LOW); digitalWrite(12, LOW); digitalWrite(13, HIGH); delay(1500); digitalWrite(13, LOW); delay(2000); } ``` ] --- class: small-font # Build 1: check it, then change it .pull-left.w48[ 1. **Upload.** Red, then red + yellow, then green. 2. Click **🔌 Serial Monitor** in the bottom bar. "Ready... Set... GO!" should print in step with the lights. 3. **One LED stays dark?** It's backwards, or its resistor isn't in the same row. Swap the code's pin numbers to test: is it the LED or the code? **Then change it:** - Make "Set..." last a **random** time, so nobody can count it: `delay(random(1000, 4000));` - Make the green light **blink** three times instead of staying on. ] .pull-right.w48[ .emphasis[ **Three pins, three outputs, one program.** On the 555 you'd need a timing network for every light. Here you need one more `digitalWrite`. ] **The Serial Monitor is a second output.** The LEDs are for the players. The text is for *you*: it tells you where the program is. When something goes wrong later, add a `Serial.println` and see how far it got. ] --- class: small-font # Build 2 (15 min): the duel, wiring .pull-left.w44[ <img src="assets/schematics/reaction-duel.svg" alt="The full reaction duel circuit: three LEDs on pins 11 to 13, player 1's button from pin 2 to ground, player 2's button from 5 V to pin 3 with a 10 kilohm pull-down to ground." style="width:100%;max-height:360px;"/> ] .pull-right.w52[ Keep all three LEDs. **Player 1:** last class's button. **Pin 2 → button → GND.** Nothing else. **Player 2:** wired differently, on purpose. 1. A second button from the Arduino's **5V** pin to **pin 3**. 2. A **10 kΩ** resistor from **pin 3** to **GND**. Use **diagonally opposite** legs of each button, and put the buttons at opposite ends of the breadboard so the players don't collide. .font-small[ Use the **5V** pin on the Arduino. **Not** the 6 V battery pack: 6 V on a pin is more than it's built to take. ] ] --- class: small-font # Build 2: the duel, code .pull-left.w46.code-tighter[ ```cpp #include <Arduino.h> int score1 = 0; int score2 = 0; bool p1Pressed() { return digitalRead(2) == LOW; } bool p2Pressed() { return digitalRead(3) == HIGH; } void setup() { pinMode(11, OUTPUT); // red pinMode(12, OUTPUT); // yellow pinMode(13, OUTPUT); // green pinMode(2, INPUT_PULLUP); // player 1 pinMode(3, INPUT); // player 2 Serial.begin(9600); randomSeed(analogRead(A0)); Serial.println("Reaction duel!"); } ``` ] .pull-right.w52.code-tighter[ ```cpp void loop() { Serial.println("Hands off the buttons..."); while (p1Pressed() || p2Pressed()) { } delay(1500); digitalWrite(11, HIGH); // ready... delay(1000); digitalWrite(12, HIGH); // set... delay(random(1000, 4000)); digitalWrite(11, LOW); digitalWrite(12, LOW); digitalWrite(13, HIGH); // GO! unsigned long start = millis(); int winner = 0; while (winner == 0) { // first press wins if (p1Pressed()) winner = 1; if (p2Pressed()) winner = 2; } unsigned long t = millis() - start; digitalWrite(13, LOW); if (winner == 1) score1 = score1 + 1; if (winner == 2) score2 = score2 + 1; Serial.print("Player "); Serial.print(winner); Serial.print(" wins in "); Serial.print(t); Serial.print(" ms. Score: "); Serial.print(score1); Serial.print(" - "); Serial.println(score2); } ``` ] .font-small[Paste the left half, then the right half under it, into `src/main.cpp`. Upload, open the **🔌 Serial Monitor**, and play.] --- class: small-font # Play, then make it yours .pull-left.w48[ **Play first to 5.** Then swap who's player 1. **Make it yours** (pick any): 1. **The cheat.** Hold your button down during "set..." and you win in 0 ms. Fix it: a player who is already pressing when the green light comes on **loses** the point. *(Last class's reaction timer had lines that catch this. Find them.)* 2. **Match point.** First to 5 wins the match. Print the champion, then start the scores over. 3. **New record!** Keep the fastest time so far, and print it when someone beats it. 4. **Is it fair?** If both players press at exactly the same moment, who gets the point? Find the line that decides. ] .pull-right.w48[ **Stuck?** - **"Hands off the buttons..." and then nothing.** The program thinks a button is held. Check player 2's 10 kΩ: is it really from **pin 3** to **GND**? - **Player 1 never wins.** Pin 2's jumper isn't in **pin 2**, or the button legs are the always-connected pair. - **Player 2 never wins.** The button isn't connected to **5V**, or the pull-down went to the wrong row. - **Nothing prints.** Open the Serial Monitor, then press the board's reset button. .emphasis[ Before the next slide: **look at `p1Pressed()` and `p2Pressed()`.** One says `LOW`, the other says `HIGH`. Why? ] ] --- # Why is player 2 different? .pull-left.w48[ Both functions answer the same question: **is this player pressing?** ```cpp bool p1Pressed() { return digitalRead(2) == LOW; } bool p2Pressed() { return digitalRead(3) == HIGH; } ``` Player 1's button connects pin 2 to **GND**. Player 2's connects pin 3 to **5V**. But then why does player 2 need a **10 kΩ** and player 1 doesn't? ] .pull-right.w48[ To answer that, we need to know what `digitalRead` is really doing. .emphasis[ **You've built player 2's circuit before.** When you built logic gates from transistors, every gate had a button from the supply and a resistor to ground on its input. What was that resistor for? ] ] --- # HIGH and LOW are voltages .pull-left.w48[ **When the pin is an output,** the chip connects it to one of two voltages: - `digitalWrite(13, HIGH)` → about **5 V** - `digitalWrite(13, LOW)` → about **0 V** **When the pin is an input,** the chip *measures* the voltage and sorts it: <div class="vbar"><div class="lo">LOW</div><div class="mid">not sure</div><div class="hi">HIGH</div></div> <div class="vscale"><span>0 V</span><span>1.5 V</span><span>3 V</span><span>5 V</span></div> ] .pull-right.w48[ **The grey zone is a problem.** Between about 1.5 V and 3 V, the datasheet makes no promise. The pin might read either one, and it might change its mind. .emphasis[ A digital pin isn't a light switch. It's a voltmeter that only gives two answers. ] **Remember your transistor gates?** Their "HIGH" wasn't a clean 6 V. Would an Arduino input have read it as HIGH? ] --- class: small-font # Experiment 1 (5 min): measure your buttons .pull-left.w48[ Keep the duel running. Multimeter on **DC volts**, black probe on **GND**. 1. Red probe on **pin 2** (player 1). Measure with the button up, then held down. 2. Red probe on **pin 3** (player 2). Same again. 3. Red probe on **pin 13** while the green light is on. | | Not pressed | Pressed | |---|---|---| | Pin 2 (player 1) | | | | Pin 3 (player 2) | | | | Pin 13, green on | | | ] .pull-right.w48[ **Questions:** - Which pin sits at **5 V** when nobody's touching it? Which sits at **0 V**? - Something holds pin 3 at 0 V when the button is up. **What?** - Nothing *you* wired holds pin 2 at 5 V. So **what does?** .font-small[ The meter's black probe can stay on any GND pin on the Arduino the whole time. ] ] --- class: small-font # Experiment 2 (5 min): pull out the 10 kΩ .pull-left.w48[ 1. Take **player 2's 10 kΩ out** of the breadboard. Leave everything else. 2. Watch the Serial Monitor. Play a few rounds. **Nobody** touches player 2's button. 3. Hold your hand near player 2's wires. Touch the jumper on pin 3. Let go. 4. Write down what happens. .font-small[ Maybe player 2 "wins" in 0 ms without being touched. Maybe the game sits at "Hands off..." forever. Maybe it works fine for a while, then doesn't. All three are the same problem. ] ] .pull-right.w48[ .emphasis[ With the resistor gone, nothing decides pin 3's voltage when the button is up. The pin picks up whatever is nearby: your hand, the lights, the wire next door. It's **floating**. ] **Why is "sometimes works" worse than "never works"?** Put the 10 kΩ back. The ghost goes away. ] --- class: small-font # Fix it: pull-down or pull-up .pull-left.w50[ <img src="assets/schematics/pull-up-pull-down.svg" alt="Left: pull-down, button from +5 V to the pin, 10 kilohm from the pin to ground. Right: pull-up, 10 kilohm from +5 V to the pin, button from the pin to ground." style="width:100%;max-height:330px;"/> ] .pull-right.w46[ A resistor gives the pin a **default answer** when nothing else is connected. - **Pull-down (player 2):** the resistor gently pulls the pin to **0 V**. Press the button and it connects the pin to 5 V. **Pressed = HIGH.** - **Pull-up (player 1):** the resistor gently pulls the pin to **5 V**. Press the button and it connects the pin to ground. **Pressed = LOW.** **Why a resistor and not a wire?** With a wire, pressing the button would connect 5 V straight to ground: a short. Through 10 kΩ, only `\(5\,\text{V} / 10\,\text{k}\Omega = 0.5\,\text{mA}\)` flows. ] --- class: small-font # Experiment 3 (5 min): make player 2 match player 1 .pull-left.w38[ Player 1 has a pull-up, but you never wired a resistor. So where is it? 1. Move player 2's button so it goes from **pin 3 → GND**. Take the 10 kΩ out. Nothing goes to 5V now. 2. In `setup()`: `pinMode(3, INPUT_PULLUP);` 3. In `p2Pressed()`: change `HIGH` to `LOW`. 4. Upload and play. Measure pin 3 again, up and pressed. ] .pull-right.w58[ | Player 2 wiring | Code | Up | Pressed | |---|---|---|---| | **Pull-down:** button 5V → pin 3, 10 kΩ pin 3 → GND | `INPUT`, `== HIGH` | | | | **Floating:** no resistor | `INPUT` | | | | **Pull-up, built in:** button pin 3 → GND | `INPUT_PULLUP`, `== LOW` | | | **Which wiring would you choose for your next project, and why?** ] --- # So that's why pressed reads LOW .pull-left.w48[ `INPUT_PULLUP` switches on a resistor **inside the chip**, from the pin to 5 V. It's the right-hand circuit, with the resistor built in. - Not pressed: the resistor holds the pin at 5 V → **HIGH**. - Pressed: the button connects the pin to GND → **LOW**. That's why every button last class needed only two wires, and why the code said `== LOW` for "pressed." ] .pull-right.w48[ .emphasis[ **The rule:** never leave an input floating. Every input pin needs something deciding its voltage: a pull-up, a pull-down, or another circuit driving it. ] **The easy default:** button from the pin to **GND**, and `INPUT_PULLUP` in the code. No extra parts. Just remember: **pressed = LOW**. ] --- class: small-font # What's next: pick a project .pull-left.w48[ **A. *Operation*, in code** *(recommended)* Bring your open-house game. The course goes to **pin 2**, the wand to **GND**: it's just a button you press by mistake. Add the LED and the speaker, and the two 555s become 15 lines of code. **B. Traffic light** Use the timings you planned last class: red, yellow and green on three pins (you just wired them). Then add a pedestrian button. ] .pull-right.w48[ **C. Duel, level 2** Finish the challenges from today, then add a **speaker** for the start beep, or a third player on pin 4. **D. Nightlight v2** Your automatic nightlight, in code. The photoresistor divider goes to **A0**, and `analogRead` gives a number from 0 to 1023. Pick the "dark" threshold yourself. ] .emphasis-center[ Every one of these is a pin that reads, a decision, and a pin that acts. ] --- class: small-font # *Operation*, in code: wiring .pull-left.w54[ <img src="assets/schematics/operation-arduino.svg" alt="Arduino Uno: pin 13 through 470 ohm and an LED to ground; pin 8 through 220 ohm and 100 ohm to the speaker and ground; pin 2 to the course, with the wand to ground" style="width:100%;max-height:330px;"/> ] .pull-right.w42[ - **Course → pin 2. Wand → GND.** Touching closes the circuit, exactly like the button. - **LED** on pin 13, same as always. - **Speaker** on pin 8, through **220 Ω + 100 Ω** in series. **Why two resistors?** In the *Operation* game the 555 could push about 200 mA. An Arduino pin should stay under about **20 mA**. `$$R = \frac{5\,\text{V}}{20\,\text{mA}} = 250\,\Omega$$` The kit has no 250 Ω, so 220 + 100 = 320 Ω → about **15 mA**. Same move as building a value from two resistors in series. ] --- class: small-font # *Operation*, in code: starter program .pull-left.w54.code-tight[ ```cpp #include <Arduino.h> int strikes = 0; void setup() { pinMode(2, INPUT_PULLUP); // the course pinMode(13, OUTPUT); // the LED Serial.begin(9600); Serial.println("Steady hands... go!"); } void loop() { if (digitalRead(2) == LOW) { // wand touched the course strikes = strikes + 1; Serial.print("BZZT! Strikes: "); Serial.println(strikes); digitalWrite(13, HIGH); tone(8, 690); // the 555's 690 Hz buzz delay(2400); // the 555's 2.4 s penalty noTone(8); digitalWrite(13, LOW); } } ``` ] .pull-right.w42[ **Then make it better than the 555 version:** - **Game over** after 3 strikes: a long sad tone, then nothing until reset. - A **finish pad** at the end of the course on **pin 3**. Touch it to win. - **Time each run** with `millis()`, like the duel, and print it. - A different tone for each strike. .font-small[ **Compare:** with 555s this took two chips, two timing networks, and a lot of wires. Which version would you rather debug? Which would you rather leave running at open house for three hours? ] ] --- class: center, middle # Wrap-up — in your notebook .hw-box[ - In one or two sentences: **why does a button on `INPUT_PULLUP` read LOW when pressed?** Use the words *pull-up* and *ground*. - **What happened when you pulled out player 2's 10 kΩ?** Use the word *floating*. - **Choose your project** (A, B, C, or D) and draw it as three blocks: **input → decision → output**. Label which pins go where. Bring your *Operation* game if you're choosing A. ]