I wanted a DIY LAN-only light source, the primary function of which is to gradually fade up before my morning alarm goes off. I wanted the project to support multiple, individually addressable instances of the lamp, and to support an extensible set of actions (e.g. pulsing), in addition to a slow fade up. Extras include being sunrise- (and, maybe, calendar-) aware (i.e. can be configured to not switch on after the sun is up and/or if it's a public holiday today).
The following SVG files can be used to laser-cut the physical components of the 'SunriseLamp' (note that some of the SVG files contain enough parts for three lamps, as I was making multiple lamp instances). The body of the lamp is comprised of:
- 1 x front face, laser-cut from 3mm transparent, frosted white acrylic. Eight holes around the perimeter facilitate alignment to...
- 2 x 4 edges, laser-cut from 6mm MDF. Four edge pieces make a complete circle, and each circle supports the front or rear of the lamp. I painted these white between cutting and assembly. Note that the SVG file contains enough edges for three lamps.
- 1 x rear cone, laser-cut from 1mm white cardboard. The sector is 'zipped' closed with tape to make the cone. The cone helps distribute light in an interesting way and creates a cavity in which to hide the electronics.
- 1 x 75mm x ~1250mm strip of 1mm white cardboard (no file, just cut it by hand). I'd recommend cutting this slightly long (say, +50mm), and cutting it to length during assembly.
Assembly is fairly straightforward. I used clear craft glue and white duct tape. Do not secure the rear cone until everything is tested and working. See the following photos for guidance (ignore the internal tabs - I didn't end up using those):
Rear view: left to 12V power adapter; right to LEDs; 12V→USB/5V adapter has been electrically isolated with a small plastic bag.
Electronic Assets: 12V power supply; 12V→USB/5V adapter; ESP8266 microcontroller; LED strip; custom circuit board.
12V power supply: capable of driving the LEDs + 12V→USB/5V adapter; suggest 2Amp+.
12V→USB/5V adapter: used to supply power to the ESP8266 micro controller, via it's USB input; deconstruct/rewire a car cigarette lighter adapter.
ESP8266 microcontroller (or similar): WiFi connected; single GPIO required for PWM output.
LED strip: 12V, warm white; long enough to cover lamp circumference (~120cm); threaded through hole in 'rear cone' and attached to the inside wall of the lamp.
Custom circuit board
We need a 'driver' circuit as the microcontroller can not supply anywhere near enough current to the LEDs by itself.
BOM: 1 x IRF540N MOSFET; 1 x MPS2222A NPN transistor; 2 x 1KΩ resistor; vero board.
SunriseLamp repository on Bitbucket.
This is the code that talks to the microcontroller inside the lamp. Note that this code is optional - any MQTT frontend will work.
The C_TRIGGERS array maps a label to a device+action. I've hard-coded this, as it won't change very often, but you could read this in as a JSON file, if that would suit you better.
A 'trigger' includes: a label (CLI argument); the MQTT topic (address of specific lamp instance); the MQTT payload ('b' = brightness target, or "-1" to switch to OTA-mode; 'd' = duration , in seconds; 'p' = number of pulses to finish with; 'f' = final brightness, or "-1" to hold at the target); and an 'always' flag ('false' = don't do this action if the sun is up).
Multiple triggers can be passed into the Python script via the CLI (comma separated): $> python sunriseLamp.py bed1-sunrise,bed2-sunrise
Schedule via cron, e.g.: 0 7 * * * python /home/pi/sunriseLamp.py test <- runs trigger 'test' everyday @ 07h00
Python imports: astral; datetime; paho.mqtt.publish; pytz; socket; sys
This is the code that runs on the microcontroller inside the lamp. You'll need to update the 'build_flags' in your platformio.ini to match your situation (see below).
I'm using a Adafruit Feather HUZZAH ESP8266 microcontroller, but any module with WiFi and a single PWM GPIO will work.
I'm using an MQTT interface to control the lamp - see code comments for examples. Search the code for '###', and replace, as appropriate.
It should be noted that the microcontroller can be sent a new set of instructions at any time (as opposed to an action being triggered, and you having to wait until its fully complete). Amongst other things, this means you can cancel/switch off any action before it has completed.
PlatformIO includes: ArduinoOTA; ESP8266mDNS; ESP8266WiFi; PubSubClient@2.7
My platformio.ini for this project:
platform = espressif8266
board = huzzah
framework = arduino
upload_protocol = espota
upload_port = 192.168.1.###
upload_flags = --auth=### YOUR OTA PASSWORD ###
-DC_ADDRESS=### 4TH IP ADDRESS BLOCK - AS PER upload_port, ABOVE ###
-DC_CLIENT=\"### MQTT CLIENT ID ###\"
-DC_TOPIC=\"### MQTT TOPIC FOR THIS DEVICE ###\"
-DC_SSID=\"### YOUR WIFI SSID ###\"
-DC_SSPW=\"### YOUR WIFI PASSWORD ###\"
-DC_HASH=\"### YOUR OTA PASSWORD HASH, AS PER 'upload_flags = --auth', ABOVE ###\"
Version 1 of 'SunriseLamp.cpp' used a constant time step (orange line), which gave a linear brightness increase (green line). In practice, this resulted in a slightly too aggressive ramping of brightness. Version 2 transitioned to a linearly decreasing time step (red line) resulting in a more pleasant brightness increase profile (blue line).
All Sunrise Lamp assets by Chris Molloy are licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
¤ Copyright 1999-2023 Chris Molloy ¤ All rights reserved ¤