Introduction: Open, Sesame! LAMP

About: PLC, Arduino - Do it yourself project

"Open, sesame! LAMP" is a decorative spherical lamp which color is adjusted through a potentiometer and push button. Sphere cover of lamps is made from thermocouple head and this lamp can be closed or opened through mechanical lock by hand.

My project have an industrial style look by reusing components from industrial waste, such as: thermocouples, local box, cable gland...

There are so many reasons why one should go for this look in my opinion...

And below video is my result:

Step 1: B.O.M

  1. Broken industrial thermocouple: I reused a broken thermocouple head cover as picture below. It is in spherical shape and can contains a Neopixel Ring 16.
  2. Local box: I used Schneider’s local box with specification XAPM1501H29 - Metal Enclosure, 22mm, Control Station, Control, Zinc Alloy, 80 mm, 80 mm, 74.5 mm, IP65. LINK
  3. 1 x Arduino Nano.
  4. 1 x NeoPixel Ring 16. LINK
  5. 16 x RGB Led, 5mm, common anode LINK
  6. 3 x 74HC595. LINK
  7. 3 x ULN2803. LINK
  8. 3 x 0.1uF Electrolytic Capacitor.
  9. 24 x 100Ω Resistor.
  10. 1 x 5kΩ potentiometer.
  11. 1 x A1013 Power NPN Transistors
  12. 1 x Female DC Power Plug Socket Jack Connector. LINK
  13. 2 x Cable gland & lock nut: Polyamide Type 20mm or Nikel Type 20mm.
  14. 1 x Power supply 5VDC. LINK
  15. 1 x Single-Side Copper Prototype PCB.

Step 2: PROJECT SCHEMATIC

The diagram above is used to control 16 x RGB LEDs and 1 x Neopixel Ring 16 via a button and a potentiometer by an Arduino Nano.

  • Push Button: select working modes.
  • Potentiometer: adjust color of RGB LEDs and Neopixel Ring .

Step 3: ASSEMBLY WORKS

1. Build LED bulb and Neopixel Ring 16

  • Prepare broken Thermocouple head: It has 2 holes, one with diameter 21mm will be connected to local box and the other with diameter 32mm will be connected to snowman head with push button. Let's see on next step.

  • Soldering the RGB LED bulb: 16x RGB LEDs - common anode were soldered on small Prototype DIY board with 2 layers and in cylindrical arrangement. Later it will be putted inside the bulb cover.

  • Prepare Neopixel Ring 16: This RGB LED fitted just fine when placed inside thermocouple head above.

2. Build shift register board and Arduino Nano Adapter

  • Shift Register Board: Following project schematic, I installed 3x 74HC595, 3 x ULN2803, 24 x 100Ω resistors, 3 x 0.1uF capacitors on prototype board and did soldering work. Each shift register 74HC595 + ULN2803 will control one color, so we have totally 3 shift registers for handling: Red, Green, Blue colors. Arduino Nano pins are assigned for SPI controlling as follow:

    - BLANK PIN (ENA) ------------> 03

    - LATCH PIN (RCK) ------------> 02

    - CLOCK PIN (SCK) ------------> 13

    - DATA PIN (MOSI) -------------> 11

For anode layer of RGB LED, I used Trasistor A1013 and it also was soldered on this board.

  • Arduino Nano Adapter: Soldering some male headers as picture below after connecting Arduino pins following project schematic shown on previous step.

3. Box assembly

  • Prepare OLAF head - snowman for Christmas: To connect snowman head with push button to thermocouples hole, I used PVC pipe with diameter 32mm and glued them together. Actually, I don't know whether it's like a snowman or not :-)

  • Connecting OLAF head, LED bulb and Neopixel Ring 16 on top of local box.

  • Power supply and Potentiometer assembly.

  • Putting Arduino Nano and Shift Register Board inside the box to make sure everything OK.

  • Connecting all components.

  • Do testing before final assembly

  • DONE

  • Mounting local box on the wall for decoration

Step 4: CODING

//************************************************************************************************************//
#include 
#include 

#define blank_pin 3   // Defines actual BIT of PortD for blank - is Arduino UNO pin 3
#define latch_pin 2   // Defines actual BIT of PortD for latch - is Arduino UNO pin 2
#define clock_pin 13  // used by SPI, must be 13 SCK 13 on Arduino UNO
#define data_pin  11   // used by SPI, must be pin MOSI 11 on Arduino UNO

#define ROW       4
#define BUTTON    5
#define PIN       6
#define POTPIN    7

Adafruit_NeoPixel strip = Adafruit_NeoPixel(16, PIN, NEO_GRB + NEO_KHZ800);

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

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

int BAM_Bit, BAM_Counter=0; 

//************************************************************************************************************//
#define BAM_RESOLUTION 4  
#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;

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

int buttonPushCounter = 0;   
int buttonState = 0;         
int lastButtonState = 0; 
int POT;
int OLD_POT;

void setup()
{
strip.begin();
strip.setBrightness(50);
strip.show(); // Initialize all pixels to 'off'

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

noInterrupts();

TCCR1A = B00000000;
TCCR1B = B00001011;
TIMSK1 = B00000010;
OCR1A = 8;

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

pinMode(ROW, OUTPUT);
pinMode(BUTTON, INPUT_PULLUP);

SPI.begin();
interrupts();

fill_colour_wheel();
clearfast();
}

void loop()
{
buttonState = digitalRead(BUTTON);
 
  if (buttonState != lastButtonState) {
    if (buttonState == HIGH) {
      buttonPushCounter++;
    }
    else {

    }
  }
  lastButtonState = buttonState;

  switch (buttonPushCounter % 5) 
  {
    case 0:

      POT = map(analogRead(POTPIN), 0, 1023, 0, 255);
      if (POT < (OLD_POT * 0.95) || POT > (OLD_POT * 1.05))
        {
          OLD_POT = POT;  // save the changed value    
          fillTable_colorwheelRGB(POT, R, G, B);
          rainbow(POT);    
        }

        break;
    case 1:

      POT = map(analogRead(POTPIN), 0, 1023, 0, 255);
      if (POT < (OLD_POT * 0.95) || POT > (OLD_POT * 1.05))
        {
          OLD_POT = POT;   
          get_colour(POT, &R, &G, &B); 
          fillTable(R, G, B);
          fillring(POT);    
        }

        break; 

    case 2:

      POT = map(analogRead(POTPIN), 0, 1023, 0, 255);
      if (POT < (OLD_POT * 0.95) || POT > (OLD_POT * 1.05))
        {
          OLD_POT = POT;    
          get_colour(POT, &R, &G, &B); 
          fillTable(R, G, B); 
          fullWhite();   
        }

        break; 
    case 3:

      POT = map(analogRead(POTPIN), 0, 1023, 0, 255);
      if (POT < (OLD_POT * 0.95) || POT > (OLD_POT * 1.05))
        {
          OLD_POT = POT;     
          fillTable(0, 0, 0);
          rainbow(POT);    
        }

        break;
case 4:
        clearfast();
        fullWhite();
        break;        
  }
}

void LED(int Y, int R, int G, int B)
{
  Y = constrain(Y, 0, 7);
  
  R = constrain(R, 0, 15);
  G = constrain(G, 0, 15); 
  B = constrain(B, 0, 15);

  for (byte BAM = 0; BAM < BAM_RESOLUTION; BAM++) 
  {
    bitWrite(red[BAM], Y, bitRead(R, BAM));

    bitWrite(green[BAM], Y, bitRead(G, BAM));

    bitWrite(blue[BAM], Y, bitRead(B, BAM));
  }
}

ISR(TIMER1_COMPA_vect){
  
PORTD |= ((1< 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;
}

//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;
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
} 

Step 5: TESTING

Please see video below for my first testing:

This "Open, sesame! LAMP" has 4 working modes that is selected through push button as follows:

  • Bulb + Neopixel Ring Colorwheel mode.
  • Bulb + Neopixel Ring Single color mode.
  • Only Bulb single color mode.
  • Neopixel Ring Colorwheel mode.

Step 6: FINISH

Thank for watching !!!

Make it Glow Contest 2018

Participated in the
Make it Glow Contest 2018