Flexible Mounting RGB Dome Light

by tuenhidiy in Circuits > Arduino

1721 Views, 14 Favorites, 0 Comments

Flexible Mounting RGB Dome Light

Title_4.jpg
Title_3.jpg
Title_1.jpg
Flexible Mounting RGB Dome Light

A few years ago, when my son started going to school on his own by bike, I bought for him a QQ Watch that allowed me and my wife to stay in touch and keep track of him. This smartwatch is now broken but I still keep its box because it looks pretty. I took my time to turn it into a useful thing - this is a flexible mounting RGB light made out of this case.

Let's getting started.

Supplies

The main components are as follows:

⦾ 1pcs x DigiSpark ATTiny85.

⦾ 3pcs x Power Logic 8-Bit Shift Register TPIC6B595N.

⦾ 2pcs x A1013 Transistors.

⦾ 16pcs x RGB LED 10mm, Common Anode.

⦾ 24pcs x R100.

⦾ 3 pcs x R10K.

⦾ 1 pcs x R22K.

⦾ 2pcs x Capacitor 0.1uF.

⦾ 2pcs x Female 40pin 2.54mm Header.

⦾ 2pcs x Male 40pin 2.54mm Header.

⦾ 3pcs x 20-Pin DIP IC Base Socket Connector (For TPIC6B595N).

⦾ 1pcs x Mini Round Momentary Push Button.

⦾ 2pcs x Double Sided DIY Protoboard Circuit 5x7cm.

⦾ 1 meter x 8P/16P Rainbow Ribbon Cable.

⦾ 2pcs x 5mm DC Male And Female Power Plug.

⦾ Some Neodymium Magnets.

Schematic

ATTINY85_DOM_schem.jpg

I stuffed 16pcs x RGB leds 10mm and control board into the watch box. ATTiny85 microcontroller was used in this project to control RGB led and one push button for mode selection. The schematic is shown on the image above.

First of all, I would like to thank the JLCPCB for supporting me on this project. If you have a PCB project, please visit the JLCPCB website to get exciting discounts and coupons as follows:

Assembly and Soldering

I disassembled the watch box parts and measured the dimensions. It fits with 5x7cm PCB protoboards.

RGB led matrix 4x4 was soldered on a protoboard 5x7 cm.

Soldering control component on the remaining PCB 5x7 cm following the schematic on Step 2.

⦾ Top view.

⦾ Bottom view.

Remove the watch holder and glue the led matrix 4x4 to the middle part's top.

Thread all wires from the led matrix down to the control board which is located at the middle part's bottom.

Plug ATTiny85 on its header.

Drill and install one push button and female power plug at bottom part.

Glue some Neodymium magnets.

Assemble all the parts together. DONE.

Programing

The following core, libraries and driver for DigiSpark ATTiny85 need to be installed in this project:

ATTinyCore.

Digistump Driver.

Arduino tinySPI Library.

AnalogButtons.

SPI Connections:

⦾ DigiSpark ATTiny85 PB0 to TPIC6B595N Pin 12 (RCK - LATCH PIN).

⦾ DigiSpark ATTiny85 PB1 to TPIC6B595N Pin 3 (SER IN - DATA IN).

⦾ DigiSpark ATTiny85 PB2 to TPIC6B595N Pin 13(SRCK - CLOCK PIN).

⦾ TPIC6B595N Enable Pin (OE) to Ground.

The DigiSpark ATTiny85 code is shown below:

#include <tinySPI.h>
#include <AnalogButtons.h>

// Pin Definitions
const int
    LATCH_PIN(0),
    DATA_PIN(1),
    CLOCK_PIN(2),
    ROW_0(3),
    ROW_1(4);

//Analog Button DigiSpark ATTiny85 PB5/A0
#define ANALOG_PIN A0
AnalogButtons analogButtons(ANALOG_PIN, INPUT);
Button ButtonMode = Button(728, &ModeClick, &ModeHold, 3000);
int ClickCounter = 0;
int HoldCounter =0;

// B.A.M Parameters
byte red[4][2];
byte green[4][2];
byte blue[4][2];
int row=0, BAM_Bit, BAM_Counter=0; 

// Color-wheel Parameters
#define BAM_RESOLUTION        4  
#define COLOUR_WHEEL_LENGTH   64
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

void setup()
{
  row = 0;
  SPI.begin();
  pinMode(ROW_0, OUTPUT);
  pinMode(ROW_1, OUTPUT);
  pinMode(LATCH_PIN, OUTPUT);
  digitalWrite(LATCH_PIN, HIGH);
  noInterrupts();
  // Clear registers
  TCNT1 = 0;
  TCCR1 = 0;
  // 15 x 3.636 = 54.54us
  OCR1C = 15;
  // Interrupt COMPA
  OCR1A = OCR1C;
  // CTC
  TCCR1 |= (1 << CTC1);
  // Prescaler 64 - 16.5MHz/64 = 275Kz or 3,636us
  TCCR1 |= (1 << CS12) | (1 << CS11) | (1 << CS10);
  // Output Compare Match A Interrupt Enable
  TIMSK |= (1 << OCIE1A);
  interrupts();
  clearfast();
  analogButtons.add(ButtonMode);
  fill_colour_wheel(); 
}

void loop()
{
  analogButtons.check();
  switch (ClickCounter % 10) 
  {
    case 0:
    fillTable(15, 0, 0);
    break;
    case 1:
    fillTable(0, 15, 0);
    break;
    case 2:
    fillTable(0, 0, 15); 
    break;
    case 3:
    fillTable(15, 15, 15);
    break;    
    case 4:
    fillTable(15, 15, 0);
    break;
    case 5:
    fillTable(0, 15, 4);
    break;
    case 6:
    fillTable(15, 0, 15); 
    break;
    case 7:
    fillTable(15, 9, 0);
    break;  
    case 8:
    fillTable_colorwheelRGB();
    break;
    case 9:
    clearfast();
    break;
  }   
}

void ModeClick()
{  
  ClickCounter++;
}
void ModeHold()
{  
  HoldCounter++; 
}
void LED(int X, int Y, int R, int G, int B)
{
  X = constrain(X, 0, 1);
  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][X], Y, bitRead(R, BAM));
    bitWrite(green[BAM][X], Y, bitRead(G, BAM));  
    bitWrite(blue[BAM][X], Y, bitRead(B, BAM));
  } 
}

void clearfast()
{
    memset(red, 0, sizeof(red[0][0]) * 4 * 2);
    memset(green, 0, sizeof(green[0][0]) * 4 * 2);
    memset(blue, 0, sizeof(blue[0][0]) * 4 * 2);        
}


void rowScan(byte row)
{  
  if (row == 0)
  {
    PORTB |= 1<<ROW_0;
    PORTB &= ~(1<<ROW_1);
  }
  else 
  {
    PORTB &= ~(1<<ROW_0);
    PORTB |= 1<<ROW_1;
  }      
}


ISR(TIMER1_COMPA_vect)
{ 
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:
      SPI.transfer(red[0][row]); SPI.transfer(green[0][row]); SPI.transfer(blue[0][row]);
      break;
    case 1: 
      SPI.transfer(red[1][row]); SPI.transfer(green[1][row]); SPI.transfer(blue[1][row]);         
      break;
    case 2:       
      SPI.transfer(red[2][row]); SPI.transfer(green[2][row]); SPI.transfer(blue[2][row]);        
      break;
    case 3:
      SPI.transfer(red[3][row]); SPI.transfer(green[3][row]); SPI.transfer(blue[3][row]);           
      if(BAM_Counter==120)
      {
        BAM_Counter=0;
        BAM_Bit=0;
      }
      break;
}

rowScan(row);
PORTB &= ~(1<<LATCH_PIN);           // Set LATCH PIN low - TPIC6B595           
//delayMicroseconds(2);  
PORTB |= 1<<LATCH_PIN;              // Set LATCH PIN high - TPIC6B595
//delayMicroseconds(2);
row++;
if(row==2) row=0;
}

void fillTable(byte R, byte G, byte B)
{
  for (byte x=0; x<2; x++)
    {
      for (byte y=0; y<8; y++)
      {
        LED(x, y, R, G, B);
      }
    }
}


void fillTable_colorwheelRGB()
{ 
  for (byte x=0; x<2; x++)  
    {      
       for (byte y=0; y<8; y++) 
        {
          get_colour(4*(HoldCounter + x + y), &R, &G, &B);
          LED(x, y, 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;
}


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

It has 10 working modes selected by the Click and Hold features of the AnalogButtons library:

⦾ Mode 0 to 7: show the basic colors, such as: RED, GREEN, BLUE, WHITE, ORANGE, TEAL, PURPLE, YELLOW.

⦾ Mode 8 (color-wheel mode): The color of each RGB led can be adjusted by holding the pushbutton for longer than 3 seconds.

⦾ Mode 9: Turn off all LED.

Flexible Mounting

I soldered a USB to male DC plug cable converter so it can be powered from a power bank or laptop.

It can be placed in many different poses, such as:

⦾ Placed on a table.

⦾ Mounted horizontally.

⦾ Upside down.

⦾ Or upright hanging.

⦾ It can also turn into a fake dome camera. :-)

Conclusion

F_1.jpg
F_2.jpg
F_3.jpg

Thank you for reading my work!!!