Every programmer's very first program, on every new machine, since basically forever, does one tiny, ridiculous-sounding thing: it makes the machine say "Hello, World!"
It sounds like a joke. It is not. That one little greeting is the moment you know EVERYTHING is working at once: your code was written correctly, it got translated into something the chip understands, it made it onto the chip, the chip ran it, and it talked back to you. It is a handshake between you and the machine. Today, you make the AirBox shake your hand.
But there is a catch, and that catch is the whole reason this lesson exists.
Cast your mind back to the CS course. A computer is a machine that runs a program: it fetches an instruction, decodes it, executes it, and does that again and again, forever. Your laptop is one of these machines. And, the part that matters to us, the AirBox is one too, a tiny computer built around that little chip in the middle of the board.
But your laptop has a screen, a keyboard, speakers, a hundred ways to show you what it is thinking. The AirBox has none of that. No screen. No keyboard. Just a chip, some sensors, and a USB socket.
So here is the honest question. If you write a program for the AirBox and it runs, how would you ever KNOW? The chip cannot pop up a window. It cannot print to a screen it does not have. It could be doing something brilliant, or absolutely nothing, and you would be staring at a silent circuit board with no way to tell which.
The answer is the one thing the AirBox does have: that USB cable. We use it to make the chip talk, sending words back up the wire to your laptop, where a little window shows you everything the chip says. That window is called the Serial Monitor, and it is your single window into a machine that otherwise cannot speak a word. For the rest of these lessons, whenever you want to know what the AirBox is thinking, this is how you will find out.
The big idea: the AirBox is a computer with nothing to show you. The Serial Monitor, over the USB cable, is how it talks back. Every single time you catch yourself wondering "is my code even running?", the answer is going to come through this window.
Here it is. The entire thing. Type it out exactly as you see it (we are going to explain every single character in a moment, so do not panic if it looks like nonsense right now):
void setup() {
Serial.begin(115200);
Serial.println("Hello, World!");
}
void loop() {
}
That is the whole program. Six lines, and two of them are just a closing bracket sitting on its own. You send it to the AirBox, open the Serial Monitor, and the board says hello. Now let us take it completely apart, until every piece makes sense.
Before we read a single line, you need to understand the shape the whole program is built in, because nothing else will make sense without it.
An AirBox program is built out of blocks called functions. A function is just a named box that holds a list of instructions. (If you did the CS course, you met functions right at the end. Exact same idea, a named box of steps.) Our program has two of them, named setup and loop.
Here is the part that trips up everyone coming from a normal computer, so read it twice. On your laptop, a C program has one starting point, usually called main, it runs from top to bottom once, and then it is finished. The AirBox does not work like that, and it is on purpose. It does not use main at all. Instead, it always looks for two functions with these exact special names, and it treats them specially:
setup runs exactly ONCE, the instant the AirBox powers on.loop runs over and over, FOREVER, after that.The easiest way to hold this in your head: setup is the stuff you do once to get ready (like opening the blinds the moment you wake up), and loop is the thing you then keep doing all day. Our greeting only needs to happen once, so it is going to live in setup. We will get to loop at the end.
The AirBox always wants BOTH. Even when one of them has nothing to do, the AirBox still expects
setupandloopto be there. You will see us leaveloopcompletely empty in a minute, and that is fine. Leaving it out entirely is not: the program will refuse to build.
void setup() { and its }This is the opening of the setup box. Let us break the line into its pieces:
void is a promise about what this function hands back to you when it finishes. void means "nothing, it does not hand anything back, it just does its job and stops." Do not overthink it. For now, void is simply the word that goes in front of setup and loop. (You will learn what else could go in that spot in a later lesson.)setup is the name. The AirBox knows this exact name is special, and runs whatever is inside it one time at power-on.() the round brackets are the slot where a function could be handed extra information to work with. setup does not need any, so the brackets are empty. They still have to be there, empty or not.{ the curly brace opens the box. Everything from this { down to its matching } is the list of instructions inside setup.} (the one sitting alone a few lines down) closes the box. setup is everything between the two braces, and nothing outside them.So the shape void setup() { ... } reads, in plain English, as: "here is a function called setup, it hands nothing back, it takes nothing in, and here between the braces is what it does."
Those two spaces of indentation are for YOU, not the chip. Notice the lines inside the braces are pushed to the right a little. The AirBox does not care about that spacing one bit, you could delete it and the program would run identically. It is purely so a human can see at a glance what is inside the box. Keep it tidy anyway. Future-you, reading this at midnight trying to find a bug, will be grateful.
Serial.begin(115200);This is the first real instruction, and it is the one that switches on the AirBox's ability to talk at all. Nothing can be said before this line runs.
Break it into pieces:
Serial is the name for the connection between the AirBox and your computer, running down that USB cable. The word "serial" means the data travels one bit at a time, single file, down the wire (as opposed to a whole bundle of wires side by side). It is a two-way channel, but for now we only ever use one direction: the AirBox talks, and your Serial Monitor listens..begin(...) means "turn it on, open the channel." That little dot is how you tell a thing to perform one of its actions: Serial.begin reads as "hey Serial, do your begin." Until you call begin, the channel is shut and nothing the AirBox tries to say will get through. This is why it is always the very first thing you do before printing anything.115200 is the number sitting inside the brackets, and it is the speed: how many bits per second the two sides have agreed to talk at. It has a name, the baud rate. 115200 is a common, fast, perfectly sensible choice. And there is exactly one rule about it that matters: both ends must use the same number. If the AirBox is talking at 115200 but your Serial Monitor is set to listen at some other speed, you get garbage on your screen, random symbols instead of words. (We come back to this gotcha at the end, because it catches everyone once.); the semicolon on the end is a full stop. It tells the AirBox "this instruction is finished, here ends the sentence." Almost every line of instructions ends with one.So the whole line Serial.begin(115200); reads as one complete sentence: "Serial, open the channel and talk at 115200 bits per second. Done."
A missing semicolon is the number-one beginner trip. Leave the
;off the end of a line and the whole program will refuse to build, often with a confusing error pointing at the next line, not the one you actually broke. When a build fails for no obvious reason, check the line above the error for a missing semicolon first.
Serial.println("Hello, World!");This is the star of the whole show: the line that actually says hello.
Serial.println(...) again starts with Serial. (hey Serial, do something), and the action this time is println, which means "print a line." It takes whatever you put inside the brackets, sends it up the wire to the Serial Monitor, and then moves down to a fresh line. (That "ln" tacked on the end is just short for "line," and it is the part that means "and then start a new line afterward.")"Hello, World!" the text inside the double quotes is exactly what gets sent. The quotes are doing an important job: they tell the AirBox "this is literal text, send it as-is, do not try to make sense of it." A run of text wrapped in quotes like this has a name, it is called a string (which is just a fancy programming word for "some text"). You can put whatever you like between the quotes: "Hello, World!", "the AirBox is awake", "potato", anything. The AirBox will print it back faithfully, character for character.; full stop again. One instruction, finished.So this line reads as: "Serial, print the text Hello, World! and then start a new line. Done."
And because this line lives inside setup, and setup runs exactly once at power-on, the AirBox says "Hello, World!" one time when it boots up, and then never again (until you reset it). Which is exactly what you want from a greeting.
The AirBox does NOT understand a word of that. It is tempting to think the board "read" your message. It did not. The chip has no idea what "Hello" means, or that it is even English. That is the entire reason for the quotes: they tell the chip "do not try to understand this, just faithfully shuffle these exact characters up the wire." Remember from the CS course, the chip is gloriously dumb. It is not reading your greeting, it is a very fast, very obedient machine moving characters it does not comprehend from one place to another. That is a feature, not a flaw.
println. There is a near-twin action calledprintlnhere so that each message sits on its own tidy line and the monitor stays readable. Both are perfectly good, they differ only by that trailing newline.
void loop() { and its }The last piece: the second of the two special functions.
Remember the shape from earlier: setup runs once, loop runs forever after that. So here is loop, built in the exact same way as setup (a function named loop, hands nothing back with void, takes nothing in with the empty (), and a { } for its body).
But look inside its braces. There is nothing there. It is empty. Why?
Because our entire program is a one-time greeting. We say hello once, in setup, and then, honestly, we are done. There is nothing we want to keep doing over and over. But the AirBox insists on a loop (it is one of the two functions it always goes looking for), so we hand it one, and we leave it empty. An empty loop is completely fine and completely normal: the AirBox runs it forever, finds nothing to do each time around, and just keeps quietly looping through an empty box, doing nothing, which is precisely what we asked of it.
Reading the program is one thing. To actually watch the AirBox say hello, there are two short steps.
First, get the program onto the chip. The code you typed is text, and text is for humans, the chip cannot run it directly (remember from the CS course: the chip only understands raw numbers, machine code). So your computer first translates your text into those numbers, and then sends them down the USB cable into the AirBox's memory. In the Arduino software this whole thing, translate and send, happens when you press the Upload button. Once it is done, the AirBox restarts and immediately runs your program.
Second, open the Serial Monitor. This is the window we have been talking about the whole time, and it is where the AirBox's words appear. Open it, and then check one thing: the little speed setting in its corner. It must be set to 115200, the exact same number you wrote in Serial.begin(115200). Match those two, and you should see it, sitting there in the window:
Hello, World!
That is the handshake. Your code was written correctly, translated, uploaded, run, and the AirBox talked back. Everything works.
If you do not see it, one of two small things is almost always the reason:
The Serial Monitor is completely blank. The most common cause on the AirBox is a setting the board needs in order to speak over USB at all. In the Arduino software, under Tools, find USB CDC On Boot and make sure it is set to Enabled. (Also make sure the correct board/port is selected.) If the window is empty, check this first, before you suspect your code.
The Serial Monitor shows garbage, random symbols instead of words. That is the baud-rate mismatch we warned about. Your code is talking at 115200, but the monitor is set to listen at a different speed, so the words come out scrambled. Set the monitor's speed to 115200 to match your
Serial.begin, and the text will snap back into real words.
The big idea, one more time: you just made a screenless computer talk to you.
Serial.beginopened the channel,Serial.printlnsent the words,setupran it once, and the Serial Monitor showed you the result. That is the whole loop of writing code for the AirBox in miniature: you make it do something, and you make it tell you what it did. Every program you write from here builds on this one moment, the machine speaking back through that little window.
So far, the AirBox has done exactly one thing: talk. Hello world was the AirBox speaking to you, words going OUT of the chip and up the wire. That is called output: the AirBox producing something for the world.
But a real device does not only talk. It also has to listen, to notice things happening around it and react. That is called input: the world producing something for the AirBox. And the simplest thing in the world to notice is a button: right now, is someone pressing it, yes or no?
Your AirBox has a little button on the board itself, the one labeled 0 (there is usually a second one labeled RST that resets the board, leave that one alone). We are going to make the AirBox watch that button, and tell us over the Serial Monitor whether it is being pressed, live, as you press and release it.
So: hello world was the AirBox's mouth. This is its first ear.
Here is the whole thing. A lot of it will already look familiar from hello world (Serial.begin, setup, loop, the semicolons), so we will move fast over the old parts and slow right down on the new ones:
const int BUTTON_PIN = 0; // the onboard button (the one labeled 0)
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP); // this pin is an input, pull-up on
Serial.begin(115200);
}
void loop() {
int state = digitalRead(BUTTON_PIN); // read the pin right now: HIGH or LOW
if (state == LOW) { // LOW means pressed (pull-up, see below)
Serial.println("pressed");
} else {
Serial.println("released");
}
delay(200); // slow down so we don't flood the monitor
}
Notice that loop is not empty this time. Hold that thought, it is the payoff, and we come back to it at the end.
const int BUTTON_PIN = 0; giving a name to a valueThis line sits at the very top, outside both setup and loop, which means everything below it can see it.
What it does is give a name to a value. Let us read it right to left:
0 is the value: the pin number of that onboard button. On the AirBox, the button labeled 0 is connected to the chip's pin number 0. (Makes sense, that is why it is labeled 0.)= 0 the single equals sign means "set this to." We are setting our named thing to 0.BUTTON_PIN is the name we are inventing. We could have called it almost anything, but BUTTON_PIN says out loud what this number MEANS, which is the whole point.int says what KIND of value it is. int is short for "integer," a plain whole number (no decimal point). A pin number is a whole number, so int fits.const is short for "constant," and it is a promise: this value will never change while the program runs. The pin the button is wired to is not going to move, so we lock it down. (If you later tried to change it by accident, the AirBox would stop you and complain, which is exactly what you want for something that should never move.)Why bother, instead of just writing 0 everywhere we need it? Two reasons. First, digitalRead(BUTTON_PIN) reads like a sentence, while digitalRead(0) makes you stop and wonder "wait, what was pin 0 again?" Second, if the button ever ends up on a different pin, you change the number in this ONE place and the whole program follows along. (A named value like this is called a variable. If you did the CS course, you have met these. This one happens to be const, a variable that is not allowed to vary, which sounds like a contradiction but is genuinely useful.)
pinMode(BUTTON_PIN, INPUT_PULLUP); making the pin listenThis line lives in setup, so it runs once at power-on, and it is where we tell the chip how we intend to use the pin.
Here is a new idea first. Every pin on the AirBox can be used in one of two directions:
as an OUTPUT, where the AirBox drives a voltage out of the pin (this is how you would light an LED, for example), or
as an INPUT, where the AirBox reads a voltage coming in on the pin (this is how you read a button).
A pin does not magically know which one you want. You have to tell it, and pinMode is the tool for that. It takes two things: which pin, and which mode.
pinMode is the function that configures a pin.
BUTTON_PIN the first thing in the brackets is which pin (so, pin 0).
INPUT_PULLUP the second is the mode. The INPUT part is us saying "we are going to READ from this pin, not drive it." The _PULLUP part switches on a little helper built into the chip, and that helper is important enough to get its own section right below. For now, just know this line says: "pin 0, you are an input, and turn your pull-up on."
This has to happen in setup, once, before we ever try to read the pin. Configure first, then read.
This is the one genuinely tricky idea in the whole lesson, so slow down here. It explains that _PULLUP we just glossed over, AND it explains something that confuses everybody the first time: why a pressed button reads LOW and a released one reads HIGH, which feels completely backwards.
First, two words we need. When the AirBox reads a pin, it does not hand you a voltage as a number. It hands you one of two answers: HIGH or LOW. HIGH means "there is voltage here," roughly 3.3 volts, the chip's "on" level. LOW means "there is basically no voltage here," roughly 0 volts, which is ground. That is the whole vocabulary: HIGH or LOW, on or off, nothing in between (that is the "digital" in digital input).
Now the problem. Think about what a button actually is: a switch that connects two things only while you hold it down. Our onboard button, when you press it, connects pin 0 straight to ground (LOW). When you are NOT pressing it, the button connects nothing at all, so pin 0 is attached to... nothing. And here is the trap: a pin attached to nothing does not sit politely at LOW. It floats. It behaves like a tiny antenna, picking up electrical noise from the board and the air, and reads randomly HIGH, then LOW, then HIGH, pure garbage. So a plain button on a plain input is useless: pressed reads LOW, but released reads noise.
The fix is a pull-up. A pull-up is a gentle connection to 3.3 volts that quietly holds the pin HIGH whenever nothing stronger is pulling on it. (If you did the electronics course, a pull-up is nothing exotic, it is just a resistor tying the pin up to 3.3 volts.) With a pull-up in place, here is what happens:
With a pull-up, pressed is LOW and released is HIGH. Yes, it feels backwards ("pressing makes it go LOW?"), but that is exactly right and it is not a mistake. The pull-up holds the pin HIGH as its resting state, and pressing the button PULLS it down to ground. Released = HIGH, pressed = LOW. Every button you ever read this way behaves like this.
And the good news: you do not have to wire up an actual resistor, because the chip has pull-ups built in. That is the entire reason we wrote INPUT_PULLUP instead of plain INPUT: it means "input, and switch on the built-in pull-up for me." Write just INPUT, and the pin floats, and you get that random garbage.
Plain
INPUTwill bite you here. If you set the button pin toINPUTinstead ofINPUT_PULLUP, the released button leaves the pin floating, and your Serial Monitor will happily print "pressed" and "released" at random even when your hands are nowhere near the board. If a button reading is flickering on its own, a missing pull-up is the usual reason.
int state = digitalRead(BUTTON_PIN); taking the readingNow we are inside loop, so this runs over and over, forever. Each time around, this is the line that actually checks the button.
digitalRead(BUTTON_PIN) is the reading itself. digitalRead asks the AirBox a simple question about a pin: "right now, this instant, is this pin HIGH or LOW?" You hand it the pin (BUTTON_PIN, pin 0), and it hands back one of the two answers, HIGH or LOW. That is all it does: look at the pin, report which side of the line it is on.int state = ... takes that answer and stores it in a box named state, so we can look at it on the next lines. This is another variable, like BUTTON_PIN, but with a big difference: this one is NOT const. It changes every single time around the loop, holding whatever the latest reading was. (int again, because HIGH and LOW are, under the hood, just whole numbers to the chip.)digitalRead is the listening counterpart to the talking we did with Serial.println, and it only works because we told the pin it was an input back in setup.if (state == LOW) { ... } else { ... } deciding what to doWe have the reading sitting in state. Now we DECIDE what to say based on it, and that is what if and else are for.
if means "if the following is true, run this block."(state == LOW) is the question if is asking. It reads as "is the value in state equal to LOW?" That double equals == is the important bit: it means "is equal to," a question, a comparison. If state is LOW (which, from the pull-up, means the button is pressed), the question is true.Serial.println("pressed");, runs only when that question is true. So a pressed button prints "pressed."else means "otherwise." If the question was NOT true (state is HIGH, so the button is released), the AirBox skips the first block and runs the else block instead: Serial.println("released");.One equals sign is not two. A single
=means "set this to that" (likeBUTTON_PIN = 0). A double==means "is this equal to that?" (the question inside anif). They look almost the same and they do completely different things. Writingif (state = LOW)with one equals is a classic, painful bug: instead of asking a question, it quietly SETS state to LOW and charges ahead. When anifbehaves strangely, count the equals signs.
delay(200); a small pauseLast line. delay(200) tells the AirBox: "stop, and do absolutely nothing, for 200 milliseconds," which is about a fifth of a second.
Why slow ourselves down on purpose? Because loop runs fast, thousands of times every second. Without a pause, the AirBox would read the button and print "pressed" or "released" thousands of times a second, and your Serial Monitor would turn into an unreadable waterfall of text. The little delay(200) throttles it down to about five readings a second, calm enough to actually read while still feeling instant to your finger.
(Fair warning for later: delay does not just slow things down, it completely freezes the whole program for that time, the AirBox can do nothing else while it waits. For a tiny program like this one, that is totally fine. When your programs get bigger and need to juggle several things at once, you will learn better ways to handle time. But not today.)
Remember the empty loop from hello world, the one that ran forever doing nothing? Look at it now.
setup still does its once-at-power-on jobs: configure the button pin, open the Serial channel. But this time, loop is full, and that is the whole point of it. Watching a button is exactly the kind of thing you want to do continuously, forever: check, report, wait a beat, check again. Hello world had nothing to repeat, so its loop was empty. A button is the opposite, a thing you keep checking for as long as the AirBox is on, so it belongs in loop.
This is the split you will use in every program from here: the stuff you do once goes in setup, and the thing you do over and over lives in loop.
Upload it, open the Serial Monitor (baud set to 115200, same as always), and press the button. You should see the text switch between "released" and "pressed" as you press and let go.
A couple of things to expect:
Your board's button might be on a different pin. Not every board wires its onboard button to pin 0. If pressing does nothing and the monitor just repeats "released" forever, the button is probably on a different pin. You only have to change the number in
const int BUTTON_PIN = 0;to whatever your board uses, and because we gave it a name, that one edit fixes the whole program.
You might see "pressed" a few times for a single push. Real buttons are not perfectly clean. The little metal contacts inside physically bounce for a few milliseconds as they close, so one press can look like several presses in a row. This is called "bounce," and it is completely normal. Ignore it for now, you will learn how to smooth it out later.
The big idea: the AirBox can now do both halves of what a computer does out in the world: it can talk (output,
Serial.println) and it can listen (input,digitalRead). A button is just the simplest possible thing to listen to, but every sensor on the AirBox is read in the same spirit: set the pin up once insetup, then read it over and over inloop, and decide what to do with whatever comes back. You have now seen the full shape of a program that reacts to the world.
unsigned long ______ = 0; // <- where does THIS line belong? not inside loop...
const unsigned long INTERVAL = 200;
void loop() {
unsigned long now = millis();
if ( /* has INTERVAL passed since last time? */ ) {
// 1. remember that "now" is the new last time
// 2. do exactly what your old loop body did:
// read the button, decide pressed/released, print it
}
// notice: no delay() anywhere, and the loop can keep going
}
unsigned long lastCheck = 0;
const unsigned long INTERVAL = 200;
void loop() {
unsigned long now = millis();
if (now - lastCheck >= INTERVAL) {
lastCheck = now;
// ── your three lines from the old loop go here ──
}
}
You just heard that every sensor on the AirBox is read in the same spirit: set the pin up once in setup, then read it over and over in loop. Let us read a real one.
A button was about the simplest thing there is to sense: pressed or not, a single bit. Time for something with a bit more to say. Meet the DS18B20, a temperature probe.
Physically it is usually a little stainless-steel tube on the end of a cable (the waterproof kind), or a tiny black component with three legs. Either way, it is a digital thermometer, and that word "digital" is the important part. It does not hand the AirBox a raw, wobbly voltage that you would have to make sense of yourself. It does the measuring inside itself and hands back an actual temperature as a number. Your job is just to ask for that number and read it.
Roughly, here is what is going on inside the probe. It has a tiny temperature-sensing element and a small digital circuit that turns whatever temperature it sees into a number, and it keeps that number in its own little memory. It talks to the AirBox over a single data wire, using a scheme called one-wire (yes, really, a whole conversation down one wire). You will not write any of that wire-wiggling yourself, because, just like with the button and with Serial, someone has already packaged the hard part into libraries you can lean on. This time you use two libraries stacked together: one that handles the low-level one-wire timing, and a friendlier one on top that gives you plain commands for the DS18B20 specifically.
And here is where this lesson works differently from the last two. So far, I have explained every single line to you. This time I am going to hand you a skeleton, a function with a deliberate hole in it, and you are going to fill that hole yourself. I will explain everything around it, but there is one crucial step I am leaving out on purpose, and finding it is your job. More on that below the code. First, the wiring.
Three wires and one resistor:
(If your probe is the bare three-legged kind, check its datasheet for which leg is which, the middle one is usually the data line. And if pin 33 is not broken out on your board, any free GPIO works, just change the number.)
#include <OneWire.h> // the low-level one-wire bus driver
#include <DallasTemperature.h> // the friendly DS18B20 commands, sits on top
const int PROBE_PIN = 33; // the probe's data wire connects here
OneWire oneWire(PROBE_PIN); // the bus, on our data pin
DallasTemperature probe(&oneWire); // the thermometer, talking over that bus
void setup() {
Serial.begin(115200);
probe.begin(); // wake the probe up
}
float readTemperature() {
// one important step is missing right here, before the read below.
// without it the number you get back is wrong (or stale). figure out
// what goes here, add it, then explain what it does. i'm not telling.
float t = probe.getTempCByIndex(0); // read the number the probe has stored
return t;
}
void loop() {
float temp = readTemperature();
Serial.printf("temp = %.2f C\n", temp);
delay(1000);
}
Let us walk everything except the hole. Top to bottom.
The two #include lines pull in the two libraries we just talked about. OneWire.h is the low-level one that knows the precise timing of the single-wire conversation (microsecond-level stuff you would never want to write by hand). DallasTemperature.h is the friendly one that sits on top and gives you readable commands like "give me the temperature." You include both.
const int PROBE_PIN = 33; is a named constant, exactly like BUTTON_PIN from the button lesson: a whole number, locked down with const, holding the pin the probe's data wire is on. Same reasons as before, it reads clearly and it is a single place to change if the pin moves.
OneWire oneWire(PROBE_PIN); This one is new, and it is worth slowing down for. Up to now, the only thing you called a dot on was Serial (as in Serial.begin). Serial is an object: a bundle that carries both some data and a set of actions you can ask it to perform. The AirBox handed you Serial for free. Here, for the first time, you are making your own object. This line creates a OneWire object, names it oneWire, and tells it which pin the bus lives on (PROBE_PIN). Read it as "set up the one-wire bus on pin 33, and call it oneWire."
DallasTemperature probe(&oneWire); creates the second object, the friendly thermometer, and names it probe. The interesting bit is what is inside the brackets: &oneWire. We are handing the thermometer the bus we just made, so it knows where to talk. That little & means "the address of," it is us saying "here is the oneWire bus, share it, do not make a copy of it." So the line reads as "make a DS18B20 helper named probe, and point it at the oneWire bus." From here on, probe is the thing you give commands to.
probe.begin(); in setup starts the probe up and gets it ready, the same way Serial.begin started the serial channel. You do it once. (Notice the pattern: an object, a dot, an action. probe.begin() is "hey probe, do your begin." Every object works this way.)
float t = probe.getTempCByIndex(0); is the line that reads a temperature out of the probe. getTempCByIndex means "get the temperature, in Celsius, of the probe at this index." The (0) is the index: you can actually hang several DS18B20s on the same single wire, and they get numbered 0, 1, 2, and so on. We have just one, so it is number 0. Whatever number comes back is stored in a variable named t. Notice its type is float, not int: a temperature like 21.44 has a decimal part, and float is the type for numbers with a decimal point (a plain int could only hold 21, and would throw the .44 away).
return t; hands that temperature back to whoever called readTemperature. That is what the float in front of the function name is about: the function promises to hand back a float, and return t is it keeping that promise. (Remember void from hello world? void meant "hands nothing back." This function is the opposite: it hands back a float.)
The loop at the bottom is all things you already know: it calls readTemperature, stores the answer in temp, prints it with the %.2f from hello world (two decimal places), and waits a second before going again.
Now, the hole.
Look again at readTemperature. There is a comment marking a gap, right before the read. As written, that function will compile and it will print a number. But the number will be wrong: stale, or a meaningless default, not the real temperature in the room right now.
Why? Think back to how the probe works inside. Reading the value out with getTempCByIndex just grabs whatever number is currently sitting in the probe's memory. But what put a fresh, current number there? Nothing, yet. That is the missing step, and I am deliberately not telling you what it is.
Here is your job:
readTemperature, the two includes, the two objects, begin, the line you added, the read, the return, and write yourself a short note on each saying what it does. The parts I already explained, that is you checking your understanding. But the line you added, nobody handed you that explanation. Being able to explain it clearly is how you know you actually understand it, and did not just get it working by luck.Getting it to compile is not the same as getting it right. A wrong temperature that looks believable (say, a steady 85.00) is more dangerous than an obvious crash, because you might just trust it. That is exactly the kind of quiet bug the "run it both ways" step is built to catch. Always find a way to check that a reading is real, not just that your code ran.
The big idea: this is what real engineering feels like most of the time. The library already exists. The examples already exist. Your job is to find the right piece, understand WHY it works, and prove that it does, rather than being handed the answer. You just did that. And that skill, reading someone else's example and genuinely understanding it instead of blindly copying, will carry you further than any single line I could have written into the skeleton for you.