PyRaspBeta

Night light

A night light is the friendliest complete build: one sensor, a little maths, and an output you can see. A light sensor (LDR) sits in a divider, the MCP3008 digitises the divider's midpoint, and the program switches an LED on when the reading falls below a dark threshold — and back off when the room is bright again, with a gap in between so it does not flicker at dusk.

Cheap version — wire it by hand:

Fast version — build it from kits:

Both versions run the same program: the light reading arrives on MCP3008 channel 0, and the lamp hangs off GPIO18.

The cheap version

The LDR is the top half of the divider and the 10 kΩ resistor the bottom half, so the tap voltage — and the reading — rises with light: about 0.9 in a lit room and 0.09 at 1 lux. The LED gets its usual series resistor.

circuit.py
from wiring import *

pi = Pi()

ldr = LDR()
adc = MCP3008()

pi.V3_3 >> ldr.a
ldr.b >> adc.ch0
adc.ch0 >> Resistor(10000) >> pi.GND

pi.V3_3 >> adc.vdd
adc.vdd >> adc.vref
adc.agnd >> pi.GND
adc.dgnd >> pi.GND
pi.SCLK >> adc.clk
pi.MISO >> adc.dout
pi.MOSI >> adc.din
pi.CE0 >> adc.cs

pi.GPIO18 >> Resistor(330) >> LED("yellow") >> pi.GND
main.py
from gpiozero import LED, MCP3008
from time import sleep

light = MCP3008(channel=0)     # the LDR divider: 0.0 dark … 1.0 bright
lamp = LED(18)

DARK_BELOW = 0.3      # switch on below this
BRIGHT_ABOVE = 0.5    # …and off above this; the gap stops dusk flicker

while True:
    level = light.value
    if level < DARK_BELOW:
        lamp.on()
    elif level > BRIGHT_ABOVE:
        lamp.off()
    print(f"light {level:.2f}  lamp {'on' if lamp.value else 'off'}")
    sleep(0.5)

The fast version

The light sensor + ADC board is the divider and the chip in one piece, and the LED module is the LED with its resistor, so the wiring is six connections instead of fifteen. The program does not change at all — the board exposes the same channel 0.

circuit.py
from wiring import *

pi = Pi()

light = kits.LightADC()
lamp = kits.LEDModule()

pi.V3_3 >> light.vdd
light.gnd >> pi.GND
pi.SCLK >> light.clk
pi.MISO >> light.dout
pi.MOSI >> light.din
pi.CE0 >> light.cs

pi.GPIO18 >> lamp.sig
lamp.gnd >> pi.GND
main.py
from gpiozero import LED, MCP3008
from time import sleep

light = MCP3008(channel=0)     # the LDR divider: 0.0 dark … 1.0 bright
lamp = LED(18)

DARK_BELOW = 0.3      # switch on below this
BRIGHT_ABOVE = 0.5    # …and off above this; the gap stops dusk flicker

while True:
    level = light.value
    if level < DARK_BELOW:
        lamp.on()
    elif level > BRIGHT_ABOVE:
        lamp.off()
    print(f"light {level:.2f}  lamp {'on' if lamp.value else 'off'}")
    sleep(0.5)

How it works

  • MCP3008(channel=0) from gpiozero talks to the chip over the Pi's hardware SPI pins and returns the tap voltage as a number from 0 to 1. Brighter light means less LDR resistance, a higher tap and a higher reading: roughly 0.09 at 1 lux, 0.34 at 10 lux, 0.72 at 100 lux and 0.93 at 1000 lux.
  • LED(18) is the output. lamp.value follows the threshold test, and the program prints both values every half second so the console shows why the lamp changed.
  • The two thresholds are hysteresis. With a single threshold at 0.4 the lamp would chatter whenever the reading sat on the boundary (clouds, a passing shadow); switching on below 0.3 and off above 0.5 makes every change deliberate.
  • The LDR's reading follows the simulated light on the cell, not a fixed number: the LDR by default watches the illuminance of its zone, refreshing every 50 ms, so the program can genuinely darken and brighten.

Try it

Build the board in the Workspace and press Run. The LDR starts in the indoor zone, where the simulated room's lamps are on from 07:00 to 23:00 (about 300 lux) and off overnight (about 0.5 lux) — so at night the console shows light 0.09 lamp on, and in daylight light 0.91 lamp off.

To fake darkness at any hour, select the LDR and use its Datafeed section in the inspector:

  • Set Source to Manual and drag the value slider to 1 lux, then 300 lux. While the simulation runs the slider moves the live value — the lamp follows within a poll or two.
  • Or set the field's Zone to outdoor to follow real daylight, or add a Wave source (period 60 s) to sweep the light automatically. The sparkline previews the next minute, hour or day before you run it.

As a contest submission, the run would be an objective that pins the world and checks behaviour: the built-in medium objective Night light sets the sensor's illuminance to 1000 lux, drops it to 2 lux at 2 s and restores it at 5 s, then requires the LED to be off, on and off again — plus a damage check. Prints are not part of the verdict, so the console lines are for you.

Variations

  • Fade, don't switch: use PWMLED(18) and map the reading to brightness, e.g. lamp.value = max(0.0, min(1.0, (0.6 - level) / 0.5)), for a lamp that dims up as dusk falls. The ready-made Night light example project in the Projects dialog does exactly this.
  • Dusk in a minute: set the World dialog's "Simulation starts at" to an evening time and run at 100× speed. Point the LDR's zone at outdoor and you can watch a whole sunset move the lamp across its thresholds.
  • Wake-up light: invert the logic and drive a white LED or the relay + lamp kit on a schedule, using the same sensor to know when to stop.

Going deeper