Skip to content

Embedded Systems

Build an Arduino night lamp with a button and a dimmer

An Arduino night lamp needs three parts and one idea. A button toggles the lamp, a knob sets its brightness, and an LED on a PWM pin is the lamp. The idea is a bool that remembers whether the lamp is on, flipped once per press, so the lamp stays on after you let go.

2345678910111213A0A1A2A3A4A55VGNDLLearnEE Core5 Vbutton: pin 2LED: pin 9knob: A0
The night lamp: a button to turn it on and off, a knob for the brightness, an LED for the light

The wiring

  • A push button from pin 2 to GND, with the pin set to INPUT_PULLUP: released reads HIGH, pressed reads LOW
  • A 10 kΩ potentiometer with its ends on 5V and GND and its wiper on A0
  • An LED on pin 9, a PWM pin, through a 220 Ω resistor to GND

The plan

  1. Remember: a bool lampOn, false at power-up, so the lamp starts off whatever the knob says.
  2. The press: when pin 2 reads LOW, flip lampOn, then wait out the bounce and wait for the release.
  3. The brightness: while the lamp is on, read A0, map 0 to 1023 into 0 to 255, and analogWrite it to pin 9.
  4. Off means off: while it's off, analogWrite 0.

Live experiment

Find the right glow

A dark bedroom with Pip as the bulb. One press of the button turns the lamp on, the next turns it off, and it stays as you left it in between. Turn the knob to set how bright it glows while it's on. While it's off, the knob does nothing.

Try it yourself: free lesson

“Project: Night lamp” is the project that ends Blink Bay, Embedded Systems World 1. Free with an account. Lessons unlock in order, so you’ll do the 6 before it first.

The sketch

bool lampOn = false;

void setup() {
  pinMode(2, INPUT_PULLUP);
  pinMode(9, OUTPUT);
}

void loop() {
  if (digitalRead(2) == LOW) {        // a press
    lampOn = !lampOn;                 // flip on or off
    delay(50);                        // let the contacts settle
    while (digitalRead(2) == LOW) {}  // wait for the release
    delay(50);                        // let the release settle
  }

  if (lampOn) {
    int level = map(analogRead(A0), 0, 1023, 0, 255);
    analogWrite(9, level);
  } else {
    analogWrite(9, 0);
  }
}

! means NOT, so lampOn = !lampOn turns false into true and back again. The while holds the program until the finger lifts, so one press flips the lamp exactly once, however long it's held.

Check the numbers

The knob is a voltage divider: three quarters of the way round, A0 sees about 3.75 V and reads about 768. map() turns that into 768 × 255 ÷ 1023 ≈ 191, rounded down, so pin 9 is on about 75% of each cycle. The brightness is read on every pass of loop(), so turning the knob changes the glow straight away.

Free lesson

Now build it yourself

Each lesson comes in three short parts: see the idea, try it on a live board, then build it yourself, with quick checks as you go.

Try it yourself: free lesson

“Project: Night lamp” is the project that ends Blink Bay, Embedded Systems World 1. Free with an account. Lessons unlock in order, so you’ll do the 6 before it first.

Or start from the very first lesson, no account needed

Practice questions

Three quick questions from the lessons. Pick an answer to check it.

  1. Question 1 of 3bool lightOn = false; then lightOn = !lightOn; Now lightOn is…
    Show the answer

    Answer: true. ! means NOT: it flips a bool. false becomes true, and the next flip makes it false again. That's how one line toggles a light.

    • Why not “false”? The NOT operator flips a bool, so false becomes true. It would be false again only after a second flip.
    • Why not “an error”? The NOT operator works on any bool, so the line compiles and flips the value.
    • Why not “LOW”? lightOn is a bool, true or false. LOW is a pin level, not something the NOT operator produces.
  2. Question 2 of 3With INPUT_PULLUP and a button from pin 2 to GND, what does digitalRead(2) give while the button is pressed?
    Show the answer

    Answer: LOW. The button joins the pin to 0 V, which beats the weak pull-up, so it reads LOW. Released, the pull-up lifts it back to HIGH.

    • Why not “1023”? 1023 is a top analogRead value. digitalRead only ever gives HIGH or LOW.
    • Why not “HIGH”? HIGH is what the pin reads with the button released, when the pull-up holds it at 5 V. A press joins it to GND.
  3. Question 3 of 3The knob is halfway round, so analogRead(A0) gives about 512. About what does map(reading, 0, 1023, 0, 255) give?
    Show the answer

    Answer: About 127. map keeps the fraction. Halfway up 0 to 1023 becomes halfway up 0 to 255, about 127, so the LED glows about half bright.

    • Why not “About 512”? 512 is the reading before mapping. map() squeezes 0 to 1023 into 0 to 255, so halfway becomes about 127.
    • Why not “1023”? 1023 is the top of analogRead's range. map()'s output here tops out at 255.
    • Why not “0”? 0 would be the knob turned all the way down. Halfway maps to about halfway up 0 to 255.
Keep practising: free lesson

“Project: Night lamp” is the project that ends Blink Bay, Embedded Systems World 1. Free with an account. Lessons unlock in order, so you’ll do the 6 before it first.

Questions people ask

How do I make a button toggle an LED on Arduino?

Keep a bool for the lamp's state. When a press arrives, flip it to its opposite with the NOT operator, then ignore the button until it has been released and settled. Without that wait, one press flips the state on every pass of loop() and it lands at random.

Why does my lamp turn on and off randomly when I press the button?

Either the button bounces, so one press looks like several, or the code flips the state on every pass while the button is held. Wait about 50 ms after the press, then wait for the release, or act only on the change from HIGH to LOW.

Which pin should the LED use for dimming?

A PWM pin, marked with a ~. On an Uno that's 3, 5, 6, 9, 10 or 11. analogWrite on any other pin can only switch the LED fully on or fully off.

Every topic