Flexible Mounting RGB Dome Light
by tuenhidiy in Circuits > Arduino
1721 Views, 14 Favorites, 0 Comments
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
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:
- JLCPCB PCB Prototype only $2.
- Get $24 Register Coupon here: jlcpcb.com/cyt
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.
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
Thank you for reading my work!!!