Arduino Development
Senior 6 · ICT
Arduino Development
Lesson 2 of 5
25 min
A microcontroller does nothing until it is given a program. On an Arduino the program is called a sketch; you write it on a computer in the Arduino IDE (or in a simulator), the IDE translates it into machine code, and the code is copied into the board's flash memory, where it runs every time the board is powered. In this lesson you write your first sketches: blinking an LED, reading a button, and doing both at once without one blocking the other.
Definition
Sketch: an Arduino program, saved as a .ino file in a folder of the same name. It is written in C++ with Arduino's own functions.
Compile (the IDE's Verify button): translate the sketch into machine code, reporting any errors. Upload: compile, then copy the machine code to the board.
Digital pin: a pin that is either HIGH (5 V on the Uno) or LOW (0 V). As an output it can switch an LED; as an input it reads whether a voltage is high or low.
Every sketch has two functions. setup() runs once when the board starts or is reset; loop() then runs again and again, for as long as the board has power.
| Function | What it does | Example |
|---|---|---|
pinMode(pin, mode) | Sets a pin as OUTPUT, INPUT or INPUT_PULLUP | pinMode(8, OUTPUT); |
digitalWrite(pin, value) | Sets an output pin HIGH or LOW | digitalWrite(8, HIGH); |
digitalRead(pin) | Returns HIGH or LOW | digitalRead(2) |
delay(ms) | Pauses the whole program for a number of milliseconds | delay(500); |
millis() | Milliseconds since the board started (an unsigned long) | unsigned long t = millis(); |
Serial.begin(baud), Serial.println(x) | Send text to the computer's Serial Monitor | Serial.println("LED on"); |
An LED is a diode: current flows only from its anode (+, the longer leg) to its cathode (−, the shorter leg, beside the flat edge of the case). Put it in backwards and it simply stays dark. The resistor limits the current; without it the LED and the pin can be damaged. On a breadboard, the five holes in each short row are connected to each other, and the long rails along the edges carry 5 V and GND.
Worked example 1: choose the LED resistor
A red LED needs about 2.0 V across it and should carry about 15 mA. The pin gives 5 V. Find the resistor.
All the sketches in this lesson were compiled for the Arduino Uno with arduino-cli without errors.
// Blink an LED on pin 8 and report each change to the Serial Monitor
const int LED_PIN = 8;
void setup() {
pinMode(LED_PIN, OUTPUT); // pin 8 will drive the LED
Serial.begin(9600); // open serial at 9600 bits per second
}
void loop() {
digitalWrite(LED_PIN, HIGH); // 5 V on pin 8: LED on
Serial.println("LED on");
delay(500); // wait 500 ms
digitalWrite(LED_PIN, LOW); // 0 V: LED off
Serial.println("LED off");
delay(500);
}
Click Upload, then open the Serial Monitor (set to 9600 baud): it prints "LED on" and "LED off" every half second, while the red LED blinks. Printing to the Serial Monitor is your main tool for finding out what a sketch is really doing.
// Light the LED on pin 8 while the button on pin 2 is pressed
const int BUTTON_PIN = 2;
const int LED_PIN = 8;
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP); // internal resistor holds pin 2 HIGH
pinMode(LED_PIN, OUTPUT);
}
void loop() {
bool pressed = (digitalRead(BUTTON_PIN) == LOW); // pressing connects pin 2 to GND
if (pressed) {
digitalWrite(LED_PIN, HIGH);
} else {
digitalWrite(LED_PIN, LOW);
}
}
INPUT_PULLUP switches on a resistor inside the chip that connects pin 2 to 5 V. With the button up, pin 2 reads HIGH. Pressing the button connects pin 2 straight to GND, so it reads LOW. This is why "pressed" means LOW: the logic is active low. Without a pull-up, an unconnected input pin "floats" and reads random values.
While delay(500) runs, the Uno does nothing else at all: a button pressed during the delay is not noticed. The fix is to check the clock instead of waiting:
// Blink without delay(): the loop never stops, so the button is always read
const int BUTTON_PIN = 2;
const int LED_PIN = 8;
const int LED2_PIN = 7; // second LED, lit while the button is held
const unsigned long INTERVAL = 500; // ms between changes
unsigned long lastChange = 0; // millis() needs unsigned long
bool ledOn = false;
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
}
void loop() {
unsigned long now = millis(); // ms since the board started
if (now - lastChange >= INTERVAL) { // time for the next change?
ledOn = !ledOn;
digitalWrite(LED_PIN, ledOn ? HIGH : LOW);
lastChange = now;
}
// this runs thousands of times a second, between LED changes
digitalWrite(LED2_PIN, digitalRead(BUTTON_PIN) == LOW ? HIGH : LOW);
}
Each pass of loop() takes a few microseconds. Most passes only read the button; once every 500 ms the if condition is true and the red LED changes. The green LED follows the button at once, while the red LED keeps blinking. This pattern, comparing millis() with a stored time, is how real controllers handle several jobs.
Worked example 2: why unsigned long?
A student stores millis() in an int. On the Uno an int is 16 bits, so its largest value is . When does it go wrong?
millis() passes 32,767 after 32,767 ms, about 33 seconds.now - lastChange >= INTERVAL gives wrong results, so the LED stops blinking properly after about half a minute.unsigned long holds 32 bits, up to ms, about 49.7 days. And because the subtraction is done on unsigned values, now - lastChange stays correct even when millis() itself wraps round after those 49.7 days.Autodesk Tinkercad: Blink an LED With Arduino in Tinkercad. Watch how the Uno, LED and resistor are placed and wired in the simulator and how the code is started; then build the circuit in this lesson the same way.
Warning
Common faults, and what they look like. LED never lights: it is backwards, or in the wrong row of the breadboard, or the sketch uses a different pin from the wiring. Upload fails: the wrong board or port is selected under Tools, or something is connected to pins 0 and 1. Button behaves randomly: INPUT was used instead of INPUT_PULLUP, so the pin floats. Compile error expected ';': a missing semicolon on the line before the one reported.
Tip
Name your pins. Write const int LED_PIN = 8; once at the top and use the name everywhere. If you rewire the LED to pin 9, you change one line, and the sketch explains itself.
millis() instead of delay(), so that a second press during the green phase is noticed (and printed as "already crossing").Check: the lights follow the sequence every time, the Serial Monitor shows each change, and your millis() version notices a press at any moment.
Practice
setup() and loop()?INPUT_PULLUP read LOW when pressed?delay(3000) inside loop() and complains that the button "only works sometimes". Explain and suggest a fix.INTERVAL is changed to 50. Would a person see the LED blink?setup() runs once at start-up or reset, for preparation such as pinMode; loop() then repeats for as long as the board is powered.pinMode(12, OUTPUT); and digitalWrite(12, HIGH);delay(3000) the board does nothing, so presses during those 3 seconds are missed. Use millis() timing so that the loop keeps running and reads the button every pass.Sources and credits
This lesson adapts CC BY-SA material and is shared under CC BY-SA 4.0.