Arduino Ladder · Bozoma Innovation Hub

Bozoma Innovation Hub  ·  Arduino Ladder  ·  Band 5

Simon Says

The hardest band so far, and the most important one. You write your own instructions for the first time. And you find out that delay, which you have used in every band since Band 0, has been quietly holding you back the whole time.

About 8 hours. Finish Bands 3 and 4 first. This band uses buttons, arrays, the serial monitor and for loops as though you already know them, because you do.

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

Why this band matters more than the others

Everything you have built so far does one thing at a time, in order, and never has to pay attention to anything while it is busy. That has been fine. It stops being fine here.

A game has to watch for a button while a light is doing something else. As soon as you want two things happening at once, the way you have been writing code falls apart. Not slowly. Completely.

So this band does three things:

  • You write your own instructions. Until now you have used instructions somebody else wrote: digitalWrite, analogRead, map. Now you make your own and give them names. This is called writing a function, and it is the single most useful idea in programming.
  • You break delay on purpose. You will build something that fails, understand exactly why, and then fix it properly.
  • You build a real game. Not a demonstration. A thing people will actually pick up and play, and lose at, and ask for another go.

What you need

  • Your board, USB cable and breadboard
  • Four LEDs, ideally four different colours
  • Four 220 ohm resistors
  • Four push buttons
  • A passive buzzer. This is the one where you can see a small green circuit board underneath, rather than a solid black cover.
  • About twenty jumper wires
About the breadboardThis build uses more room than anything before it: it runs to about column 42, and a small breadboard only has 30. If your kit has a large breadboard, use it.

If you only have small ones, split the build across two: lights on the first board, buttons and buzzer on the second. On the second board put the buttons at columns 1&3, 6&8, 11&13 and 16&18, each straddling the middle channel, and the buzzer with its long leg in b21 and its short leg in b23. Use the bottom rails on both boards, the pair nearest row a. Then run one wire between the two − rails to join them, and take one wire from either rail to a GND pin. Until you run that joining wire the two boards' rails are separate, and a build split across two boards with unjoined rails looks exactly like a build with a bad wire.

Important: if you split it this way, the hole names in the tables below no longer match your boards. The tables assume one long board. Use them for which pin goes to which part, which is the part that matters, and work out the hole names on your own two boards from the column numbers listed just above. Write your own version of the table on paper before you start wiring, or you will lose your place halfway through.

By the end you will be able to

  • Write your own instruction, give it a name, and hand it values
  • Get an answer back out of an instruction you wrote
  • Make a sound at any pitch you choose
  • Explain, with a real example, why delay makes a program deaf
  • Use millis to make something happen on time without stopping everything else
  • Build a program long enough that it has to be organised, and organise it
Part 1

Think first: counting to sixty

12 min

If you have someone with you

Give them two jobs at once.

  1. Job one: count out loud from 1 to 60, one number per second, slowly.
  2. Job two: if I clap, put your hand up straight away.
  3. The rule: you must finish counting to 60 before you are allowed to check whether I clapped.

Start them counting. Clap once when they reach about 12. Then wait and watch.

You should seeThey keep counting. The clap happens at about 12 and their hand stays down. It only goes up once they have said “sixty”. That is roughly forty-eight seconds late, and you can watch the whole delay happen.

If you are on your own

You can run the same thing alone. You need a clock you can see, so use the clock on your phone, a wall clock with a second hand, or a watch.

Put a pen and paper in front of you, and put the clock where you can see it without turning your head.

Pick a number between 10 and 20 and say it out loud now. That is your signal number. Say it is 14.

Count out loud from 1 to 60, one number per second. To keep one per second, say “one and, two and, three and” in your head. Do not rush, and do not stop for anything.

When you say your signal number, that is the moment the clap happens. Notice the time on the clock as you say it, and keep counting anyway. Your rule is that you may not write anything down until you have finished saying “sixty”.

The instant you finish saying “sixty”, look at the clock and write down the time. Then write down the difference between the two times.

You should seeA gap of about forty-five seconds between the signal and the moment you were finally allowed to act on it.

Now change one rule and run it again. This time you are allowed to stop and write the time down the very moment you say your signal number, then carry on counting to 60 from where you left off.

You should seeThe gap is now under a second. The counting still reaches 60. It took you a heartbeat longer overall, and nothing else was harmed.
Write these down
  1. In the first run, how long was it between the signal and the response?
  2. Was the ear broken? Was the signal missed?
  3. In the second run, how long was it?
  4. In the second run, did the counting get noticeably slower, or was it about the same?

That is this whole band in one activity. Keep the paper.

The person was never deaf. They were busy, and the rule said they could not look up until they had finished. Change the rule so they look up constantly, and the response becomes instant while the counting carries on exactly as before.

Your board is that person. delay is that rule.

Part 2

Making a sound

20 min

Sound is something being pushed back and forth very fast. Fast pushing makes air wobble, the wobble reaches your ear, and you hear a note.

How fast it wobbles is the pitch. Fast wobbling is a high note. Slow wobbling is a low note. We measure it in wobbles per second, and the unit is called hertz, written Hz.

Two kinds of buzzer, and why it matters here

KindHow to spot itWhat it does
ActiveSolid black cover on the bottom, usually with a stickerHas its own wobbler inside. Switch it on with digitalWrite and it makes one fixed note. You used this in Band 3.
PassiveYou can see a small green circuit board underneathHas no wobbler of its own. You have to push it back and forth yourself, from the board. That means you choose the note.

This band needs the passive one, because the game plays four different notes.

If you only have an active buzzerEverything in this band still works. You will get the same fixed beep for all four lights instead of four different notes, so the game becomes a bit harder to play but no harder to build. Note it in your log and carry on.

The instruction

tone(buzzerPin, 262);   // start a note and keep it going
noTone(buzzerPin);      // stop it

The second number is the pitch in hertz. Bigger number, higher note.

tone starts the note and then carries straight on to the next line. It does not wait. If you want a note to last, follow it with a delay, and then noTone to stop it.

Here are four notes that sound good together. You do not have to use these.

NumberRoughly
262a low note
330a bit higher
392higher again
523the same as the first, but one octave up

Notice 523 is almost exactly double 262. Doubling the number always gives you the same note, higher. That is not a coincidence, it is what an octave is.

Words to know
pitch
How high or low a note sounds.
hertz
Wobbles per second. The unit of pitch. Written Hz.
tone
Starts a note on a pin at a pitch you choose. Does not wait.
noTone
Stops the note on that pin.
Part 3

Read the code before you run it

6 min
b5_01_tone_given.ino
int buzzerPin = 12;

void setup() {
  pinMode(buzzerPin, OUTPUT);
}

void loop() {
  tone(buzzerPin, 262);
  delay(400);

  tone(buzzerPin, 330);
  delay(400);

  tone(buzzerPin, 392);
  delay(400);

  noTone(buzzerPin);
  delay(1200);
}
Write your answers down first
  1. How many separate notes will you hear each time round?
  2. Is there any silence between the notes? Look carefully.
  3. How long is the silence at the end?
  4. What would happen if you deleted every delay line but left everything else?
An answer to think about, after you have guessed

Question 2 catches people. There is no silence between the three notes. tone replaces whatever note was playing, immediately. To get a gap you would have to put noTone and a short delay between them.

Question 4: you would hear only the last thing, and only for an instant, because all four lines would run in a few millionths of a second. Probably a faint click.

Part 4

Build the four lights and the buzzer

40 min
+ − − + j i h g f e d c b a 1 5 10 15 20 25 30 35 40 45 USB ARDUINO UNO + + 220 220 + + 220 220 + + 220 220 + + 220 220 button button button button button button button button + + buzzer buzzer a1 a1 a11 a11 a12 a12 a13 a13 a15 a15 a16 a16 a21 a21 a26 a26 a3 a3 a31 a31 a36 a36 a4 a4 a40 a40 a42 a42 a5 a5 a7 a7 a8 a8 a9 a9 b1 b1 b11 b11 b12 b12 b13 b13 b15 b15 b16 b16 b3 b3 b4 b4 b40 b40 b42 b42 b5 b5 b7 b7 b8 b8 b9 b9 e19 e19 e21 e21 e24 e24 e26 e26 e29 e29 e31 e31 e34 e34 e36 e36 f19 f19 f21 f21 f24 f24 f26 f26 f29 f29 f31 f31 f34 f34 f36 f36 j19 j19 j24 j24 j29 j29 j34 j34 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
Nine parts, twenty-one wires, one ground. Worth counting against your own board before you plug anything in.
Start from an empty boardUnplug the USB. Take everything from Band 4 off the breadboard: pull out every wire, every light, every resistor, the light sensor and the potentiometer, the round knob with three legs, and put them all back in the box. The potentiometer matters more than it looks: it sits on A0, and Part 8 of this band needs A0 completely empty or your game will play the same sequence every single time. This build needs almost the whole board, and a single leftover leg from the last build will send current somewhere you did not intend. An empty board also means that when something does not work, it is something you put there today.

Both ends of every wire. Pull them out of the Arduino header too, so that nothing is left in Arduino pins 2 to 12. A wire you forgot is invisible when you are looking at the breadboard, and one Arduino pin takes one wire, so it will stop you dead later and you will hunt for it in the wrong place.

Build in stages and test each stage. There are thirteen parts in the finished thing. If you wire all of it and it fails, you have thirteen suspects.

The pin plan

PartPinHolesWiring
Light 12long b3, short b4Resistor a3 to a1; wire b1 to pin 2; wire a4 to − rail
Light 23long b7, short b8Resistor a7 to a5; wire b5 to pin 3; wire a8 to − rail
Light 34long b11, short b12Resistor a11 to a9; wire b9 to pin 4; wire a12 to − rail
Light 45long b15, short b16Resistor a15 to a13; wire b13 to pin 5; wire a16 to − rail
Button 18legs in e19, e21, f19, f21Wire j19 to pin 8; wire a21 to − rail
Button 29legs in e24, e26, f24, f26Wire j24 to pin 9; wire a26 to − rail
Button 310legs in e29, e31, f29, f31Wire j29 to pin 10; wire a31 to − rail
Button 411legs in e34, e36, f34, f36Wire j34 to pin 11; wire a36 to − rail
Buzzer12long b40, short b42Wire a40 to pin 12; wire a42 to − rail. No resistor.
GroundGND—One wire from the − rail to a GND pin

Each light’s pin wire goes in row b, not row a, because the resistor leg is already in the row a hole and a hole takes one leg only. The five holes in a column are joined underneath, so b1 reaches the resistor in a1 perfectly well.

Everything shares the one − rail along the bottom edge. Buttons straddle the middle channel, so their pin wire comes from the top side and their ground wire from the bottom side. That is the diagonal rule from Band 3.

How each button sits on the board

You have done this once, in Band 3, and now you are doing it four times. It is worth a minute to get it right, because a button that is turned the wrong way is the single most common reason a build like this half works.

A push button has four legs, not two. They come out of the four corners of its square body, and they need four holes. The body sits over the middle channel of the breadboard, the groove running down the centre, so that two legs land in the top half and two land in the bottom half.

jihgf edcba 1921 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.
One button, seen from above. Its body covers the middle channel. Two legs go into row e and two into row f, in two columns. The two circled legs are a matching diagonal pair: one wire goes to the Arduino pin, the other to the minus rail.

Take button 1 as the example. Its four legs go into e19, e21, f19 and f21. To seat it, line the four legs up over those four holes, then press straight down on the body with your thumb, not on a leg, until the body is almost touching the board. It will feel stiff and it may take real pressure. If it goes in crooked, pull it straight back out by the body, straighten any bent leg with your fingers, and try again.

You should seeThe button sitting flat and square, its body over the channel, and it does not rock or fall out when you nudge it sideways.

Then the two wires. The pin wire goes into the top half in one column, and the ground wire into the bottom half in the other column. For button 1 that is j19 to pin 8, and a21 to the minus rail. Different column, different half. That is the diagonal.

Why diagonalTwo of a button's four legs are permanently joined inside it, and it is the two on the same side. Wiring across a joined pair means your circuit is complete before you ever press anything, and the board will behave as if the button is held down forever.
Pin 2 220 Ω long short Pin 3 220 Ω long short Pin 4 220 Ω long short Pin 5 220 Ω long short the − rail GND pin one wire only
The four lights. Each has its own pin and its own resistor, and all four return to the same minus rail. The buttons and the buzzer join the same rail.
Before you startUSB unplugged. Every time.

Stage one: the four lights. Wire all four as in the table, then test them one at a time.

To test them, open b0_01_blink_given.ino from Band 0. That sketch has the number 13 written in it three times, on these three lines:

pinMode(13, OUTPUT);
digitalWrite(13, HIGH);
digitalWrite(13, LOW);

Change all three of those 13s to 2, upload, and watch light 1. Then change all three to 3, upload again, and watch light 2. Then 4, then 5. Miss one and nothing blinks, which is itself worth seeing once.

You should seeOne light, and only one, blinking on and off about once a second, each time you upload. Four uploads, four lights, one at a time.
If one light does not come onTurn that LED round first. Then check its two legs are in different columns, and that its resistor really joins the light's column to the wire's column. These lights are in the bottom block, rows a and b, so their ground goes to the bottom − rail.
If none of them come onCheck the one wire from the − rail to a GND pin. One missing wire there stops all four at once and everything else looks perfect.

Stage two: the buzzer. Long leg towards the pin. Upload b5_01_tone_given.ino.

You should seeThree notes rising, then a pause, over and over.
If it clicks instead of playing notesYou probably have an active buzzer. See Part 2. Carry on with a fixed beep.
If it makes no sound at allCheck the long leg is on the pin side, and that your buzzer is really on pin 12.

Stage three: the four buttons. Seat all four as described above, wire them as in the table, then test each one on its own.

To test them, open b3_02_serial_watch.ino from Band 3. Near the top it says int buttonPin = 2;. Change the 2 to 8, upload, then open the serial monitor: in the Arduino software click Tools, then Serial Monitor, and check the little box in the corner says 9600. Press button 1 and watch the numbers. Then change it to 9 for button 2, 10 for button 3, and 11 for button 4.

You should seeA steady stream of 1 while you are not touching the button, changing to 0 for exactly as long as you hold it down, and back to 1 when you let go.
If it reads 0 all the time, even untouchedThat button is wired across a joined pair of legs instead of a diagonal. Move one of its two wires to the other column, keeping it in the same half of the board.
If it reads 1 all the time, even when pressedEither the button is not pushed fully into the board, or one of its two wires is in a column the button does not actually reach. Check the four holes against the table.
Do not skip thisFour buttons is where the diagonal-leg mistake usually appears. Two minutes of testing now saves an hour later.

Now upload b5_02_functions_worked.ino.

You should seeEach light coming on in turn with its own note, then a pause, over and over. All four lights and all four notes in one sketch.
If the lights go in the wrong orderYour lights are not physically left to right in the order 2, 3, 4, 5. Either move the wires or just note down which is which. Nothing is broken.
If one light stays dark but worked in stage oneLook at the arrays at the top of the sketch. ledPins must list the four pins you actually used.
Part 5

Writing your own instruction

35 min

Look at what that sketch would need without any new ideas. To play light 1 with its note, then light 2, then 3, then 4, you would write this five-line block four times over:

digitalWrite(ledPins[0], HIGH);
tone(buzzerPin, notes[0]);
delay(400);
digitalWrite(ledPins[0], LOW);
noTone(buzzerPin);

Twenty lines, nearly identical, differing by one number. And if you later decide the note should last 300 instead of 400, you have to find and change it in four places, and getting one wrong gives you a bug that is very hard to see.

Here is the alternative. Open b5_02_functions_worked.ino and find this:

b5_02_functions_worked.ino
void playStep(int which) {
  digitalWrite(ledPins[which], HIGH);
  tone(buzzerPin, notes[which]);
  delay(400);

  digitalWrite(ledPins[which], LOW);
  noTone(buzzerPin);
  delay(120);
}

And then, down in loop:

playStep(0);
playStep(1);
playStep(2);
playStep(3);

Twenty lines became four. And the 400 exists in exactly one place.

How to read a function, piece by piece

void playStep(int which) {
PieceWhat it means
voidThis instruction does not hand an answer back. It just does things. You have been writing this word since Band 0 without knowing why. Now you do.
playStepThe name you chose. Any name you like, as long as it has no spaces and starts with a letter. Choose one that says what it does.
(int which)An empty box that gets filled in when the instruction is used. int says it holds a whole number, and which is the name that box has inside the instruction.
{ ... }The lines to run, exactly like setup and loop.
What happens when you write playStep(2)
  1. The board stops what it is doing in loop and remembers where it was.
  2. It puts the number 2 into the box called which.
  3. It runs every line inside playStep, and everywhere it sees which, it uses 2.
  4. When it reaches the closing curly bracket, it goes back to loop and carries on from where it stopped.

So the same seven lines do a different job each time, depending on the number you hand them.

Getting an answer back

Some instructions do hand something back. digitalRead does: you get a HIGH or a LOW. To write one of your own, you replace void with the type of thing you are handing back, and use the word return.

int waitForPress() {
  ...
  return i;
}

Read it as: this instruction hands back a whole number. When it reaches return i, it stops immediately and gives back whatever is in i.

Then you can use it like any other instruction that produces a value:

int pressed = waitForPress();

return also ends the instruction there and then. Any lines below it do not run.

Three rules that will save you
  1. Write your functions above setup. The Arduino software is clever enough to find them wherever you put them, so it will still work if you do not. Put them at the top anyway: a reader meets each tool before they meet the code that uses it, and that is much easier to follow.
  2. A name made inside a function only exists inside it. If playStep makes a name called x, then loop cannot see it. That is a feature, not a problem: it means you can never accidentally break one part of your program by changing another.
  3. One function, one job. If you cannot say what yours does in one short sentence, it is doing too much. Split it.

Try it now. In your copy of b5_02, change the delay(400) inside playStep to delay(150) and upload.

You should seeAll four steps get shorter. One edit, four changes, and no chance of missing one.

Now write one yourself. Add a function called flashAll that switches all four lights on, waits, and switches them all off. Then call it from loop before the four playStep lines, and upload.

You should seeAll four lights come on together for a moment and go off again, then the usual four steps play one after another with their notes, then the whole thing repeats.
If nothing changedYou wrote the function but never called it. A function that is never called by name never runs. Check that the line flashAll(); really is inside loop.
Check yours against this, after you have written it
void flashAll() {
  for (int i = 0; i < 4; i = i + 1) {
    digitalWrite(ledPins[i], HIGH);
  }
  delay(300);
  for (int i = 0; i < 4; i = i + 1) {
    digitalWrite(ledPins[i], LOW);
  }
}

Yours does not have to match. If it works and you can say what it does in one sentence, it is right. Notice the empty round brackets: this one needs no values handed to it.

Words to know
function
A named group of instructions you can run from anywhere by using its name.
call
To run a function by writing its name, like playStep(2);
parameter
The box in the round brackets that gets filled in when the function is called.
return
Hands a value back to whatever called the function, and ends it immediately.
Part 6

Now break it on purpose

25 min

Here is a small thing that ought to be easy. A lamp fades slowly up and down, and a second light comes on whenever you hold a button. Two simple jobs you can already do.

Move four wires

You do not need to rebuild anything. Keep everything exactly where it is and move four wires. Unplug the USB first.

Work the table from the top down, in order. The order is not decoration. Only one wire fits in an Arduino pin, and pins 9 and 8 are both occupied right now, so the two wires that are in the way have to come out before the two new ones can go in.

WireWas inMove it toWhat it becomes
1. From j24pin 9nowhere — take it right outFrees pin 9. Pull it out of the Arduino end only and leave the other end sitting in j24, so you cannot lose it. Before Part 9 you push that free end back into pin 9.
2. From j19pin 8pin 6Frees pin 8. Button 1 is now the button.
3. From b1pin 2pin 9Light 1 is now the fading lamp
4. From b5pin 3pin 8Light 2 is now the button's answer light

Pin 9 matters. It is the fading lamp, and only a pin with a ~ can fade. Pin 2, where that light was, cannot. Lights 3 and 4 and buttons 3 and 4 are simply not used in this part, so leave them exactly where they are. Button 2 is not used either, but its wire had to come out of pin 9 to make room.

When you have finished Part 7Put all four wires back before Part 9, or the game will not work. That means b1 back to pin 2, b5 back to pin 3, j19 back to pin 8, and j24 back into pin 9. The pin table in Part 4 is the one to check against.

Upload b5_03_blocking_problem.ino. Then press the button.

What you will actually seeThe fade works perfectly. The button almost never does. You press it and nothing happens. You press it again and nothing happens. Then once in a while, for no reason you can see, it works.
Before you read on, answer this

Look at the sketch. Nothing in it is wrong. The wiring is right. The button test is exactly the same one that worked perfectly in Band 3.

So why does the button not work? Write your answer down.

The answer

Count the delays. The two for loops run 128 times in total, each with delay(20). That is 2560 thousandths of a second, about two and a half seconds, spent doing nothing.

The button is checked once, at the very bottom, after all of that. So the board looks at the button for a few millionths of a second, once every two and a half seconds.

Your press has to still be held down at exactly that instant to be seen. A normal press lasts perhaps a fifth of a second out of every two and a half, so you get lucky about one time in twelve. That matches what you saw: mostly nothing, and then once in a while it works for no reason you could see.

The board is not broken and your code is not wrong. It is the person counting to sixty, from Part 1. It is busy, and the rule says it may not look up.

This is worth sitting with, because it is not a small technical detail. It is the reason your projects have all been simple so far.

Every delay you have ever written has stopped the entire board. Not the light. Not the line it is on. Everything. For that whole time the board cannot read a button, cannot check a sensor, cannot do anything at all.

Up to now that never mattered, because your projects only ever did one thing. From here it matters in everything you build.

Part 7

millis: waiting without stopping

35 min

Here is the change in thinking, and it is a real change, so go slowly.

Instead of stopping until it is time, keep running and keep asking whether it is time yet.

That is exactly the rule change in the last step of Part 1, the one where you were allowed to write the time down the moment you said your signal number instead of waiting until sixty. The person keeps counting and checks the door after every number.

using delay() frozen inside delay(20) doing something you press the button here nothing notices using millis() loop runs again and again, thousands of times a second same press, noticed straight away
The same button press, in two programs. With delay the board spends nearly all its time frozen, so a press that lands during a wait is never noticed. With millis the loop keeps running the whole time, so the same press is caught within a few millionths of a second.

The instruction

millis()

It hands back how many thousandths of a second the board has been running since it was switched on. That is all it does. It does not wait, it does not pause, it just tells you the time.

It is a clock that started when the board did. Not the time of day. The board has no idea what day it is.

Why it needs a strange type

After one minute, millis() is already 60 000. After an hour it is 3 600 000. An int cannot hold numbers that big.

So you store it in an unsigned long, which is a name for a number that can be very large but never negative.

unsigned long now = millis();

Use int here and your program will work for about half a minute and then behave very strangely. It is a horrible bug to find, so learn the type now.

The pattern

Every non-blocking timer you will ever write looks like this. Learn the shape, not the details. The sketch calls these two names lastFadeTime and fadeGap; here they are shortened to lastTime and gap so the shape is easier to see.

unsigned long lastTime = 0;
unsigned long gap = 20;

void loop() {
  unsigned long now = millis();

  if (now - lastTime >= gap) {
    lastTime = now;

    // it is time. Do the thing.
  }

  // everything else, every single round
}

Read it as three ideas:

  1. lastTime remembers when you last did the thing.
  2. now - lastTime is how long it has been since. If that is at least gap, it is time.
  3. The moment you do the thing, write down the new time, so the count starts again.

And crucially: everything below the if runs on every single round, thousands of times a second, whether it was time or not.

Upload b5_04_millis_fixed.ino, with the same wiring as before.

You should seeThe lamp fading exactly as before, and the button responding instantly, every single time, even in the middle of a fade.

Compare the two sketches side by side. The fade looks the same to a person watching. The code is completely different in shape.

In b5_03, the brightness lived in a for loop. In b5_04, the brightness has to be remembered in a name outside loop, because loop now only does one step of the fade each time it runs and then leaves.

Find the two lines that reverse the direction. They are the ones that check whether level has reached an end and flip step between 4 and −4.

A for loop could not do this, because a for loop insists on finishing before it lets go. Once you stop letting the loop control the timing, you have to control the counting yourself. That is the trade.

When delay is still fine

Do not now decide that delay is always wrong. It is not, and thinking so leads people to write complicated code for no reason.

delay is fine whenever the board genuinely has nothing else to do. A 50 thousandth of a second debounce is fine. Playing a note in the game, while the player is only watching, is fine.

The rule: delay is a problem only when something else needs to happen during it. Ask that question and the answer is usually obvious.

Words to know
millis
Gives you how many thousandths of a second the board has been running.
unsigned long
A name that holds a very large whole number that is never negative. Needed for millis.
blocking
Code that stops the whole board while it waits. delay is blocking.
non-blocking
Code that checks whether it is time yet and carries on regardless.
Part 8

Making it unpredictable

15 min

A memory game is no fun if it plays the same sequence every time. You need a number the player cannot predict.

int which = random(0, 4);

That gives you 0, 1, 2 or 3, chosen at random.

Read the two numbers carefully

random(0, 4) includes the first number and excludes the second. So you get 0, 1, 2 or 3. Never 4.

That is deliberate and it matches how arrays count. random(0, 4) gives you exactly the valid positions in a list of four, which is almost always what you want. But it surprises everybody the first time.

The problem with random on a small chip

The board has no real source of randomness. It runs a calculation that produces numbers that look random. And it starts that calculation from the same place every time it is switched on.

So without help, your game plays the identical "random" sequence every single time you power it up. Try it if you like: it is a strange thing to watch.

The fix is to start the calculation from somewhere different each time:

randomSeed(analogRead(A0));

You do not have to type this in anywhere. The finished game sketch you will upload in Part 9, b5_05_simon_worked.ino, already has that line in its setup, just below the loop that sets the pin modes. Your only job here is to understand why it is there, and to make sure nothing is wired to A0. It reads an analog pin with nothing connected to it, which, as you found out in Band 4, gives a meaningless drifting number. Meaningless is exactly what is wanted here. For once, a floating pin is useful.

Make sure A0 really is emptyIf you have something wired to A0, use a different unused analog pin. A pin with a sensor on it gives a steady number, which defeats the purpose.
Part 9

Build the game

40 min

Everything is wired from Part 4. Put your wiring back the way the pin table describes it if you moved anything for Part 6. All four lights, all four buttons and the buzzer need to be working.

Which button belongs to which light

The game pairs them up by position in the sketch's two lists. Button 1 answers light 1, button 2 answers light 2, and so on:

When this light flashesPress this buttonLight pinButton pin
Light 1Button 128
Light 2Button 239
Light 3Button 3410
Light 4Button 4511

If you built the board left to right in the order the table in Part 4 gives, then light 1 is the leftmost light and button 1 is the leftmost button, and the pairing is simply straight down the board. If your parts ended up in a different order, that is fine, but write the pairing on a slip of paper and put it next to the board before you play, or every guess will be a guess.

Upload b5_05_simon_worked.ino and open the serial monitor.

You should seeOne light flashes with its note. Copy it by pressing the button that goes with it. Then two lights, then three. Get one wrong and all four flash with a low buzz, and your score prints.

Read it as a set of small jobs

This is the longest sketch in the course so far, at about ninety lines. It is not harder than what you have read before. It is organised, which is the whole point of this band.

Open it and find each of these. Notice that you can understand any one of them without reading the others.

FunctionIts one job
playStep(which)Show one light and play its note.
allLights(state)Switch all four lights on or off together.
waitForPress()Wait until a button is pressed, play its light and note, wait for release, and hand back which one it was.
gameOver()Print the score, flash and buzz, and reset the game.
setup()Set up all the pins, start the randomness, start the serial monitor.
loop()One round of the game: add a step, play the sequence, listen to the answer.

Now look at loop on its own. Strip away the details and it says: add a new random step, play the whole sequence, then check the player's answer one press at a time. If any press is wrong, game over.

Nine lines of real work. Everything else has a name and lives somewhere else. That is what functions bought you.

Two things worth noticing in waitForPress

It waits for you to let go. There is a small loop that does nothing until the button is released. Without it, one long press would count as many presses, and the whole sequence would fail instantly. It is Band 3's problem in a new place.

It blocks, on purpose. It sits there and refuses to do anything else until a button is pressed. Ten minutes ago you learned that blocking is bad. Here it is fine, because while the game is waiting for the player there is genuinely nothing else it needs to do. In Part 7 you learned when delay causes trouble. Here it causes none, so delay is the right choice. A rule is worth more when you know when it does not apply.

If the game starts and immediately says game overA button is reading LOW when nothing is pressed. Test each button on its own with the Band 3 serial watch sketch. It is almost always two legs from the same side.
If it plays the same sequence every time you switch it onThe randomSeed line is missing, or A0 has something wired to it. See Part 8.
Part 10

Look closer

30 min

a. Follow the timing by hand

Fill this in for b5_04, assuming fadeGap is 20 and the board has been running a while. Do not run it.

RoundnowlastFadeTimenow - lastFadeTimeIs it 20 or more?Fade moves?Button checked?
1100010000nonoyes
210051000    
310191000    
410201000    
51021     
Check your table

Round 2: difference 5, not enough, fade does not move, button still checked. Round 3: difference 19, still not enough, button still checked. Round 4: difference 20, which is 20 or more, so the fade moves and lastFadeTime becomes 1020. Round 5: lastFadeTime is now 1020, so the difference is 1, not enough.

The column that matters is the last one. The button is checked on every single round, whether the fade moved or not. That is the whole difference from b5_03, and it is why the button feels instant.

b. Break it on purpose

Move the four wires again firstb5_04 needs the Part 6 wiring, not the game wiring. Unplug the USB and work down the Part 6 table again, in order: j24 out of pin 9, j19 from pin 8 to pin 6, b1 from pin 2 to pin 9, b5 from pin 3 to pin 8. When you have finished this experiment, put all four back the way the Part 4 pin table says: b1 to pin 2, b5 to pin 3, j19 to pin 8, j24 to pin 9.

In b5_04, delete the line lastFadeTime = now; from inside the if. Predict what will happen, then upload.

You should seeLight 1, the fading lamp, stops fading smoothly. It goes to a flicker, or looks oddly dim and steady, because the fade is now racing far too fast for your eye to follow.
Answer

lastFadeTime stays at 0 forever. So now - lastFadeTime is just now, which is always far bigger than 20. The if is true on every round, so the fade advances thousands of times a second instead of fifty times a second.

The lamp will flicker or look oddly dim, and the fade will be far too fast to see as a fade.

The lesson: in this pattern, writing down the new time is not tidying up. It is the timer. Put it back.

c. Put the lines back in order

These lines from a function have been shuffled:

  return total;
int addUp(int a, int b) {
  int total = a + b;
}

Put them in order. Then answer two questions: why is the first word int and not void? And what would happen if return total; came before int total = a + b;?

Answer

Order: the header line, then make total, then return it, then the closing bracket.

It says int because this function hands a whole number back. void means it hands nothing back, and this one clearly does.

If return came first, the sketch would not compile, because total would not exist yet. And even if it did, return ends the function immediately, so the line below it would never run.

d. Explain one line

In b5_05, inside waitForPress, there is this:

while (digitalRead(buttonPins[i]) == LOW) {
  delay(5);
}

It does nothing at all. It has no body worth speaking of. Why is it there, and what exactly breaks without it?

Answer

It waits for you to let go of the button. while means "keep doing this for as long as the test is true", so as long as the button is still down, it keeps doing nothing.

Without it, waitForPress would hand back an answer the instant you pressed. Then loop would call it again immediately for the next step in the sequence, your finger would still be on the button, and it would instantly count a second press of the same button. A three-step sequence would be "answered" in one press and you would lose straight away.

It is exactly the Band 3 edge detection problem, solved a different way. There, we compared with the last reading. Here, we simply refuse to move on until the button is released. Both are correct. The second is easier to read when the program has nothing else to do at that moment.

Part 11

Change it

40 min

Work on a copy of b5_05_simon_worked.ino.

Bronze
Make the game speed up as it gets longer. The sequence should play faster with every round. You will need to hand playStep a second value: how long the step should last.
Silver
Write a function called playSequence() that plays the whole sequence, and use it in loop instead of the for loop that calls playStep(sequence[i]). Leave the second for loop, the one that reads the player's answer, alone. Then say in one sentence what loop reads like afterwards.
Gold
Add a time limit. If the player does not press within three seconds, they lose. This one is hard, and it is hard for an interesting reason: waitForPress blocks, so it can never notice time passing. You will have to rewrite it using millis, so that it watches the buttons and the clock at the same time.
A hint for Bronze

Change the header to void playStep(int which, int howLong) and use howLong instead of the fixed 400. Then work out the value where you call it, for example 400 - sequenceLength * 15. Do not let it go below about 120, or the notes become impossible to hear.

A hint for Gold, after you have tried it properly

The shape you want: record the start time before the waiting begins, then loop while checking two things each round. Have the buttons been pressed? Has too long passed?

Hand back the button number if one was pressed, and hand back something impossible, like −1, if the time ran out. Then in loop, check for that impossible value and treat it as a loss.

Using an impossible value to mean "nothing happened" is a very common trick, and you have now met it.

Part 12

Make: a game somebody will play twice

120 min
What to build

A game or toy that a person picks up, understands without instructions, and asks to play again. Pick one, or bring your own:

  • Your own version of the memory game, with your own rules
  • A reaction race for two players (needs a second person): a light comes on at a random moment, first to press wins
  • A rhythm game: copy the pattern of gaps rather than the order of lights
  • A one-button timing game: stop the moving light on the right one
  • A simple instrument: four buttons, four notes, and a way to record and play back
It is finished when all of these are true
  • Your sketch has at least four functions you wrote yourself, and each one does a single job you can say in one sentence
  • Nothing important is missed because the board was stuck in a delay
  • A player knows whose turn it is at all times, without being told
  • Losing feels like something happened, not like the game just stopped
  • Somebody works out how to play it with no more than one sentence from you. If you are working alone, this one counts as passed when you leave it for a full day, come back, pick it up cold, and know what to do without stopping to think.

The test that matters most in this band

Hand it to somebody and say one sentence, no more. Then be quiet and watch. Do not help. Do not explain. Let them be confused.

Write down every moment they did not know what to do, and every moment they thought it was their turn when it was not.

Then ask one question at the end: would you play it again? Write down the answer exactly, including the pause before it.

If you cannot find anyone todayPut the game down and do not touch it for a full day. Then pick it up and play it cold. You will have forgotten enough of your own design to notice the confusing parts. It is not as good as a real player, but it is much better than nothing. 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 somebody else playing it, if you had somebody. If you tested it alone, post the video of yourself playing it cold the next day, and say so. Include your build log, your list of confusions, and a list of your functions with the one sentence each.

Then read one other person's sketch and tell them one place where a job could be pulled out into a function of its own.

If there is no other sketch to readDo it to b5_05 instead. It is somebody else's code, it is ninety lines long, and there is at least one job in loop that could still be pulled out into a function. Find it and write the function header you would give it.
Part 13

Check yourself

20 min

Code you have not seen. Answer from reading only.

int lampPin = 5;
int buttonPin = 7;
int count = 0;

unsigned long lastBlink = 0;
int gap = 500;

void blinkOnce(int pin, int howLong) {
  digitalWrite(pin, HIGH);
  delay(howLong);
  digitalWrite(pin, LOW);
}

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

void loop() {
  unsigned long now = millis();

  if (now - lastBlink >= gap) {
    lastBlink = now;
    blinkOnce(lampPin, 300);
    count = count + 1;
    Serial.println(count);
  }

  if (digitalRead(buttonPin) == LOW) {
    Serial.println("pressed");
  }
}
  1. How many values does blinkOnce need when you call it, and what are they for?
  2. Why does blinkOnce start with void rather than int?
  3. How often does the lamp blink?
  4. Out of every 500 thousandths of a second, roughly how much of it is the board unable to see the button? Explain how you worked it out.
  5. What will the serial monitor show if you hold the button down for two seconds?
  6. gap is an int but lastBlink is an unsigned long. Is that a problem here? What would happen if lastBlink were an int too?
Answers

1. Two: which pin to blink, and how long to keep it on.

2. Because it does not hand anything back. It just does something.

3. Every 500 thousandths of a second, so twice a second.

4. About 300 out of every 500, which is roughly three fifths of the time. The delay(300) inside blinkOnce is blocking, and it runs every time the lamp blinks. So although the outer timing uses millis properly, a blocking delay has been hidden inside a function and the same old problem is back. This is the question that matters most in this band. Using millis outside does not help if there is a delay inside.

5. The word pressed, printed thousands of times, flooding the window. There is no edge detection and no debounce, so it prints on every round of loop that the button is down. The counting numbers will still appear twice a second, mixed into the flood.

6. It is not a problem here. The subtraction happens on the unsigned long, and comparing the result against an int works fine for these sizes. If lastBlink were an int, it would work for about 32 seconds and then millis() would grow past what an int can hold, and the timing would go wrong in a way that looks random. Accept any answer that says the type matters because millis gets very large.

Part 14

When something goes wrong

What you seeWhat it usually isWhat to do
The game says game over immediatelyA button reads LOW when untouchedTwo legs from the same side. Test each button on its own with the Band 3 serial watch sketch.
The same sequence every time you power upNo randomSeed, or A0 has something on itSee Part 8. The seeding pin must be genuinely unconnected.
One press counts as several stepsThe wait-for-release loop is missingSee Part 10d.
The buzzer clicks instead of playing notesIt is an active buzzerPassive buzzers show a green circuit board underneath. An active one still works, just with one fixed note.
Everything is far too fastA missing lastTime = now;See Part 10b.
Timing works for half a minute, then goes strangeAn int where an unsigned long is neededAny name holding a value from millis must be unsigned long.
"not declared in this scope" on a function you wroteA spelling difference, or a name that only exists inside another functionCheck the spelling and the capital letters match exactly, in both places. Then check you are not using a name that was made inside a different function, because those do not exist outside it.
A name inside a function is not visible outside itThat is correct behaviourNames made inside a function only exist there. If two parts need it, make it at the top of the file.
A button works but the wrong light respondsYour button order and light order do not matchButtons are pins 8 to 11 and lights are pins 2 to 5, in the same order. Check physically, left to right.
The board resets or behaves oddly under loadToo many lights on at once through the boardCheck every LED has its own resistor. Four LEDs and a buzzer is fine; four LEDs with no resistors is not.
Still stuck after 30 minutes?

Photograph the board from above, copy some serial output, and say which stage of Part 4 last worked. That last detail is the useful one: it tells whoever helps you where to start.

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.
Done

What you know now

This was the hinge of the course, and you are through it.

Before this band you could only write programs that were one long list. Now you can break a job into pieces, give each piece a name, and put the pieces together. That is the difference between programs that fit on a page and programs that do something real.

And you know why delay was never quite enough, because you built something that failed on account of it and then fixed it yourself.

Before you move on, make sure you have

  • Your game, with a video of somebody else playing it, or of you playing it cold a day later
  • Your list of the moments they were confused, and their answer to "would you play again"
  • The list of your own functions with one sentence each
  • Your completed timing table from Part 10a
  • Your answers from the counting activity in Part 1
  • Your build log for this band
  • Five or six correct answers in Part 13, including question 4

Next is Band 6: Things That Move. Everything you have built so far makes light or sound. Next it moves, and you find out why a motor cannot simply be plugged into a pin.

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: somebody else playing your game, not you.

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 delay made the game miss presses, and what replaced it.

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.