Digital I/O is the foundation of all embedded control: read a button, write to a relay, respond to events. Today you'll master digital pins and debouncing.
Arduino digital pins can output 5V (HIGH) or 0V (LOW). pinMode(pin, OUTPUT) configures direction. digitalWrite(pin, HIGH) sets it high. Output current per pin is limited to 40mA max, 20mA recommended. For loads over 20mA (motors, relays, high-power LEDs), use a transistor or driver IC. Pins 0 and 1 are also Serial RX/TX — avoid them if using Serial.
pinMode(pin, INPUT) reads a pin. Without a pull-up or pull-down resistor, a floating input reads random values. Use pinMode(pin, INPUT_PULLUP) to enable the internal 20–50kΩ pull-up. With INPUT_PULLUP: pin reads HIGH normally, LOW when connected to ground (inverted logic — button press = LOW). Always use INPUT_PULLUP unless you have an external pull-down.
Mechanical buttons bounce — the contact opens and closes 5–50 times in 10ms when pressed. This causes multiple reads of a single press. Software debounce: after detecting a state change, wait 50ms before reading again. Store the previous state. Only act when state changes after the debounce period. Millis-based debounce is more reliable than delay-based.
// Button with debouncing — correct approach
const int BTN_PIN = 2;
const int LED_PIN = 9;
const unsigned long DEBOUNCE_MS = 50;
int ledState = LOW;
int btnState;
int lastBtnState = HIGH; // INPUT_PULLUP: normally HIGH
unsigned long lastDebounce = 0;
void setup() {
Serial.begin(9600);
pinMode(BTN_PIN, INPUT_PULLUP); // internal pull-up
pinMode(LED_PIN, OUTPUT);
btnState = digitalRead(BTN_PIN);
}
void loop() {
int reading = digitalRead(BTN_PIN);
// State changed — start debounce timer
if (reading != lastBtnState) {
lastDebounce = millis();
}
// If stable for DEBOUNCE_MS, it's a real change
if ((millis() - lastDebounce) > DEBOUNCE_MS) {
if (reading != btnState) {
btnState = reading;
// Button pressed = LOW (INPUT_PULLUP inverted)
if (btnState == LOW) {
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
Serial.print("Button press! LED: ");
Serial.println(ledState ? "ON" : "OFF");
}
}
}
lastBtnState = reading;
}
Implement a combination lock: 4 buttons represent digits 1–4. User must press them in a specific sequence (e.g., 1,3,2,4). If the sequence is correct, turn on a green LED for 2 seconds. If wrong, flash a red LED. Use a state machine with a current_position variable and a target_sequence array.