Introduction: Interactive Cylindrical Cube

About: PLC, Arduino - Do it yourself project

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:

Step 6: SOME MORE PICTURES

THANK YOU FOR WATCHING !!!

Make it Glow Contest 2018

Participated in the
Make it Glow Contest 2018