Tommaso
Published © CC BY-SA

MySensor Dioder (Ikea)

How to hack an ikea dioder to make it an IoT device. The project uses the mysensors opensource libraries.

IntermediateProtip5,055
MySensor Dioder (Ikea)

Things used in this project

Hardware components

Arduino Pro Mini 328 - 5V/16MHz
SparkFun Arduino Pro Mini 328 - 5V/16MHz
×1
NRF24L01+
×1
ams1117
×1
Capacitor 4.7 µF
Capacitor 4.7 µF
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Original controller unit schema

Capacitors and diode are reported as general and the value are not correct.

Mod schema

Capacitors and diode are reported as general and the value are not correct.

Code

MySensor Dioder

C/C++
MySensors libraries v1.5 needed.
/**
 * The MySensors Arduino library handles the wireless radio link and protocol
 * between your home built sensors/actuators and HA controller of choice.
 * The sensors forms a self healing radio network with optional repeaters. Each
 * repeater and gateway builds a routing tables in EEPROM which keeps track of the
 * network topology allowing messages to be routed to nodes.
 *
 * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
 * Copyright (C) 2013-2015 Sensnology AB
 * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
 *
 * Documentation: http://www.mysensors.org
 * Support Forum: http://forum.mysensors.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 *******************************
 *
 * REVISION HISTORY
 * Version 1.0.0 - Morello Tommaso - Ikea Dioder futures replication with no mysensor support. Button S1 and S2 changed as up/down dimmer buttons.
 * Version 1.0.1 - Morello Tommaso - code lines review.
 * Version 1.0.2 - Morello Tommaso - store the last brightness value when dimmer up/down and use it when switch on.
 * Version 1.1.0 - Morello Tommaso - Integration with Mysensors, only send the current status to the gateway.
 * Version 1.1.1 - Morello Tommaso - manage when the dimmer down button switch off the light.
 * Version 1.2.0 - Morello Tommaso - Add messages from gateway processing
 * Version 1.3.0 - Morello Tommaso - fade transition
 * Version 1.3.1 - Morello Tommaso - add message status request
 * Version 1.3.2 - Morello Tommaso - bug fixing
 * Version 1.3.3 - Morello Tommaso - auto sensor ID
 * 
 * 
 * DESCRIPTION
 * Example sketch showing how to control ikea DIODER. 
 */
 
#include <MyTransportNRF24.h>
#include <MyHwATMega328.h>
#include <MySensor.h> 
#include <SPI.h>

#define WHEEL_PIN A0  
#define RED_PIN 3
#define GREEN_PIN 6
#define BLUE_PIN 5
#define POWER_BUTTON_PIN 4 			//S3
#define DIMMER_UP_BUTTON_PIN 7 		//S1
#define DIMMER_DOWN_BUTTON_PIN 2 	//S2
#define FADE_DELAY 5

const int numReadings = 10;

int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average

const int brightMin = 0; 		//min bright value
const int brightMax = 255; 		//max brigtness value
const int brigthOffset = 15; 	//dimmer step
const int saturation = 255; 	//we'll keep stauration always max
int hue, brightness, oldBrightness;

int wheelValueHue = 0; 			//Wheel position converted in hue value
int oldWheelValueHue = 255;
int statusOn;

unsigned int r, g, b; 			//final RGB value
unsigned int c_r, c_g, c_b; 	//current RGB value
String rgb = "";

// NRFRF24L01 radio driver (set low transmit power by default) 
MyTransportNRF24 radio(RF24_CE_PIN, RF24_CS_PIN, RF24_PA_LEVEL_GW);  
// Select AtMega328 hardware profile
MyHwATMega328 hw;
// Construct MySensors library
MySensor gw(radio, hw);

MyMessage dimmerMsg(0, V_DIMMER);
MyMessage lightMsg(0, V_LIGHT);
MyMessage rgbMsg(1, V_RGB);



void setup()  
{   
  //Mysensors setup  
  //Initialize library and add callback for incoming messages
  gw.begin(incomingMessage, true, true);
  //Register the LED Color Light and the dimmer light as two sensors with the gateway
  gw.present(0, S_DIMMER);
  gw.present(1, S_RGB_LIGHT);
  //Send the sketch version information to the gateway and Controller
  gw.sendSketchInfo("MySensorDioder", "1.3.2");
	
	//Set pins to output. We'll use PWM output.
  pinMode(RED_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(BLUE_PIN, OUTPUT);

  //Wheel connected to an analogue pin as input.
  pinMode(WHEEL_PIN, INPUT);

  //Buttons connected to digital input
  pinMode(POWER_BUTTON_PIN, INPUT);
	pinMode(DIMMER_UP_BUTTON_PIN, INPUT);
	pinMode(DIMMER_DOWN_BUTTON_PIN, INPUT);
	
	// Activate internal pull-ups
	digitalWrite(POWER_BUTTON_PIN, HIGH);
	digitalWrite(DIMMER_UP_BUTTON_PIN, HIGH);
	digitalWrite(DIMMER_DOWN_BUTTON_PIN, HIGH);
	
	//in this version we start with min brightness
	brightness = oldBrightness = brightMin;
	statusOn = 0;
	c_r = c_g = c_b = 0;

	//initialize the analog reading array
	//to avoid noise in analog input we make several reading and then we get the average value
  for (int thisReading = 0; thisReading < numReadings; thisReading++) 
  {
		//The Hue potentiometer value is mapped to degrees - 0 to 360 - as the range of hue values.
		readings[thisReading] = map(analogRead(WHEEL_PIN), 0, 1023, 0, 360);
    //Serial.println(readings[thisReading]);
		total = total + readings[thisReading];
  }//for    
	
	
	
	Serial.println("Setup... end.");
}//setup

void loop()  
{	
   
	//to avoid noise in analog input we make several reading and then we get the average value
	//subtract the last reading
	total = total - readings[readIndex];
	//read from the sensor
	//The Hue potentiometer value is mapped to degrees - 0 to 360 - as the range of hue values.
	readings[readIndex] = map(analogRead(WHEEL_PIN), 0, 1023, 0, 360);
  //add the reading to the total
	total = total + readings[readIndex];
	//advance to the next position in the array:
	readIndex = readIndex + 1;

	//if we're at the end of the array...
	if (readIndex >= numReadings) {
	  // ...wrap around to the beginning:
	  readIndex = 0;
  }//if

	//calculate the average and truncate it
	wheelValueHue = int(total / numReadings);
    
	//we consider only significative changes of wheel
	if((wheelValueHue > oldWheelValueHue+1) || (wheelValueHue < oldWheelValueHue-1))
  {
		Serial.println("Wheel position changed.");
		Serial.print("Wheel position: ");
		Serial.println(wheelValueHue);
		
		hue = map(wheelValueHue, 0, 360, 0, 255);
		Serial.print("Hue value of the Wheel position: ");
		Serial.println(hue);
		
		//Conversion to RGB.
		HSBToRGB(hue, saturation, brightness, &r, &g, &b);
		//send value to lights
		commandLights(r, g, b, false);
		
		//send the value to the mysensor gateway
		gw.send(dimmerMsg.set(map(brightness, 0, 255, 0, 100)));
		gw.send(rgbMsg.set(rgb.c_str()));
		
		oldWheelValueHue = wheelValueHue;    
	}//if
	
	//check of power button
	if(!digitalRead(POWER_BUTTON_PIN))
	{    
		//Delay to avoid noise values. Can also be managed by debouncer.
		delay(100);
		if(!digitalRead(POWER_BUTTON_PIN))
		{
			//after 100ms the value is 0, so it is a real push button
			Serial.println("Power button push.");	
			if(!statusOn && (brightness == oldBrightness))
				//this is a first switch on, we use the max brightness value
				brightness = brightMax;
			else
				brightness = statusOn ? brightMin : oldBrightness;
					
			Serial.print("Brightness: ");
			Serial.println(brightness);
			statusOn = !statusOn;
			HSBToRGB(hue, saturation, brightness, &r, &g, &b);
			commandLights(r, g, b, true);	

			//send the value to the mysensor gateway
			gw.send(lightMsg.set(statusOn));
			gw.send(dimmerMsg.set(map(brightness, 0, 255, 0, 100)));
			gw.send(rgbMsg.set(rgb.c_str()));			
		}//if   
	}//if
	
	//check of up button
	if(!digitalRead(DIMMER_UP_BUTTON_PIN))
	{
		//Delay to avoid noise values. Can also be managed by debouncer.
		delay(100);
		if(!digitalRead(DIMMER_UP_BUTTON_PIN))
		{
			//after 100ms the value is 0, so it is a real push button
			Serial.println("Dimmer up button push.");	
			brightness = brightness + brigthOffset;
			//max value allowed
			if(brightness > brightMax)
				brightness = brightMax;
			oldBrightness = brightness;
			statusOn=1; //this button makes always the status as on
			Serial.print("Brightness: ");
			Serial.println(brightness);	
			HSBToRGB(hue, saturation, brightness, &r, &g, &b);
			commandLights(r, g, b, true);	

			//send the value to the mysensor gateway
			gw.send(lightMsg.set(statusOn));
			gw.send(dimmerMsg.set(map(brightness, 0, 255, 0, 100)));
			gw.send(rgbMsg.set(rgb.c_str()));
		}//if		
	}//if
	
	//check of down button
	if(digitalRead(DIMMER_DOWN_BUTTON_PIN) == 0)
	{
		//Delay to avoid noise values. Can also be managed by debouncer.
		delay(100);
		if(digitalRead(DIMMER_DOWN_BUTTON_PIN) == 0)
		{
			//after 100ms the value is 0, so it is a real push button
			Serial.println("Dimmer down button push.");	
			brightness = brightness - brigthOffset;
			//min value allowed
			if(brightness <= brightMin)
			{
				brightness = brightMin;
				statusOn=0;
			}
			else
			{
				statusOn=1;
				oldBrightness = brightness;	
			}
			Serial.print("Brightness: ");
			Serial.println(brightness);		
			HSBToRGB(hue, saturation, brightness, &r, &g, &b);
			commandLights(r, g, b, true);	

			//send the value to the mysensor gateway
			gw.send(lightMsg.set(statusOn));
			gw.send(dimmerMsg.set(map(brightness, 0, 255, 0, 100)));
			gw.send(rgbMsg.set(rgb.c_str()));
		}//if
	}//if	

  
  gw.process();
}//loop

void HSBToRGB(unsigned int inHue, unsigned int inSaturation, unsigned int inBrightness,unsigned int *oR, unsigned int *oG, unsigned int *oB)
{
	  //based on http://academe.co.uk/2012/04/arduino-cycling-through-colours-of-the-rainbow/
		
    if (inSaturation == 0)
    {
        // achromatic (grey)
        *oR = *oG = *oB = inBrightness;
    }
    else
    {
        unsigned int scaledHue = (inHue * 6);
        unsigned int sector = scaledHue >> 8; //sector 0 to 5 around the color wheel
        unsigned int offsetInSector = scaledHue - (sector << 8);	//position within the sector         
		    unsigned int p = (inBrightness * ( 255 - inSaturation )) >> 8;
        unsigned int q = (inBrightness * ( 255 - ((inSaturation * offsetInSector) >> 8) )) >> 8;
        unsigned int t = (inBrightness * ( 255 - ((inSaturation * ( 255 - offsetInSector )) >> 8) )) >> 8;

        switch( sector ) {
        case 0:
			//The Hue standard wheel has a range of color as: Red -> yellow -> green -> blue -> Red (again)
			//The ikea dioder goes: white -> yellow -> green -> blue -> red
			//so we use the tradionals 6 levels but with a customization of the first sector	
			
            //*oR = inBrightness; 
            //*oG = t;
            //*oB = p;
			
			*oR = inBrightness; 
            *oG = inBrightness;
            *oB = q;
            break;
        case 1:
            *oR = q;
            *oG = inBrightness;
            *oB = p;
            break;
        case 2:
            *oR = p;
            *oG = inBrightness;
            *oB = t;
            break;
        case 3:
            *oR = p;
            *oG = q;
            *oB = inBrightness;
            break;
        case 4:
            *oR = t;
            *oG = p;
            *oB = inBrightness;
            break;
        default:    // case 5:
            *oR = inBrightness;
            *oG = p;
            *oB = q;
            break;
        }//switch

    //int RGB = ((int)r << 16L) | ((int)g << 8L) | (int)b;
		//rgb = String(RGB, HEX);
   String hex_r = String(r, HEX);
   if(hex_r.length() < 2) hex_r =  "0" + hex_r;

   String hex_g = String(g, HEX);
   if(hex_g.length() < 2) hex_g =  "0" + hex_g;

   String hex_b = String(b, HEX);
   if(hex_b.length() < 2) hex_b =  "0" + hex_b;

   rgb = hex_r + hex_g + hex_b;
   
   }//else
}//HSBToRGB

void commandLights(unsigned int r, unsigned int g, unsigned int b, bool fade)
{
	Serial.println("Send command to lights.");
	
	if(fade)
  {
  
   int delta_r = ( r < c_r ) ? -1 : 1;
   int delta_g = ( g < c_g ) ? -1 : 1;
   int delta_b = ( b < c_b ) ? -1 : 1;
   
   while((r != c_r) || (g != c_g) || (b != c_b))
   {
  		c_r += (r != c_r) ? delta_r : 0;
  		c_g += (g != c_g) ? delta_g : 0;
  		c_b += (b != c_b) ? delta_b : 0;
  
      analogWrite(RED_PIN, c_r);
  		analogWrite(GREEN_PIN, c_g);
  		analogWrite(BLUE_PIN, c_b);
  		
  		delay(FADE_DELAY);
   }//while	
  }
  else
  {
     analogWrite(RED_PIN, r);
     analogWrite(GREEN_PIN, g);
     analogWrite(BLUE_PIN, b);
  }

  Serial.print("Bright: ");
	Serial.println(brightness);
	Serial.print("Red: ");
	Serial.println(r);
	Serial.print("Green: ");
	Serial.println(g);
	Serial.print("Blue: ");
	Serial.println(b);
	Serial.print("RGB in HEX: ");
	Serial.println(rgb);
	Serial.println("");
}//commandLights

void incomingMessage(const MyMessage &message) 
{
  long int message_status;
  int messageCommand = mGetCommand(message);
  Serial.print("Incoming message: ");
  Serial.println(message.type);
    
  if (message.type == V_LIGHT) 
  {
    if(messageCommand == 2)
    {
        //requested light status from gateway
        gw.send(lightMsg.set(statusOn));     
	}//if
  }//if
  else if (message.type == V_DIMMER)
  {
      if(messageCommand == 1)
      {
        Serial.print("Incoming light level from gateway. Level: ");
        message_status = message.getInt();
        Serial.println(message_status);
        if((message_status > 100) || (message_status < 0))
          Serial.println("Wrong level received.");
        else
        {
          int newBrightness = map(message_status,0,100,0,255);
          if(newBrightness == 0)
          {
            statusOn = 0;
            oldBrightness = brightness;
          }//if
          else
            statusOn = 1;
            
          brightness = newBrightness;
        }//else
     }//if
     else
      //requested dimmer status from gateway
      gw.send(dimmerMsg.set(map(brightness, 0, 255, 0, 100)));
  }//else if
  else if (message.type == V_RGB)
  {
    if(messageCommand == 1)
    {
      Serial.print("Incoming light rgb change from gateway. rgb requested: ");
      char convBuffer[8];
      message_status = strtol(message.getString(convBuffer),0,16);
      Serial.println(message_status);
      r=(int)(message_status>>16);
      g=(int)(message_status>>8)& 0xFF;
      b=(int)(message_status)& 0xFF;
      commandLights(r, g, b, true);  
    }//if
    else
      //requested rgb values from gateway
      gw.send(rgbMsg.set(rgb.c_str()));
  }//else if
}//incomingMessage

Github

https://github.com/cdinu/ikea-led-bar-hacking

Credits

Tommaso

Tommaso

1 project • 0 followers
Thanks to Jason Judge and Robert Walter.

Comments