Cylindrical Stainless Steel Decoration Lamp

3,718

33

2

Introduction: Cylindrical Stainless Steel Decoration Lamp

About: PLC, Arduino - Do it yourself project

Here was a broken lamp I picked up in scrap yard that at first I really did not know what it was. It looked very nice & it gave me the idea to turn this broken lamp into a colorful decoration lamp.

According to technical information on internet, this is a modern stainless steel outdoor wall light which has 2 light fittings for directing light upwards and downwards, suitable for lighting doorways and paths or to light up features.

Take a look at some videos & pictures after completion of fixing from broken up-down wall lighting to a colorful decoration lamp

  • FINISH PRODUCT PICTURES

  • VIDEOS:

This decoration lamp is controlled with 3 modes: Color-wheel, Morph & HSBtoRGB by using 3 ways switch and potentiometer rotary knob.

It looks awesome when this lamp is put in the dark.

Follow me on the next steps to see how I did for reviving this broken lamp...

Teacher Notes

Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.

Step 1: Ideas

Diameter of lamp's stainless steel body is about 60mm. My idea is to put 32 RGB leds into the cylindrical stainless steel casing at one side (upwards – 32 RGB LED; downwards – 32 RGB LED). Of course these leds will also be arranged in cylindrical shape – diameter 50mm, too.

In order to arrange leds in cylindrical form D50mm, I made a flat wooden template with LED spacing 19.7mm. And after all leds were soldered, I bent them to a cylindrical shape of 50mm outer diameter.

To calculate arc length and number of LEDs, we can refer to table below:

Step 2: Bill of Materials

Beside main components, we need to buy some more parts like: power adapter 5V - 2A, capacitor 0.1uF (x4), computer ribbon cable ( LINK ), power wires, small bolts & nuts, A4 single clad copper plate, Ferric Chloride for etching....
Tools: wooden plate, gloves, round rod D3mm, ruler, soldering iron...

Step 3: Project Schematic

There are 2 options below to control RGB LEDs using Bit Angle Modulation:

  • Option 1: Layers are selected from shift register 74HC595 to control transistor 2N2222 & TIP42C. It save Arduino pins but response time is low. It doesn't matter in my case when I just used transistors (not MOSFET).

  • Option 2: Layers are directly controlled from Arduino pins. We used 8 pins for layer selection, plus 4 pins for shift register controlling and no digital pins remain for other application.

For high resolution schematic, you can down load PDF version at LINK.

Step 4: Eagle Design

To put all the printed circuit boards, ribbon cables, power cables and cylindrical LEDs inside the tube lamp, I had to design all printed circuit boards in circle shape and drilled some holes for wires passing through. Due to big amount of ribbon cables, during the actual assembly I have to drill and cut in some more positions.

I also marked two circles outside PCB for later cutting them in circle form. You can see this tip on next step.

  • Shift Register Board: It includes 2 x 74HC595, 2 x ULN2803, 16 x R100. For this project, we need 2 shift register boards for layer selecting and RGB led - BAM color controlling.

  • Layer board: It includes 4 x NPN-Transistor 2N2222, 4 x PNP-Transistor TIP42C, 8 x R1K, 4 x R10K. For this project, we also need 2 layers boards, these boards receive control signal from shift register boards to determine which layer to be selected (total 8 layers).

  • Total print out: It fit to A4 size to print out and save paper.

You can download printable PCB Eagle files with real size atLINK.

Step 5: PCB Etching – Drilling – Components Installation

  • Clothes Iron Toner Transfer

  • Etching PCB by Ferric Chloride

  • Cutting PCB in circular shape

TIP: To cut PCB in circular shape by hand, I drilled some holes between two circles as marked on the printed circuit board. It's really effective for small print circuits.

  • Drilling, assembly and soldering components: Due to limited size, I have to change 2 x Capacitor 0.1uF to bottom size.

Step 6: Cylindrical LED Soldering

  • Prepare led

Leds are RGB common anode type. I use a stainless steel round rod with a diameter about 3mm to bend the led pins.

  • RGB pin soldering:

TIP: Cut the gloves and wear only the middle finger. It will help you not getting hot during soldering and not get encumbered as you wear the entire gloves.

  • Led pins separated by ruler.

  • Finish RGB pins

  • Bending & soldering common anode pin

Continue to use stainless steel round rod above to bend the common anode pins.

  • Finish RGB pins and common anode pin:

  • Led testing

Let keep all LEDs in wooden template, we will easily fix if there was any LED damaged.

It looks good to me!

  • Bending led into cylindrical form.

Bending the LEDs slowly until they form cylindrical LED. Due to putting inside stainless body, I have to bend LED pins towards outside and LED bulbs towards center of cylindrical lamp.

  • Cylindrical LED finish and ready to be put inside lamp stainless steel body

Step 7: Connection All PCB Boards to Arduino & Cylindrical RGB LED

I took time and effort to do this work. Note: I used ribbon cables to connect all together in this case because this cable is small, steady and flexible. I also drilled some more holes on the PCB so that I could thread all ribbon cable through the holes

  • Cylindrical RGB LED connecting to Layer & Shift Register Board

  • Prepare stainless steel casting body

  • Final assembly

Finally, I put everything in stainless body as pictures. I also added one 3 ways switch to change lamp's mode and one potentiometer to adjust lamp's color.

TIP: For electrical isolation between PCB (or cylindrical LED) and stainless body, I used a clear plastic PVC sheet - book cover

Step 8: Programming

#include <SPI.h>

#define latch_pin   2
#define blank_pin   4
#define data_pin    11
#define clock_pin   13

#define SWITCH_A    6
#define SWITCH_B    7

#define POTPIN      0
#define COLORWHEEL  0
#define MORPH       1
#define HSBTORGB    2
#define LIMIT_BAND  15

// Switch & potentiometer variables
byte Switch_State = 0;             // to store switch reading 0 = COLORWHEEL, 1 = MORPH,  2 = HSBTORGB
int POT;
int OLD_POT;


//*************************************************ColorWheel******************************************//

#define myPI      3.14159265358979323846
#define myDPI     1.2732395
#define myDPI2    0.40528473

#define COLOUR_WHEEL_LENGTH 256

uint8_t colourR[COLOUR_WHEEL_LENGTH];
uint8_t colourG[COLOUR_WHEEL_LENGTH];
uint8_t colourB[COLOUR_WHEEL_LENGTH];
int16_t ColPos = 0;
uint16_t colourPos;
uint8_t R, G, B;

//************************************************************************************************************//

byte anode[8];//byte to write to the anode shift register, 8 of them, shifting the ON level in each byte in the array

byte red[4][8];
byte blue[4][8];
byte green[4][8];


#define     BAM_RESOLUTION 4
const byte  Size_X = 8;
const byte  Size_Y = 8;

int level=0;
int anodelevel=0;
int BAM_Bit, BAM_Counter=0;

void setup(){

SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV2);

noInterrupts();

TCCR1A = B00000000;
TCCR1B = B00001011;
TIMSK1 = B00000010;
OCR1A=5; // Best value in my case, without camera flicker 

anode[0]=B00000001;
anode[1]=B00000010;
anode[2]=B00000100;
anode[3]=B00001000;
anode[4]=B00010000;
anode[5]=B00100000;
anode[6]=B01000000;
anode[7]=B10000000;

pinMode (latch_pin, OUTPUT); 
//pinMode (3, OUTPUT);
pinMode(data_pin, OUTPUT);
pinMode(clock_pin, OUTPUT);

pinMode(SWITCH_A, INPUT);
pinMode(SWITCH_B, INPUT);

SPI.begin();
interrupts();
fill_colour_wheel();
}

void loop()
{
  POT = map(analogRead(POTPIN), 0, 1023, 0, 255);
  // Read the switch and put the result is Switch_State
  if (digitalRead(SWITCH_A) == HIGH) Switch_State = MORPH;
  else if (digitalRead(SWITCH_B) == HIGH) Switch_State = HSBTORGB;
  else Switch_State = COLORWHEEL;
  
    switch(Switch_State){
      case 0:      
        if (POT < (OLD_POT * 0.95) || POT > (OLD_POT * 1.05))
        {
        OLD_POT = POT;  // save the changed value    
        fillTable_colorwheelRGB(POT, R, G, B);      
        }
        break;      
      
      case 1:
        if (abs(POT - OLD_POT) > LIMIT_BAND)
        {
        OLD_POT = POT;  // save the changed value
        get_colour(POT, &R, &G, &B);
        fillTable(R, G, B);     
        }
        break;
      case 2:
     
        if (abs(POT - OLD_POT) > LIMIT_BAND)
        {
        OLD_POT = POT;  // save the changed value
        
        fillTable_HSBToRGB(map(POT, 0, 255, 0, 7), R, G, B);
        }
        break;
    
    }
}

void LED(int Y, int X, int R, int G, int B)
{
  Y = constrain(Y, 0, Size_Y - 1);
  X = constrain(X, 0, Size_X - 1); 
  R = constrain(R, 0, (1 << BAM_RESOLUTION) - 1);
  G = constrain(G, 0, (1 << BAM_RESOLUTION) - 1);
  B = constrain(B, 0, (1 << BAM_RESOLUTION) - 1);
  
    for (byte BAM = 0; BAM < BAM_RESOLUTION; BAM++) 
    {
      //*** RED ***
      bitWrite(red[BAM][Y], X, bitRead(R, BAM));
      //*** GREEN ***
      bitWrite(green[BAM][Y], X, bitRead(G, BAM));
      //*** BLUE ***
      bitWrite(blue[BAM][Y], X, bitRead(B, BAM));
    }
}
ISR(TIMER1_COMPA_vect)
{
  PORTD |= 1<<blank_pin;
  if(BAM_Counter==8)
  BAM_Bit++;
  else
  if(BAM_Counter==24)
  BAM_Bit++;
  else
  if(BAM_Counter==56)
  BAM_Bit++;  
  BAM_Counter++;
  
  switch (BAM_Bit)
  {
  case 0:
      //Red
        SPI.transfer(red[0][level]);
              
      //Green
        SPI.transfer(green[0][level]);

      //Blue
        SPI.transfer(blue[0][level]);

      break;
    case 1:       
      //Red
        SPI.transfer(red[1][level]);
      
      //Green
        SPI.transfer(green[1][level]);

      //Blue
        SPI.transfer(blue[1][level]);

      break;
    case 2:      
      //Red
        SPI.transfer(red[2][level]);
        
       //Green
        SPI.transfer(green[2][level]);

      //Blue
        SPI.transfer(blue[2][level]);

      break;
    case 3:
      //Red
        SPI.transfer(red[3][level]);
        
      //Green
        SPI.transfer(green[3][level]);

      //Blue
        SPI.transfer(blue[3][level]);


  if(BAM_Counter==120)
    {
  BAM_Counter=0;
  BAM_Bit=0;
    }
  break;
  }
    SPI.transfer(anode[anodelevel]);//finally, send out the anode level byte
    PORTD &= ~(1<<latch_pin);
    PORTD |= 1<<latch_pin;    
    //delayMicroseconds(3); 
    PORTD &= ~(1<<blank_pin);//Blank pin LOW to turn on the LEDs with the new data
    //delayMicroseconds(3);
    
    anodelevel++;
    level++;
    if(anodelevel==8)
    anodelevel=0;
    if(level==8)
    level=0;
    pinMode(blank_pin, OUTPUT);
} 

void clearfast ()
{
for (unsigned char j=0; j<8; j++)
        {
        red[0][j]   = 0;
        red[1][j]   = 0;
        red[2][j]   = 0;
        red[3][j]   = 0;
        green[0][j] = 0;
        green[1][j] = 0;
        green[2][j] = 0;
        green[3][j] = 0;
        blue[0][j]  = 0;
        blue[1][j]  = 0;
        blue[2][j]  = 0;
        blue[3][j]  = 0;
        }
}

void fillTable(byte R, byte G, byte B)
{ 
    for (byte x=0; x<8; x++)
    {  // scan thru every column
      for (byte y=0; y<8; y++)
      {  // scan thru every panel
        LED(y, x, R, G, B);
      }
    }
}

void fillTable_colorwheelRGB(int potentio, byte R, byte G, byte B){  // This subroutine fills the cube with a colour
    for (byte x=0; x<8; x++)
    {
      for (byte y=0; y<8; y++)
      {
        get_colour(potentio + 24*x , &R, &G, &B); // 24*x - Best in my case
        LED(y, x, R, G, B);      
      }
    }  
}

void fillTable_HSBToRGB(byte hue, uint8_t R, uint8_t G, uint8_t B) {
  byte inSaturation, inBrightness;
  inSaturation = rand()%15+8;
  inBrightness = rand()%15+8;
    for (byte x = 0 ; x < 8 ; x++) {
      for (byte y = 0 ; y < 8 ; y++) {
          HSBToRGB(hue + y + x, inSaturation, inBrightness, &R, &G, &B);
          LED(y, x, R, G, B);          
      }     
    }    
} 
/////////////////////////////////////////////////////////////////////////////////

//FAST SINE APPROX
float mySin(float x){
  float sinr = 0;
  uint8_t g = 0;

  while(x > myPI){
    x -= 2*myPI; 
    g = 1;
  }

  while(!g&(x < -myPI)){
    x += 2*myPI;
  }

  sinr = myDPI*x - myDPI2*x*myAbs(x);
  sinr = 0.225*(sinr*myAbs(sinr)-sinr)+sinr;

  return sinr;
}

//FAST COSINE APPROX
float myCos(float x){
  return mySin(x+myPI/2);
}

float myTan(float x){
  return mySin(x)/myCos(x);
}

//SQUARE ROOT APPROX
float mySqrt(float in){
  int16_t d = 0;
  int16_t in_ = in;
  float result = 2;
  
  for(d = 0; in_ > 0; in_ >>= 1){
    d++;
  }
  
  for(int16_t i = 0; i < d/2; i++){
    result = result*2;
  }
  
  for(int16_t i = 0; i < 3; i++){
    result = 0.5*(in/result + result);
  }
  
  return result;
}

//MAP NUMBERS TO NEW RANGE
float myMap(float in, float inMin, float inMax, float outMin, float outMax){
  float out;
  out = (in-inMin)/(inMax-inMin)*(outMax-outMin) + outMin;
  return out;
}

//ROUND A NUMBER
int16_t myRound(float in){
  int8_t s = in/myAbs(in);
  return (int16_t)(s*(myAbs(in) + 0.5));
}

//ABSOLUTE VALUE
float myAbs(float in){
  return (in)>0?(in):-(in);
} 

void fill_colour_wheel(void) 
{
  float red, green, blue;
  float c, s;
  int32_t phase = 0;
  int16_t I = 0;

  while (phase < COLOUR_WHEEL_LENGTH) 
  {
    s = (1 << BAM_RESOLUTION)*mySin(myPI*(3 * phase - I*COLOUR_WHEEL_LENGTH) / (2 * COLOUR_WHEEL_LENGTH));
    c = (1 << BAM_RESOLUTION)*myCos(myPI*(3 * phase - I*COLOUR_WHEEL_LENGTH) / (2 * COLOUR_WHEEL_LENGTH));

    red = (I == 0 ? 1 : 0)*s + (I == 1 ? 1 : 0)*c;
    green = (I == 1 ? 1 : 0)*s + (I == 2 ? 1 : 0)*c;
    blue = (I == 2 ? 1 : 0)*s + (I == 0 ? 1 : 0)*c;

    colourR[phase] = red;
    colourG[phase] = green;
    colourB[phase] = blue;

    if (++phase >= (1 + I)*COLOUR_WHEEL_LENGTH / 3) 
      I++;
  }
}

void get_colour(int16_t p, uint8_t *R, uint8_t *G, uint8_t *B)
{
  if (p >= COLOUR_WHEEL_LENGTH)
    p -= COLOUR_WHEEL_LENGTH;

  *R = colourR[p];
  *G = colourG[p];
  *B = colourB[p];
}

void get_next_colour(uint8_t *R, uint8_t *G, uint8_t *B)
{
  if (++ColPos >= COLOUR_WHEEL_LENGTH)
    ColPos -= COLOUR_WHEEL_LENGTH;

  *R = colourR[ColPos];
  *G = colourG[ColPos];
  *B = colourB[ColPos];
}

void increment_colour_pos(uint8_t i)
{
  colourPos += i;
  while (colourPos >= COLOUR_WHEEL_LENGTH)
  {
    colourPos -= COLOUR_WHEEL_LENGTH;
  }
}


/////////////////////////////////////////


void HSBToRGB( unsigned int inHue, unsigned int inSaturation, unsigned int inBrightness, uint8_t *RED, uint8_t *GREEN, uint8_t *BLUE )
{
 if( inSaturation == 0 ) 
 {
   // achromatic (grey)
   *RED = *GREEN = *BLUE = inBrightness;
 }
 else
 {
   unsigned int scaledHue = (inHue * 6);                          
   unsigned int sector = scaledHue >> BAM_RESOLUTION; // sector 0 to 5 around the color wheel
   unsigned int offsetInSector = scaledHue - (sector << BAM_RESOLUTION);      // position within the sector
   unsigned int p = (inBrightness * ( ((1 << BAM_RESOLUTION) - 1) - inSaturation )) >> BAM_RESOLUTION;
   unsigned int q = (inBrightness * ( ((1 << BAM_RESOLUTION) - 1) - ((inSaturation * offsetInSector) >> BAM_RESOLUTION) )) >> BAM_RESOLUTION;
   unsigned int t = (inBrightness * ( ((1 << BAM_RESOLUTION) - 1) - ((inSaturation * ( ((1 << BAM_RESOLUTION) - 1) - offsetInSector )) >> BAM_RESOLUTION) )) >> BAM_RESOLUTION;

   switch( sector ) {
   case 0:
     *RED = inBrightness;
     *GREEN = t;
     *BLUE = p;
     break;
   case 1:
     *RED = q;
     *GREEN = inBrightness;
     *BLUE = p;
     break;
   case 2:
     *RED = p;
     *GREEN = inBrightness;
     *BLUE = t;
     break;
   case 3:
     *RED = p;
     *GREEN = q;
     *BLUE = inBrightness;
     break;
   case 4:
     *RED = t;
     *GREEN = p;
     *BLUE = inBrightness;
     break;
   default:            
     *RED = inBrightness;
     *GREEN = p;
     *BLUE = q;
     break;
   }
 }
}

Step 9: First Functional Testing Before Final Assembly

    Before putting all PCBs and LEDs into stainless steel casting body, I have to do some functional tests to avoid mistakes.

    • COLORWHEEL TEST

    • HSBTORGB TEST

    • MORPH TEST

    You can see my testing on this video:

    It look very nice, right !!!

    Step 10: Second Testing After Putting All Parts Inside Stainless Body

    Step 11: FINISH PROJECT

    DONE !!! Now, I can adjust this colorful lamp by using 3 ways switch and potentiometer rotary knob

    - COLORWHEEL: Lamp's color is adjusted by rotating the knob according to the color-wheel rule.

    - MORPH: Lamp's color is transformed (256 colors) using the rotary knob..

    - HSBTOGRB: similar to color-wheel above, but lamp's color is mixed by the parameters hue, saturation, brightness through rotary knob.

    Fix It! Contest

    Runner Up in the
    Fix It! Contest

    Be the First to Share

      Recommendations

      • LED Strip Speed Challenge

        LED Strip Speed Challenge
      • Sculpting Challenge

        Sculpting Challenge
      • Clocks Contest

        Clocks Contest

      2 Discussions

      0
      oldhan
      oldhan

      1 year ago on Step 11

      I think ULN2803 & 2n2222 can be omitted.

      0
      tuenhidiy
      tuenhidiy

      Reply 1 year ago

      Thank for your constructive comment to reduce the size of PCB.

      *** From 74HC595 datasheet:

      - Io (Continuous Output Current) = 35 mA

      - Io (Continuous Current through Vcc or GND) = 70 mA

      I think if 8 ports of 74HC595 work with current 15mA, total consumption is: 15x8 = 120mA > Max(70mA). With ULN2803, we can drain up to 500 mA at each port and keep 74HC595 in safe working condition.