Tuesday, March 5, 2013 by darco
Posted in ,

Over a year and a half ago, I put some money into the Hexbright Kickstarter and promptly forgot about it. Earlier this week a box appeared on my doorstep with a brand-new Hexbright Flex, "the world's first open-source LED light".

Notable features:

  • Light output over 500 Lumens. CREE XM-L U2 LED.
  • TIR (Total internal refraction) lens.
  • Waterproof solid aluminum housing.
  • Comes with a 3.7v 2400mAh 18650 rechargeable lithium-ion battery.
  • USB rechargeable, comes with micro-usb cable.
  • USB programmable. It works just like an Arduino...!
  • Built-in three-axis accelerometer. (!?)

This flashlight is probably one of the most over-engineered devices I own, and I love it, but I'm sure you are wondering, "It's a f'ing flashlight. Why bother programming for it? There is only so many things you can do with one button and 500 lumen LED..."

Well, I thought that, too. But after trying the factory firmware and a few of the samples, I realized that this is actually a very interesting human interface problem. Just how should a flashlight behave? What behavior makes it feel right? What is the bare minimum that a flashlight needs to be able to do to be considered seriously?

After doing a lot of thinking and experimentation, I've determined that the following attributes are needed for a single-button flashlight (no matter how many modes or other what-not) to "feel right":

  • If the button is pressed, the light should be on (or getting brighter).
  • The flashlight should never get more dim while the button is being pressed.
  • If you are pressing the button to turn the flashlight off, the light should only turn off upon the release of the button.

Anything else just feels "weird" from my experience, and the stock firmware (along with many of the samples) felt quite weird to me. So I set out with the modest goal to write a better firmware that followed those rules. I used the hexbright4 firmware from Hexbright's Sample Code as the starting point, but I've since pretty much rewritten everything. Here is how it works:

There are four modes:

  1. Constant. Light stays on when button is released.
  2. Momentary. Light is only on while button is pressed. (Flashlight turns off two minutes after last button release)
  3. Blinky. Light blinks for 100ms every half second.
  4. Knob. Rotating the light changes the brightness. (From hexbright4 example, but with filtering and other improvements)

The exact behavior of the button depends on what mode you are in. You change modes by holding down the button for two seconds. The light will then flash up to three times, once every half second. How many flashes you let happen before you let go determines which mode you will be switched to. When switching modes, the current brightness level is preserved, allowing you to select the brightness level for "momentary" and "blinky".

You can change the brightness in both "constant" and "knob" modes. Holding down the button for even longer will allow you to "save" the current settings as the default.

Additional features:

  • Three brightness levels in constant mode, which work just like the factory firmware(hexbright4 originally only had two).
  • Smooth brightness transitions.
  • Smooth Green LED "pulse" when charging(instead of the blinking of the original firmware, which was annoying when trying to sleep).
  • Improvements to the "knob" mode from hexbright4:
    • Accelerometer readings are now median-filtered to reduce spurious readings.
    • Changing the brightness level no longer requires the button to be held down(which is fine now that the accelerometer input is filtered).
    • Accelerometer input is ignored if the flashlight is standing vertically. (Previously, it would randomly flicker in this orientation)
  • Automatic low-power/thermal brightness throttling (so that the light doesn't just cut-out)

If you have a HexBright, you can check out the firmware from Github: https://github.com/darconeous/darcbright. I haven't had a lot of time to clean up the code since I've gotten it working well, so keep that in mind if you would like to hack on it. The Arduino IDE doesn't seem to give me warnings for things like unused variables, etc, so there are more than a few. Each mode is implemented as a protothread, which made the code easier to write and should make it easier to follow than a state machine.

If you give it a try, let me know what you think!