Introduction: Interactive Cylindrical Cube
This is a creative cylindrical LED cube with proximity sensing by infrared led and photo-transistors. Interactive Cylindrical Cube includes: 8X8 royal blue LEDs, 12 infrared LEDs, 16 photo-transistors and all of them are bent & formed a cylindrical shape.
Please see video below when this interactive cylindrical cube is used as flowerpot.
Step 1: Bill of Material
Step 2: Project Schematic
You can download high resolution version at link: Interactive Cylindrical Cube Schematic
Step 3: Assembly and Soldering
- Cylindrical cube arrangement:
Let see the picture above to understand how to arrange Leds - Phototransistors - Infrared Leds.
- Yellow row: photo-transistors (x16 pcs), Common - Collector type with with RE ~ 10Kohm.
- Green color: infrared Leds (x12 pcs), connect to 5V with current limit resistor 100 ~ 150 ohm.
- White color: royal blue LED (x64 pcs), with current limit resistor 100 ohm.
- Build a cylindrical led 8x8:
In order to arrange leds in cylindrical form, I used a flat wooden template with LED spacing 20mm. And after all leds were soldered, I bent them to a cylindrical shape of 50mm inside diameter and 80mm outside diameter.
TIP: I used a PVC pipe with 50mm diameter and bent it slowly following this shape, then using a wooden template to fix the first column of leds while soldering last column and first column together.
- Build IR led and photo-transistors circuit:
Picture below is shown how to solder IR leds & photo-transistor circuit. I reused the led's wooden template to arrange the IR leds (white led) and photo-transistors (look like black led), note that 4 analog output pins of the photo-transistors should be arranged cleverly.
Soldering cylindrical led and IR led - phototransistors to DIY Prototype PCB base.
- Build a control board:
Following schematic above to solder the control board
- Finish interactive cylindrical cube
- Build cover box
I reused one portable bottle, clear color with diameter 90mm, height 160mm and end cap of PVC pipe diameter 114mm to make a box cover.
I also added a push button and a potentiometer for further adjustment, ex:change working mode/ adjust the phototransistor response time/ fading time. Finally, I bought some flowers for decorating and testing.
Step 4: Programming
See the red cell on picture above, it represent for the 16 photo-transistors, marked from Zone00 to Zone15 and around each zone, we have 4 LEDs correspondingly.
Example: Zone00[4][2] = {{3, 0}, {4, 0}, {3, 1}, {4, 1}}; It tells us that photo-transistor number 0 is read by A0 pin of Arduino Nano and around it is 4 LEDs with coordinates: {6, 0}, {7, 0}, {6}, { 7, 1}. When photo-transistor receives infrared ray higher than presetting value (ProximityValue), corresponding 4 LEDs will be turned on.
byte Zone00[4][2] = {{3, 0}, {4, 0}, {3, 1}, {4, 1}}; byte Zone01[4][2] = {{3, 2}, {4, 2}, {3, 3}, {4, 3}}; byte Zone02[4][2] = {{3, 4}, {4, 4}, {3, 5}, {4, 5}}; byte Zone03[4][2] = {{3, 6}, {4, 6}, {3, 7}, {4, 7}}; byte Zone04[4][2] = {{5, 0}, {6, 0}, {5, 1}, {6, 1}}; byte Zone05[4][2] = {{5, 2}, {6, 2}, {5, 3}, {6, 3}}; byte Zone06[4][2] = {{5, 4}, {6, 4}, {5, 5}, {6, 5}}; byte Zone07[4][2] = {{5, 6}, {6, 6}, {5, 7}, {6, 7}}; byte Zone08[4][2] = {{7, 0}, {0, 0}, {7, 1}, {0, 1}}; byte Zone09[4][2] = {{7, 2}, {0, 2}, {7, 3}, {0, 3}}; byte Zone10[4][2] = {{7, 4}, {0, 4}, {7, 5}, {0, 5}}; byte Zone11[4][2] = {{7, 6}, {0, 6}, {7, 7}, {0, 7}}; byte Zone12[4][2] = {{1, 0}, {2, 0}, {1, 1}, {2, 1}}; byte Zone13[4][2] = {{1, 2}, {2, 2}, {1, 3}, {2, 3}}; byte Zone14[4][2] = {{1, 4}, {2, 4}, {1, 5}, {2, 5}}; byte Zone15[4][2] = {{1, 6}, {2, 6}, {1, 7}, {2, 7}};
I used 2x74HC4051 as Multiplexer so Arduino Nano can read 2x8 different analog inputs from phototransistors every 200 microseconds through A0 & A1 pins.
MAIN PROGRAM:
//********************* THE INTERACTIVE CYLINDRICAL CUBE ********************// //************************************************************************************************************// #include <SPI.h> #include <Wire.h> //*************************************************SPI - 74HC595******************************************// #define blank_pin 3 // Defines actual BIT of PortD for blank - is Arduino UNO pin 3, MEGA pin 5 #define latch_pin 2 // Defines actual BIT of PortD for latch - is Arduino UNO pin 2, MEGA pin 4 #define clock_pin 13 // used by SPI, must be 13 SCK 13 on Arduino UNO, 52 on MEGA #define data_pin 11 // used by SPI, must be pin MOSI 11 on Arduino UNO, 51 on MEGA //*************************************************3 TO 8 - 74HC138******************************************// #define RowA_Pin 4 #define RowB_Pin 5 #define RowC_Pin 6 //*************************************************PHOTO-TRANSISTOR******************************************// #define select1 7 #define select2 8 #define select3 9 #define sensing_enable 10 #define ProximityValue 500 volatile int phototransistor_data[16]; volatile byte phototransistor_group = 0; volatile byte phototransistor_group_adder = 0; unsigned long samplingtime = 0; //***************************************************BAM Variables*********************************************************// byte blue[4][8]; int level=0; int row=0; int BAM_Bit, BAM_Counter=0; #define BAM_RESOLUTION 4 //***************************************************COLOR TEMPLATE*********************************************************// byte myblue; struct Color { unsigned char blue; Color(int b) : blue(b) {} Color() : blue(0) {} }; //************************************************PHOTOTRANSISTOR & LED ZONE*****************************************************// byte Zone00[4][2] = {{3, 0}, {4, 0}, {3, 1}, {4, 1}}; byte Zone01[4][2] = {{3, 2}, {4, 2}, {3, 3}, {4, 3}}; byte Zone02[4][2] = {{3, 4}, {4, 4}, {3, 5}, {4, 5}}; byte Zone03[4][2] = {{3, 6}, {4, 6}, {3, 7}, {4, 7}}; byte Zone04[4][2] = {{5, 0}, {6, 0}, {5, 1}, {6, 1}}; byte Zone05[4][2] = {{5, 2}, {6, 2}, {5, 3}, {6, 3}}; byte Zone06[4][2] = {{5, 4}, {6, 4}, {5, 5}, {6, 5}}; byte Zone07[4][2] = {{5, 6}, {6, 6}, {5, 7}, {6, 7}}; byte Zone08[4][2] = {{7, 0}, {0, 0}, {7, 1}, {0, 1}}; byte Zone09[4][2] = {{7, 2}, {0, 2}, {7, 3}, {0, 3}}; byte Zone10[4][2] = {{7, 4}, {0, 4}, {7, 5}, {0, 5}}; byte Zone11[4][2] = {{7, 6}, {0, 6}, {7, 7}, {0, 7}}; byte Zone12[4][2] = {{1, 0}, {2, 0}, {1, 1}, {2, 1}}; byte Zone13[4][2] = {{1, 2}, {2, 2}, {1, 3}, {2, 3}}; byte Zone14[4][2] = {{1, 4}, {2, 4}, {1, 5}, {2, 5}}; byte Zone15[4][2] = {{1, 6}, {2, 6}, {1, 7}, {2, 7}}; void setup() { 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(RowA_Pin, OUTPUT); pinMode(RowB_Pin, OUTPUT); pinMode(RowC_Pin, OUTPUT); pinMode(sensing_enable, OUTPUT); digitalWrite(sensing_enable, LOW); pinMode(select1, OUTPUT); pinMode(select2, OUTPUT); pinMode(select3, OUTPUT); SPI.begin(); interrupts(); } void loop() { Interactive(); } void LED(int X, int Y, int B) { X = constrain(X, 0, 7); Y = constrain(Y, 0, 7); B = constrain(B, 0, 15); for (byte BAM = 0; BAM < BAM_RESOLUTION; BAM++) { bitWrite(blue[BAM][Y], X, bitRead(B, BAM)); } } void rowScan(byte row) { if (row & 0x01) PORTD |= 1<<RowA_Pin; else PORTD &= ~(1<<RowA_Pin); if (row & 0x02) PORTD |= 1<<RowB_Pin; else PORTD &= ~(1<<RowB_Pin); if (row & 0x04) PORTD |= 1<<RowC_Pin; else PORTD &= ~(1<<RowC_Pin); } 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: myTransfer(blue[0][level]); break; case 1: myTransfer(blue[1][level]); break; case 2: myTransfer(blue[2][level]); break; case 3: myTransfer(blue[3][level]); if(BAM_Counter==120){ BAM_Counter=0; BAM_Bit=0; } break; } rowScan(level); PORTD |= 1<<latch_pin; PORTD &= ~(1<<latch_pin); delayMicroseconds(5); PORTD &= ~(1<<blank_pin); level++; if(level==8) level=0; pinMode(blank_pin, OUTPUT); } inline static uint8_t myTransfer(uint8_t C_data){ SPDR = C_data; asm volatile("nop"); asm volatile("nop"); } void SetLightZone(byte Zone, byte B) { switch (Zone) { case 0: DrawLight(Zone00, B); break; case 1: DrawLight(Zone01, B); break; case 2: DrawLight(Zone02, B); break; case 3: DrawLight(Zone03, B); break; case 4: DrawLight(Zone04, B); break; case 5: DrawLight(Zone05, B); break; case 6: DrawLight(Zone06, B); break; case 7: DrawLight(Zone07, B); break; case 8: DrawLight(Zone08, B); break; case 9: DrawLight(Zone09, B); break; case 10: DrawLight(Zone10, B); break; case 11: DrawLight(Zone11, B); break; case 12: DrawLight(Zone12, B); break; case 13: DrawLight(Zone13, B); break; case 14: DrawLight(Zone14, B); break; case 15: DrawLight(Zone15, B); break; } } void DrawLight(byte zone_dots[4][2], byte B) { for (int i = 0; i < 4; i++) { LED(zone_dots[i][0], zone_dots[i][1], B); } } void Interactive(){ Color setcolor; if ( (unsigned long) (micros() - samplingtime) > 200 ) { phototransistor_data[0 + phototransistor_group_adder] = analogRead(A0); phototransistor_data[8 + phototransistor_group_adder] = analogRead(A1); if (phototransistor_group < 7 ) { phototransistor_group++; } else { phototransistor_group = 0; } phototransistor_group_adder = phototransistor_group; digitalWrite(select1, bitRead(phototransistor_group, 2)); digitalWrite(select2, bitRead(phototransistor_group, 1)); digitalWrite(select3, bitRead(phototransistor_group, 0)); samplingtime = micros(); } for (byte i=0; i<16; i++){ if (phototransistor_data[i] > ProximityValue) { SetLightZone(i, 15); } else { SetLightZone(i, 0); } } }
Step 5: Project Simulation
You can download Proteus simulation version for this project: LINK.
First testing video:
Attachments
Step 6: SOME MORE PICTURES
THANK YOU FOR WATCHING !!!