Lesson 3 of 10

Arduino Basics

Reading the Board: What Each Pin Is For

Before writing any code, learn to read the board itself. An Arduino Uno has four groups of connections along its edges, and the labels printed on the board tell you almost everything you need.

The digital pins, numbered 0 to 13, deal in two states only: HIGH, which on an Uno means roughly 5 volts, and LOW, which means 0 volts. Every one of them can be an input that reads whether a voltage is present, or an output that supplies one. A few are marked with a tilde symbol (~), which means they can also do PWM — a trick covered in a later section that lets a digital pin fake an in-between value.

The analog input pins, labelled A0 to A5, do something the digital pins cannot: they measure a voltage as a number across a range rather than reporting just "present" or "absent". That is what you need for a sensor whose output varies smoothly, like a potentiometer knob or a light-dependent resistor. Note the asymmetry that trips people up constantly — these pins can read a varying voltage but they cannot output one.

The power pins are the group to treat with respect. 5V and 3.3V are outputs from the board's own regulators, meant for powering small sensors. GND is ground, the zero reference for everything, and there are several GND pins because you will need more than one. VIN is the raw input voltage. Pins 0 and 1 deserve a warning of their own: they are also the serial pins the USB connection uses, so anything wired to them can stop code from uploading.

Two more things are worth knowing about. Pin 13 has a small LED already soldered to it on the board itself, which is why the standard first program blinks pin 13 — it works before you have wired anything. And the RESET button simply restarts your program from the beginning, exactly as if you had unplugged and replugged the power.

  • Digital pins 0–13 — read or write HIGH (about 5 V) or LOW (0 V)
  • Pins marked ~ — the same digital pins, but they also support PWM output
  • Pins 0 and 1 — shared with the USB serial link; keep them free while you are still uploading code
  • Analog inputs A0–A5 — measure a varying voltage as a number from 0 to 1023
  • 5V and 3.3V — regulated outputs for powering sensors, not for powering motors
  • GND — ground, the zero reference; every part of the circuit must connect back to it
  • Built-in LED on pin 13 — the free test light you get without wiring anything
Notes
  • Pin numbers are not interchangeable between boards. An Arduino Nano uses the same numbering as the Uno, but an ESP32 or a Mega does not, and an ESP32 additionally runs on 3.3 V logic. When you copy a sketch from the internet, check which board it was written for before wiring anything.

setup() and loop(): the Only Two Functions You Must Have

Every Arduino sketch — that is what Arduino calls a program — contains exactly two required functions. setup() runs once, the instant the board gets power or is reset. loop() runs immediately afterwards and repeats forever. There is no main() to write and nothing to call these functions from; the board does that for you.

This split exists because there are two kinds of instruction. Some things are true for the whole life of the program — which pins are inputs, which are outputs, what speed the serial link runs at — and doing them repeatedly would be pointless. Those go in setup(). Everything that has to keep happening goes in loop().

pinMode() is the setup instruction you will use most. It tells the board whether a pin should behave as an input or an output. Forgetting it is a classic first bug, and an unusually confusing one: without pinMode(pin, OUTPUT) the pin stays an input, and an input pin can only supply a trickle of current. Your LED then glows so faintly you might not see it in daylight, and you conclude the LED is faulty when your code simply skipped one line.

Serial.begin(9600) in setup() opens a text channel back to your computer, and Serial.println() sends a line down it, which you read in the Arduino IDE's Serial Monitor. This is your debugger. A microcontroller has no screen and cannot pause on a breakpoint, so printing values is genuinely how professionals diagnose embedded problems, not a beginner's shortcut. If the monitor shows random symbols instead of text, the baud rate selected in the monitor window does not match the number you passed to Serial.begin().

Example
// The classic first sketch: blink the LED that is already on the board.

const int ledPin = 13;   // named once, so changing the pin means changing one line

void setup() {
  pinMode(ledPin, OUTPUT);   // this pin will SUPPLY a voltage
  Serial.begin(9600);        // open the text link to the computer
  Serial.println("Board is awake");
}

void loop() {
  digitalWrite(ledPin, HIGH);   // about 5 V on the pin - LED on
  delay(1000);                  // wait 1000 milliseconds
  digitalWrite(ledPin, LOW);    // 0 V on the pin - LED off
  delay(1000);
}
Notes
  • delay() counts in milliseconds, so delay(1000) is one second and delay(1) is a thousandth of one. Writing delay(1) when you meant one second produces a blink far too fast for your eye to separate, which looks exactly like an LED that is simply on.

Your First Circuit: an LED and Why It Needs a Resistor

Wire an LED between a pin and ground with nothing else in the path and it will light up beautifully — for a while. Then it dims, or it dies, and possibly it takes the pin with it. Understanding why teaches you more about electronics than any other five-minute experiment.

An LED is a diode, and a diode does not behave like a resistor. A resistor obeys a simple proportion: double the voltage across it and double the current flows. A diode does not. Below a certain voltage almost no current flows at all, and above it the current rises extremely steeply for a very small increase in voltage. There is no voltage at which an LED politely settles for a sensible current. Left to itself with a 5 V supply, it will take as much current as the circuit can deliver, heat up, and destroy itself.

A series resistor fixes this by taking control of the current. The supply voltage gets shared between the LED and the resistor, and because the resistor does obey the simple proportion, it decides how much current flows through the whole path. That is why the resistor is called a current-limiting resistor. It protects the LED and, just as importantly, protects the Arduino pin, which has its own limit on how much current it can source.

LEDs are also polarised — they only work one way round. The longer leg is the anode, which goes towards the positive side, and the shorter leg is the cathode, which goes towards ground. Many LEDs also have a flat spot on the rim next to the cathode. Wire one backwards and nothing happens at all; it is not damaged, it simply blocks. That silence is the number one reason beginners think an LED is dead. Before assuming so, pull it out, turn it round, and try again.

The resistor can go on either side of the LED — before it or after it — because in a series path the same current flows through everything. What matters is only that a resistor is somewhere in that path. Values in the low hundreds of ohms are the usual choice for a 5 V board; a bigger value gives a dimmer but perfectly safe light, so if you are unsure, start with a larger resistor and work down.

Example
// Circuit for an external LED on pin 9
//
//   Arduino pin 9 ---> LED long leg (anode)
//                       LED short leg (cathode) ---> resistor ---> GND
//
// The resistor limits the current through the whole path.
// Never wire the LED from a pin straight to GND with nothing in between.

const int ledPin = 9;

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

void loop() {
  digitalWrite(ledPin, HIGH);
  delay(200);
  digitalWrite(ledPin, LOW);
  delay(800);        // short flash, long gap - a "heartbeat" pattern
}
Notes
  • If your LED does not light: check it is the right way round, check the resistor is actually in the path, check pinMode(pin, OUTPUT) is present, and check the breadboard row you used really connects to the pin. In that order — the first two explain most cases.
  • A breadboard's long side rails are usually split in the middle on many boards. If half your circuit is dead, that break is a likely culprit.

Uploading Code, and the Errors You Will Actually Hit

Install the Arduino IDE from the official site, connect the board with a USB cable, then set two things under the Tools menu: Board (choose Arduino Uno) and Port (choose the port that appears when the board is plugged in and disappears when it is not — unplugging to check is the quickest way to identify it). Then press Upload. Two small LEDs on the board will flicker as the code transfers, and your sketch starts running immediately afterwards.

The most common upload failure reads something like port not found or programmer is not responding. Work through the causes in order rather than guessing. First, the cable: many cheap USB cables are charge-only and carry no data lines, so the board gets power and never appears as a port. Second, the port selection: if no port is listed at all on Windows, the driver for a clone board's USB chip may not be installed — clone boards often use a different USB chip from genuine ones and need its driver.

Third, and easy to overlook: if you have wired anything to pins 0 or 1, disconnect it and upload again. Those pins carry the same serial link the upload uses, so a Bluetooth module or a sensor sitting on them will block the transfer. This bites people badly in the Bluetooth robot lesson later in the course, which is exactly why that project puts the module on other pins.

Compiler errors are different and are almost always simpler than they look. Read only the first error message, not the wall of text beneath it — one mistake usually cascades into many complaints. A missing semicolon at the end of a line and a missing closing brace account for the large majority of a beginner's compile failures, and the IDE points at the line after the mistake rather than at the mistake itself, so always check the line above the one it names.

Example
// A first sketch that proves the whole chain works:
// board alive, code uploaded, serial link readable.

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);   // LED_BUILTIN is pin 13 on an Uno
  Serial.begin(9600);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  Serial.println("on");
  delay(500);

  digitalWrite(LED_BUILTIN, LOW);
  Serial.println("off");
  delay(500);
}

// Open Tools > Serial Monitor and set it to 9600 baud.
// You should see "on" and "off" alternating in step with the LED.
Notes
  • Opening the Serial Monitor resets the board on an Uno, so your sketch restarts from setup(). That is normal, not a fault — and it is why a message printed in setup() can seem to appear twice.
  • Save your sketches with names that mean something. "sketch_aug02a" tells you nothing in three weeks; "line-follower-two-sensors" tells you everything.

Variables, Constants and Keeping Sketches Readable

Arduino code is C++, so the types are the ones you meet in any C-family language, with two practical wrinkles from running on a small chip. Memory is genuinely scarce, so an int on an Uno is a 16-bit value and cannot hold arbitrarily large numbers. And there is no console to print to unless you open one with Serial.begin().

Two habits keep a robot sketch maintainable. First, name your pins with const int at the top of the file instead of scattering bare numbers through the code. When you rewire the robot at midnight because a jumper broke, you want to change one line, not hunt for every occurrence of the number 9. Second, name the numbers that tune behaviour — the motor speed, the distance at which the robot decides to turn — for the same reason. A sketch full of unexplained numbers is unreadable a week later, including to you.

One type deserves special mention now because it matters later. Times measured with millis() must be stored in an unsigned long, not an int. An int on an Uno overflows after about 32 seconds of milliseconds, which produces a robot that works perfectly during testing and then behaves bizarrely half a minute in. That is a genuinely hard bug to find if you do not already know to look for it.

Example
// Readable structure for any robot sketch

// --- pin assignments: change hardware, change one line ---
const int ledPin      = 9;
const int buttonPin   = 2;
const int trigPin     = 12;

// --- tuning values: named so they can be explained ---
const int  motorSpeed    = 180;   // 0-255, PWM duty
const int  turnDistance  = 20;    // centimetres before we turn away

// --- state that changes while running ---
unsigned long lastBlinkTime = 0;  // times MUST be unsigned long
bool ledIsOn = false;

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

void loop() {
  // ...
}
Notes
  • const int for a pin number costs nothing at run time and makes the sketch self-documenting. Getting into this habit now will make the multi-motor projects later in the course far less error-prone.
Ask AI