Lampe - overengineered desk lamp, part 1

So, I love dimmed light. If there's one thing I can't stand, it's bright artificial lights in the evening that fill the entire room. To me, this is the opposite to cozy feeling. Which is why the main light in my apartment will be turned on 5-6 times a year at most.

There's one problem though: my other lights suck as well. But hey, we can solve that with some stupid over-engineering and hardware hacking! I'll start with a desk lamp, which I use most of the time.

Requirements

I won't settle for less than this:

Nice to haves

First implementation

I've had these neat little esp8266 modules lying in a drawer for over a year now, and I thought that this was a really cool small project to start getting familiar with these things. small, very cheap, pretty much no extra components needed, tons of processing power, wifi, just enough pins to do something useful with them. Awesome! For this Ive used the ESP-07 modules which have an optionally attachable antenna and 1MiB flash. I've also got lots of ws2812b RGB LEDs lying around, so I've used a bunch of those as well. And because I'm a really bad C programmer and a bit lazy, I've decided to use the NodeMCU lua firmware.

Hardware

16 ws2812b LEDs wired to a freestanding 4x4 matrix (remember those cool 3D LED cubes?) with some thick wire. you do not want to see pictures of this, believe me. Also, an AMS1117-3.3 linear voltage regulator to drop the incoming 5 volts from my homeserver to 3.3 volts for the esp8266. I've had a few of these super-cheap rotary quadrature encoders flying around, so I've slapped that on as well. With everything somewhat securely placed in a small candy can (to act as a lampshade) I sacrificed a microphone clip to have a 3/8" thread and some positioning control.

Connect the ws2812b data pin to GPIO2, rotary a&b to GPIO12 and GPIO13, rotary button to GPIO14, and off you go!

Lampe from the bottom Lampe from the top

Software

That's what I came up with after a bit of fighting with lua. (lua is a really weird language. I don't think I'm going to be a big fan of it!)

ws2812.init()
buf = ws2812.newBuffer(16,3)

rotary.setup(0, 6, 7, 5, 100, 250)
pos = 10000

activated = false
brightness = 0
blue = 128
green = 255

pressed_turn = false

function set_leds()
  local i = brightness
  if not activated then i = 0 end
  local b = math.floor(blue * i / 255)
  local g = math.floor(green * i / 255)
  -- DEBUG: print(string.format("active: %s, brightness: %d, resulting colour: %d %d %d", tostring(activated), brightness, g, i, b))
  buf:fill(g, i, b)
  ws2812.write(buf)
end

rotary.on(0, rotary.TURN, function (_, new_pos, _)
  local _, pressed, _ = rotary.getpos(0)
  local diff = new_pos - pos
  pos = new_pos

  if pressed ~=1 then -- contrary to the docs this is not a boolean, but some integer
    brightness = math.max(0, math.min(255, brightness + diff))
  else
    blue = math.max(0, math.min(255, blue + (diff * 4)))
    green = math.max(0, math.min(255, green + diff))
    pressed_turn = true
  end

  set_leds()
end)

rotary.on(0, rotary.CLICK, function (_, _, _)
  if pressed_turn then
    pressed_turn = false
  else
    activated = not activated
  end

  set_leds()
end)

Result so far

This is what I've implemented already with this low effort:

Huge dynamic range?

Even though I can adjust the brightness level with the rotary encoder, it needs some further work. Especially the lower levels are really coarse compared to the higher levels. Wouldn't call that done. Maybe I can start turning off individual LEDs on lower levels to increase the available resolution? (switching off half of the LEDs would effectively give me one extra bit of resolution - 512 vs. 256 levels)

Also: I could use more brightness. 16 ws2812b do light up plenty in the evening, but at daytime I can use a lot more brightness.

Colour temperature?

Right now I can adjust the blue-level, but I would not call that adjustable colour temperature. It's more like a "suck less" control. Also, the white from RGB-only LEDs sucks. I would love to switch to a mixture of RGBNW and RGBW SK LEDs. Depending on the brightness level of either RGBNW or RGBW LEDs I can shift the colour temperature in between those values. I would need a lot more LEDs for that though to retain enough dynamic range for either temperature extreme.

Heat management?

If I set the lamp to full brightness the LEDs will warm up quite a bit. I would rather slap on a lot more LEDs and in turn set these to lower levels for the same amount of light output, thus spreading the heat over more components.Maybe add a small optional fan (PWM?) and temperature sensor for a sustainable "ZOMFG SUPER BRIGHT" mode.

DIY LED control?

ws2812b LEDs are really, really easy to implement electronically, but maybe we can get better (and brighter) results by rolling our own PWM control for regular LEDs? I don't really need to be able to control every LED individually, a grouping of 3, with RGBW controls for each would be plenty.

Outlook

Well, first up I want to get the software polished up. Implement the missing features, especially cool stuff like timed brightness & temperature (NTP?) before shelling out lots of money for custom PCBs and LEDs. Watch this space for future posts!