Ikea DIODER custom controller

Like lots of folks on the internet, I saw the DIODER and felt that it could be improved. And what better way than to make it internet-controlled? Given that I had a Nanode lying around looking for a use it seemed like providence.

Finished DIODER controller

So the plan was this:

  • Instead of just cycling between some gaudy colours, lets pick some classier ones
  • Let’s allow the colours to be updated via the web, saving them to EEPROM
  • Using 1D Perlin noise to blend between a pair of colours will allow for pleasing fades and far more interest than just transitioning from A->B (so much interest that it turned out 2 colours is all it needs … for now)
  • Use HSL colour space for super-classy fading
And here’s the code that does all that: rgb.pde.
The hardware side was pretty straightforward – a MOSFET (Stp36nf06l) per channel to buffer the output from the microcontroller (the LEDs run at 12v). I just used the 12v adaptor that came with the DIODER, and ditched the Ikea controller and little junction box thingy. I did add a switch to kick the ethernet chip into a low power mode (because it saves ~350mA and it was easy :))


closeup of the controller board
It took a while to figure out the HSL colour space, so here’s what I ended up with:
// based on http://www.dipzo.com/wordpress/?p=50
void hslToRgb( int hue, byte sat, byte light, byte* lpOutRgb )
  if( sat == 0 )
    lpOutRgb[0] = lpOutRgb[1] = lpOutRgb[2] = light;

  float nhue   = (float)hue * (1.0f / 360.0f);
  float nsat   = (float)sat * (1.0f / 255.0f);
  float nlight = (float)light * (1.0f / 255.0f);

  float m2;
  if( light < 128 )
    m2 = nlight * ( 1.0f + nsat );
    m2 = ( nlight + nsat ) - ( nsat * nlight );

  float m1 = ( 2.0f * nlight ) - m2;

  lpOutRgb[0] = hueToChannel( m1, m2, nhue + (1.0f / 3.0f) );
  lpOutRgb[1] = hueToChannel( m1, m2, nhue );
  lpOutRgb[2] = hueToChannel( m1, m2, nhue - (1.0f / 3.0f) );

byte hueToChannel( float m1, float m2, float h )
  float channel = hueToChannelInternal( m1, m2, h );
  byte  uchan   = (byte)(255.0f * channel);
  return uchan;

float hueToChannelInternal( float m1, float m2, float h )
   if( h < 0.0f ) h += 1.0f;
   if( h > 1.0f ) h -= 1.0f;

   if( ( 6.0f * h ) < 1.0f )  return ( m1 + ( m2 - m1 ) * 6.0f * h );
   if( ( 2.0f * h ) < 1.0f )  return m2;
   if( ( 3.0f * h ) < 2.0f )  return ( m1 + ( m2 - m1 ) * ((2.0f/3.0f) - h) * 6.0f );
   return m1;

I also experimented with adding a gamma curve to the output, but while it definitely improved colour matching, it made fading a lot more jerky, so I didn’t go with it in the end.

Once I had reliable HSL->RGB working, I needed a good HSL interpolate function. That took some effort. I found you can use a straight linear interpolate on the S & L channels, but you need a custom one for the Hue channel as it needs to pick the shortest way to go around the circle. I also wrote it all to treat the interpolation parameter as a value from 0 to 1 in 0.8 fixed point format. This is how it looks:

void lerpHsl( const int* a, const int* b, byte t, int* lpOutHsl )
  lpOutHsl[0] = lerpHueFp8( a[0], b[0], t );
  lpOutHsl[1] = lerpFp8( a[1], b[1], t );
  lpOutHsl[2] = lerpFp8( a[2], b[2], t );

int lerpFp8( int a, int b, byte t )
  long t0 = ( b - a ) * t;
  return ( a + ( t0 >> 8 ) );

int lerpHueFp8( int a, int b, byte t )
  // adjust inputs so we take the shortest route around the circle
  int cwdist = b - a;
  if( abs(cwdist) > 180 )
    if( b > a )  a += 360;
    else         b += 360;

  long t0 = ( b - a ) * t;
  int l = a + (t0 >> 8);

  return ( l % 360 );

The last colour-related piece was to get a good 1D Perlin noise-like function. After some googling failed to turn up a nice implementation for Arduino, I built this, based on Hugo Elias’ great page here.

#define kPerlinOctaves      3
static const float kaPerlinOctaveAmplitude[kPerlinOctaves] =
  0.75f, 0.4f, 0.1f,

float perlinNoise1( long x )
  x = ( x<<13 ) ^ x;
  return ( 1.0f - ( (x * (x * x * 15731 + 789221L) + 1376312589L) & 0x7fffffff) * (1.0f / 1073741824.0f) );

float perlinSmoothedNoise1( long x )
  return perlinNoise1(x)*0.5f
       + perlinNoise1(x-1)*0.25f
       + perlinNoise1(x+1)*0.25f;

float perlinLerpedNoise1( float x )
  long  xInteger  = long(x);
  float xFraction = x - xInteger;

  float v1 = perlinSmoothedNoise1( xInteger );
  float v2 = perlinSmoothedNoise1( xInteger + 1 );

  return lerpFloat( v1, v2, xFraction );

float perlinNoise1D( float x )
  float total = 0.0f;

  for( int octave = 0; octave < kPerlinOctaves; octave++ )
    int   frequency = 1 << octave;
    float amplitude = kaPerlinOctaveAmplitude[octave];

    total += perlinLerpedNoise1( x * frequency ) * amplitude;

  // map from [-1,1] to [0,1] and constrain
  return constrain( (total + 1.0f) * 0.5f, 0.0f, 1.0f );

This generates the following output, which looks pretty sweet when used as the interpolation parameter

With that, all of the RGB LED controlling was done 🙂

For the web interface, I used the excellent EtherCard library, which made it super-easy.


Job done*!


*actually, I still need to build a nice jQuery’d webpage to make setting colours easier. Currently I need to go to, which isn’t exactly as futuristic as I’d like 🙂

digital clock

I saw this cool clock by Jonas Damon in magma:

However, I didn’t like that the digits had wires between them. This is the 21st century etc. So I set out to make my own version where each digit was completely independent.

Cracking out the trusty Arduino, I soon had a single digit…

While I could have carried on and purchased another 3 Arduini, that would have been a bit flash, and these digits needed to be battery powered (or the no wires thing wouldn’t work). After some digging, I plumped for using the ATtiny2313V as they’re nice and low voltage (and crucially cheap). Using the Arduino to flash the firmware onto the new chip, I soon had my first independent digit!

Things got a bit Matrix and it went into replication…

Then I used some super-expensive circuit layout software to plan the schematic (I built it on Tri-Pad board, hence the groups of 3 connected holes). I also ventured into the realms of sticking stuff on the back of the board for the first time – that’s what the bits in red are.

With that, it was all done bar days of soldering and manufacturing.

The 8 rechargeable, Lithium Ion batteries (costing as much as the entire rest of the build) last about 2 months. It’s well worth the hassle of 6 recharges per year 🙂

The firmware is available here: avr-clock.c and Makefile