⚡ Spark Academy53 lessons

Program the Night-Light

A Raspberry Pi Pico, a light sensor and two dozen lines of MicroPython: a lamp that decides for itself — with every threshold and fade under your editorial control.

lesson 1 of 1 in this unit

Builds on: 11.2 Reading the Analog World11.1 Hello, Microcontroller8.1 Build the PWM Dimmer6.1 The Operational Amplifier

The mission

Build a lamp that switches itself on at dusk, off at dawn, with a knob to set your idea of “dark” — and, because software makes it a two-line change, a fade mode where brightness grows with darkness. This is the complete embedded-systems loop: sense → decide → act, and every stage is a lesson you own: divider sensing (2.2), ADC (11.2), hysteresis (6.1), PWM dimming (Unit 8).

Shopping list (beyond your kit)

PartSpecQty≈ Cost
Raspberry Pi PicoPico or Pico W, with header pins (or solder them — a rite of passage)1$5–7
Micro-USB cabledata-capable, not charge-only!1$2
Photoresistor (LDR)GL5528 or similar1$0.50
Resistor10 kΩ — brown·black·orange1$0.10
Reused: breadboard, LED, 470 Ω, jumpers, and the 100 kΩ potentiometer from Unit 8. The 9 V battery retires — the Pico runs from USB and its pins speak 3.3 V.
New voltage, new rule

The Pico is a 3.3 V citizen. Power your breadboard rails from its 3V3(OUT) pin — never connect the old 9 V battery to any Pico pin, and never feed a GPIO more than 3.3 V. (Your LED at 3.3 V through the same 470 Ω still lights fine: (3.3 − 1.8)/470 ≈ 3 mA — dimmer than before, and PWM at 100% is its new maximum.)

The wiring

Seat the Pico across the breadboard’s centre gap (USB connector at one end, like the 555’s notch rule at a bigger scale). Pin numbers below are the physical pins counted counter-clockwise from the USB end — the same DIP convention you learned on the 555, forty pins instead of eight:

#FromToWith
1Pico 3V3(OUT) — physical pin 36+ railjumper (red)
2Pico GND — physical pin 38− railjumper (black)
3+ raila free column (the sensor node)LDR
4sensor node− rail10 kΩ (divider's bottom leg)
5sensor nodeGP26 / ADC0 — physical pin 31jumper
6pot outer legs+ rail and − railthe pot IS a divider (2.2)
7pot wiper (middle leg)GP27 / ADC1 — physical pin 32jumper
8GP15 — physical pin 20LED anode via 470 Ω, cathode to − railyour standard LED branch
9Pico USBcomputerthe data cable

Notice what the divider does here: bright room → LDR small → sensor node pulled toward 3.3 V → high ADC reading. Dark room → low reading. The code’s comparisons follow from that one sentence.

Breathing life into it

  1. Download the MicroPython firmware file (a .uf2) for the Pico from micropython.org.
  2. Hold the Pico’s BOOTSEL button while plugging in the USB — it appears as a tiny flash drive.
  3. Drag the .uf2 onto it. The drive vanishes; the Pico reboots as a Python machine.
  4. Install Thonny (free, thonny.org), choose MicroPython (Raspberry Pi Pico) as the interpreter — bottom-right corner.
  5. Paste the program below and press Run. Cover the LDR with your hand.
  6. When it behaves, save it to the Pico as main.py — from then on it runs on power-up, no computer needed. Congratulations: you have shipped firmware.

The firmware

from machine import Pin, ADC, PWM
from time import sleep_ms

ldr = ADC(26)            # light sensor divider  -> GP26
knob = ADC(27)           # threshold potentiometer -> GP27
led = PWM(Pin(15))       # night-light LED       -> GP15
led.freq(1000)           # 1 kHz: your Unit 8 dimmer, in code

HYST = 3000              # Lesson 6.1: no chatter at dusk

lamp_on = False
while True:
    light = ldr.read_u16()        # 0 (dark) .. 65535 (bright)
    threshold = knob.read_u16()   # set by the knob

    if light < threshold - HYST:  # definitely dark
        lamp_on = True
    if light > threshold + HYST:  # definitely bright
        lamp_on = False

    if lamp_on:
        darkness = max(0.0, min(1.0, (threshold - light) / 20000))
        led.duty_u16(int(20000 + 45535 * darkness))
    else:
        led.duty_u16(0)

    sleep_ms(50)                  # 20 decisions per second

Two dozen lines, and every one traceable to a lesson: the two ADC reads (11.2), the hysteresis pair of ifs (6.1), the PWM duty computation (Unit 8), the superloop with its polite sleep_ms(50) (11.1).

If it misbehaves

SymptomLikely causeFix
No board in ThonnyCharge-only USB cable, or interpreter not selectedUse a data cable; pick the Pico interpreter bottom-right
LED never lightsLED direction or wrong pinLong leg toward the 470 Ω on GP15 (physical pin 20)
Lamp logic invertedLDR and 10 kΩ swapped in the dividerLDR to +, fixed resistor to − (or swap the comparisons)
Knob does nothingOuter leg wired instead of the wiperMiddle leg to GP27 — same mistake, same fix as Unit 8
Flickers at duskHYST too small for your roomRaise HYST — you have a lab above to pick a value

Experiments — now it's software

  • Breathe: replace the fade math with a slow sine of duty — a MacBook-style breathing lamp. Three lines.
  • Log the dawn: print(light) each loop and watch sunrise as a column of numbers in Thonny. Your first data logger.
  • Average 16 ADC reads (11.2's tip) and watch the dusk transition steady itself.
  • Pico W owners: the same chip has Wi-Fi — a web-controlled lamp is a weekend, not a career.

Where you stand now

Three builds sit on your desk: a blinker you sized with RC math, a dimmer you steered with diodes, and a lamp that senses and decides because you told it how. You can read schematics, size components, debug with Kirchhoff and a multimeter, reason about signals in time and frequency, follow logic from gate to program counter — and now flash firmware. One summit remains: the master course — sampling and Fourier, PID control, the art of real-world tolerances and debugging — ending with the finest full-circle build this course could offer: your own oscilloscope, pointed back at everything you have made.

⚡ Lab — Digital Twin — the Whole Signal Chain

Every block of the night-light, live: room light → LDR → divider → ADC → decision → PWM → LED. Play with dusk before you wire it.

  • Sweep room light down slowly, then back up — the on and off points differ. That’s your HYST.
  • Set the threshold knob low: the lamp waits for deep darkness.
  • Compare switch mode and fade mode — one variable in the firmware.
70%
50%
Dusk test
sweep room light down slowly: one clean turn-on. Sweep back up: it turns off at a *different* level — hysteresis at work
Every lesson at once
divider (2.2) → ADC (11.2) → Schmitt (6.1) → PWM (8) — now in two dozen lines of code

Build checklist — 0 / 14