Module 3: Inputs & Outputs — Talking to the Physical World

Level: 🟢 Beginner
Board: Arduino Uno
Prerequisites: Module 2
Estimated time: 2.5–3.5 hours
Goal: Learn digital and analog I/O to read sensors and control components.


What You'll Learn

So far, your Arduino can do one thing: turn pins on and off. In this module, it learns to listen. You'll read button presses (digital input), sensor values (analog input), and create smooth brightness control (PWM output). You'll also meet the Serial Monitor — your most important debugging tool from here through the entire course.


3.1 Digital vs. Analog Signals

Digital: On or Off

A digital signal has exactly two states: HIGH (5V) or LOW (0V). There's nothing in between. A light switch is digital — it's either on or off.

When the Arduino reads a digital pin, it returns one of two values:

Analog: A Range of Values

An analog signal varies continuously. A dimmer switch is analog — it can be at any position between fully off and fully on.

The Arduino's analog pins read a voltage between 0V and 5V and convert it to a number between 0 and 1023. This conversion is done by the ADC (Analog-to-Digital Converter) built into the ATmega328P.

Voltage at pin analogRead() returns
0V 0
1.25V ~256
2.5V ~512
3.75V ~768
5V 1023

The formula: reading = (voltage / 5.0) × 1023

Resolution: With 10-bit resolution (0–1023), each step represents about 4.9 mV (5V ÷ 1024). That's precise enough for most hobby sensors, but professional applications often use external ADCs with 12-bit or 16-bit resolution.


3.2 Digital Input — Reading a Button

The Problem with Floating Pins

If you connect a button between a pin and 5V, what voltage does the pin see when the button is not pressed? You might think 0V, but actually the pin is "floating" — disconnected from everything, picking up random electrical noise. It might read HIGH, LOW, or flicker between both.

This is called a floating pin, and it's one of the most common beginner mistakes.

The Solution: Pull-Down and Pull-Up Resistors

Pull-down resistor (10kΩ to GND):

Pull-up resistor (10kΩ to 5V):

Arduino's Built-in Pull-Up Resistors

The ATmega328P has internal ~20kΩ pull-up resistors on every digital pin. You can activate them:

pinMode(BUTTON_PIN, INPUT_PULLUP);  // Enables internal pull-up

With INPUT_PULLUP:

This is the most common approach because it uses fewer components.

Reading a Button — Complete Example

/*
 * Module 3: Button Read
 * Reads a pushbutton and turns an LED on/off.
 *
 * Circuit:
 * - Button: one side to digital pin 7, other side to GND
 * - LED: anode → 220Ω resistor → digital pin 8, cathode → GND
 *
 * Uses INPUT_PULLUP — no external pull-up resistor needed for button.
 *
 * Board: Arduino Uno
 */

const int BUTTON_PIN = 7;
const int LED_PIN = 8;

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);  // Button with internal pull-up
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  // Read the button state
  int buttonState = digitalRead(BUTTON_PIN);

  // With INPUT_PULLUP, LOW means pressed
  if (buttonState == LOW) {
    digitalWrite(LED_PIN, HIGH);  // LED on when button pressed
  } else {
    digitalWrite(LED_PIN, LOW);   // LED off when button released
  }
}

Why is LOW = pressed? With INPUT_PULLUP, the internal resistor pulls the pin to 5V (HIGH) by default. Pressing the button connects the pin to GND, which overrides the weak pull-up because the button's resistance is nearly zero — much less than 20kΩ.


3.3 Analog Input — Reading a Sensor

How analogRead() Works

The ADC inside the ATmega328P takes about 100 microseconds to convert a voltage to a number. That's about 10,000 readings per second — fast enough for most applications.

int sensorValue = analogRead(A0);  // Returns 0–1023

Converting ADC Values to Real Units

The raw 0–1023 number isn't very useful by itself. You'll often need to convert it:

To voltage:

float voltage = sensorValue * (5.0 / 1023.0);

To percentage:

float percentage = sensorValue / 1023.0 * 100.0;

To a sensor-specific range (using map()):

// Map 0–1023 to temperature range 0–100°C
int temperature = map(sensorValue, 0, 1023, 0, 100);

Note about map(): It uses integer math, so it truncates decimals. For precision, use the float conversion formula instead. Also, map() doesn't constrain the output — if the input is outside the expected range, the output will be too. Use constrain() if you need limits.


3.4 Voltage Dividers — The Circuit Behind Most Analog Sensors

Many sensors (photoresistors, thermistors, force sensors) are variable resistors. To read them with analogRead(), you need to convert a resistance change into a voltage change. That's what a voltage divider does.

The Voltage Divider Formula

V_out = V_in × (R2 / (R1 + R2))

Where:

How This Works with a Photoresistor

A photoresistor (LDR) changes resistance based on light:

Pair it with a fixed 10kΩ resistor in a voltage divider:

In bright light (LDR ≈ 1kΩ):

V_out = 5V × (10kΩ / (1kΩ + 10kΩ)) = 5V × 0.91 = 4.55V → analogRead ≈ 930

In darkness (LDR ≈ 100kΩ):

V_out = 5V × (10kΩ / (100kΩ + 10kΩ)) = 5V × 0.091 = 0.45V → analogRead ≈ 92

The resistance change becomes a measurable voltage change. This is the foundation for reading most analog sensors.

Choosing the Fixed Resistor

The fixed resistor should be close to the middle of the sensor's resistance range. For a photoresistor that varies from 1kΩ to 100kΩ, a 10kΩ fixed resistor gives you good sensitivity across the range. Too small and you lose sensitivity in darkness; too large and you lose sensitivity in bright light.


3.5 PWM — Faking Analog Output

The Arduino can't output true analog voltages. Its pins are either 5V or 0V. But it can fake intermediate voltages using Pulse Width Modulation (PWM).

How PWM Works

PWM rapidly switches a pin between HIGH and LOW — so fast that the result looks like a steady intermediate voltage. The key parameter is the duty cycle: what percentage of the time the pin is HIGH.

Duty cycle Average voltage analogWrite() value
0% 0V 0
25% ~1.25V 64
50% ~2.5V 128
75% ~3.75V 191
100% 5V 255

Using analogWrite()

analogWrite(pin, value);  // value: 0–255

Only works on PWM pins: 3, 5, 6, 9, 10, 11 (marked with ~ on the board).

Despite the name analogWrite, this is NOT a true analog output — it's a digital square wave. But for LEDs (dimming) and motors (speed control), the effect is the same.

LED Dimming Example

/*
 * Module 3: LED Fade
 * Smoothly fades an LED up and down.
 *
 * Circuit:
 * - LED anode → 220Ω resistor → digital pin 9 (PWM)
 * - LED cathode → GND
 *
 * Board: Arduino Uno
 */

const int LED_PIN = 9;  // Must be a PWM pin (~)

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

void loop() {
  // Fade up
  for (int brightness = 0; brightness <= 255; brightness += 5) {
    analogWrite(LED_PIN, brightness);
    delay(30);
  }

  // Fade down
  for (int brightness = 255; brightness >= 0; brightness -= 5) {
    analogWrite(LED_PIN, brightness);
    delay(30);
  }
}

3.6 The Serial Monitor — Your Debugging Window

The Serial Monitor is a text communication channel between the Arduino and your computer. It's the single most useful debugging tool you have.

Setting Up Serial Communication

void setup() {
  Serial.begin(9600);  // Start serial at 9600 baud (bits per second)
}

Printing Values

Serial.print("Sensor value: ");   // Print text (stays on same line)
Serial.println(sensorValue);       // Print value + new line

Opening the Serial Monitor

In the Arduino IDE: Tools → Serial Monitor (or click the magnifying glass icon).

Make sure the baud rate dropdown at the bottom matches what you set in Serial.begin(). If they don't match, you'll see garbled text.

Debugging with Serial — The Essential Workflow

When something doesn't work, your first instinct should be: add Serial prints.

void loop() {
  int sensorValue = analogRead(A0);

  // Print the raw reading
  Serial.print("Raw: ");
  Serial.print(sensorValue);

  // Convert and print the voltage
  float voltage = sensorValue * (5.0 / 1023.0);
  Serial.print("  Voltage: ");
  Serial.print(voltage, 2);  // 2 decimal places
  Serial.println("V");

  delay(250);  // Don't flood the Serial Monitor
}

This tells you:


3.7 Debugging Basics: Is It Hardware or Software?

When your project doesn't work, the problem is either in the circuit or in the code. Here's a decision tree:

The Hardware-or-Software Decision Tree

1. Does the Arduino have power?

2. Does the sketch upload?

3. Add Serial prints to verify your code logic.

4. Use the multimeter to verify your circuit.

5. The "one change at a time" rule.

Common Beginner Issues

Symptom Likely Cause
LED doesn't light Wrong polarity, wrong pin in code, loose wire
LED always on Pin set wrong in code, wired to 5V instead of data pin
Sensor reads 0 always Wiring disconnected, wrong analog pin in code
Sensor reads 1023 always Voltage divider wired wrong (sensor and resistor swapped)
Serial shows garbage Baud rate mismatch between code and monitor
Button doesn't respond Floating pin (forgot pull-up/pull-down), wrong pin number
Values fluctuate wildly Loose connection, floating pin, electrical noise

Module Project: Light-Responsive LED

Objective

Build a circuit where a photoresistor reads ambient light and automatically adjusts an LED's brightness. When it's bright, the LED dims; when it's dark, the LED brightens. Use the Serial Monitor to display real-time sensor readings.

Components Needed

Component Quantity Notes
Arduino Uno 1
USB-B cable 1
Breadboard 1
Photoresistor (LDR) 1
10kΩ Resistor 1 For the voltage divider
5mm LED (any color) 1
220Ω Resistor 1 For the LED
Jumper wires 5

Circuit Description

The circuit has two parts:

Sensor side (voltage divider):

Output side (LED):

Wiring Steps

Step 1: Build the voltage divider

  1. Photoresistor: one leg into row 5 column E, other leg into row 8 column E
  2. 10kΩ resistor: one leg into row 8 column A, other leg into row 12 column A
  3. Jumper wire from positive (+) power rail to row 5 column A
  4. Jumper wire from row 12 column B to negative (−) power rail
  5. Jumper wire from row 8 column B to Arduino A0

Step 2: Wire the LED 6. LED: anode (long leg) into row 20 column E, cathode into row 21 column E 7. 220Ω resistor: one leg into row 20 column A, other leg into row 17 column A 8. Jumper wire from row 17 column B to Arduino pin 9 9. Jumper wire from row 21 column A to negative (−) power rail

Step 3: Connect Arduino power to breadboard 10. Jumper wire from Arduino 5V to positive (+) power rail 11. Jumper wire from Arduino GND to negative (−) power rail

The Code

/*
 * Module 3 Project: Light-Responsive LED
 * Reads ambient light with a photoresistor and adjusts LED brightness inversely.
 * Bright room → dim LED. Dark room → bright LED.
 * Real-time values displayed on Serial Monitor.
 *
 * Circuit:
 * - Photoresistor + 10kΩ voltage divider → A0
 * - LED + 220Ω resistor → pin 9 (PWM)
 *
 * Board: Arduino Uno
 */

const int SENSOR_PIN = A0;   // Analog input from photoresistor
const int LED_PIN = 9;       // PWM output to LED

void setup() {
  Serial.begin(9600);
  pinMode(LED_PIN, OUTPUT);
  // Analog pins don't need pinMode — they default to input
}

void loop() {
  // Read the light level (0 = dark, 1023 = bright)
  int lightLevel = analogRead(SENSOR_PIN);

  // Convert to voltage for debugging
  float voltage = lightLevel * (5.0 / 1023.0);

  // Map the sensor range to LED brightness — INVERTED
  // More light (high reading) → less LED brightness
  // Less light (low reading) → more LED brightness
  int ledBrightness = map(lightLevel, 0, 1023, 255, 0);

  // Constrain to valid PWM range (safety net)
  ledBrightness = constrain(ledBrightness, 0, 255);

  // Set the LED brightness
  analogWrite(LED_PIN, ledBrightness);

  // Print debug info to Serial Monitor
  Serial.print("Light: ");
  Serial.print(lightLevel);
  Serial.print("  Voltage: ");
  Serial.print(voltage, 2);
  Serial.print("V  LED: ");
  Serial.print(ledBrightness);
  Serial.print("/255 (");
  Serial.print((ledBrightness / 255.0) * 100, 0);
  Serial.println("%)");

  delay(100);  // Read 10 times per second
}

What to Observe

  1. Open the Serial Monitor at 9600 baud
  2. Watch the values change as you cover/uncover the photoresistor
  3. The LED should get brighter as you block light from the sensor
  4. Note the sensor's range in your environment — it might not span the full 0–1023

Calibration Tip

Your actual sensor range likely won't be 0–1023. In a typical room, you might see 200–800. You can improve the response by calibrating:

// Replace the generic map with your measured range
int ledBrightness = map(lightLevel, 200, 800, 255, 0);
ledBrightness = constrain(ledBrightness, 0, 255);

Measure your actual minimum (hand covering sensor) and maximum (flashlight on sensor) using the Serial Monitor, then plug those values in.

Circuit Diagram

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 400" font-family="monospace" font-size="12">
  <!-- Arduino Uno -->
  <rect x="50" y="80" width="160" height="280" fill="#1a7a8a" stroke="#333" stroke-width="2" rx="8"/>
  <text x="130" y="110" text-anchor="middle" fill="white" font-size="15" font-weight="bold">Arduino</text>
  <text x="130" y="130" text-anchor="middle" fill="white" font-size="13">Uno</text>

  <!-- Pin labels -->
  <rect x="210" y="150" width="25" height="16" fill="#2a8a9a" stroke="white" stroke-width="1" rx="2"/>
  <text x="222" y="162" text-anchor="middle" fill="white" font-size="9">5V</text>

  <rect x="210" y="180" width="25" height="16" fill="#2a8a9a" stroke="white" stroke-width="1" rx="2"/>
  <text x="222" y="192" text-anchor="middle" fill="white" font-size="9">GND</text>

  <rect x="210" y="230" width="25" height="16" fill="#2a8a9a" stroke="white" stroke-width="1" rx="2"/>
  <text x="222" y="242" text-anchor="middle" fill="white" font-size="9">A0</text>

  <rect x="210" y="290" width="25" height="16" fill="#2a8a9a" stroke="white" stroke-width="1" rx="2"/>
  <text x="222" y="302" text-anchor="middle" fill="white" font-size="9">D9</text>

  <!-- === SENSOR CIRCUIT (top) === -->
  <text x="500" y="75" text-anchor="middle" font-size="13" font-weight="bold" fill="#333">Sensor Circuit</text>

  <!-- 5V wire to photoresistor -->
  <line x1="235" y1="158" x2="350" y2="158" stroke="red" stroke-width="2"/>
  <line x1="350" y1="158" x2="350" y2="120" stroke="red" stroke-width="2"/>

  <!-- Photoresistor (as a box with zigzag symbol) -->
  <rect x="330" y="100" width="40" height="20" fill="none" stroke="#996600" stroke-width="2" rx="3"/>
  <text x="350" y="114" text-anchor="middle" font-size="9" fill="#996600">LDR</text>
  <text x="350" y="95" text-anchor="middle" font-size="10" fill="#996600">☀</text>

  <!-- Junction wire down -->
  <line x1="370" y1="110" x2="450" y2="110" stroke="#333" stroke-width="2"/>
  <circle cx="450" cy="110" r="4" fill="#333"/>

  <!-- Wire to A0 -->
  <line x1="450" y1="110" x2="450" y2="238" stroke="green" stroke-width="2"/>
  <line x1="450" y1="238" x2="235" y2="238" stroke="green" stroke-width="2"/>
  <text x="450" y="175" text-anchor="start" font-size="10" fill="green"> → A0</text>

  <!-- 10k resistor down to GND -->
  <line x1="450" y1="110" x2="530" y2="110" stroke="#333" stroke-width="2"/>
  <rect x="530" y="100" width="60" height="20" fill="none" stroke="#333" stroke-width="2" rx="3"/>
  <text x="560" y="114" text-anchor="middle" font-size="10">10kΩ</text>
  <line x1="590" y1="110" x2="650" y2="110" stroke="blue" stroke-width="2"/>
  <line x1="650" y1="110" x2="650" y2="188" stroke="blue" stroke-width="2"/>

  <!-- GND wire -->
  <line x1="235" y1="188" x2="350" y2="188" stroke="blue" stroke-width="2"/>
  <line x1="350" y1="188" x2="650" y2="188" stroke="blue" stroke-width="2"/>
  <text x="650" y="205" text-anchor="middle" font-size="10" fill="blue">GND</text>

  <!-- === LED CIRCUIT (bottom) === -->
  <text x="500" y="260" text-anchor="middle" font-size="13" font-weight="bold" fill="#333">LED Circuit</text>

  <!-- D9 wire to resistor -->
  <line x1="235" y1="298" x2="350" y2="298" stroke="orange" stroke-width="2"/>

  <!-- 220 ohm resistor -->
  <rect x="350" y="288" width="60" height="20" fill="none" stroke="#333" stroke-width="2" rx="3"/>
  <text x="380" y="302" text-anchor="middle" font-size="10">220Ω</text>

  <!-- Wire to LED -->
  <line x1="410" y1="298" x2="480" y2="298" stroke="orange" stroke-width="2"/>

  <!-- LED -->
  <polygon points="480,278 480,318 520,298" fill="none" stroke="red" stroke-width="2"/>
  <line x1="520" y1="278" x2="520" y2="318" stroke="red" stroke-width="2"/>
  <text x="500" y="270" text-anchor="middle" font-size="11" fill="red">LED</text>
  <!-- Light rays -->
  <line x1="510" y1="276" x2="518" y2="263" stroke="red" stroke-width="1" stroke-dasharray="2,2"/>
  <line x1="520" y1="273" x2="528" y2="260" stroke="red" stroke-width="1" stroke-dasharray="2,2"/>

  <!-- LED cathode to GND -->
  <line x1="520" y1="298" x2="580" y2="298" stroke="black" stroke-width="2"/>
  <line x1="580" y1="298" x2="580" y2="340" stroke="black" stroke-width="2"/>
  <line x1="580" y1="340" x2="350" y2="340" stroke="black" stroke-width="2"/>
  <line x1="350" y1="340" x2="350" y2="188" stroke="blue" stroke-width="2"/>

  <!-- Annotations -->
  <text x="430" y="330" text-anchor="middle" font-size="10" fill="#666">→ GND rail</text>
</svg>

Circuit Schema (JSON)

{
  "module": 3,
  "project": "Light-Responsive LED",
  "board": "Arduino Uno",
  "schematic": {
    "components": [
      {
        "id": "U1",
        "type": "arduino_uno",
        "pins_used": {
          "5V": "net_vcc",
          "GND": "net_gnd",
          "A0": "net_sensor_junction",
          "D9": "net_led_drive"
        }
      },
      {
        "id": "LDR1",
        "type": "photoresistor",
        "resistance_range": {
          "bright": "1k",
          "dark": "100k",
          "unit": "ohm"
        },
        "pins": {
          "pin1": "net_vcc",
          "pin2": "net_sensor_junction"
        }
      },
      {
        "id": "R1",
        "type": "resistor",
        "value": "10000",
        "unit": "ohm",
        "power_rating": "0.25W",
        "color_code": ["brown", "black", "orange", "gold"],
        "purpose": "Voltage divider fixed resistor",
        "pins": {
          "pin1": "net_sensor_junction",
          "pin2": "net_gnd"
        }
      },
      {
        "id": "R2",
        "type": "resistor",
        "value": "220",
        "unit": "ohm",
        "power_rating": "0.25W",
        "color_code": ["red", "red", "brown", "gold"],
        "purpose": "LED current limiter",
        "pins": {
          "pin1": "net_led_drive",
          "pin2": "net_r2_led"
        }
      },
      {
        "id": "LED1",
        "type": "led",
        "color": "red",
        "forward_voltage": 2.0,
        "forward_current_ma": 20,
        "pins": {
          "anode": "net_r2_led",
          "cathode": "net_gnd"
        }
      }
    ],
    "nets": [
      {
        "name": "net_vcc",
        "description": "5V supply rail",
        "nodes": ["U1.5V", "LDR1.pin1"]
      },
      {
        "name": "net_gnd",
        "description": "Ground rail",
        "nodes": ["U1.GND", "R1.pin2", "LED1.cathode"]
      },
      {
        "name": "net_sensor_junction",
        "description": "Voltage divider midpoint — LDR meets 10kΩ — sensed by A0",
        "nodes": ["LDR1.pin2", "R1.pin1", "U1.A0"]
      },
      {
        "name": "net_led_drive",
        "description": "PWM output from pin 9 to LED resistor",
        "nodes": ["U1.D9", "R2.pin1"]
      },
      {
        "name": "net_r2_led",
        "description": "LED current-limiting resistor to LED anode",
        "nodes": ["R2.pin2", "LED1.anode"]
      }
    ],
    "power": {
      "source": "USB",
      "board_voltage": 5.0,
      "sensor_current_ma": 0.5,
      "led_max_current_ma": 20,
      "total_max_current_ma": 20.5
    }
  },
  "breadboard": {
    "connections": [
      {
        "step": 1,
        "instruction": "Place photoresistor: one leg into row 5 column E, other leg into row 8 column E",
        "component": "LDR1"
      },
      {
        "step": 2,
        "instruction": "Place 10kΩ resistor: one leg into row 8 column A, other leg into row 12 column A",
        "component": "R1"
      },
      {
        "step": 3,
        "instruction": "Jumper wire (red) from positive (+) power rail to row 5 column A",
        "type": "wire",
        "color": "red"
      },
      {
        "step": 4,
        "instruction": "Jumper wire (black) from row 12 column B to negative (−) power rail",
        "type": "wire",
        "color": "black"
      },
      {
        "step": 5,
        "instruction": "Jumper wire (green) from row 8 column B to Arduino A0",
        "type": "wire",
        "color": "green"
      },
      {
        "step": 6,
        "instruction": "Place LED: anode (long leg) into row 20 column E, cathode into row 21 column E",
        "component": "LED1"
      },
      {
        "step": 7,
        "instruction": "Place 220Ω resistor: one leg into row 20 column A, other leg into row 17 column A",
        "component": "R2"
      },
      {
        "step": 8,
        "instruction": "Jumper wire (orange) from row 17 column B to Arduino pin 9",
        "type": "wire",
        "color": "orange"
      },
      {
        "step": 9,
        "instruction": "Jumper wire (black) from row 21 column A to negative (−) power rail",
        "type": "wire",
        "color": "black"
      },
      {
        "step": 10,
        "instruction": "Jumper wire (red) from Arduino 5V to positive (+) power rail",
        "type": "wire",
        "color": "red"
      },
      {
        "step": 11,
        "instruction": "Jumper wire (black) from Arduino GND to negative (−) power rail",
        "type": "wire",
        "color": "black"
      }
    ]
  },
  "code": {
    "filename": "module03_light_responsive_led.ino",
    "language": "cpp"
  },
  "validation": {
    "expected_behavior": "LED brightness inversely follows ambient light. Serial Monitor shows live values.",
    "measurements": {
      "sensor_voltage_bright": { "min": 3.0, "max": 4.8, "unit": "V" },
      "sensor_voltage_dark": { "min": 0.2, "max": 1.5, "unit": "V" },
      "led_max_current": { "min": 12, "max": 22, "unit": "mA" }
    },
    "common_mistakes": [
      "Sensor reads 0 always: LDR and 10kΩ resistor may be swapped in the divider",
      "Sensor reads 1023 always: check that the junction wire actually reaches A0",
      "LED doesn't dim smoothly: verify pin 9 is a PWM pin (marked with ~)",
      "Serial shows garbage: baud rate mismatch — check Serial.begin matches monitor setting",
      "Values don't change when covering sensor: photoresistor legs might not be in connected rows"
    ]
  }
}

Experiments to Try

Experiment 1: Night Light with Threshold

Instead of smooth dimming, make the LED turn fully on below a threshold and fully off above it:

const int THRESHOLD = 400;  // Adjust based on your readings

void loop() {
  int lightLevel = analogRead(SENSOR_PIN);

  if (lightLevel < THRESHOLD) {
    digitalWrite(LED_PIN, HIGH);  // Dark — light on
  } else {
    digitalWrite(LED_PIN, LOW);   // Bright — light off
  }

  delay(100);
}

Experiment 2: Serial Plotter

Instead of the Serial Monitor, try Tools → Serial Plotter. Print just the number:

void loop() {
  Serial.println(analogRead(SENSOR_PIN));
  delay(50);
}

You'll see a live graph of the sensor readings — much easier to visualize than scrolling numbers.

Experiment 3: Button + Sensor + LED

Combine a button from section 3.2: when the button is pressed, the LED responds to light; when released, the LED is off.

const int BUTTON_PIN = 7;
const int SENSOR_PIN = A0;
const int LED_PIN = 9;

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  if (digitalRead(BUTTON_PIN) == LOW) {
    // Button pressed — respond to light
    int light = analogRead(SENSOR_PIN);
    int brightness = map(light, 0, 1023, 255, 0);
    analogWrite(LED_PIN, constrain(brightness, 0, 255));
  } else {
    // Button not pressed — LED off
    analogWrite(LED_PIN, 0);
  }
  delay(50);
}

Self-Check: Module 3

Before moving to Module 4, make sure you can:


Key Terms Glossary

Term Definition
Digital signal A signal with exactly two states: HIGH (5V) or LOW (0V)
Analog signal A signal that varies continuously across a range of values
ADC Analog-to-Digital Converter — reads a voltage and returns a number (0–1023)
PWM Pulse Width Modulation — rapidly switching a digital pin to simulate analog output
Duty cycle The percentage of time a PWM signal is HIGH
Pull-up resistor Connects a pin to VCC so it reads HIGH by default
Pull-down resistor Connects a pin to GND so it reads LOW by default
Floating pin A disconnected input pin that reads unpredictable values
INPUT_PULLUP Arduino pin mode that activates the internal ~20kΩ pull-up resistor
Voltage divider Two resistors in series that output a fraction of the input voltage
map() Arduino function that scales a number from one range to another
constrain() Arduino function that limits a number to a min/max range
Serial Monitor Text display in the IDE for debugging (Tools → Serial Monitor)
Baud rate Communication speed for serial — must match between code and monitor
Photoresistor (LDR) A resistor whose value changes based on light level

Previous: ← Module 2 — Meet the Arduino Next: Module 4 — Making Decisions: Logic & Control Flow →