Arduino Ladder · Bozoma Innovation Hub

Bozoma Innovation Hub  ·  Arduino Ladder  ·  Band 3

Combination Lock

So far the board has only ever talked. Now it starts listening. You will find out that a button is much stranger than it looks, and you will build a lock that only opens for the right secret.

About 7 hours. This is the longest band so far and the ideas are close together. Take it in three or four sittings rather than one.

Choose your build

Swap means build it instead of the one on this page: it teaches the same things and leaves you just as ready for the next band. Everything else on this page still applies, including the Check yourself questions. Practice is short, comes after the core, and gives you no sketch: the job, the check, and the code is yours. Bench is not a build at all: it is an instrument or an idea, done once, and everything after it is easier for having done it.

Sketches for this band

Click one to save it, or get every sketch at once from the downloads page. Each file opens with a plain-English header telling you what it does and how to wire it.

Start here

What this band is for

Everything you have built so far runs on its own and ignores you. This band changes that. By the end, your board will wait for you, notice what you do, remember it, and decide what to do about it.

Four new ideas do all the work:

  • Reading a pin instead of writing to it.
  • Deciding, with if and else.
  • Remembering, by keeping a value in a name between one round and the next.
  • Storing a list of things in one name, which is called an array.

You also get a new tool: the serial monitor. It lets you see numbers from inside your program while it is running. Once you have it, most problems in this course become findable instead of mysterious.

What you need

  • Your board, USB cable and breadboard
  • Four push buttons, the small square kind with four legs
  • Two LEDs, ideally one green and one red
  • Two resistors of 220 ohms
  • An active buzzer, the kind with a solid black cover on the bottom
  • About sixteen jumper wires

No resistors are needed for the buttons. You will find out why in Part 4, and it is one of the more useful things you will learn this band.

By the end you will be able to

  • Say which legs of a push button are joined to which, and why that matters
  • Explain what a floating pin is and why it lies to you
  • Use the serial monitor to see what your program is really doing
  • Write if and else to make the board choose
  • Make the board remember something between one round and the next
  • Count one press as one press, instead of forty
  • Keep a list of numbers in one name
Part 1

Think first: the door and the spring

10 min

Find a door. Any door will do. This takes five minutes and it will save you an hour of confusion later.

Pick the right door. You want one that swings easily: on a hard floor, not dragging on a carpet, and not one that pulls itself shut. Test it by pushing it gently. If it swings freely, use it.

Push the door until it is about half open, then take your hand off it completely.

You should seeIt stays roughly where you left it. It is not open and it is not shut. It is just wherever you happened to leave it.

Now blow on the edge of it, hard, from about a hand's width away. Or fan it with a book. Or just walk quickly past it.

You should seeIt moves. Something as small as a puff of air changes where it sits, and then it stays at the new place.

That is the whole point. Nothing decides where that door sits except whatever touched it last. You cannot look at it from across the room and know anything for certain, because it will have moved again by the time you get there.

Now make a spring door. A real one is a shop door or a gate that pulls itself shut. If you do not have one, make one: push the door until it is half open, and lean something heavy against it that pushes it towards shut. A bag, a chair, a box.

Now push the door open against that weight, and let go.

You should seeIt goes back to shut. Every single time. It only stays open while you are actively pushing it.
If you have no door you can useUse a hinged lid instead: a laptop, a book stood on its spine, a box lid. Balance it half open, and blow on it. Then hold it half open with a rubber band or a weight pulling it closed, and do the same.
Write these down before you go on
  1. With the ordinary door, could you tell from across the room whether it was "open" or "shut"? Or was it somewhere in between and moving?
  2. With the spring door, if you look at it and it is open, what do you know for certain?

That is the whole idea of the next few parts, and it is the thing most beginners get wrong.

A pin on the Arduino that has nothing holding it is like the ordinary door. It is not at 1 and it is not at 0. It drifts, and it moves when anything happens nearby. You cannot trust what it tells you.

A pin with a spring on it is different. It always sits at one value unless something is actively pushing it to the other. And that is exactly what you are going to switch on.

Keep hold of the spring door. When you meet the word INPUT_PULLUP in Part 4, it means: put a spring on this pin.

Part 2

What a push button really is

15 min

Take one of the small square buttons out of your kit and look at it. It has four legs, not two.

That surprises people. A button has one job, so why four legs?

The answer is that the four legs are really two pairs, and the pairs are already joined together inside.

These two legs are always joined These two are always joined too Pressing joins the left side to the right side So you only ever use ONE leg from the left and ONE from the right leg leg leg leg
Inside a push button. The two legs on one side are permanently joined to each other, and so are the two on the other side. Pressing the button closes the gap in the middle, joining the two sides together.

So the button is not a device with four separate connections. It is a device with two connections, each of which has been given two legs so that it sits firmly in a board.

When you press it, a metal disc inside is pushed down and touches both sides at once. That closes the circuit. Let go and a small spring lifts the disc away, and the circuit is broken again.

The rule that saves you every time

You only need two of the four legs. Use two legs that are diagonally opposite each other: one at the top left and one at the bottom right, or the other diagonal.

Diagonal legs are always one from each side. So they are always a switching pair, whichever way round you happen to push the button into the board. You never have to work out which way it is facing.

Why this mattersIf you pick two legs from the same side by mistake, they are already joined. Your circuit will behave as though the button is being held down forever, and pressing it will change nothing. It is a very confusing fault, and the diagonal rule prevents it completely.
Words to know
push button
A switch that only connects while you are holding it down. Also called a momentary switch.
input
A pin used for reading something coming in, rather than sending something out.
output
A pin used for sending electricity out. All your pins have been outputs until now.
diagonal rule
Use two legs of a button that are diagonally opposite. They are always a switching pair.
Part 3

The serial monitor: your new eyes

20 min
+ − − + j i h g f e d c b a 1 5 10 USB ARDUINO UNO button button a3 a3 e1 e1 e3 e3 f1 f1 f3 f3 j1 j1 SCL SCL SDA SDA AREF AREF GND GND 13 13 12 12 11 11 10 10 9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 0 0 IOR IOR RST RST 3V3 3V3 5V 5V GND GND GND GND VIN VIN A0 A0 A1 A1 A2 A2 A3 A3 A4 A4 A5 A5
The simplest circuit in the course. Two wires, no resistor, and the button's body sitting over the middle channel.

Until now, the only way your program could tell you anything was by switching a light on. That is a very small window. From here on you get a much bigger one.

The serial monitor is a window in the Arduino IDE that shows text and numbers sent by the board, while the board is running. Your program can say things to you.

Three lines make it work

LineWhat it does
Serial.begin(9600);Goes in setup. Starts the conversation. The 9600 is how fast the two sides agree to talk. It must match the number in the corner of the serial monitor window.
Serial.print(x);Sends x to the window and stays on the same line.
Serial.println(x);The same, but then moves to a new line. The extra "ln" means "line".

You can send a number, or you can send words by putting them in double quotation marks: Serial.println("hello");

Wire one button and try it

This is the simplest circuit in the whole course. There is no resistor at all.

Pin 2 button GND pin the − rail No resistor. INPUT_PULLUP uses the one inside the chip.
How one button is wired. Pin 2 goes to one leg of the button. A leg diagonally opposite goes to the minus rail, and one wire takes that rail to a GND pin.
Before you startUSB unplugged.

Push the button into the board so it sits over the middle channel. The middle channel is the groove running the length of the breadboard, between row e and row f.

Put it in columns 1 and 3. Its four legs will land in f1, f3, e1 and e3: two legs above the groove and two below, with the square plastic body sitting over the groove itself.

How to actually push it in: line the four legs up over the four holes, then press down on the four corners of the plastic square with your thumb. Press on the body, not on the round button on top.

You should seeThe button sitting flat on the board, not rocking. Nudge it: it should not move. You should be able to press the round top and feel it click.
It takes more force than feels safeThat is normal. Buttons need a proper press. Keep pushing until it stops going down.
If only two legs go in and it sits at an angleThe legs are pointing the wrong way. Lift it out, turn it flat on the table by a quarter turn so the legs point the other way, and try again. The legs only line up one way round.

Pick two diagonal legs. Your four legs are in f1, f3, e1 and e3. Take f1, above the groove, and e3, below it and at the other end. Those two are diagonally opposite each other.

You do not plug wires into those holesThose four holes have legs in them already. You wire into other holes in the same columns, which are joined to them inside the board. That is what the next two steps do.
jihgf edcba 13 Use these two. They are diagonally opposite, so they are always a switching pair. Wire from another hole in the same column, not from the leg itself. The body sits over the middle channel. Two legs above it, two below.
The button sits over the middle channel with two legs above it and two below. The two circled legs are diagonally opposite, so they are always a switching pair. You wire into other holes in those same two columns, not into the legs themselves.

Wire from j1 to digital pin 2. Hole j1 is the same column as f1, so it is joined to that leg.

Wire from a3 to the − rail along the bottom edge, and one more wire from that − rail to a GND pin.

Hole a3 is the same column as e3, so it is joined to the other leg.

Plug in and upload b3_02_serial_watch.ino.

Open the serial monitor. Click Tools then Serial Monitor. A new panel opens.

Now find the baud setting. It is a small dropdown box showing a number with the word "baud" next to it, like 9600 baud. Where it sits depends on your version: newer versions put it at the top right of the panel, older ones at the bottom right. Look along both edges until you find it.

Make sure it says 9600. If it says anything else, click it and choose 9600.

"Baud" just means how fast the two sides have agreed to talk. Both sides must pick the same number or the words arrive as nonsense. Your sketch says 9600 in the Serial.begin line, so the window must say 9600 too.

You should seeA column of 1s scrolling down the window, about five a second. Press and hold the button. They change to 0. Let go and they go back to 1.
If you see strange symbols instead of numbersThe baud number does not match. Set the serial monitor to 9600.
If nothing appears at allCheck that Serial.begin(9600); is in your sketch, and that the board is still on the right Port under Tools.
If it always shows 0, even when you are not pressingYou have picked two legs from the same side of the button, so they are permanently joined. Use the diagonal rule from Part 2.
One thing that catches everybody: you usually have to close the serial monitor before you can upload a new sketch. If an upload suddenly fails and it worked a minute ago, close the serial monitor and try again.
Part 4

Why pressed means 0

25 min

You have just seen something that feels backwards. Not pressed gives 1. Pressed gives 0.

Most people expect the opposite, and it is worth understanding properly rather than just memorising, because it comes back in every band from here on.

First, see the problem for yourself

In your sketch, find this line:

pinMode(buttonPin, INPUT_PULLUP);

Change it to this, and upload again:

pinMode(buttonPin, INPUT);

Watch the serial monitor without touching anything.

You should seeThe numbers jumping about between 0 and 1 with no pattern. Nobody is pressing anything.
If the numbers sit still at 1, or still at 0Very common, and nothing is wrong. A short wire sometimes settles on one value. Pull the wire that runs from j1 to pin 2 out of the breadboard at the j1 end only, leaving its other end in pin 2, so one end dangles loose in the air. Then touch that loose end with a finger. Now they will jump about, because that wire is joined to the very pin the sketch is reading, and your body is acting as an aerial.

Pull the other wire, the one going to the − rail, and nothing interesting happens: that end sits at ground, which is a steady voltage, so there is nothing to drift.

It is completely safe to touch. Five volts cannot hurt you. This is also the one place in this whole course where you change a wire with the power on, and it is safe precisely because that wire goes nowhere except a pin.

Put the wire back in j1 when you have seen it.

Now wave your hand near the wire, or touch the wire with a finger. The numbers change even more.

Change it back to INPUT_PULLUP and upload again. Steady 1s return.

That jumping is a floating pin. It is your ordinary door from Part 1, swinging in the draught.

pinMode(pin, INPUT) pin 2 button GND Not pressed: nothing holds the pin. It reads 1 or 0 at random. pinMode(pin, INPUT_PULLUP) pin 2 resistor inside the chip 5V button GND Not pressed: reads HIGH (1). Pressed: reads LOW (0).
Why the two settings behave differently. With plain INPUT nothing holds the pin, so it reports whatever it feels like. With INPUT_PULLUP a resistor inside the chip gently holds the pin up at 5 volts, and pressing the button pulls it down to ground.

What INPUT_PULLUP actually switches on

Inside the chip there is a resistor connected to 5 volts. Normally it is disconnected. Writing INPUT_PULLUP connects it to your pin.

Now trace what happens:

  • Button not pressed. The only thing touching the pin is that resistor, which is joined to 5 volts. So the pin sits at 5 volts. The board reports HIGH, which is 1.
  • Button pressed. Now the pin also has a direct path to GND through the button. A direct path wins easily against a resistor. The pin drops to 0 volts. The board reports LOW, which is 0.

So the reading is not backwards at all. It is telling you the truth about the voltage. The spring holds the door shut, and pressing the button pulls it open.

The sentence to remember

With INPUT_PULLUP: not pressed is 1, pressed is 0.

Say it out loud twice. Write it at the top of your glossary. You will use it in every band from here to the end of the course, and it is the single most common reason a beginner's button code does the opposite of what they meant.

Why not just use a resistor of your own?You can, and many older tutorials do. It works the same way and takes an extra resistor and two extra wires per button. Using the one inside the chip is fewer parts, fewer wires, and fewer things to get wrong. With four buttons that saves you four resistors and eight wires.
Words to know
floating
A pin with nothing holding it at 0 or 5 volts. Its readings cannot be trusted.
INPUT_PULLUP
A pin setting that switches on a resistor inside the chip, holding the pin at 5 volts until something pulls it down.
digitalRead
Reads a pin and gives you back HIGH or LOW.
HIGH and LOW
The two things a digital pin can be. HIGH counts as 1, LOW counts as 0.
Part 5

Read the code before you run it

8 min
b3_01_button_light_given.ino
int buttonPin = 2;
int ledPin = 8;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  if (digitalRead(buttonPin) == LOW) {
    digitalWrite(ledPin, HIGH);
  } else {
    digitalWrite(ledPin, LOW);
  }
}
Write your answers down first
  1. What do you think happens when you hold the button down?
  2. What happens when you let go?
  3. Why does the code test for LOW rather than HIGH?
  4. There is no delay anywhere in this sketch. How many times a second do you think loop runs? Guess a number, even if you have no idea.
Part 6

Build it and run it

20 min
+ − − + j i h g f e d c b a 1 5 10 15 20 USB ARDUINO UNO button button + + 220 220 a16 a16 a18 a18 a20 a20 a3 a3 b16 b16 b18 b18 b20 b20 e1 e1 e3 e3 f1 f1 f3 f3 j1 j1 SCL SCL SDA SDA AREF AREF GND GND 13 13 12 12 11 11 10 10 9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 0 0 IOR IOR RST RST 3V3 3V3 5V 5V GND GND GND GND VIN VIN A0 A0 A1 A1 A2 A2 A3 A3 A4 A4 A5 A5
The Part 3 button stays exactly where it is. Only the light is new, and it goes in the bottom block so it shares the same − rail.

Your button is already wired from Part 3. You only need to add one light.

Before you start: clear the boardUSB unplugged. Then take Band 2 off completely: the RGB LED, its three resistors and all five of its wires, and anything else still standing — except your button from Part 3. That one stays exactly where it is: four legs in f1, f3, e1, e3, the wire from j1 to pin 2, and the wire from a3 to the − rail. Everything below assumes it is there.

This is not tidiness. Band 2's RGB LED has legs in f5 and f7, and this band's second button goes in exactly those two holes. A hole takes one leg, so a leftover leg there stops you before you start. Band 2 also leaves wires in Arduino pins 9, 10 and 11, and this band's lock needs 9 and 10.

Both ends of every wire. Pulling a wire out of the breadboard and leaving its other end in the Arduino is the fault that catches everybody, because a leftover wire in an Arduino pin is invisible when you are looking at the breadboard. One Arduino pin takes one wire, so a wire you forgot will stop you dead later in the band and you will look for it on the board, where it is not.

When you are done, columns 4 to 29 are bare and Arduino pins 3 to 11 are empty. Columns 1 and 3 still hold the button, and pin 2 still holds its wire.

Add a green LED. Long leg into b18, short leg into b20.

Those two holes are two columns apart, with column 19 empty between them. An LED's legs come out closer together than that, so gently spread them apart first, holding the plastic dome rather than the legs.

This time the LED goes in the bottom block, rows a to e, because everything in this band shares the bottom − rail.

Resistor from a18 to a16.

Wire from b16 to pin 8. Not a16: the resistor leg is in that hole, and a hole takes one leg only. b16 is in the same column, so it reaches the resistor just the same.

Wire from a20 to the − rail.

Plug in and upload b3_01_button_light_given.ino.

You should seeThe light comes on while you hold the button and goes off the moment you let go.
If the light is on until you press, and off while you pressYour test is the wrong way round, or you used INPUT instead of INPUT_PULLUP. Read Part 4 again.
If the light never comes onCheck the LED direction first, then the checklist at the bottom of this page.

Check your four answers from Part 5.

Question 4 is worth knowing: this loop runs roughly one hundred thousand times a second. Almost nobody guesses anywhere near. Hold on to that number, because it explains everything in Part 9.

Part 7

if and else, explained slowly

20 min

This is how a program makes a choice. Here is the shape:

if (something is true) {
  do these lines
} else {
  do these other lines instead
}

Break it into pieces:

  • if is the word that starts a choice.
  • The round brackets hold the question. The question must have an answer of yes or no. Nothing else.
  • The first pair of curly brackets holds what to do if the answer is yes.
  • else means "otherwise".
  • The second pair of curly brackets holds what to do if the answer is no.

The else part is optional. Leave it out and, when the answer is no, nothing happens and the program carries on below.

Asking the question

WrittenMeansExample
==is the same asif (reading == LOW)
!=is not the same asif (score != 0)
<is less thanif (light < 300)
>is more thanif (count > 10)
<=is less than or the same asif (level <= 255)
&&and. Both must be true.if (a == LOW && b == HIGH)
||or. Either one being true is enough.if (a == LOW || b == LOW)
The mistake everybody makes, at least once

One equals sign and two equals signs are completely different things.

= means put this value into that name. You met it in Band 2. level = 5 puts 5 into level.

== means are these two the same? It asks a question and gets back yes or no.

If you write if (reading = LOW) with one equals sign, you have not asked a question. You have put LOW into reading, thrown away what was there, and then asked the computer to treat the result as an answer. It compiles. It runs. And it behaves very strangely.

When an if seems to always go one way, this is the first thing to check.

You can also chain choices together with else if, which means "otherwise, if this other thing is true". The board tries each one from the top and stops at the first one that is true.

Part 8

Making the board remember

20 min

So far your button only works while you hold it. A real switch is not like that. You press once and the light stays on. Press again and it goes off. That needs memory.

Here is the idea. Keep a name outside loop, at the top of the sketch. Names declared up there survive from one round of loop to the next. Names declared inside loop are thrown away and made fresh every round.

The name we want holds only yes or no, so it uses a new type: bool.

Words to know
bool
A type of name that can only hold true or false. Nothing else.
state
Something the program remembers about the situation right now.

Try it, and watch it fail

Upload b3_03_toggle_buggy.ino. It is meant to switch the light on with one press and off with the next.

b3_03_toggle_buggy.ino  —  the part that matters
bool lightIsOn = false;

void loop() {
  if (digitalRead(buttonPin) == LOW) {

    if (lightIsOn == true) {
      lightIsOn = false;
    } else {
      lightIsOn = true;
    }

  }
  ...
}

Read it carefully. The logic looks completely right. If it is on, turn it off. Otherwise turn it on.

Press the button once, quickly, and let go.

You should seeThe light go dim, and stay dim, for as long as you hold the button down. Not flickering, and not half brightness by design: it is flipping on and off far faster than your eye can follow, which is Band 2's PWM happening entirely by accident. When you let go, it lands on whichever state it happened to be in at that instant.

Test it properly: press ten times, and after each press write down whether the light ended up on or off. You will get a roughly random list. There is no way to predict it, and pressing twice does not reliably get you back where you started.

Do not fix it yet. First work out why, in Part 9. Write down your best guess now.

If your guess involves the number you learned in Part 6, you are already close.

Part 9

Why one press counts as thousands

25 min

Two separate things are going wrong. They are often confused with each other, so we will take them one at a time.

Problem one: the loop is far faster than your finger

Your loop runs around a hundred thousand times a second. Your finger holds the button down for maybe a fifth of a second, even when you press quickly.

So the board does not see one press. It sees roughly twenty thousand rounds in a row where the button is down, and it flips the light on every single one of them.

The fix is called edge detection. Instead of asking "is the button down?", ask "has the button just gone down?" That only happens once per press, no matter how long you hold it.

To ask that, the board must remember what the button was doing last time round, and compare:

int reading = digitalRead(buttonPin);

if (reading == LOW && lastReading == HIGH) {
  // it was up, and now it is down. This is the moment of the press.
}

lastReading = reading;

Read those three parts:

  1. Take a fresh reading.
  2. Act only if it is down and it was up last time. That is the exact instant it changed.
  3. Remember this reading, so that next round it becomes "last time".

That last line is easy to leave out and the code will not complain. If you forget it, lastReading never changes and nothing works.

Problem two: the button itself is not clean

Here is a small experiment. You need a metal coin and a hard surface: a table, a tiled floor, or a plate.

Hold the coin flat, about 20 cm above the surface, and simply let go. Do not throw it. Now listen carefully.

You should hearNot one clean tap, but a little run of clicks, getting faster and quieter until they die away. Try it three or four times and listen for the run.
If you only hear one soundDrop it from higher, onto something harder, in a quiet room. A coin onto a plate is much clearer than a coin onto a cloth-covered table. A metal object works far better than a plastic bottle cap.

You let it go once. But it landed several times. Metal hitting a hard surface always does this: it bounces, touching and lifting off again a few times before it settles. And the metal inside a push button does exactly the same thing.

1 0 time about 5 thousandths of a second your finger presses once, here but the pin sees five changes
What one press really looks like to the pin. Your finger goes down once, but the metal inside the button bounces for a few thousandths of a second before it settles, so the pin sees several separate changes.

This is called bounce. In those few thousandths of a second, edge detection sees several separate presses, because as far as the pin is concerned there really were several.

The fix is called debouncing, and the simplest version is almost rude in how easy it is. Once you have accepted a press, wait a moment before looking again:

delay(50);

Fifty thousandths of a second is much longer than the bounce and far shorter than a human can press twice. By the time the board looks again, the metal has settled.

Now fix it

Upload b3_04_toggle_fixed.ino and compare it line by line with the broken one. Find the three lines that are different.

You should seeOne press, one change. Every time. Hold the button down for ten seconds and nothing extra happens.

Try breaking it on purpose. First use File then Save As to save a copy called b3_04_broken, so your working sketch stays working. Then, in the copy, delete the line lastReading = reading; and upload it.

You should seeThe old broken behaviour back again: wild flickering while you hold the button. Without that line, lastReading never changes, so the test becomes "is the button down right now", which is true thousands of times per press.

Breaking working code on purpose, to see what each piece was doing, is one of the fastest ways to learn. It costs you nothing and you can always undo it.

Words to know
edge detection
Noticing the moment something changes, rather than what it is now.
bounce
The metal inside a button making and breaking contact several times in a few thousandths of a second.
debounce
Ignoring those extra changes so one press counts as one press.
Part 10

Arrays: many things, one name

20 min

Your lock needs four buttons. Writing out four sets of everything would be long and easy to get wrong. An array solves it.

An array is one name that holds a whole list of values, side by side.

int buttonPins[4] = {2, 3, 4, 5};

Piece by piece:

  • int says these are whole numbers.
  • buttonPins is the name.
  • [4] says there are four of them.
  • {2, 3, 4, 5} are the four values, in order.

Getting one value out

You use square brackets and a position number:

buttonPins[0]   is 2
buttonPins[1]   is 3
buttonPins[2]   is 4
buttonPins[3]   is 5
Counting starts at zero

The first thing in an array is at position 0, not 1. The second is at 1. In a list of four, the last one is at position 3.

This trips up everybody at first. It is worth writing down.

And this is the reason for the pain: if you ask for buttonPins[4] in a list of four, there is nothing there. The board does not stop you and does not warn you. It reads whatever happens to be sitting in memory next door and hands you a meaningless number. Bugs like that are miserable to find, so count carefully.

Why arrays and for loops belong together

You met for in Band 2 for counting brightness. Here is what it is really for. Instead of this:

pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);

you write this:

for (int i = 0; i < 4; i = i + 1) {
  pinMode(buttonPins[i], INPUT_PULLUP);
}

The counter i takes the values 0, 1, 2, 3 in turn, and each time round, buttonPins[i] means a different pin.

Four lines became three, which does not sound like much. But go to eight buttons and the first version becomes eight lines while the second one stays exactly as it is. You change one number.

Notice the test is i < 4, not i <= 4. With four things, the positions are 0, 1, 2 and 3, so you must stop before 4. Using <= here is one of the most common bugs in all of programming.

Part 11

Build the lock

50 min
+ − − + j i h g f e d c b a 1 5 10 15 20 25 30 USB ARDUINO UNO button button button button button button button button + + 220 220 + + 220 220 + + buzzer buzzer 220 220 a11 a11 a15 a15 a16 a16 a18 a18 a20 a20 a21 a21 a23 a23 a25 a25 a27 a27 a29 a29 a3 a3 a31 a31 a7 a7 b16 b16 b18 b18 b20 b20 b21 b21 b23 b23 b25 b25 b27 b27 b29 b29 b31 b31 e1 e1 e11 e11 e13 e13 e15 e15 e3 e3 e5 e5 e7 e7 e9 e9 f1 f1 f11 f11 f13 f13 f15 f15 f3 f3 f5 f5 f7 f7 f9 f9 j1 j1 j13 j13 j5 j5 j9 j9 SCL SCL SDA SDA AREF AREF GND GND 13 13 12 12 11 11 10 10 9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 0 0 IOR IOR RST RST 3V3 3V3 5V 5V GND GND GND GND VIN VIN A0 A0 A1 A1 A2 A2 A3 A3 A4 A4 A5 A5
The finished lock. Four buttons over the channel, two lights and a buzzer in the bottom block, and one ground wire for all of them.

Everything from this band comes together now. Four buttons, two lights, one buzzer.

Build it in three stages and test after each one. Do not wire all of it and hope. If you build the whole thing and it does not work, you have seven things to check instead of two.

The pin plan

PartPinHolesNotes
Button 12legs f1, f3, e1, e3Wire j1 to pin 2, a3 to the − rail
Button 23legs f5, f7, e5, e7Wire j5 to pin 3, a7 to the − rail
Button 34legs f9, f11, e9, e11Wire j9 to pin 4, a11 to the − rail
Button 45legs f13, f15, e13, e15Wire j13 to pin 5, a15 to the − rail
Green LED8long b18, short b20Resistor a18 to a16, wire b16 to pin 8, wire a20 to the − rail
Red LED9long b23, short b25Resistor a23 to a21, wire b21 to pin 9, wire a25 to the − rail
Buzzer10long b27, short b29Wire a27 to pin 10; 220 ohm resistor a29 to a31; wire b31 to the − rail
GroundGND—One wire from the − rail to a GND pin
Pin 8 220 Ω long short Pin 9 220 Ω long short the − rail GND pin one wire only
The two lights. Each has its own resistor and its own pin, and both return to the same minus rail. The buzzer wires the same way, and it gets a 220 ohm resistor too.
Before you startUSB unplugged. Every time.

Stage one: the four buttons. Wire all four as in the table. Nothing else yet.

Every button goes in the same way as the one in Part 3
  • Sitting over the middle channel, two legs above it and two below.
  • Pressed down until it is flat and does not rock.
  • Wired using two diagonally opposite legs: the pin wire from a j hole above the groove, the rail wire from an a hole below it and at the other end.

Getting two legs from the same side is the single most common fault in this band, and it makes a button read as pressed forever. The diagonal rule prevents it every time.

Test them. Change b3_02_serial_watch.ino so buttonPin is 3, upload, and check button 2 gives 0 when pressed. Then try 4, then 5. Four quick tests, and now you know all four buttons work.

Do not skip this testFinding one bad button now takes two minutes. Finding it after everything else is wired takes an hour.

Stage two: the two lights. Wire the green and red LEDs as in the table.

Test them with b0_01_blink_given.ino from Band 0, changing all three 13s to 8 and uploading, then changing all three to 9 and uploading again.

If a light does not come onTurn the LED round first. Then work down the Band 1 checklist: both legs in the same column, legs touching, and whether the − rail really reaches a GND pin. These lights sit in the bottom block, rows a and b, so their ground goes to the bottom rail, not the top one.

Stage three: the buzzer. The active buzzer has a long leg and a short leg, just like an LED, and it only works one way round. Long leg towards the pin.

Push it into the board first: long leg into b27, short leg into b29. A buzzer is a tall black cylinder and its two legs are a few millimetres apart, so spread them to reach two columns apart, the same as the LEDs.

If its legs are set wider and will not reachUse b27 and b30 instead, and put the − rail wire in a30. Buzzers are made to two different leg spacings and both are common. Do not force the legs; they snap off flush with the case and the buzzer is then finished.
You should seeThe cylinder standing upright on the board, not leaning.

Give it a resistor, unlike the buttons. Wire a27 to pin 10 as before. Then put a 220 ohm resistor from a29 to a31, and wire b31 to the − rail. (b31, not a31: the resistor leg is in a31 and a hole takes one leg.)

The resistor sits in the buzzer's return leg rather than its pin leg, and that makes no difference at all: everything in a single loop carries the same current, so a resistor slows it down wherever you put it. You proved this in Band 1 Part 5 by moving an LED's resistor to the other side and watching nothing change. Column 25 is already the red light's, which is why the resistor goes down here.

Why a buzzer gets a resistor when the buttons do notAn active buzzer, the kind this band asks for, pulls about twenty-five to thirty thousandths of an amp, and an Arduino pin is designed for twenty. It will work without the resistor and thousands of kits are used that way, but it is outside what the chip is built for, and this course does not ask you to do things it has told you not to do. The 220 ohms makes it a little quieter and nothing else. A passive buzzer pulls far less and does not need it, but leaving the resistor in costs you nothing.

Test it with b0_01_blink_given.ino from Band 0. That sketch has the number 13 in it three times. Change all three to 10, then upload.

If it makes no soundCheck the long leg is on the pin side. If it still does nothing, you probably have a passive buzzer: the kind where you can see a small green circuit board underneath, rather than a solid black cover. A passive buzzer has no note of its own, so switching the pin on just clicks it once.

You are not stuck. Open b3_05_lock_worked.ino and make two swaps everywhere they appear: write tone(buzzerPin, 1000); in place of digitalWrite(buzzerPin, HIGH);, and noTone(buzzerPin); in place of digitalWrite(buzzerPin, LOW);. That is all. tone is Band 5's instruction and you are meeting it a band early; it pushes the buzzer back and forth for you, which is the job the active one does inside itself. The 1000 is the note, and you may change it.

Now upload b3_05_lock_worked.ino and open the serial monitor.

You should seeLock ready. Press four buttons. Then, as you press, one line per press telling you which button and how many so far. After the fourth press it tells you UNLOCKED or WRONG.

The secret is button 1, then 3, then 3, then 2. Try it.

Then get it wrong three times on purpose and watch what happens.

You should seeAfter the third wrong try, the serial monitor says LOCKED OUT for 10 seconds. The red light then blinks slowly, ten times, about once a second. During that time the lock ignores your presses completely. When it finishes, the monitor says You may try again and the lock works normally.
If nothing seems to happen after three wrong triesCount your wrong tries again. A "try" is four presses. Three wrong tries is twelve presses in total, with a red flash and a double beep after each set of four.

Reading the lock sketch

It is longer than anything you have read so far, but there is nothing in it you have not met. Here is the map. Open the file and find each piece.

PieceWhat it is doing
The arrays at the topbuttonPins holds the four pins. secret holds the correct answer. entered holds what you have typed so far.
lastReading[4]Edge detection, but four of them. Each button needs to remember its own last state, so it is an array too.
The for loop in setupSets all four button pins to INPUT_PULLUP in three lines.
The for loop in loopChecks all four buttons every round. Exactly the code from Part 9, run four times with a different i.
entered[pressCount] = i + 1;Stores which button was pressed. The + 1 turns position 0 into "button 1", because people count from one even though arrays count from zero.
if (pressCount == 4)Only when four presses are in do we check anything.
The correct checkStart by assuming it is right. Look at all four. If any single one does not match, set correct to false. One wrong digit is enough to fail.
wrongTriesCounts failures. Reset to zero on success. Three failures trigger the lockout.
pressCount = 0; at the endClears the count so the next attempt starts fresh. Leave this out and the lock jams after one try.
Part 12

Look closer

25 min

a. Follow the code by hand

Take the edge detection from b3_04. Fill in this table by hand, for a press that lasts three rounds. Do not run it.

RoundreadinglastReading at the startDoes the if run?lightIsOn after
1HIGHHIGHnofalse
2LOW   
3LOW   
4LOW   
5HIGH   
Check your table

Round 2: lastReading is HIGH, reading is LOW, so the if does run and lightIsOn becomes true.

Round 3: lastReading is now LOW, so the test fails. Nothing happens. lightIsOn stays true.

Round 4: the same. Nothing happens.

Round 5: reading is HIGH and lastReading is LOW. The test needs reading to be LOW, so it fails. Nothing happens on release, which is what we want.

One press, one change, no matter how long it was held. That is the whole point of edge detection, and the table proves it without a board.

b. Break it on purpose

In b3_04, change this line:

if (reading == LOW && lastReading == HIGH) {

to this:

if (reading == LOW || lastReading == HIGH) {

One character changed: && became ||. Upload it. Predict what will happen before you press anything, then find out.

Answer

|| means "or", so now only one of the two needs to be true. When the button is not pressed, lastReading is HIGH, so the condition is true, so the light flips. Every round, whether you touch anything or not.

But look at how fast it actually flickers. It is about ten times a second, not thousands, and the reason is worth working out: the delay(50) is inside the if, and the if now runs every round, so that delay runs every round too. It is holding the whole thing back.

Now delete that delay(50) as well and upload again. Nothing holds it back any more, so it flips far too fast to see, and the light simply looks dimly on. That is Band 2's PWM happening by accident.

Change both back.

c. Put the lines back in order

These four lines from the edge detection have been shuffled:

lastReading = reading;
int reading = digitalRead(buttonPin);
}
if (reading == LOW && lastReading == HIGH) {

Put them back in a working order. Then say in one sentence what breaks if lastReading = reading; is moved to the top instead of the bottom.

Answer

The order is: take the reading, test it, close the if, then remember the reading.

If you remember the reading first, then lastReading and reading are always the same by the time you test them, so reading == LOW && lastReading == HIGH can never be true. Nothing ever happens. The order matters because you have to compare with the old value before you overwrite it.

d. Explain one line

In b3_05, there is a line near the end of the check:

pressCount = 0;

Why is it there, and what exactly would go wrong without it?

Answer

It clears the count so the next attempt starts from the beginning.

Without it, pressCount stays at 4 forever. The check runs again every round of loop, so the lock repeats its answer over and over, roughly once every two seconds because of the delays inside it. And no new attempt is ever possible, because the guard pressCount < 4 now refuses every further press. The lock is dead until you reset the board.

That guard is worth looking at too. Take it out as well, and your next press would try to store at entered[4], which is outside a list of four, so it would write into memory belonging to something else.

That is worth sitting with. Forgetting one line does not just make the wrong thing happen. It can quietly damage another part of the program, in a way that shows up somewhere completely unrelated.

Part 13

Change it

35 min

Work on a copy of b3_05_lock_worked.ino. Use File then Save As first.

Bronze
Change the secret to four presses of your own choosing. Then make the code check for a five press secret instead of four. Count how many places you had to change the number 4, and write that number down.
Silver
Make the buzzer beep at a different length for each button, so you could tell which button was pressed with your eyes shut.
Gold
Add a reset: holding button 1 down for two seconds clears whatever has been entered so far and starts again, without counting as a wrong try. You will need to remember when the press started, which means storing a time.
A hint for Bronze, after you have tried it

The number 4 appears in several places: the size of two arrays, the test pressCount == 4, and the limit inside the for loops that check the answer.

This is Band 1's lesson coming back, one level harder. The tidy answer is to make a name at the top, like int secretLength = 4;, and use it everywhere. Then five presses is one edit.

Try it that way and count again. The difference is the point of the exercise.

A hint for Gold, if you are stuck

There is an instruction called millis() that tells you how many thousandths of a second the board has been running. Record it when the press starts, then compare it with the current value while the button is still held.

You meet millis() properly in Band 5. Doing it early here is meant to be hard, which is why it is Gold. If it beats you, write in your log what you tried. That is a perfectly good outcome.

Part 14

Make: something that guards something

100 min
What to build

A device that only does something when the right sequence is entered. Pick one, or bring your own:

  • A box lock: the green light means "you may open it", and it stays on for ten seconds
  • A quiz buzzer for two players: first to press locks the other one out until reset
  • A door code for your room, with a different secret for each family member
  • A reaction game: a light comes on at a random moment and you press as fast as you can
  • A phone charger timer: press to start, and it counts down in lights
It is finished when all of these are true
  • Every press counts once, however long it is held
  • It tells you clearly that it heard you, before it tells you whether you were right
  • Something happens when you get it wrong, not only when you get it right
  • It survives someone pressing buttons at random for thirty seconds without getting stuck
  • Somebody who has never seen it can work out what to do, with one sentence of help

The test that matters most in this band

Hand it to somebody and say only: "this is a lock, the secret is one three three two." Then say nothing else, and watch.

Watch their hands, not their face. Do they press too fast? Do they hold too long? Do they know whether a press was heard? Write down every moment they hesitated.

Hesitation is the finding. It means your device did not tell them something it should have.

If you cannot find anyone todayRecord a video of your own hands using it and watch it back tomorrow. You will spot the same hesitations, because by then you will have forgotten what you meant. If you can send it to someone, do that too. Say in your log which you did.

Show your work

If you are doing this course aloneThere may be nobody to post to and nobody else’s work to look at. That is fine, and it does not let you off this part. Do it this way instead: save the photos and the video into a folder named for this band, write the same notes into your build log, and then be your own second reader. Come back to your own sketch the next day, read it cold, and write down the one thing you would do differently now. That is the whole value of looking at somebody else’s work, and you can get most of it from your own.

Post a video of a correct entry and a wrong entry, your build log, and the list of hesitations you saw. Then look at one other person's sketch and find one place where a press might be counted twice.

If there is nobody else's sketch to look atDo it to your own, but properly. Read b3_05 line by line and write down every place a press could be counted twice if one line were removed. There are at least three. Finding them in code you wrote yourself is harder than finding them in somebody else's, so this is a fair substitute.
Part 15

Check yourself

15 min

Code you have not seen. Answer from reading only.

int buttonPin = 4;
int ledPin = 7;
int count = 0;
int lastReading = HIGH;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int reading = digitalRead(buttonPin);

  if (reading == LOW && lastReading == HIGH) {
    count = count + 1;
    Serial.println(count);
  }

  lastReading = reading;

  if (count >= 3) {
    digitalWrite(ledPin, HIGH);
  }
}
  1. What does this do, in one sentence?
  2. The button is wired with INPUT_PULLUP. What does digitalRead give when nobody is touching it?
  3. Once the light comes on, can anything ever turn it off? Explain.
  4. There is no delay for debouncing. What will you actually see in the serial monitor when you press once?
  5. Somebody deletes the line lastReading = reading;. Describe exactly what happens then.
  6. Somebody changes if (reading == LOW ...) to if (reading = LOW ...), with one equals sign. It still compiles. What will the program do?
Answers

1. It counts button presses, prints the running total, and switches a light on permanently once the count reaches three.

2. HIGH, which is 1. The resistor inside the chip holds the pin up at 5 volts until the button pulls it down.

3. No. Nothing in the sketch ever sets ledPin to LOW, and count never goes back down. Once it is on it stays on until the board is reset or unplugged.

4. Usually more than one number. Without a debounce delay, the bounce inside the button produces several separate changes from up to down, and edge detection counts each one. You will often see the count jump by two or three from a single press. It will not be a hundred thousand, because edge detection is working, but it will not reliably be one either. Getting this apart from question 5 is the whole point of Part 9.

5. lastReading stays HIGH forever. So the test becomes "is the button down right now", which is true on every round while you hold it. The count races upward far too fast to control, and the light comes on almost instantly. It will not literally be a hundred thousand a second, because printing to the serial monitor is slow and holds the loop back, but it will be tens or hundreds per press and it is completely unusable.

6. One equals sign puts LOW into reading instead of asking a question. LOW is 0, and a result of 0 counts as "no", so the if never runs. The count stays at zero, the light never comes on, and nothing ever gets printed. This is the question that matters most in this band. It compiles, it runs, and it silently does nothing.

Part 16

When something goes wrong

Work down this list in order.

What you seeWhat it usually isWhat to do
The button always reads 0, even untouchedYou used two legs from the same sideThey are permanently joined. Use two diagonally opposite legs.
The reading jumps about with nobody touching itINPUT instead of INPUT_PULLUPThe pin is floating. Change the pinMode line.
The button does nothing at allA leg not pushed fully in, or the wrong columnPress the button down hard until it clicks flat. Then check the wire is in the same column as the leg.
One press counts as severalNo debounceAdd delay(50); inside the if, after you accept the press.
One press counts as thousandsNo edge detection, or a missing lastReading = reading;Check both. They are different faults with different fixes. See Part 9.
Strange symbols in the serial monitorBaud rate mismatchSet the serial monitor to 9600, the same as in Serial.begin.
Upload suddenly fails and used to workThe serial monitor is holding the portClose the serial monitor and upload again.
An if always goes the same wayOne equals sign instead of twoSearch your sketch for = inside round brackets. It should almost always be ==.
The buzzer makes no soundWrong way round, or it is a passive buzzerLong leg to the pin. If it is passive, swap every digitalWrite(buzzerPin, HIGH) for tone(buzzerPin, 1000) and every LOW for noTone(buzzerPin). Part 11 stage three explains it.
The lock works once then jamspressCount not resetSee Part 12d.
Still stuck after 30 minutes?

Photograph the breadboard from directly above in good light. Post the photo, your sketch, and one sentence saying what you expected and what happened instead. Do not rebuild it first.

If there is nobody to send it toWrite the three things down anyway, in your build log: what you expected, what happened, and what you have already tried. Putting a problem into words solves a surprising number of them on its own. Then work down the checklist above one more time, slowly. If it is still stuck, leave it until tomorrow and come back fresh. Do not sit and stare at it.

In this band, also post a few lines copied from your serial monitor. Very often that alone tells someone exactly what is wrong.

Done

What you know now

Your board can listen now. It can decide. It can remember. And you have a window into it, so when it does something odd you can ask it what it thinks is happening instead of guessing.

You also met a fault that no error message will ever find for you: code that is perfectly correct English and completely wrong. That is most of real programming.

Before you move on, make sure you have

  • Your lock, working, with a video of a right and a wrong entry
  • Your list of hesitations, from the person who tested it or from watching your own video back
  • Your completed table from Part 12a
  • Your number from Bronze
  • The sentence "not pressed is 1, pressed is 0" in your glossary
  • Your build log for this band
  • Five or six correct answers in Part 15, including question 6

Next is Band 4: Night Guard. Your buttons only ever say yes or no. Next you meet sensors, which give you a number between 0 and 1023, and you find out that the number means nothing until you decide what it means.

Ship it

One video under sixty seconds, and one post of five lines. The video is the same one this band already asks you for as evidence, so this is not extra work. It is the same work, done once, somewhere a person can see it.

Point the camera at: a right entry, a wrong entry, and the lockout, in that order.

The four shots: three seconds of the thing sitting still, fifteen of you doing something to it, fifteen of it responding all the way to the end, and ten of the honest bit, which is your measurement or the wire that was wrong the first time.

The line worth writing in the post: why a pressed button reads as 0 and not 1.

Show your work has the five-line template, a worked example, and the three checks to make before anything goes public. Tag it #BozomaBuilds so all nine of yours sit together.