Bozoma Innovation Hub · Arduino Ladder · Band 7
Distance and Display
Your board learns to measure how far away something is, using sound you cannot hear. Then it learns to say the answer on a screen of its own, instead of into a serial monitor nobody is watching. That second half is what turns a project into a device.
About 8 hours. Finish Bands 4 and 5 first. Band 6 is useful but not required: the servo here is optional.
Choose your build
- CoreDistance and Displaythe parking guard on an LCD. This page. Everybody builds this one.
- SwapThe guard on a four-digit displayThe same parking guard, two pins instead of six, a much shorter sketch, and digits you can read across a room.
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
- b7_01_ping_given.ino
- b7_02_lcd_i2c_given.ino
- b7_03_lcd_parallel_given.ino
- b7_04_buggy_flicker.ino
- b7_05_bin_worked_i2c.ino
- b7_05_bin_worked_parallel.ino
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.
Why a screen changes everything
Everything you have measured so far has been reported to a serial monitor. That means the board has to be attached to a computer, and somebody has to be looking at the right window.
A device with its own screen is a different thing. Unplug it, carry it to the tank, and it still tells you what it knows. That is the whole difference between an experiment and something a person can use.
So this band does two jobs at once:
- A new sense. Distance, measured with a click you cannot hear, and the arithmetic that turns a time into a length.
- A new output. Sixteen characters by two rows, which sounds like plenty and turns out to be a design problem all of its own.
By the end you will be able to
- Measure a distance and say where every number in the calculation comes from
- Tell the difference between “zero centimetres” and “I heard nothing”
- Wire an LCD screen, of either kind, and get letters onto it
- Explain why a screen flickers, and write to it in a way that does not
- Fit a useful message into sixteen characters
- Install a library you did not already have
What you need
- Your board, USB cable and breadboard
- An HC-SR04 ultrasonic sensor, the one with two silver eyes and four pins
- An LCD1602 screen, either kind. Part 5 tells you which you have.
- A passive buzzer, the one where you can see a small green circuit board underneath
- A 10k potentiometer, only if your screen has sixteen bare pins
- A tape measure or a ruler, and a wall
- About twenty jumper wires
- A full-size breadboard, the long kind with numbers running past 60. This band runs out to column 42.
- Optional: a servo from Band 6, for a lid that opens
Think first: shouting at a wall
15 minNo board for this part. You need a wall, a large open space, and a friend if you have one.
If you have somewhere with an echo
A long corridor, a big empty room, a wall across a field, the side of a building.
Stand well back from the wall and clap once, hard. Listen.
Walk further back and clap again. Then further again.
If there is nowhere with an echo
Do it with a ball instead, which is the same experiment slowed down so you can see it.
Stand two big steps from a wall and roll a ball at it, hard enough to come back to you. Count out loud, one number per second, from the moment it leaves your hand to the moment it returns.
Now stand four big steps back and do exactly the same, rolling with the same strength.
Write these down
- What exactly did you measure? A distance, or something else?
- The sound went to the wall and came back. So does your measurement describe the distance to the wall, or something else?
- If you knew how fast sound travels, could you work out the distance from your time? Say how, in words, without doing any arithmetic.
- You clapped in a room with a soft curtain on one wall and heard no echo at all. Does that mean the curtain is not there?
That last question is the one that will save you an hour later. No echo does not mean nothing is there. It means nothing came back.
Soft things swallow the click. Angled things send it off sideways. Very close things get their echo back before the sensor has finished listening. Your sensor reports the same thing for all three as it does for an empty room.
Keep the paper. In Part 3 your sensor will read 0, and you will already know what that means.
What the sensor actually gives you
20 minTake out the HC-SR04. It is a small board with two silver cylinders on the front, which look like eyes and are not. One is a tiny loudspeaker. The other is a tiny microphone.
Look at the four pins along the bottom edge. Their names are printed on the board itself: VCC, Trig, Echo, GND. Read them off your own board now rather than trusting any picture, including the one below.
The two numbers, and where they come from
Here is the line that turns the sensor's answer into centimetres. Every part of it means something.
long cm = microseconds / 29 / 2;
- 29
- Sound travels about one centimetre every 29 millionths of a second in ordinary air. That is the speed of sound written upside down, in a form that is convenient here. Dividing by it turns a time into a length.
- 2
- Your click went to the wall and came back. So the length you just calculated covers the distance twice. Halving it gives you the distance to the wall.
Neither number is magic and neither should be copied without understanding. If you were measuring underwater the 29 would be completely different, because sound travels four times faster in water. The 2 would stay the same, because there and back is there and back wherever you are.
Why 29 and not 0.034
You may see this written as cm = microseconds * 0.034 / 2 elsewhere. It is the same sum. 0.034 centimetres per microsecond is the speed of sound; 29 microseconds per centimetre is the same fact upside down.
Dividing by 29 keeps everything in whole numbers. This chip is much faster at those. It also brings back the trap from Band 4: 147 / 29 / 2 gives 2, not 2.53.
For a parking guard, whole centimetres are plenty. For something needing millimetres, you would keep the microseconds and scale them differently.
Words to know
- ultrasonic
- Sound too high for a person to hear. This sensor uses it so that measuring does not make a noise.
- microsecond
- A millionth of a second. There are a thousand of them in a millisecond.
pulseIn- Waits for a pin to go HIGH, times how long it stays HIGH, and hands you that time in microseconds.
- time of flight
- Measuring a distance by timing how long something takes to get there and back. Radar and depth sounders work the same way.
Guess, then measure something
40 minRead the sketch first. You have seen every instruction in it except two.
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
unsigned long microseconds = pulseIn(echoPin, HIGH, 25000);
long cm = microseconds / 29 / 2;
Guess before you run it
delayMicroseconds(10). You knowdelaycounts in milliseconds. How long is this wait, as a fraction of a second?- What do you think happens if nothing at all is in front of the sensor? Give a number.
- The
25000at the end ofpulseInis a time limit. Using the 29 from Part 2, roughly how far away is that in centimetres? Show your working. - What would happen without that limit, if no echo ever came back?
Now wire it
Both ends of every wire. Pull them out of the Arduino header too, so nothing is left in the pins. A wire you forgot is invisible when you are looking at the breadboard, and one Arduino pin takes one wire.
Which rails this band means. Your breadboard has two pairs, one along each long edge, and on most boards the two pairs are not joined to each other. In this band, “the + rail” and “the − rail” always mean the pair along the top edge, nearest row
j. Pick that pair now and use only it. The buzzer sits down in rows a and b, so its rail wire is a long one that runs round the end of the board; run it rather than reaching for the nearer rail, because mixing the two pairs means nothing works and nothing looks wrong.Push the sensor into the breadboard. Its four pins go into f20, f21, f22 and f23. Stand it up so the two silver eyes face away from the board.
All four pins go in one block, rows f to j. The sensor is not wide enough to straddle the middle channel, and it does not need to: its four pins are separate anyway.
Check the pin order on your own board. Read the printing next to the pins. On almost all of them the order is VCC, Trig, Echo, GND, reading left to right with the eyes facing you. Column 20 is the leftmost one.
Four wires: j20 to 5V, j21 to pin 10, j22 to pin 11, j23 to GND.
You can leave Echo on pin 11 for this part, because the screen is not wired yet. Part 6b will tell you when to move it to pin 9, and every sketch after that already expects it there. Write a note to yourself now so you are not surprised.
Plug in, upload b7_01_ping_given.ino, and open the serial monitor (Tools, then Serial Monitor, and check the box in the corner says 9600).
The answers to the four questions
1. Ten millionths of a second. A hundred thousand times shorter than delay(1). That is why there is a separate instruction for it: delay cannot do waits this short.
2. Most people say a very large number. The answer is 0, and that surprises everybody. When no echo returns, pulseIn gives up and hands back 0. A reading of 0 means “I heard nothing”, which is the opposite of “something is touching me”.
3. 25000 divided by 29 is about 862 centimetres of travel, and half of that is about 4.3 metres. So anything further than about four metres reads as 0.
4. Without a limit, pulseIn falls back to its own default, which is a whole second. It sits there for that second waiting for an echo that never comes, and while it waits nothing else on the board runs at all. That is the Band 5 problem in a new place: a single instruction that stops everything. Your own limit of 25000 turns a one-second stall into a twenty-five thousandth of a second one.
Now check it against a real ruler
A number on a screen is a claim. Claims get checked.
Lay a tape measure or ruler on the table with its zero right at the front of the sensor's eyes.
Stand a book upright at 10 cm, flat side facing the sensor. Read the serial monitor. Write down what the sensor says next to what the ruler says.
Repeat at 20, 40, 80 and 150 cm. Fill in this table.
| Ruler says | Sensor says | Difference |
|---|---|---|
| 10 cm | ||
| 20 cm | ||
| 40 cm | ||
| 80 cm | ||
| 150 cm |
Now try three awkward things. Write down what happens for each one.
- A folded towel or a jumper at 40 cm.
- The book at 40 cm, but turned at a steep angle.
- Your hand at 2 cm.
This is the calibration lesson from Band 4, in a new coat
In Band 4 you found that a light reading means nothing until you go and measure what it means in your room. Here you have found something slightly different and just as important.
The sensor is accurate in the middle of its range and lies at both ends. It also lies about certain surfaces, at any distance at all.
So write two numbers in your build log: the closest and the furthest distance you would trust from your own sensor. Take them from your table, not from this page. Those two numbers belong to your build.
Look closer: the shape of a measurement
25 minLook at the five lines that start the measurement.
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
That is a pulse: off, on for exactly ten microseconds, off again.
The sensor's own instructions say it needs at least ten. The two microseconds of LOW at the start are there to make sure the pin has settled before the pulse begins.
You are not choosing these numbers. They come from the sheet the sensor's maker published, and reading such a sheet is a skill worth having. Search for “HC-SR04 datasheet” and look for the timing diagram. It will look hard to read, and you only need two lines of it.
Why unsigned long and not int
unsigned long microseconds = pulseIn(echoPin, HIGH, 25000);
You met this in Band 5 with millis, and the reason is the same. An ordinary int on this chip holds numbers up to 32767. The limit here is 25000, which fits, but only just, and a longer limit would not.
The habit is worth keeping even when a number happens to fit today: anything holding a duration in microseconds or milliseconds is unsigned long. You will not remember which ones were tight.
The function with a name
In b7_04 and b7_05, all of that lives in one place:
long readDistance() {
...
return microseconds / 29 / 2;
}
This is exactly what you learned to do in Band 5. Nine lines with one job, given a name, so that everywhere else in the sketch you can write readDistance() and be done.
Notice what else it buys you. Suppose you decide that a reading of 0 should mean “far away” rather than “touching”. You change that in one place.
That decision is a rule about your device. Rules about your device belong in one place each.
A question to answer in your log
In b7_05, readDistance turns a 0 into 999 before handing it back. In b7_01 it does not.
Why is that right for one and wrong for the other? Answer in two sentences.
Check your answer
b7_01 is a measuring instrument. You want to see exactly what the sensor said, including 0, because you are learning what it does. Hiding the 0 would hide the lesson.
b7_05 is a device that has to act. Acting on a 0 as though it meant “touching” would make the guard scream at an empty room. So the decision “no echo means far away” is made once, on purpose, in the one place that reads the sensor.
Which screen do you have?
20 minTake out the LCD and turn it over. What you see on the back decides everything about the next hour, so do this before reading further.
| If the back has… | You have | You need | Go to |
|---|---|---|---|
| A small green board soldered on, with four pins labelled GND VCC SDA SCL | The I²C kind | 4 wires. No potentiometer. | Part 6a |
| Nothing. Just a strip of 16 pins along the top of the screen itself | The bare kind | 12 wires and a 10k potentiometer | Part 6b |
The trap, before you build either one
This is the single most common wasted evening in all of hobby electronics, and you can avoid it by reading one paragraph.
Every one of these screens has a contrast adjustment. On the I²C kind it is a tiny blue screw on the back. On the bare kind it is the potentiometer you are about to wire to pin 3.
Set wrongly, a perfectly working screen shows absolutely nothing. No hint. No faint text. Nothing at all, exactly as though it were dead.
Turn it too far the other way and you get a row of solid blocks. That is also not broken. Somewhere between blank and blocks is readable text, and the range is narrow.
Wire the screen and get letters on it
50 minDo one of the two builds below, whichever matches the back of your screen. Then rejoin at Part 7.
6a. The I²C kind, four wires
First, install the library
This one does not come with the Arduino software, so you have to fetch it once.
- In the Arduino software, click Tools, then Manage Libraries.
- Type
LiquidCrystal I2Cin the search box. - Find the one by Frank de Brabander and click Install.
Four wires from the screen's four pins: GND to the − rail, VCC to the + rail, SDA to A4, SCL to A5. Then, if you have not already: one wire from the + rail to 5V, and one from the − rail to a GND pin, and move both of the sensor's power wires off the Arduino: j20 from the 5V pin onto the + rail, and j23 from the GND pin onto the − rail. One Arduino pin takes one wire, and the screen wants both of those pins too. Part 8's pin plan assumes you have done this.
Why the rails rather than straight to the Arduino: the sensor is already using the one 5V pin, and one Arduino pin takes one wire. The rails are how two things share one pin without two wires ever meeting in one hole.
A4 and A5 are not being used as ordinary analog pins here. On this chip they are the two pins used for talking to other boards, and you cannot choose different ones. Nothing else in your build may use A4 or A5 from now on.
Plug in and upload b7_02_lcd_i2c_given.ino.
Bozoma Hub on the top row and Band 7 ready on the bottom.0x27 near the top of the sketch to 0x3F and upload again. Those are the only two addresses these boards use, so one of them is right.Now break it on purpose, so you have seen it once. With the text showing, turn the little blue screw on the back slowly, all the way in one direction.
Now turn it back until the words return, and stop there.
Two minutes now, and the next time a screen goes blank on you, you will reach for that screw instead of pulling wires out. That is the whole reason for this step.
6b. The bare kind, twelve wires and a knob
This is the longest single build in the course. Do it in one sitting, slowly, and test in the middle rather than at the end.
Move the sensor's Echo wire first. Take the wire out of Arduino pin 11 and put it into pin 9. Leave its other end in j22.
The screen is about to take pin 11 for itself, and only one wire fits in an Arduino pin. Every sketch from here on already expects Echo on pin 9.
Push the screen's pin strip into row j, with its sixteen pins in columns 1 to 16. So screen pin 1 sits in j1, screen pin 2 in j2, and so on all the way along: j1 j2 j3 j4 j5 j6 j7 j8 j9 j10 j11 j12 j13 j14 j15 j16. Sixteen pins, sixteen columns, none skipped. The numbers match, which makes everything after this easier.
Row j on purpose. The screen's body will lie over the rows behind it, and every wire you are about to place goes into row f of the same column, which stays clear.
Put the 10k potentiometer in at f30, f31, f32. Wire j30 to the − rail and j32 to the + rail. Its middle leg is f31, and a wire from j31 will go to the screen in a moment.
The four power and contrast wires first. Do these before the six data wires, because they are the ones that let you test.
| Screen pin | Called | Wire from | To |
|---|---|---|---|
| 1 | VSS | f1 | − rail |
| 2 | VDD | f2 | + rail |
| 3 | V0 | f3 | j31, the knob’s middle leg |
| 5 | RW | f5 | − rail |
| 15 | A | f15 | + rail (backlight) |
| 16 | K | f16 | − rail (backlight) |
Then + rail to 5V and − rail to GND.
j20 went straight to the Arduino's 5V pin and its j23 straight to a GND pin. The screen wants both of those pins as well, and one Arduino pin takes one wire. So move them: j20 to the + rail, j23 to the − rail. From here on, one wire runs from the + rail to 5V and one from the − rail to GND, and everything else on the board feeds from the rails. Part 8's pin plan assumes you have done this.Every wire goes into row f, in the same column as the screen pin it belongs to. The screen's own pin is in row j of that column, and the column joins them underneath. One hole, one leg, exactly as in every band since Band 1.
f to i clear for the wires you are about to place. It does cover the rail holes above roughly columns 1 to 24, so put the rail end of every wire in this build into a rail hole to the right of column 25, where you can still reach it.If your screen will not sit that way, do not fight it: use female-to-male jumper wires straight onto its sixteen pins and leave the breadboard for the sensor and the knob. The wiring is identical, and the table above still tells you what joins to what.
f15 and the + rail, rather than a plain wire. Some of these boards have a small resistor built into the back next to pins 15 and 16 and some do not, you cannot tell reliably by looking, and on a board that has one the extra 220 ohms costs almost no brightness. On a board that has none, a plain wire lets the backlight pull far more current than the Arduino's 5V pin should give: the whole board dims, the screen may reset, and the backlight burns out early. Fitting the resistor either way costs you nothing and cannot go wrong.Test now, before the other six wires. Plug in the USB, then turn the potentiometer slowly all the way from one end to the other and watch the screen.
Those rectangles are the best news in this build. They prove power and contrast are right, which is most of what goes wrong, and you now know it before adding six more wires.
Now the six data wires. Unplug the USB first.
| Screen pin | Called | Wire from | To Arduino pin |
|---|---|---|---|
| 4 | RS | f4 | 12 |
| 6 | E | f6 | 11 |
| 11 | D4 | f11 | 5 |
| 12 | D5 | f12 | 4 |
| 13 | D6 | f13 | 3 |
| 14 | D7 | f14 | 2 |
Screen pins 7, 8, 9 and 10 stay empty, and that is correct, not a mistake. They are a faster way of talking to the screen that needs four more wires, and this way is fast enough.
Upload b7_03_lcd_parallel_given.ino. The LiquidCrystal library it uses already comes with the Arduino software, so there is nothing to install.
Bozoma Hub on the top row and Band 7 ready on the bottom.Words to know
- I²C
- A way for two boards to talk over just two wires. Said “eye squared see”. On this chip it always uses pins A4 and A5.
- address
- A number that says which board on those two wires you mean, the way a house has a number. These screens are 0x27 or 0x3F.
- contrast
- How dark the letters are against their background. Set wrongly, a working screen shows nothing at all.
- padding
- Extra spaces printed after your text so the row always receives the same number of characters, and nothing old is left behind.
Both kinds, same three instructions
Whichever you wired, from here on the sketch talks to the screen the same way:
screen.setCursor(0, 0); // column 0, top row
screen.print("Hello");
screen.setCursor(0, 1); // column 0, bottom row
screen.print(42);
Columns are numbered 0 to 15 and rows 0 to 1. Both start at zero, like the array positions in Band 3. print takes text in quotes or a number without them, exactly like Serial.print, which you already know.
Now break it on purpose
35 minJoin the sensor and the screen together in the obvious way and something unpleasant happens.
b7_04 is written for the I²C kind and will not run as it stands on yours. Read this part rather than running it. Everything it teaches is the same either way, and you will meet both faults for real in Part 8.If you would rather run it, two changes are needed, not one: replace its top three lines with the two from
b7_03, and change int echoPin = 11; to 9, because your screen has pin 11.Upload b7_04_buggy_flicker.ino with the sensor still on pins 10 and 11, as you wired it in Part 3. That is the I²C screen's arrangement; if you have the bare screen your Echo wire moved to pin 9 in Part 6b, so read the box above rather than uploading this.
Work out why before you read on
- Look at the sketch. What is the first thing it does to the screen every round?
- How long is the screen blank while that happens?
- Hold the sensor perfectly still, so the number stops changing. Does the flicker stop?
- What does that last answer tell you?
The answer, after you have written yours
Two mistakes, and they add up.
One. screen.clear() wipes all thirty-two character positions and takes about two milliseconds. Every round the screen is genuinely blank for a moment, and your eye sees it.
Two. It rewrites the number even when the number has not changed. Question 3 is the giveaway: hold it still, the number stays at 42, and the flicker carries on regardless. Nothing on the screen needed to change and it changed anyway.
Notice the shape of this. It is Band 6's lesson again, in a completely different place. The reading was perfectly good. The fault was in acting on it when you did not need to.
The fix, and the trap inside the fix
The obvious repair is to stop calling clear(). Do it: comment that line out and upload again.
Move the sensor so the reading goes from 100 down to 99.
990. Or go from 42 to 8 and it reads 82.Why, and this catches everybody once
A screen never wipes itself. Printing 99 where 100 was writes over the first two characters and leaves the third exactly as it was. The old 0 is still sitting there and it always will be.
So you cannot use clear(), because it flickers, and you cannot not use it, because of leftovers. The answer is neither.
Send a fixed width every time, padded with spaces. Here is the version from b7_05:
if (cm < 100) screen.print(" ");
if (cm < 10) screen.print(" ");
screen.print(cm);
screen.print(" cm ");
The spaces are not decoration. They are an eraser, and they cost nothing, because those character positions were going to be written anyway.
The two if lines at the top are the part worth understanding. They make the number itself always three characters wide: 7 becomes 7, 42 becomes 42, and 742 is already 3. Without them a three-digit reading would send one character more than a two-digit one, and the row would stop adding up.
Put that together with only writing when the value actually changed, and you have a screen that is steady, correct, and never blank.
if (cm == lastShown) {
return;
}
lastShown = cm;
One space from the first
if, because 42 is under 100. Then 42, which is 2. That is 3 so far, and the number is now three wide as promised. Then " cm" plus ten spaces, which is 13. Three plus thirteen is 16.Now do it for 7 and for 742 and check you get 16 both times.
Write too few and a long value leaves a leftover digit behind. Write too many and the extra characters go off the end into memory you cannot see, so the row quietly stops matching what you counted, which is harder to spot than the leftover.
Build the parking guard
60 mina and b, so its rail wire runs the long way round to the top pair.Now a whole device. It measures how far away you are and tells you three ways at once.
- The number, on the bottom row.
- A word saying what to do, on the top row.
- A beep that gets faster as you close in, and becomes one solid tone under 10 cm.
Three ways at once is the point. A driver is not looking at a screen, and a person across the room cannot hear a small buzzer. A device that reports one way only works for one person in one position.
The pin plan
| Part | Pin | Holes | Wiring |
|---|---|---|---|
| Sensor VCC | + rail | f20 | Wire j20 to the + rail, not straight to 5V; you moved it there in Part 6, and the row below runs the + rail to 5V. The screen wants 5V too, and one Arduino pin takes one wire. |
| Sensor Trig | 10 | f21 | Wire j21 to pin 10 |
| Sensor Echo | 11 or 9 | f22 | Wire j22 to pin 11 (I²C screen) or pin 9 (bare screen) |
| Sensor GND | GND | f23 | Wire j23 to the − rail |
| Buzzer | 6 | long b40, short b42 | Wire a40 to pin 6; wire a42 to − rail. No resistor. |
| Screen | — | — | Exactly as you wired it in Part 6. Do not move it. |
| Power the + rail | 5V | — | One wire from the + rail to the 5V pin. If you skipped Parts 5 to 7 because you have no screen, this is the wire you have never run, and without it the sensor's VCC row above connects to dead metal. |
| Ground | GND | — | One wire from the − rail to a GND pin |
Stage one: the buzzer. Long leg towards the pin. Test it with b5_01_tone_given.ino from Band 5, changing buzzerPin from 12 to 6 at the top.
Stage two: check the sensor still works with b7_01_ping_given.ino, changing echoPin to 9 first if you have the bare screen.
Now upload the worked sketch for your screen. Use b7_05_bin_worked_i2c.ino if yours has the four-pin board on the back. Use b7_05_bin_worked_parallel.ino if it has sixteen bare pins.
Parking guard for a second, then a word on the top row and a distance on the bottom. Walk your hand slowly in: Clear, then Keep coming, then Nearly there, then STOP. The beeping starts slow and speeds up, and becomes one solid tone under 10 cm. Take your hand away and it goes quiet and reads --- cm.--- cm and never changesThe sensor is reading 0, so the sketch is treating it as far away. Go back to stage two and get the sensor printing sensible numbers on its own first.lastShown in the sketch. If you have changed the screen-writing part, you may be updating the word but returning before the number.Fit it into sixteen characters
Look at the top row. Nearly there is 12 characters and fits. Now try writing your own words for the four ranges, and find out how quickly sixteen runs out.
The exercise
Write four messages of your own, one for each range. Use whatever language your users actually speak. Each one must fit in 16 characters, and a stranger must be able to act on it at once. Count every character, including spaces.
Then ask somebody to read them cold and tell you what they would do. If they hesitate, the message is too long or too clever. Sixteen characters is a real constraint and it is a good teacher.
Look closer
35 mina. Read loop first
void loop() {
long cm = readDistance();
showDistance(cm);
updateBeep(cm);
delay(50);
}
Four lines: measure, show, beep, pause. Anyone can read that and say what the device does. Everything complicated is behind a name.
That delay(50) at the bottom is a blocking wait, and Band 5 taught you to be suspicious of those. Be suspicious of it. It means the beep timing can only be checked twenty times a second, so a beep can be up to 50 thousandths of a second late. On a beep that lasts 350, that is small enough to accept. That is a judgement, not an exemption. If your device needed millisecond timing, this delay would have to go.
b. Follow the beeping by hand
Assume gapFor returns 350 and lastBeep is currently 4000. Fill this in.
| millis() | now − lastBeep | ≥ 350? | Buzzer changes? | lastBeep after |
|---|---|---|---|---|
| 4100 | ||||
| 4300 | ||||
| 4350 | ||||
| 4500 | ||||
| 4700 |
Answer, after you have filled it in
4100: gap 100, no, no change, stays 4000. 4300: gap 300, no, stays 4000. 4350: gap 350, yes, buzzer flips, lastBeep becomes 4350. 4500: gap 150, no, stays 4350. 4700: gap 350, yes, flips again, becomes 4700.
So it flips every 350 milliseconds, which means on for 350 and off for 350, which is a full beep every 700. If you want a beep every 350 you halve the gap. Work out why before reading on: a beep is two flips, not one.
This is exactly the millis pattern from Band 5, and nothing about it is new. What is new is where you found it: the same shape solves a different problem.
c. Break it on purpose
In your copy, delete the line lastBeep = now; from inside the if. Predict, then upload.
Answer
lastBeep stays at 0 forever, so now - lastBeep is always enormous and always bigger than the gap. The if is true on every single round, so the buzzer flips thousands of times a second.
You will not hear beeping. You will hear a rough continuous noise, because you are now switching the tone on and off faster than a beep.
This is the same bug as the one in Band 5 Part 10b. If you recognised it before uploading, that is the course doing its job.
d. The number that decides everything
Change delay(50) at the bottom of loop to delay(500). Upload and walk your hand in.
Answer
The screen is calm and the guard is useless. It notices you half a second late, and the beeping becomes lumpy because the beep timing can only be checked twice a second.
Now try delay(5). The screen becomes noisy again. The number flickers between 41 and 42 as the reading wobbles, and every wobble is a screen write.
There is a right answer for your device and this page does not know it. It depends on how fast a car moves, how far away it starts, and how quickly a person can react. 50 is a reasonable starting point for a hand. Write down what you chose and why, in your log.
Change it
45 minWork on a copy of the worked sketch that matches your screen.
- Bronze
- Make the bottom row show a bar instead of a number: one block character for every 10 cm, up to sixteen. You will need a
forloop andscreen.print((char)255), which prints a solid block. Then say in one sentence which is easier to read from three metres away, and why. - Silver
- Add a smallest ever reading that is remembered and shown on the top row, with a button to reset it. This is genuinely useful for a parking guard: it tells you how close you actually got. Watch out for the reading of 0.
- Gold
- Take the average of the last five readings instead of using each one raw, and show that instead. The screen will become noticeably calmer. This is hard for an interesting reason: you need somewhere to keep five numbers and a way to throw the oldest away each time. An array from Band 3 and a position that wraps round will do it.
Make: something a person reads and acts on
120 minWhat to build
A device with a screen that somebody who is not you can walk up to, read, and know what to do about. Pick one, or bring your own:
- A parking guard for a real doorway, gate or vehicle, mounted where it would live
- A bin that tells you how full it is, with the sensor in the lid pointing down
- A queue counter for a charging kiosk: how many people, and roughly how long
- A water level display for a tank or a bucket, reading in centimetres from the top
- A doorway counter that adds one every time somebody passes
- A smart bin whose lid opens on a servo when you come near, if you did Band 6
It is finished when all of these are true
- The screen never flickers and never shows a leftover character
- It behaves sensibly when the sensor hears nothing, rather than reporting zero
- Every message fits in sixteen characters and needs no explanation
- It is housed and mounted where it would actually be used, not lying on a desk
- You measured its accuracy against a ruler and wrote the numbers down
- Somebody read the screen and did the right thing without you saying anything. If you are working alone, this counts as passed when you photograph the screen from where a user would stand, look at the photograph a day later, and know what to do from the photograph alone.
The test that matters most in this band
Put it where it belongs, hand nobody any instructions, and watch one person walk up to it.
Write down what they read first, how long before they acted, and whether they did the right thing. If they read the number before the word, your layout is telling them to. If they hesitated, count the characters in the message they hesitated over.
Show your work
Post four things: a video of somebody using it, a close photo of the screen with a real reading on it, your ruler-accuracy table, and your build log. Then look at one other person's screen layout and tell them one thing you had to read twice.
Check yourself
20 minCode you have not seen. Answer from reading only. Do not upload it.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C screen(0x27, 16, 2);
int trigPin = 10;
int echoPin = 11;
int lastCm = -1;
void setup() {
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
screen.init();
screen.backlight();
}
void loop() {
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
unsigned long us = pulseIn(echoPin, HIGH);
int cm = us / 29 / 2;
if (cm != lastCm) {
lastCm = cm;
screen.setCursor(0, 0);
screen.print(cm);
screen.print(" cm");
}
delay(100);
}
- The sensor is pointed at a wall 100 cm away, then slowly at one 99 cm away. What does the top row of the screen say?
- The sensor is now pointed out of a window at open sky, so no echo ever comes back. What does the device do? Answer carefully: this is not a question about the screen.
- Compare the
pulseInline with the one inb7_01_ping_given.ino. One value is missing here. Which, and what does leaving it out change? - The wall is 60 cm away and nothing is moving. Roughly how many times a second is the screen written to?
int cmandunsigned long us. The wall is 3 metres away. Isintbig enough to hold the answer? Show the arithmetic.- Somebody wants this to also sound a buzzer that beeps faster as things get nearer, and adds a
toneand adelayinside theif. Name two separate things that will go wrong.
Answers
1. 99 cm followed by a leftover, so it reads 99 cmm or similar. "100 cm" is six characters and "99 cm" is five, so the sixth character from before is never written over. There is no padding here, and this is the fault Part 7 was about.
2. It stops dead for a whole second, over and over. With no limit given, pulseIn uses its own default of one second, and it waits out every bit of that whenever no echo comes back. Nothing else on the board runs during that wait: not the screen, not a buzzer, nothing. So the loop that was running ten times a second now runs about once a second, and the screen keeps showing the last number it had, which makes it look like it is working perfectly. This is the question that matters most in this band. A missing timeout does not give you a wrong number. It stops the whole board, and it does it silently.
3. The third value, the timeout, which is 25000 in b7_01. With it, a missing echo returns 0 after about 25 thousandths of a second and everything carries on. Without it, the wait can be far longer, and the device stops for all of it.
4. Almost never. The reading is steady, so cm != lastCm is false nearly every round and nothing is written. It only writes when the number genuinely changes, which is the correct behaviour and the point of lastCm.
5. Yes, easily. 3 metres is 300 cm, and an int on this chip holds up to 32767. The reason us has to be unsigned long is different: the time for 3 metres is about 17400 microseconds, which fits, but the timeout and longer distances do not fit as comfortably, and durations are the thing that overflows first. Keep the habit.
6. Two things, and they are both from earlier bands. One: a delay inside the if blocks, so the sensor stops being read while the beep is on, which is the Band 5 problem. Two: the beep would only ever happen when the number changes, because it is inside the if. Stand still at 20 cm and the warning goes silent, which is exactly when you most want it.
Say these out loud
Part 11 was the real test. This is a warm-up for it, and a list to come back to. Say each of these out loud, to somebody or to yourself, without looking anything up.
- Say where the 29 and the 2 come from in the distance calculation
- Say what a reading of 0 means, and name two things that cause it
- Name three surfaces this sensor is bad at, and say why
- Explain why a screen flickers, and give the two-part fix
- Say why printing
99over100shows990 - Say what to try first when a screen shows nothing at all
- Install a library you do not already have, without help
The one sentence from this band
A measurement is a claim, and an unchecked claim is a guess with a number on it.
You checked yours against a ruler and found where it could be trusted and where it could not. Every device you build from here on deserves the same treatment, and most devices in the world never get it.
Still stuck after 30 minutes?
Photograph the board from above in good light, and photograph the screen itself showing whatever it is showing, even if that is nothing. Post both, plus one sentence on what you expected. Say which kind of screen you have. Nearly every screen question is answered by those two photographs together.
When something goes wrong
| What you see | What it usually is | What to do |
|---|---|---|
| Screen shows nothing at all | Contrast | Turn the knob or the blue screw slowly from end to end. Do this first, every time. See Part 5. |
| Screen shows solid blocks on the top row | Contrast, the other way | Same knob, other direction. The screen is fine. |
| Backlight on, contrast done, still nothing (I²C) | Wrong address | Change 0x27 to 0x3F and upload again. |
| Rectangles show but no words (bare) | One of the six data wires | The rectangles prove power is right. Check pins 4, 6, 11, 12, 13, 14 against the table. |
| Distance always 0 | Trig and Echo swapped, or nothing in range | Point it at a flat wall 30 cm away first. Then check those two wires. |
| Distance stuck at one number | The sensor is pointing at your own breadboard or a wire | Look along the line of the two eyes and clear everything in the way. |
| Readings jump wildly | The sensor is loose or tilting | Fix it down. A sensor that moves measures a different thing each time. |
| Screen flickers | clear() every round | See Part 7. Write only when the value changed, and pad with spaces. |
| Old digits left on screen | Short text over long text | Pad to a fixed width with spaces. See Part 7. |
| Text wraps onto the wrong line | More than 16 characters | Count them, including your spaces. |
LiquidCrystal_I2C not declared | Library not installed | Tools, Manage Libraries, search, install. See Part 6a. |
| Everything freezes for a second at a time | No timeout on pulseIn | The third value, 25000, is not optional. Put it back. |
What you know now
Your board can measure a distance now, and it can tell somebody about it without a computer in the room. Those two together are what a device is.
The more useful thing you learned is smaller and harder to see. You took a number your sensor gave you and you did not believe it until you had checked it against a ruler. Then you found the places it could be trusted and the places it could not, and wrote those down.
Most people never do that, and it is the difference between a project that works in a video and one that works in a room.
Before you move on, make sure you have
- A video of somebody using your device, and a close photo of the screen with a real reading on it
- Your completed ruler-accuracy table from Part 3, all five rows
- Your notes on the three awkward surfaces, and the two distances you decided you would trust
- Your completed beep timing table from Part 9b
- Your four sixteen-character messages, counted
- Your build log for this band
- Five or six correct answers in Part 11, including question 2
Next is Band 8: Build for Nzemaland. The last band, and the only one with no new parts in it. You find a problem somebody actually has, decide beforehand what would count as solving it, and then find out honestly whether you did.
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.
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: your ruler-accuracy table, and where the sensor lied to you.
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.