No blue, no flicker, no cord. AshLight drives 24 red-orange LEDs with true DC current from a 21700 cell, so it reads by candle-like light without telling your body it's morning. Everything here is real and buildable: the schematics, the board, the firmware, the printable case, and the bill of materials.
The light, flicker and button logic you see come from the actual firmware, ported line-for-line to JavaScript and checked frame-for-frame against the C build (3,601 of 3,601 frames identical). Light output and the sleeve's optical efficiency are estimates until the first units are measured. Your screen can't show 620 nm exactly; it's the closest sRGB red.






Any view can be rendered full-frame: render.html?room=living&pattern=flower&mode=candle
Melanopsin, the photopigment that sets your circadian clock, responds most strongly to blue-cyan light around 490 nm and barely at all past 600 nm. A narrow-band LED centred near 620 nm still looks clearly bright to the cones you see with, while contributing almost nothing to the melanopic signal.
The chart compares melanopic stimulation per unit of visible light (melanopic daylight efficacy ratio, D65 = 1). Note the log scale: by this estimate AshLight is roughly 220× lower than a warm-white 2700 K bulb and about 6× lower than an amber 590 nm LED.
These are analytic estimates (hardware/optics.py, calibrated to CIE illuminant A within 6 %). The real claim needs a spectrometer reading of an assembled lamp and the CIE S 026 toolbox. AshLight is a lighting product, not a medical device. It makes no clinical sleep claims.
Every LED current is set by a voltage, not chopped by a switch. A 12-bit DAC sets four set-points, and four op-amp + MOSFET sinks hold each vertical zone of LEDs at exactly that current. Steady mode is flat DC by construction. Candle mode moves the four set-points slowly and independently, so the flame moves up the lamp instead of the whole thing blinking.
| Gesture | From | Result |
|---|---|---|
| Click | Off | On, steady, at the remembered level. 150 ms fade-in. |
| Click | On (any mode) | Off, with a 220 ms fade. |
| Double-click | Off or steady | Candle mode. The first click's fade is caught mid-way, so it never goes dark. |
| Double-click | Candle | Back to steady (toggle). |
| Hold | On | Steps one level every 0.6 s, up then back down. A gentle wink marks each end. Release to keep. Saved to flash 2 s later. |
| Hold 0.8 s | Off | Battery gauge: 1–4 zones light, then fade. (Replaces the green LED the spec removed.) |
| Plug in USB | Off | One slow red breath at the base: charging. No other indicator, ever. |
| Hold 8 s, plugged in | Off | Reboots into the USB bootloader to install new firmware. |
Levels follow a perceptual ladder: 3, 8, 20, 45 and 100 % of full current, so each step looks like a similar change. The renders' linear 10/25/50/75/100 % bunches the top steps together and has no true night-light step.
From the 3D lamp above, last 8 seconds. Double-click for candle mode and watch the tip (Z4) move while the base stays calm.
A Ø38 mm 4-layer main board sits in the lamp's foot, and a 2-layer polyimide flex wraps the LED mast and runs its tail past the cell to a flip-lock connector. Every LCSC part code is checked against JLCPCB's live catalogue by hardware/verify_sourcing.py: …






Ø38 mm, 4 layers (signal / GND / power / signal), JLCPCB 4-layer rules. A KiCad script places all 117 parts with simulated annealing, which minimises wire length while keeping courtyards, the board edge and the enclosure's screw bosses clear. Placement passes KiCad DRC. Routing is the next step. Downloads: main.kicad_pcb · main.kicad_pro · JLC CPL (pick and place)

The flex is routed by its own generator (hardware/pcb/flex_layout.py) with no autorouter. The layout is regular enough to make the routing planar by construction. Each face has an LED_A spine on top. Four zone buses on the bottom are ordered so that a higher zone's bus always runs outside every bus that exists at its height. A collector strip then gathers everything into a 6.5 mm tail that runs past the cell (carrying the NTC) to eight 0.5 mm gold fingers.
KiCad DRC: 0 violations, 0 unconnected. 24 LEDs, 136 track segments, 50 vias. It folds 120° along two lines with no vias or pads within 1 mm of a fold.
Downloads: Gerbers + drill (zip) · BOM · CPL · flex.kicad_pcb
Prices are JLCPCB catalogue prices per part at the quantity for 10 / 100 / 1,000 lamps, fetched . LEDs are consigned (bought from Digi-Key/Mouser), so they're priced from distributor quotes.
Downloads: main board JLC BOM · LED flex JLC BOM · full BOM with notes · live sourcing report · KiCad netlist (main) · (flex) · design calculations
216 mm (8.5 in) tall, a 43 mm flared foot and a 34 mm body, close to the original's 33 mm. The 21700 cell stands upright in the lower body. The round main board sits in the foot with the USB-C port in the flare and the button underneath. The patterned sleeve slides over a thin diffuser around the LED mast.
Every part is generated by mechanical/build.py, the same script that feeds the 3D view above, and checked watertight. Three sleeve patterns ship today, each printing as a single solid piece.
Optical note: Stars & moons leaves only about 6 % of the sleeve open, so it's the most decorative and the dimmest, an estimated ~30 % of the light escapes. Flower of life and quatrefoil are about 35 % open, so expect roughly twice the light in the room.
| Part | Material | Volume | File |
|---|
Print settings: 0.2 mm layers, 3 walls. Lower body upside down, no supports. Sleeve upright in copper silk PLA. Diffuser in natural PETG at 0.8 mm (vase mode works). Bottom cover flat. Three M2×8 self-tapping screws clamp the board.
picotool load -f -x ashlight.uf2 and it reboots itself.ashlight-ashlight_revb.uf2 onto it. The lamp restarts with the new firmware.s prints status, b reboots to the bootloader.Downloads: ashlight-ashlight_revb.uf2 (custom board) · ashlight-pico_proto.uf2 (Raspberry Pi Pico breadboard)
firmware/ include/ashlight_core.h behaviour API src/ashlight_core.c gestures, fades, candle, safety src/settings.c torn-write-safe flash records rp2040/ board support (Pico SDK) tests/test_core.c 15 host tests, all passing tests/trace.c reference trace for the web sim site/sim/core.js JS port, frame-exact vs C $ make test # host unit tests $ make rp2040 # → build/…/ashlight-ashlight_revb.uf2 $ make flash # over USB-C
Raspberry Pi Pico + Adafruit bq24074 and MCP4728 breakouts + four op-amp sinks on perfboard + a hand-soldered LED strip. Runs the same firmware (pico_proto build). Proves the optics, the dimming floor and the candle look before any custom PCB is ordered.
First JLCPCB run of both boards, fully assembled. Bring-up with a current-limited supply, then spectrum, flicker (photodiode), charge thermals, cutoff and runtime tests. Fix anything found and re-spin once if needed.
Ten assembled lamps plus spares, printed shells, tabbed 21700 cells, end-of-line test (5 levels, candle, charge, gauge, sleep current) and a three-unit runtime soak.
| Vendor | What | Qty | Est. cost | Status |
|---|---|---|---|---|
| JLCPCB | Main board: Ø38 mm, 4-layer, 1.6 mm, ENIG, PCBA both sides | 12 | $190–270 | placed · routing |
| JLCPCB | LED flex: 2-layer PI, 0.5 mm-pitch fingers stiffened to 0.3 mm, PCBA with consigned LEDs | 12 | $140–220 | Gerbers ready |
| Digi-Key / Mouser | Lumileds L128-RNG1003500000 (alt Cree JE2835ARO), ship to JLC | 320 | $75–100 | ready |
| 18650BatteryStore or similar | 21700 ~5 Ah cells with welded solder tabs (e.g. Samsung 50S / Molicel P50B) | 12 | $80–110 | ready |
| Adafruit / Raspberry Pi | Stage A0 breadboard kit (Pico, #4755, #4470, op-amps, FETs) | 1 | $60–90 | ready |
| Local print / print farm | Copper silk PLA + natural PETG, ~85 g per lamp | 12 sets | $40–60 | ready (STLs) |
Full order specs, quantities and copy-paste RFQ text: docs/orders.md. The LED flex has Gerbers, BOM and CPL ready to upload for a quote. The main board is placed and still needs routing.
| Item | 10 lamps | 100 | 1,000 |
|---|
Hard costs only, before engineering time, certification (FCC Part 15B, IEC 62133 / UL 2054 battery safety, IEC 62471 photobiological, roughly $6–15k to sell in the US/EU) and margin. The 1,000-unit column assumes the LED price break (~$0.15) and a contract-printed or aluminium shell.
Spectrum of the assembled lamp: peak 615–625 nm, <0.1 % of output below 500 nm.
Flicker in steady mode at every level, on battery and on USB: target <1 % modulation, 1 Hz–2 kHz.
Lowest level at the pillow: dim enough to sleep by, bright enough to be useful. Retune the ladder if not.
Charge thermals at 0.74 A in the printed foot. Cell <45 °C, surfaces <45 °C at 25 °C ambient.
Runtime per level on three units. Advertise only measured numbers.
Zone matching at level 1. Op-amp offset can make zones differ by up to ±12 % at the dimmest step. Firmware can store per-zone trims if the samples show it.
Main board density. 117 parts need about 104 % of one side of the Ø38 mm disc, so the board is assembled on both sides (+≈$30 per run). Placement is done and checked. If routing turns out too tight, the fallback is a 6-layer board.
LED supply. JLC lists both LED options but rarely stocks them, so the LEDs are consigned. Keep both footprint-compatible parts qualified.
RP2040 standby while on is about 3–4 mA (estimate). It's the biggest drain at the dimmest level, so measure it.
IP65 from the original spec is not met by a printed, perforated shell (indoor use only). The original listing's patent status has not been checked.
Firmware under the MIT licence; hardware and enclosure under CERN-OHL-P v2. The whole design regenerates from a handful of Python and C files: change a value in hardware/design.py and the BOM, netlist, schematics and calculations all follow.