Photonics

Enter Your Electronics & Design Project for a chance to win an $200 Shopping cart of product!

Submit an EntrySubmit an Entry  Back to homepage
Project14 Home
Monthly Themes
Monthly Theme Poll

 

Ski Lamp

 

 

1. Introduction

     Hi, this will be my entry for the Photonics Project14 competition. For this project, I decided to upgrade an old project that I've done about 8 months ago, but haven't posted anything about here. I bought a standard RGB LED strip set, which came with 5m of RGB LED strip, a small IR remote control and a 12V power supply. One of my favorite sports is skiing and for a long time, I wanted to mount my skis on the wall, but it would be boring to do it in an ordinary way. So I cut out a mountain of a piece of wood, put the LED strip behind it and hanged it up like that, I loved how it looked and it worked okay, but with limited number of colors, and of course, only controllable using the small remote that came with it. So I decided by using an ESP8266-07 with some additional hardware, to make it better, to keep all of the functionality it has now, but add the option of controlling it by a phone app. Here is how the original lamp looked:

 

2. Plan

To start off, I needed to get familiar with the ESP8266 again, since I haven't used it in quite some time, I was using a standalone ESP8266-07 with an Adafruit Feather Huzzah for some troubleshooting because of the serial monitor. To program the ESP8266-07 I used a small USB to serial converter, but an Arduino Uno with the removed chip can be used also, here is how I connected the ESP8266-07 to program it:

Be careful if you are using this module, or other similar ESP8266 modules, they work on 3.3V and can get fried easily if you connect them to 5V

  • 3.3V --- VCC
  • GND --- GND
  • Tx on the board --- Rx on the converter
  • Rx on the board --- Tx on the converter
  • CH_PD (CH_PC) --- VCC (3.3V)
  • GPIO0 --- GND
  • GPIO15 --- GND

To control the LED strip I'm going to use 3 of the PWM pins on the ESP (12,15,4) that will be connected to more electronics to be able to handle the current of the LED-s, I wanted to stick to the single power supply, so I needed a way to lower down the voltage from 12V to 3.3V and the last thing that I needed to figure out is how to do the control over internet. Instead of making my own small app in Android Studio or MIT App Inventor 2 as I usually do for projects like this, I decided to go with something I've heard of before, but never used before, Blynk. It turned out to be perfect for what I needed it and very easy to use, as I'll show later in the blog. With all of the things figured out here are the parts and tools I used and then the prototyping phase.

 

3. Part & Tools

 

Parts

  • 1 x ESP8266-07
  • 1 x Adafruit 157 - IR sensor
  • 3 x LM258PLM258P - OpAmp
  • 3 x IRF530 - NMOS
  • 9 x 10k Resistor
  • 3 x 1k Resistor
  • 2 x Ceramic capacitor 100nF
  • 1 x 7809IC - Linear Regulator
  • Perfboard with lines
  • Nylon 4 pin connector
  • Screw terminals
  • Cable, wires

 

Tools

  • Soldering iron

  • Pliers
  • Wire Cutters
  • Drill bit - cutting the traces on the perfboard
  • Multimeter
  • Exacto knives

 

Libraries

This is a list of libraries that you don't have automatically installed but need to download

 

4. Prototyping

 

IR remote

     I began the prototyping phase with enabling the thing that the RGB strip came with, IR remote. Using the IR sensor (https://www.adafruit.com/product/157 ) I connected it to an Arduino Nano using the recommended scheme from the product site, and run the example from the IRremote library called IRrecvDumpV2 which printed out to the serial all of the needed information when one of the buttons is pressed, the thing we are interested is the HEX code value, which we can later use in our code to distinguish between different buttons, it's a bit of a tedious job, but I covered all of the buttons from the remote:

One thing to watch out for is that the ESP8266 doesn't support the standard IRremote library, if you try using it, you will be greeted with a message like this:

To go around this error, we have to use the ESP8266 specific library which can be found here: https://github.com/crankyoldgit/IRremoteESP8266

The library is pretty much identical to how it's used compared to the standard one, which is a great thing

With all of the values written down, and the library working on the ESP8266, all we needed to do is compare the value we received to the list of our values and decide what to do with that button.

 

Electronics

     With the IR remote out of the way I could focus on the electronics, there are 2 things I had to solve here, one is the power supply for all of the components and the other is driving the LED strips. I'll begin with the power supply problems.

 

Power supply

      The power supply I'll be using is the one that came with the RGB strip, a 12V one. While it says 12V, it isn't really 12V, and is more 12.6-13V, which is fine for the LED-s but I needed to drop down that voltage to 3.3V so I can power up the ESP. To drop down the voltage, I used a small 3.3V linear regulator which I had laying around, while I was reading online about it's ratings, I think I saw somewhere that it's recommended to stay under 12V input and with my power supply being over 12V I didn't want to risk it, so I made another in between stage which would lover the voltage to 9-10V.

In the, I used an 7809 linear regulator which dropped the voltage down to 9V, but before that, I found a cool method of making a small DIY power supply using a few simple components:

This power supply really worked great, but I went with the 7809 because it's smaller overall and has less components, so there's less of a chance to make a mistake when soldering everything together.

 

LED driver

     I stuck with the components I could find at home because of the quarantine due to the outbreak of COVID-19. For the LED drivers I wanted to use NMOS, the ones I had at home were the IRF530, but when I tried them, there was a problem:

So because of the threshold voltage going up to 4V, I couldn't rely on using the output of the ESP without anything in between, I need some kind of a shifter circuit in between, I decided to go with non-inverting comparators, since I had OpAmp-s laying around.

On the picture above is the driver for one of the channels, it was time to put everything on a protoboard and to see if it will work, and it did!

 

With that tested, and with all of the voltages being okay, as well as managing to run all of the LED-s without any problems, it was time to go into writing the software for this project, and after that putting everything on a perfboard, soldering it, and putting it instead of the old RGB controller.

 

5. Software

 

LED-s and Colors

     The software was pretty straight forward, I already had all of the HEX codes for the remote, so it was just the question of connecting the buttons with correct values. To "write" to the RGB strip, I used analogWrite, which outputs a PWM whose duty cycle is dependant on the value we give it, which goes from 0 which is LOW to 1023 which is HIGH. Instead of having to send 3 different values to a function every time I wanted to show a color, I decided to code all 3 values into one values, which will be easier to use in the program, the way I did it is really simple. The code that the function accepts is in this format: 1RRRGGGBBB, where it has a leading 1, with three digits reserved for each of the colors, where the values can go from 000 to 255. Having that done, here is the function that writes the colors as well as the predefined colros:

 

#define red 1255000000
#define green 1000255000
#define blue 1000000255
#define white 1255255255
#define red2 1255051051
#define green2 1051255051
#define blue2 1000128255
#define orange 1255128000
#define mint 1051255153
#define darkPurple 1051000102
#define brightOrange 1255153051
#define turquoise 1000153153
#define purple 1102000204
#define yellow 1255255000
#define aqua 1000204204
#define pink 1255000127
#define lampOff 1000000000

void displayColor(int val){
    int b = multiplier*(val % 1000);
    int g = multiplier*((val/1000)%1000);
    int r = multiplier*((val/1000000)%1000);
    analogWrite(12, r); // Pin 12 is used for the Red Channel
    analogWrite(15, g); // Pin 15 is used for the Green Channel
    analogWrite(4, b);  // Pin 4 is used for the Blue Channel
    return;
  }

 

 

IR Remote

Now, to use the remote, we have to check in a loop if there are any new values, the code on which I based that is the example called: IRrecvDemo, here is that code:

/*
 * IRremote: IRrecvDemo - demonstrates receiving IR codes with IRrecv
 * An IR detector/demodulator must be connected to the input RECV_PIN.
 * Version 0.1 July, 2009
 * Copyright 2009 Ken Shirriff
 * http://arcfn.com
 */


#include <IRremote.h>


int RECV_PIN = 11;


IRrecv irrecv(RECV_PIN);


decode_results results;


void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // Start the receiver
}


void loop() {
  if (irrecv.decode(&results)) {
    Serial.println(results.value, HEX);
    irrecv.resume(); // Receive the next value
  }
  delay(100);
}













.

It's really simple to use the IR remote, but instead of checking in loop for new values, I used the library called Ticker, where I set to check for new values from the remote and from Blynk every 100ms, which can be seen in the whole code, as for how to interpret the clicks on the remote, I used a switch with the values I wrote down:

 

switch(val){
        case 0xF720DF:
          displayColor(red);
          lastButton = val;
          lastColor = red;
          break;
        case 0xF7A05F:
          displayColor(green);
          lastButton = val;
          lastColor = green;
          break;
        case 0xF7609F:
          displayColor(blue);
          lastColor = blue;
          lastButton = val;
          break;
        case 0xF7E01F:
          displayColor(white);
          lastColor = white;
          lastButton = val;
          break;
        case 0xF710EF:
          displayColor(red2);
          lastColor = red2;
          lastButton = val;
          break;
        case 0xF7906F:
          displayColor(green2);
          lastColor = green2;
          lastButton = val;
          break;
        case 0xF750AF:
          displayColor(blue2);
          lastColor = blue2;
          lastButton = val;
          break;
        case 0xF730CF:
          displayColor(orange);
          lastColor = orange;
          lastButton = val;
          break;
        case 0xF7B04F:
          displayColor(mint);
          lastColor = mint;
          lastButton = val;
          break;
        case 0xF7708F:
          displayColor(darkPurple);
          lastColor = darkPurple;
          lastButton = val;
          break;
        case 0xF708F7:
          displayColor(brightOrange);
          lastColor = brightOrange;
          lastButton = val;
          break;
        case 0xF78877:
          displayColor(turquoise);
          lastColor = turquoise;
          lastButton = val;
          break;
        case 0xF748B7:
          displayColor(purple);
          lastColor = purple;
          lastButton = val;
          break;
        case 0xF728D7:
          displayColor(yellow);
          lastColor = yellow;
          lastButton = val;
          break;
        case 0xF7A857:
          displayColor(aqua);
          lastColor = aqua;
          lastButton = val;
          break;
        case 0xF76897:
          displayColor(pink);
          lastColor = pink;
          lastButton = val;
          break;    
        }

 

 

Blynk

     Blynk is a great app I've heard of before but never had a chance to use, so I thought to give it a try now. When you download the App, you choose with which device are you working, there is a big list from, Arduino to ESP and so on. After that, there are all kinds of widgets which you can place on the background and easily configure to receive values from. There is a lot of great documentation and getting started guides on Blynk's website. For my project, I needed 4 sliders, 3 of them for the R, G & B channels, and one for brightness, as well as a button for turning ON and OFF the lamp and a small drop down menu for choosing the desired effect, to which I'll get soon. You can configure all of the widgets to write to real pins, but I chose virtual, because I wanted more control over it, here is how my app looks, as well as how all of the widgets are configured:

 



 

The setup is really simple, the only notable thing really is that, instead of updating on release, I put the update on 100ms, so that the LEDs are updates as we are moving the slider, not just when we release it. On the ESP side I used just a single Blynk specific function to load the different values:

 

BLYNK_WRITE(V0) //Red color slider widget writes to V0
{
  redBlynk = param.asInt(); 
}


BLYNK_WRITE(V1) //Green color slider widget writes to V1
{
  greenBlynk = param.asInt(); 
}


BLYNK_WRITE(V2) //Blue color slider widget writes to V2
{
  blueBlynk = param.asInt(); 
}


BLYNK_WRITE(V3) //Brightness slider widget writes to V3
{
  brightnessBlynk = param.asInt();
  if(lastBrightnessBlynk != brightnessBlynk && breathingEffect == false){
      lastBrightnessBlynk = brightnessBlynk;
      multiplier = 0.1 + 0.039*brightnessBlynk;
      displayColor(lastColor);
    }
}


BLYNK_WRITE(V4) //Button ON/OFF Widget is writing to pin V4
{
  int buttonBlynk = param.asInt();
  if(buttonBlynk == 1){
      if(lampTurnedOn == false){
          resetEffects();
          solidEffect = true;
          lampTurnedOn = true;
          displayColor(1000000000 + 1000000*redBlynk + 1000*greenBlynk + blueBlynk);
        }
        else{
            resetEffects();
            solidEffect = true;
            displayColor(lampOff);
            lampTurnedOn = false;
          }
    }
}


BLYNK_WRITE(V5) //Menu for choosing the effect is writing to V5
{
  int effectBlynk = param.asInt();
  if(effectBlynk != lastEffectBlynk){
        lastEffectBlynk = effectBlynk;
        resetEffects();
        if(effectBlynk == 1) solidEffect = true;
        if(effectBlynk == 2) breathingEffect = true;
        if(effectBlynk == 3) changeColorEffect = true;
        if(effectBlynk == 4) fadeEffect = true;
    }
}

Besides this function, I just copied the rest from Examples->Blynk-Boards)WiFi->ESP8266_Standalone

 

Effects

     The original RGB LED controller came with a few built in effects, so I had to do a couple of effects as well, in the end I set on using 4 different effects for the lamp:

  1. Solid - this effect just shows the color we have chosen
  2. Breathing - this effect cycles through the brightness of the chosen color
  3. Flash (ColorChange) - this effect switches between a set of colors, in my case the colors that I've defined in the beginning
  4. Fade - this effect gradually goes through the whole spectrum

There isn't much to say about solid, as it just shows the color we chose, so here is how I did the other effects:

 

Breathing Effect

void BreathingEffect(int t){
  
    // We save the originalMultiplier in a variable to change to it after exiting this effect
    // While this effect is active, the user is unable to change the brightness manually   
    double originalMultiplier = multiplier;
    multiplier = 0.1;    
    while(breathingEffect == true){
        while(multiplier < 4){
            displayColor(lastColor);
            if(breathingEffect == false) return;
            multiplier += 0.039;
            delay(t);
          }
        while(multiplier > 0.05){
            displayColor(lastColor);
            if(breathingEffect == false) return;
            multiplier -= 0.039;
            delay(t);
          }
      }
     multiplier = originalMultiplier;
     displayColor(lampOff);
  }

 

I've used a multiplier, a value I can change which multiplies all of the R,G & B values before writing them to the LED-s to cycle through the brightness, since the values for the analogWrite() go from 0 to 1023, I've put the multiplier to go from 0.1 to 4, since the R,G & B values go from 0 to 255.

 

Flash Effect

void ChangeColorEffect(int t){
    long colors[20] = {1255000000, 1000255000, 1000000255, 1255255255, 1255051051, 1051255051, 1000128255, 1255128000, 1051255153, 1051000102, 1255153051, 1000153153, 1102000204, 1255255000, 1000204204, 1255000127};
    int n = 15;
    int i = 0;
    displayColor(colors[0]);
    long t1;
    t1 = millis();
    while(changeColorEffect == true){
        /*if(t1 - millis() >= t)
        {
          displayColor(colors[i]);
          if(i <= n){
            i++;
          }
          else{
              i = 0;
            }
          t1 = millis();
        }*/
        displayColor(colors[i++]);
        if(changeColorEffect == false) return;
        delay(t);
        displayColor(colors[i++]);
        if(changeColorEffect == false) return;
        delay(t);
        displayColor(colors[i++]);
        if(changeColorEffect == false) return;
        delay(t);
        displayColor(colors[i++]);
        if(changeColorEffect == false) return;
        delay(t);
        displayColor(colors[i++]);
        if(changeColorEffect == false) return;
        delay(t);
        displayColor(colors[i++]);
        if(changeColorEffect == false) return;
        delay(t);
        i = 0;
        
      }
    displayColor(lampOff);

  }

 

I had trouble with this effect and the timing for this effect, so in the end I went with this version which gave me an okay result, this isn't an effect I am fond of too much, so I didn't want to spend to much time on it.

Fade Effect

void FadeEffect(int t){
    long currentColor = 1255000000;
    while(fadeEffect == true){
        displayColor(currentColor);
        for(int i = 0; i <= 255; i++){
            currentColor = 1255000000 + i*1000;
            displayColor(currentColor);
            if(fadeEffect == false) break;
            delay(t);
          }
        for(int i = 255; i >= 0; i--){
            currentColor = 1000255000 + i*1000000;
            displayColor(currentColor);
            if(fadeEffect == false) break;
            delay(t);
          }
        for(int i = 0; i <= 255; i++){
            currentColor = 1000255000 + i;
            displayColor(currentColor);
            if(fadeEffect == false) break;
            delay(t);
          }
        for(int i = 255; i >= 0; i--){
            currentColor = 1000000255 + i*1000;
            displayColor(currentColor);
            if(fadeEffect == false) break;
            delay(t);
          }
        for(int i = 0; i <= 255; i++){
            currentColor = 1000000255 + i*1000000;
            displayColor(currentColor);
            if(fadeEffect == false) break;
            delay(t);
          }
        for(int i = 255; i >= 0; i--){
            currentColor = 1255000000 + i;
            displayColor(currentColor);
            if(fadeEffect == false) break;
            delay(t);
          }          
      }
    displayColor(lampOff);
  }


 

This effect is pretty cool, it goes and cycles through all of the colors, it mimics how it would cycle through colors if we used HSV values and cycles H from 0 degrees to 360 degrees, I wrote that form of the function too, but in the end used this one, if someone is interested, here is how it would work in HSV space:

 

int displayHSVColor(double h, double s, double v){
    double c, x, m, r1, g1, b1, h1;
    h1 = h/60;
    if(h1 > 2 && h1 < 4) h1 -= 2;
    if(h1 >= 4) h1 -= 4; 
    c = v*s;
    x = c*(1-abs(h1 - 1));
    m = v - c;
    if(h < 60){
        r1 = c;
        g1 = x;
        b1 = 0;
      }
      else if(h < 120){
          r1 = x;
          g1 = c;
          b1 = 0;
        }
        else if(h < 180){
          r1 = 0;
          g1 = c;
          b1 = x;
          }
          else if(h < 240){
            r1 = 0;
            g1 = x;
            b1 = c;
          }
          else if(h < 300){
            r1 = x;
            g1 = 0;
            b1 = c;
            }
            else if(h < 180){
              r1 = c;
              g1 = 0;
              b1 = x;
            }


      int r2 = int((r1 + m)*255);
      int g2 = int((g1 + m)*255);
      int b2 = int((b1 + m)*255);
      return 1000000000 + r2*1000000 + g2*1000 + b2;
  }




 

This function converts the gives HSV values to RGB, I got the formula online and tested it for a few values, and it works pretty good, it's accurate to within 1 or 2 to the online converters, but I went with the first function because there wasn't any converting or anything like that.

 

Full Code

/*
 *                                  Project - ESP8266 RGB Lamp
 *  --------------------------------------------------------------------------------------------
 *  This project uses an ESP8266-07 as a RGB controller for a LED strip
 *  The lamp can be controlled either by an IR Remote or by using a Blynk App
 *  For controlling the RGB strip, 3 NMOS are used as well as 3 OpAmp-s
 *  PINS:
 *  14 --- IR Receiver Pin
 *  12 --- Red Channel Output
 *  15 --- Green Channel Output
 *  4  --- Blue Channel Output
 *  --------------------------------------------------------------------------------------------
 */


//Libraries
#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRutils.h>
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <Ticker.h>


/*
 *  Predefined Color Codes
 *  --------------------------------------------------------------------------------------------
 *  Instead of sending 3 values for the color I am sending one value in the format of 1rrrgggbbb
 *  The values for all of the colors go from 0 to 255
 *  Because it's a long value, its easy to extract values with / and %
 *  --------------------------------------------------------------------------------------------
 */
#define red 1255000000
#define green 1000255000
#define blue 1000000255
#define white 1255255255
#define red2 1255051051
#define green2 1051255051
#define blue2 1000128255
#define orange 1255128000
#define mint 1051255153
#define darkPurple 1051000102
#define brightOrange 1255153051
#define turquoise 1000153153
#define purple 1102000204
#define yellow 1255255000
#define aqua 1000204204
#define pink 1255000127
#define lampOff 1000000000


/*
 * --------------------------------------------------------
 *                      VARIABLES
 * --------------------------------------------------------
 */


// Multiplier - This will control the brightness of our LED-s
double multiplier = 2;
double multiplierMin = 0.05;
double multiplierMax = 4;


//Other Variables
long lastColor = aqua;
int kRecvPin = 14;
bool lampTurnedOn = false;
int lastButton;
int redBlynk = 0;
int greenBlynk = 130;
int blueBlynk = 90;
long lastBlynk;
int brightnessBlyn
int lastBrightnessBlynk;
int lastEffectBlynk;


//These variables are used for choosing the effect we want to show
bool fadeEffect = false;
bool breathingEffect = false;
bool changeColorEffect = false;
bool solidEffect = true;


Ticker checkCommands;
IRrecv irrecv(kRecvPin);
decode_results results;


// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "";


// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "";
char pass[] = "";


/*
 * Display Color Function
 * ---------------------------------------------------------------------------------------------------------------
 * This function handles controlling the pins which are connected to the LED strip
 * It firstly decodes the value to R, G and B values as I've already described, and multiplies with the multiplier
 * I'm using pins 12, 15 and 4 for controlling the LED strip - those pins are pwm
 * ---------------------------------------------------------------------------------------------------------------
 */
void displayColor(int val){
    int b = multiplier*(val % 1000);
    int g = multiplier*((val/1000)%1000);
    int r = multiplier*((val/1000000)%1000);
    analogWrite(12, r); // Pin 12 is used for the Red Channel
    analogWrite(15, g); // Pin 15 is used for the Green Channel
    analogWrite(4, b);  // Pin 4 is used for the Blue Channel
    return;
  }


/*
 * BLYNK
 * -----------------------------------------------------------------------------------------
 * These Functions handle all of the values we are getting from out Blynk App that we made
 * BLYNK_WRITE functions handle the data coming from the Blynk app
 * HandleBlynk function handles converting the values from the Blynk app into our color code
 * -----------------------------------------------------------------------------------------
 */
BLYNK_WRITE(V0) //Red color slider widget writes to V0
{
  redBlynk = param.asInt(); 
}


BLYNK_WRITE(V1) //Green color slider widget writes to V1
{
  greenBlynk = param.asInt(); 
}


BLYNK_WRITE(V2) //Blue color slider widget writes to V2
{
  blueBlynk = param.asInt(); 
}


BLYNK_WRITE(V3) //Brightness slider widget writes to V3
{
  brightnessBlynk = param.asInt();
  if(lastBrightnessBlynk != brightnessBlynk && breathingEffect == false){
      lastBrightnessBlynk = brightnessBlynk;
      multiplier = 0.1 + 0.039*brightnessBlynk;
      displayColor(lastColor);
    }
}


BLYNK_WRITE(V4) //Button ON/OFF Widget is writing to pin V4
{
  int buttonBlynk = param.asInt();
  if(buttonBlynk == 1){
      if(lampTurnedOn == false){
          resetEffects();
          solidEffect = true;
          lampTurnedOn = true;
          displayColor(1000000000 + 1000000*redBlynk + 1000*greenBlynk + blueBlynk);
        }
        else{
            resetEffects();
            solidEffect = true;
            displayColor(lampOff);
            lampTurnedOn = false;
          }
    }
}


BLYNK_WRITE(V5) //Menu for choosing the effect is writing to V5
{
  int effectBlynk = param.asInt();
  if(effectBlynk != lastEffectBlynk){
        lastEffectBlynk = effectBlynk;
        resetEffects();
        if(effectBlynk == 1) solidEffect = true;
        if(effectBlynk == 2) breathingEffect = true;
        if(effectBlynk == 3) changeColorEffect = true;
        if(effectBlynk == 4) fadeEffect = true;
    }
}


// This function converts the R, G & B values to my format and compares to the old one to see if it needs to update it
void HandleBlynk(){
    long blynkValue = 1000000000 + 1000000*redBlynk + 1000*greenBlynk + blueBlynk;
    if(blynkValue != lastBlynk){
        lastBlynk = blynkValue;
        displayColor(blynkValue);
        lastColor = blynkValue;
      }
     
  }




//This function resets all of the variables which are used for choosing the current effect
void resetEffects(){
    fadeEffect = false;
    breathingEffect = false;
    changeColorEffect = false;
    solidEffect = false;
  }




/*
 * IR Remote Handler Function
 * ------------------------------------------------------------------------------------------------------------
 * This function handles all of the button presses on the small IR remote I got from my original RGB controller
 * In order to change colors we need to be in solid effect mode with the lamp turned on
 * Lamp Off, turns off the lamp, and sets the effect to solid
 * The brightness buttons work by consinstently pressing them or just by pressing and holding the button
 * For the color buttons we use the predefines colors I mentioned at the very beginning
 * ------------------------------------------------------------------------------------------------------------
 */
void checkButton(int val){


    if(lampTurnedOn == false && val == 0xF7C03F){
        lastButton = val;
        lampTurnedOn = true;
        resetEffects();
        solidEffect = true;
        displayColor(green2);
        return;
      }
      
    if(lampTurnedOn == true && val == 0xF740BF){
        lastButton = val;
        resetEffects();
        solidEffect = false;
        lampTurnedOn = false;
        delay(100);
        displayColor(lampOff);
        return;
      }


    if(lampTurnedOn == true && val == 0xFFFFFFFF && lastButton == 0xF700FF && multiplier < multiplierMax){
        multiplier += 0.05;
        displayColor(lastColor);
        return;
      }


    if(lampTurnedOn == true && val == 0xFFFFFFFF && lastButton == 0xF7807F && multiplier > multiplierMin){
        multiplier -= 0.05;
        displayColor(lastColor);
        return;
      }


    if(lampTurnedOn == true && val == 0xF7C837){
        resetEffects();
        fadeEffect = true;
        lastButton = val;
      }


    if(lampTurnedOn == true && val == 0xF7F00F){
        resetEffects();
        breathingEffect = true;
        lastButton = val;
      }


    if(lampTurnedOn == true && val == 0xF7D02F){
        resetEffects();
        changeColorEffect = true;
        lastButton = val;
      }


    if(lampTurnedOn == true && val == 0xF7E817){
        resetEffects();
        solidEffect = true;
        displayColor(lastColor);
        lastButton = val;
      }


    if(lampTurnedOn == true && val == 0xF700FF && breathingEffect == false){
        if(multiplier < multiplierMax) multiplier += 0.05;
        displayColor(lastColor);
        lastButton = val;
      }


    if(lampTurnedOn == true && val == 0xF7807F && breathingEffect == false){
        if(multiplier > multiplierMin) multiplier -= 0.05;
        displayColor(lastColor);
        lastButton = val;
      }
    
    if(lampTurnedOn == true && solidEffect == true){
      switch(val){
        case 0xF720DF:
          displayColor(red);
          lastButton = val;
          lastColor = red;
          break;
        case 0xF7A05F:
          displayColor(green);
          lastButton = val;
          lastColor = green;
          break;
        case 0xF7609F:
          displayColor(blue);
          lastColor = blue;
          lastButton = val;
          break;
        case 0xF7E01F:
          displayColor(white);
          lastColor = white;
          lastButton = val;
          break;
        case 0xF710EF:
          displayColor(red2);
          lastColor = red2;
          lastButton = val;
          break;
        case 0xF7906F:
          displayColor(green2);
          lastColor = green2;
          lastButton = val;
          break;
        case 0xF750AF:
          displayColor(blue2);
          lastColor = blue2;
          lastButton = val;
          break;
        case 0xF730CF:
          displayColor(orange);
          lastColor = orange;
          lastButton = val;
          break;
        case 0xF7B04F:
          displayColor(mint);
          lastColor = mint;
          lastButton = val;
          break;
        case 0xF7708F:
          displayColor(darkPurple);
          lastColor = darkPurple;
          lastButton = val;
          break;
        case 0xF708F7:
          displayColor(brightOrange);
          lastColor = brightOrange;
          lastButton = val;
          break;
        case 0xF78877:
          displayColor(turquoise);
          lastColor = turquoise;
          lastButton = val;
          break;
        case 0xF748B7:
          displayColor(purple);
          lastColor = purple;
          lastButton = val;
          break;
        case 0xF728D7:
          displayColor(yellow);
          lastColor = yellow;
          lastButton = val;
          break;
        case 0xF7A857:
          displayColor(aqua);
          lastColor = aqua;
          lastButton = val;
          break;
        case 0xF76897:
          displayColor(pink);
          lastColor = pink;
          lastButton = val;
          break;    
        }
      }
      return;
  }


// This function checks on new commands from both the remote and the blynk app, we call it every 100ms using the Ticker library
void Commands(){
    Blynk.run();
    HandleBlynk();
    if (irrecv.decode(&results)) {      
      checkButton(results.value);
      irrecv.resume();
    }
  }


/*
 * -----------------------------------------------------------
 *                        EFFECTS
 * -----------------------------------------------------------
 */




/*
 * Breathing Effect
 * -----------------------------------------------------------
 * Breathing effect cycles through the multiplier going low to
 * high and high to low for the chosen color, by adjusting the
 * input t, we can determine how fast the effect is
 * -----------------------------------------------------------
 */
void BreathingEffect(int t){
  
    // We save the originalMultiplier in a variable to change to it after exiting this effect
    // While this effect is active, the user is unable to change the brightness manually   
    double originalMultiplier = multiplier;
    multiplier = 0.1;    
    while(breathingEffect == true){
        while(multiplier < 4){
            displayColor(lastColor);
            if(breathingEffect == false) return;
            multiplier += 0.039;
            delay(t);
          }
        while(multiplier > 0.05){
            displayColor(lastColor);
            if(breathingEffect == false) return;
            multiplier -= 0.039;
            delay(t);
          }
      }
     multiplier = originalMultiplier;
     displayColor(lampOff);
  }


/*
 * Change Color Effect (FLASH effect)
 * --------------------------------------------------------------------
 * This effect just changes through different preset colors
 * --------------------------------------------------------------------
 */
void ChangeColorEffect(int t){
    long colors[20] = {1255000000, 1000255000, 1000000255, 1255255255, 1255051051, 1051255051, 1000128255, 1255128000, 1051255153, 1051000102, 1255153051, 1000153153, 1102000204, 1255255000, 1000204204, 1255000127};
    int n = 15;
    int i = 0;
    displayColor(colors[0]);
    long t1;
    t1 = millis();
    while(changeColorEffect == true){
        /*if(t1 - millis() >= t)
        {
          displayColor(colors[i]);
          if(i <= n){
            i++;
          }
          else{
              i = 0;
            }
          t1 = millis();
        }*/
        displayColor(colors[i++]);
        if(changeColorEffect == false) return;
        delay(t);
        displayColor(colors[i++]);
        if(changeColorEffect == false) return;
        delay(t);
        displayColor(colors[i++]);
        if(changeColorEffect == false) return;
        delay(t);
        displayColor(colors[i++]);
        if(changeColorEffect == false) return;
        delay(t);
        displayColor(colors[i++]);
        if(changeColorEffect == false) return;
        delay(t);
        displayColor(colors[i++]);
        if(changeColorEffect == false) return;
        delay(t);
        i = 0;
        
      }
    displayColor(lampOff);
  }


/*
 * Fade Effect
 * ---------------------------------------------------------------------
 * This effect starts with red and slowly goes through all of the values
 * and in this way goes through all of the colors, this gives a nice
 * effect of cycling through all of the colors, I did in the RGB space,
 * another way to do this is (with the same effect) is to do it in the 
 * HSV space, and just cycle the H value from 0 to 360 degrees
 * ----------------------------------------------------------------------
 */
void FadeEffect(int t){
    long currentColor = 1255000000;
    while(fadeEffect == true){
        displayColor(currentColor);
        for(int i = 0; i <= 255; i++){
            currentColor = 1255000000 + i*1000;
            displayColor(currentColor);
            if(fadeEffect == false) break;
            delay(t);
          }
        for(int i = 255; i >= 0; i--){
            currentColor = 1000255000 + i*1000000;
            displayColor(currentColor);
            if(fadeEffect == false) break;
            delay(t);
          }
        for(int i = 0; i <= 255; i++){
            currentColor = 1000255000 + i;
            displayColor(currentColor);
            if(fadeEffect == false) break;
            delay(t);
          }
        for(int i = 255; i >= 0; i--){
            currentColor = 1000000255 + i*1000;
            displayColor(currentColor);
            if(fadeEffect == false) break;
            delay(t);
          }
        for(int i = 0; i <= 255; i++){
            currentColor = 1000000255 + i*1000000;
            displayColor(currentColor);
            if(fadeEffect == false) break;
            delay(t);
          }
        for(int i = 255; i >= 0; i--){
            currentColor = 1255000000 + i;
            displayColor(currentColor);
            if(fadeEffect == false) break;
            delay(t);
          }          
      }
    displayColor(lampOff);
  }


/*
 * SETUP
 * ----------------------------------------------------------------
 * Setting up the pins as well as the other services we are using
 * ----------------------------------------------------------------
 */
void setup() {
  Serial.begin(115200);
  pinMode(12, OUTPUT); //Red channel
  pinMode(15, OUTPUT); //Green channel
  pinMode(4, OUTPUT); //Blue channel
  Blynk.begin(auth, ssid, pass);
  irrecv.enableIRIn();
  displayColor(lampOff);
  checkCommands.attach(0.1, Commands);
}


//In the loop we just turn on different effects depending on the values of the effect variables
void loop() {
  if(fadeEffect == true) FadeEffect(20);
  if(breathingEffect == true) BreathingEffect(40);
  if(changeColorEffect == true) ChangeColorEffect(10000);























}

 

The code contains all of the smaller parts which I've already mentioned with a few more things to tie in everything together. With the code complete and hardware working, it's time to properly test everything.

 

6. Testing

     Here are videos of different colors as well as effects and Blynk app working with a small piece of the LED strip

Just to give a small warning because of a potentially really dangerous thing that I've done by mistake, if you're testing out the LED-s for anything more than few seconds at a time, be sure to not keep them on the spool, they get extremely hot extremely fast, here is the situation in which I was:

It's much better to cut off a small piece or just unravel the whole thing, if you want to test out how much current will be drawn.

Solid Colors

The first 2 videos will show the switch between different colors using the remote, as well as adjusting the brightness of the LED-s

 

 

Flash Effect

 

Breathing Effect

 

Fade Effect

 

Blynk Test

 

With everything working great, all I have to do is solder this to a piece of perfboard and put instead of the old RGB controller.

Just one short note, some of the colors may look weird, or too strong because I can't setup my phone to film the correctly, and some colors won't be seen in the video as they are in real life, for example, mint color looks like blue on both pictures and videos, just wanted to point that out because of some weird frames which are in some of these videos.

 

7. Soldering

     I needed a couple of hours to finish this up, I started up with the power supply part and confirming all of the voltages are okay, and from there added the ESP, the OpAmps the MOSFET-s and the connectors.

 

 

With soldering out of the way, all that's left now is to mount this controller instead of the old one, and mount the skis again, and we are done!

 

8. Putting Everything Together

     I took the mountain off the wall and took out the old controller, here is how it looked like, a small IC with 3 small transistors:

 

As for the mountain itself, here is how it looks like:

To make this mountain, I took some flat piece of wood I found laying around, drew a modern style mountain and cut it using a table saw, measuring about 1cm from the edge I glued in small 1cm by 1cm sticks on the back side. I spray painted the front side, and for the snowy peaks I just glued in some white paper. With the sticks glued in the back, I had an edge to which I can stick my LED strip while leaving a small slot in the bottom for the cables to go through, I used some double sided sticky tape to attach the controller to the back of the board and some duct tape for securing the cables.

I put on some connectors on the power supply cable, as well as to the positive and negative terminal on the controller, connected them, put the mountain on the wall, put the skis back on the wall, and I'm done! Here are some pictures and videos of the finished project!

 

9. Finished Project

 

Pictures

 

 

Videos

 

 

10. Summary

     I love having personalized things around my room, and there's no better way than having my favorite spot in the combination with a cool looking lamp which I made. It was a fun project, and not too demanding, the only trouble I had is the way that the standalone ESP8266-07 needs to be programmed by connecting certain pins in a certain way, so I had to use a mini breadboard for that, and adjust some wires constantly, because it didn't have good connections. I plan on doing an upgraded version of this, designing a custom PCB, the only thing I wish to add is a fuse, as well as use transistors without using a shifter circuit in the middle. Hope everyone is staying healthy & safe during this outbreak. Thanks for reading the blog, hope you like it!

 

Milos