Skip to content

Nightlight

There are two configs in this folder and they are two different lights. The mono one is a single dimmable channel — one PWM pin, one MOSFET, one run of white LEDs under a kitchen counter, turned down far enough that you can cross the room at 3 a.m. without waking up. The RGB one is the kid’s-room version: a warm bulb on its own dimmer plus 54 addressable RGBW pixels around the roof, for colour wipes, twinkles and a police-car mode nobody asked for.

Both are ESPHome: they are discovered by Home Assistant with no YAML to write on its side, and both update over WiFi after the first flash.

Parts

Some links below are Amazon affiliate links. As an Amazon Associate I earn from qualifying purchases — at no extra cost to you. It helps keep these guides free. Outside the US, they should send you to your own Amazon store; if one doesn't, searching the part number there will find it.

The mono build — “BRUH Countertop”

The RGB build — “Lev Nightlight”

Both

Nothing here plugs an LED strip into a GPIO pin. An ESP32 output sources a few tens of milliamps and the mono strip wants amps — the MOSFET module is not optional, it is the part that does the switching. The pin only ever drives the module’s gate input.

Wiring

Mono — ESP32 to a single-colour strip

SignalESP32 pinGoes to
PWM outGPIO32MOSFET module signal input
GroundGNDMOSFET module signal ground, and the 24 V supply’s negative
Board power5V / VIN5 V from the buck, or the micro-USB socket
Status LEDGPIO2The board’s own blue LED. Nothing to wire

The MOSFET module sits between the supply and the strip: 24 V in on its input terminals, strip on its output terminals, GPIO32 and GND on its signal header. The buck taps the same 24 V and hands the board 5 V over USB.

RGB — ESP32-S3 to a bulb and a pixel strip

SignalESP32-S3 pinGoes to
Bulb PWMGPIO20MOSFET module signal input → 12 V G4 bulb
Pixel dataGPIO21SK6812 strip DIN, through 330 Ω
Board LEDGPIO48The DevKitC-1’s onboard addressable LED. Nothing to wire
Strip power5V / GND5 V rail from the MP1495, shared ground with the board

Put the 330 Ω resistor in series with the data line, at the ESP32 end, and keep that lead short. Everything shares one ground — the 12 V negative, the 5 V negative and the board’s GND are the same net, and the strip will do nothing at all if you forget it.

Assembly

  1. Set the buck’s output before it is connected to anything. Feed it the supply, put a meter on the output and dial it to 5.0 V. The MP1495 arrives at whatever the last person left it at, and 12 V into an ESP32’s 5 V pin is a dead board.

  2. Cut and re-terminate the strip. Cut on the copper pads, tin them, and solder a JST-SM pigtail on so the shade lifts off the base without a soldering iron. Check the arrows on the strip — data flows one way, and a strip wired backwards is silent, not dim.

  3. Wire the MOSFET module — supply in, load out, signal from the GPIO. Then bench-test it with the board on USB, before anything is glued or screwed down.

  4. Flash it and confirm the entities appear in Home Assistant, then close the box. Every flash after the first is over WiFi, but only if the WiFi credentials were right the first time.

  5. Mount the mono strip in its channel before sticking the channel down. Peel, press, cap the ends, then fix the channel to the underside of the counter. Doing it the other way round means working upside down with a strip that only sticks once.

Configuration

Both configs read from secrets.yaml: wifi_ssid, wifi_password, ota_password, and api_key for the RGB one.

In the mono config, the two substitutions at the top are the only lines you have to change:

substitutions:
device_name: "BRUH Countertop"
device_id: "bubba-nightlight"

In the RGB config, name: and friendly_name: do the same job, and the number that matters is num_leds: 54 on the led_strip_roof light — set it to however many pixels you actually cut. is_rgbw: true stays: an SK6812 pixel has a real white chip and ESPHome needs to know it is there. rgb_order: GRB is correct for these strips; if red and green come out swapped, that is the key to change.

Two things worth fixing while you are in there. The mono config’s api: block has no encryption key — give it one, the way the RGB config does. And there is a globals: block declaring dim_up that nothing in the file ever reads. It is harmless, and you can delete it.

restore_mode: restore_default_on on the mono light means it comes back on by itself after a power cut. For a nightlight that is the correct behaviour, and it is worth copying into the RGB config, which does not have it.

What you get in Home Assistant

The mono build is one light entity with pulse, fast pulse, slow pulse, strobe and random effects, plus a Fade Alert effect that breathes between 100% and 75% on a two-second cycle — that is the one to trigger from an automation when you want the light to tell you something without changing colour. There is also a WiFi signal sensor, a status binary sensor and a restart switch.

The RGB build gives you three lights: the warm bulb, the 54-pixel fill strip, and the board’s own single onboard pixel, which is genuinely useful as a “did it boot” indicator you can then turn off from Home Assistant. The fill strip carries the whole ESPHome addressable effect set plus tuned variants — Color Wipe Mist (a blue three-pixel gradient running backwards), Police, Fireworks Custom, Twinkle BRUH, Flicker BRUH and a blue-then-green Strobe Custom. The last entry in the list is wled:, which turns on WLED’s UDP realtime protocol, so anything that can drive a WLED device — an app, xLights — can take the strip over across the network without reflashing.

Troubleshooting

The pixel strip does nothing, or the first pixel lights and the rest don’t. In order: ground not shared between the 5 V rail and the board, data soldered to the output end of the strip instead of the input end, or 3.3 V data that the first pixel is not reading cleanly. The first two are free to check. For the third, a TXS0108E level shifter on the data line fixes it, though a short lead and the 330 Ω resistor usually make it unnecessary.

Colours are close but wrong — white looks pink, or red and green are swapped. rgb_order and is_rgbw. A GRB strip declared as RGB swaps two channels; an RGBW strip declared as RGB throws away the white chip and mixes a worse white out of the other three.

The strip browns out or shifts colour at high brightness. 54 RGBW pixels at full white pull well over 3 A, which is more than the MP1495 will give you. A nightlight never asks for that, so cap the brightness in Home Assistant — or, if you want the full output, feed the strip from its own 5 V supply and keep the buck for the board alone.

The bulb whines at low brightness. That is the bulb’s own driver ringing at the PWM frequency. Raise frequency: on the ledc output — 20000 Hz is above hearing, but you lose resolution at the very bottom of the dimming range, which is exactly where a nightlight lives. Try 5000 Hz first.

ESPHome rejects rmt_channel:. That key belongs to the older RMT driver. The config pins framework: type: arduino deliberately — leave that alone. If a newer ESPHome still refuses the key, delete both rmt_channel: lines and let it allocate the channels itself.

Files & downloads

ESPHome configuration s (2)

Copy this into your ESPHome directory and adjust the substitutions at the top. Secrets are referenced by name — see secrets.yaml.example .

bruh-nightlight.yaml 92 lines
bruh-nightlight.yaml
# BRUH Nightlight (Monochromatic)
# ────────────────────────────────────────────────────────────
# Simple LED nightlight with PWM brightness control and
# pulse effects. LEDC PWM output for smooth dimming.
#
# Hardware:
# - Board: ESP32
# - Output: LEDC PWM on GPIO32
# ────────────────────────────────────────────────────────────
substitutions:
device_name: "BRUH Countertop"
device_id: "bubba-nightlight"
globals:
- id: dim_up
type: float
restore_value: yes
initial_value: '1.0'
esphome:
name: ${device_id}
esp32:
board: nodemcu-32s
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ota:
password: !secret ota_password
platform: esphome
api:
status_led:
pin:
number: GPIO2
logger:
output:
- platform: ledc
pin: GPIO32
id: gpio_32
frequency: "1220Hz"
light:
- platform: monochromatic
output: gpio_32
restore_mode: restore_default_on
name: "Nightlight"
id: nightlight
default_transition_length: 0.5s
effects:
- pulse:
- pulse:
name: "Fast Pulse"
transition_length: 0.5s
update_interval: 0.5s
- pulse:
name: "Slow Pulse"
update_interval: 2s
- strobe:
- random:
- automation:
name: "Fade Alert"
sequence:
- light.turn_on:
id: nightlight
brightness: 100%
transition_length: 1s
- delay: 1s
- light.turn_on:
id: nightlight
brightness: 75%
transition_length: 1s
- delay: 1s
binary_sensor:
- platform: status
name: "${device_name} Status"
sensor:
- platform: wifi_signal
name: "${device_name} WiFi Signal"
update_interval: 300s
switch:
- platform: restart
name: "${device_name} Restart"

View on GitHub · Download raw

bruh-nightlight-rgb.yaml 164 lines
bruh-nightlight-rgb.yaml
# BRUH Nightlight RGB
# ────────────────────────────────────────────────────────────
# RGB LED strip nightlight with addressable color effects.
# Multiple RGB lighting modes with pulse and custom effects.
#
# Hardware:
# - Board: ESP32-S3
# - Output: RMT LED strip (WS2812/NeoPixel)
# - Additional: LEDC PWM monochromatic LED
# ────────────────────────────────────────────────────────────
esphome:
name: lev-nightlight
friendly_name: "Lev Nightlight"
esp32:
board: esp32-s3-devkitc-1
framework:
type: arduino
logger:
api:
encryption:
key: !secret api_key
ota:
password: !secret ota_password
platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
output:
- platform: ledc
pin: GPIO20
frequency: 1000hz
id: bulb_leds
light:
- platform: esp32_rmt_led_strip
id: board_light
rgb_order: GRB
pin: GPIO48
num_leds: 1
rmt_channel: 0
chipset: ws2812
name: "Board Light"
effects:
- pulse:
transition_length: 550ms
update_interval: 550ms
- platform: monochromatic
output: bulb_leds
name: "Lev Nightlight"
- platform: esp32_rmt_led_strip
id: led_strip_roof
rgb_order: GRB
is_rgbw: true
pin: GPIO21
num_leds: 54
rmt_channel: 1
chipset: SK6812
name: "Lev Nightlight Fill"
effects:
- addressable_color_wipe:
- addressable_color_wipe:
name: "Color Wipe Mist"
colors:
- red: 0%
green: 15%
blue: 100%
white: 0%
num_leds: 3
gradient: true
- red: 0%
green: 0%
blue: 0%
white: 0%
gradient: true
num_leds: 3
add_led_interval: 30ms
reverse: true
- addressable_color_wipe:
name: "Color Wipe Custom"
colors:
- red: 100%
green: 100%
blue: 100%
white: 0%
num_leds: 5
gradient: true
- red: 0%
green: 0%
blue: 0%
white: 0%
num_leds: 1
add_led_interval: 100ms
reverse: false
- addressable_color_wipe:
name: "Police"
colors:
- red: 100%
green: 0%
blue: 0%
white: 0%
num_leds: 6
- red: 0%
green: 0%
blue: 100%
white: 0%
num_leds: 6
add_led_interval: 30ms
reverse: false
- addressable_fireworks:
- addressable_fireworks:
name: "Fireworks Custom"
update_interval: 32ms
spark_probability: 10%
use_random_color: false
fade_out_rate: 120
- addressable_flicker:
- addressable_flicker:
name: "Addressable Flicker BRUH"
update_interval: 25ms
intensity: 15%
- addressable_rainbow:
- addressable_random_twinkle:
- addressable_scan:
- addressable_twinkle:
- addressable_twinkle:
name: "Twinkle BRUH"
twinkle_probability: 80%
progress_interval: 4ms
- flicker:
- flicker:
name: "Flicker BRUH"
alpha: 92%
intensity: 4%
- random:
- strobe:
- strobe:
name: "Strobe Custom"
colors:
- state: true
brightness: 100%
red: 0%
green: 0%
blue: 100%
white: 0%
duration: 500ms
- state: false
duration: 250ms
- state: true
brightness: 100%
red: 0%
green: 100%
blue: 0%
white: 0%
duration: 500ms
- wled:

View on GitHub · Download raw

Every file for this project on GitHub →