Sunteți pe pagina 1din 42

Audio Visual LED Cube

Team Members: Matthew Daigle, Robert Hunter, Kendra Kreider Advisor: Dr. Richard Messner Date: May 2011

ECE 791/792 Final Report

Daigle, Hunter, Kreider |2

Acknowledgments
This projects realization is due in part to the sponsorship granted from Raytheon as well as the funding provided by the University of New Hampshire. Special thanks go out to both of these organizations for providing us with the freedom to pursue our own project and their full support along the way. The A/V LED team would also like to acknowledge the support of Dr. Richard Messner, whos push for us to stay on track, as well as his intrigue and excitement for the project, really allowed the A/V LED cube to be the best possible project it could be.

Project Team

From left to right: Matthew Daigle, Robert Hunter, Kendra Kreider, Professor R. Messner

Daigle, Hunter, Kreider |3

Abstract
The goal of this project was to create a three dimensional 5x5x5 array of RGB LEDs powered by the Arduino microcontroller. This array of LEDs creates a cube shape that, with simple C programming, can display full color visual images through the use of multiplexing and serial in, parallel out shift registers. The cube can also produce visual effects that correspond to an audio input in real time through the use of the MSGEQ7 integrated circuit, which is a seven-band graphic equalizer IC. The cube also has commercial potential due to its unique and interesting design, though not at its current cost of construction. All design goals were met for this project, and future improvement will most likely consist of interactivity between the cube and the user.

Daigle, Hunter, Kreider |4

Table of Contents
INTRODUCTION ...................................................................................................................................................... 5 REACHING FINAL DESIGN.................................................................................................................................... 6 BUILD ......................................................................................................................................................................... 7 LED MATRIX ............................................................................................................................................................................ 7 CASING....................................................................................................................................................................................... 8 CIRCUIT DESIGN ..................................................................................................................................................... 9 ARDUINO ................................................................................................................................................................................... 9 MUSIC CHIP ............................................................................................................................................................................ 10 SHIFT REGISTERS .................................................................................................................................................................. 12 GROUND TRANSISTORS ........................................................................................................................................................ 13 TESTING .................................................................................................................................................................................. 14 SOFTWARE ............................................................................................................................................................ 14 DESIGN ISSUES ..................................................................................................................................................... 17 FUTURE WORK .................................................................................................................................................... 18 APPENDIX I: PROJECT PHOTOS...................................................................................................................... 21 APPENDIX II: EXAMPLE LIGHTSHOW .......................................................................................................... 28 APPENDIX III: EXAMPLE MUSIC SHOW ....................................................................................................... 36

Daigle, Hunter, Kreider |5

Introduction
Light organs have been a popular Do-It-Yourself electrical engineering design ever since their implementation in the 1970s. In their most basic design, light organs consisted of three lights: one to show low frequencies, one to show midrange frequencies, and one to show high frequencies. As technologies have advanced, improvements in this basic design have been made such as displaying the frequencies over two-dimensional banners as well as being able to select a greater number of frequencies to display. This project takes the simple concepts of light organs and electronic banners and builds off of them to create an LED light organ in three dimensions. This audio-visual LED cube (3D LED light organ) consists of a 5x5x5 array of RGB LEDs that is able to display low-resolution images as well as interact with different frequencies of an audio input through the use of an audio-equalizer chip. The primary hardware design goal was to come up with a way to control all 125 LEDs (each with 4 leads that needed individual control) using the least amount of wires and components necessary but without sacrificing performance and controllability of the cube. The software design aspect of this project consisted of creating visually appealing light and music animations. Hypothetically, this project has no definitive endpoint. An endless number of animations can be created, and a cleaner user interface could also be continuously developed. The LED cube is a viable commercial product with limitless potential in design applications.

Daigle, Hunter, Kreider |6

Reaching Final Design


The final project design was a 5x5x5 RGB LED cube. The size of this finished product was decided mostly due to cost of LEDs. To reach this final goal, incremental steps were first taken to gain familiarity with the design and coding. The first step of this project was to gain the basic tools and knowledge to build an RGB cube by first building a simpler size 3x3x3 single color LED cube. Although the 3x3x3 cube is more simplistic, there were many design similarities between it and larger cubes. Building this allowed us to foresee some problems with a larger-scale design and it also made coding simpler, which made the process of learning how to use the microcontroller easier and quicker. Figure 1 below shows the complete 3x3x3 single color cube connected to the microcontroller.

Figure 1. Initial 3x3x3 Design

Daigle, Hunter, Kreider |7

Build

Figure 2. Block Diagram of Project

LED Matrix
The LED matrix consists of 125-RGB LEDs, which are arranged in a 5x5x5 array. Each of the 5 layers (or floors) is made from 25 LEDs. All of the LEDs in a layer share a common ground, which is connected using 1/16 copper rodding. The copper rods give the lattice its rigidity making the structure stronger and sturdier. A single vertical column of LEDs is connected with 3 sub-columns for the red, green, and blue anodes. The number of columns, therefore, totals to 75 (25 LED columns x 3 sub-columns = 75 total columns). The anodes for each respective color in a column of LEDs are connected using single strands of bare copper wire. You will also notice that 5 columns are connected using the 1/16 copper rods. This was done, again, to add strength to the lattice. A single LED is addressed by applying 5V to one of the 75 columns and then grounding one of the 5 layers, which completes a circuit for only one LED so only one LED will be turned on (shown in Figure 3). At the bottom of the cube, you will see that magnet wire was used to connect the 75 columns to the circuitry inside of the wooden base. Magnet wire was chosen because it has a thin enamel coating

Daigle, Hunter, Kreider |8 for insulation, which allows the wires to touch each other without conduction. The connections on the circuit are in a small area where available space is limited, and also where unintended connections are more likely to occur. The enamel coated wire helps to solve both of these issues.

Figure 3. Example of How to Turn on a Single LED

Casing
The box that supports the lattice also holds the circuitry for the cube. It was made with mahogany, with curly maple being used for the legs. This was done to give the finished product a more visually appealing and professional look. Two latches were used for opening and closing the box so that the microcontroller and circuitry could be easily accessed. The circuit itself is attached to the under-side of the top of the box to keep it from bouncing around during transportation of the cube, which helps to assure that no connections in the circuitry come loose. The microcontroller is not anchored to anything, however, so that it may be removed and used for other projects and applications if need be.

Daigle, Hunter, Kreider |9 On the back of the wooden base, there are 4 ports DC In, USB In, Audio In, and Audio Out. The audio input accepts audio from any device that uses a 3.5mm audio jack (i.e. computer, iPod, phone). The audio output also uses a 3.5mm audio jack, but should only be connected to speakers that have an independent power supply. Connecting headphones or speakers that are without an independent power supply will load down the audio circuit. This, in turn, affects the audio-visualization lightshow performance. The DC input supply powers the Arduino microcontroller. The Arduino itself powers the LEDs in the cube. A 2.1mm, center-positive, 12 V, 1A rated AC adapter is the minimum current rating that should be used due to the current necessary to drive the electronics. The USB input can also be used to power the microcontroller when an AC adapter is not available. Although the USB can be used to power the microcontroller the USB port is normally used just to upload the lightshow sketches to the microcontroller from a computer using the Arduino application. In order to protect the cube a plexiglass case was constructed to cover the lattice. This was done purely for the protection of the fragile cube of LEDs and other cases could also be made from different types of glass or plexiglass that might give off different lighting effects from the light emanating from the cube. All pictures relating to the build process as well as how the cube was tested are provided in Appendix 1.

Circuit Design
Arduino
The Arduino UNO is the microcontroller board that is used to control the input of information to the shift registers, music chip, and ground transistor array. In total, 11 of the digital input/output pins are used as well as 1 of the 6 analog input pins. Along with these pins the Arduino UNO has a 16MHz

D a i g l e , H u n t e r , K r e i d e r | 10 crystal oscillator, USB jack, power jack, ICSP header, and a reset button. The USB jack is used to upload programs written on a computer to the Arduino and will also provide power to the board. If the user plans on using just one sketch, that sketch can be uploaded to the board and only the power jack is then necessary to power the LED cube. For this project the Arduino UNO is the heart of the LED Cube. Light shows are programmed using C coding and uploaded to the board to be sent out serially to the shift registers. The Arduino can also read in the analog data processed from the MSGEQ7 chip, which can then be manipulated in a lightshow sketch. This process is used to create the real-time audio-visual lightshows. The final circuit design for the LED cube can be split in to three significant portions (shown in Figure 4): the music chip to allow for audio analysis (1), the serial-in, parallel-out shift registers to control each column of LEDs (2), and the transistor circuit used to ground each individual layer (3).

2
3

1
Figure 4. Three Major Circuit Elements in Cube

Music Chip
The MSGEQ7 is a seven-band graphic equalizer integrated circuit that divides an audio signal into frequency bands. These seven bands are split over the range of audible frequencies and are located at 63Hz, 160Hz, 400Hz, 1kHz, 2.5kHz, 6.25kHz, and 16kHz. Since one face of the cube has a 5x5

D a i g l e , H u n t e r , K r e i d e r | 11 resolution, five of the seven frequencies were used, excluding the 1kHz and 16kHz frequency bands, for most of the audio-visual programming. These two frequencies were omitted since they tended not to be very predominant in most of the songs that were chosen. The MSGEQ7 serially samples one frequency at a time, and returns the amplitude of the sampled frequency to the Arduino. An amplitude range of 0 to 400 was consistently returned to the Arduino, which was then scaled down by 80 to result in values between 0 and 5, as the bar-height at each frequency would only range from 0 to 5 LEDs. The frequency bands were displayed on the front face of the cube where a vertical column of LEDs represented one of the five frequencies, and the height of the column represented the amplitude of a frequency. The pin diagram of the MSGEQ7 setup is shown below in Figure 5.

Figure 5. Pin Diagram for MSGEQ7

D a i g l e , H u n t e r , K r e i d e r | 12

Shift Registers
The 74HC595 8-bit serial-in, parallel-out shift registers were ultimately used as the way to address the LED matrix from the Arduino microcontroller. The setup for these shift registers is based on the 75 columns discussed earlier. Each of the 75 columns is connected to the output pin of a shift register, and each shift register has 8 output pins, therefore 10 shift registers are needed in total (leaving 5 pins disconnected). All bits are sent from the Arduino to the shift registers using a sendOut() command which serially sends out 8 bits at a time; repeating this action 9 more times will result in the shift registers being completely filled with the necessary information for a single layer of an image (an image being comprised of the 5 layers of the cube). The registers latch pins are then turned on, which sends out the information in the shift registers in parallel to each column and turns on the corresponding LEDs. The shift registers are organized in sections of 25 columns: first the necessary blue LED bits are sent in, then the 25 green LED bits are sent, and finally the 25 red LED bits are sent before the latch pins are triggered. This design minimizes the number of output pins needed by the microcontroller, as all of the information sent to the LED matrix is output through just one pin on the Arduino. If this design was utilized there would not have been enough pins on the Arduino to control all 75 columns of the LED matrix. The functional diagram for the 74HC595 is shown below in Figure 6. Q0 Q7 are the parallel outputs that are connected to the LED matrix, Q7 is the serial output to allow for multiple shift registers to be cascaded together (which is how all ten shift registers are written using only one output pin on the Arduino), DS is the serial data input read from the Arduino, SHCP is the shift register clock input (serial input shift clock), STCP is the storage register clock input (parallel output latch) which controls when the bits are sent out, MR (master reset) clears the shift registers, and OE (output enable) is always set to on as an active LOW.

D a i g l e , H u n t e r , K r e i d e r | 13

Figure 6. Functional Diagram for 74HC595

Ground Transistors
The third and final aspect of the circuit board is the most basic, and consists of five transistors acting as switches to allow each layer to be individually connected to ground. For the transistors the LM3046 Transistor Array IC was used as it consists of five matched transistors in one IC, which keeps the current consistent for all layers (maintaining even luminosity for each layer). Five output pins from the Arduino are used to turn on the individual transistors. Providing a 5V signal to the base of one of the five transistors turns on the switch which connects the respective layer to ground (shown in Figure 7). The five layers are shifted through one at a time to allow for multiplexing on the cube as the shift registers can only hold the information for one layer at a time.

D a i g l e , H u n t e r , K r e i d e r | 14

Figure 7. Circuit Diagram of Ground Layer Connections

Testing
As the circuitry consisted of many close soldered connections, as well as a very large amount of wiring, each connection was tested as it was soldered in to place. Initially, each LED was tested through the use of a power supply which was used simply to light up each LED to make sure each solder joint was properly conductive. When placing the wires from each column into place on the perforated board each wire was again tested with a power supply to make sure each connection was in its correct spot before the wire was soldered and trimmed. Next, the cube was tested using software by turning on each color of each LED one at a time to make sure the cube functioned as intended before any actual software was written for the cube.

Software
The cube is designed using C programming and the Arduino interface. Software is uploaded directly to the Arduino via USB. The Arduino is capable of storing a single program and running it without a computer. Due to the unique construction of the cube, a single image takes five iterations. The 5 layers are multiplexed through once to create an image. Multiplexing is necessary to produce ideal images. Since

D a i g l e , H u n t e r , K r e i d e r | 15 each layer has a common ground, LEDs in two different columns and two different layers cannot be lit up without additional, unwanted LEDs also lighting. Figure 8 displays the ideal case versus what happens in reality when two columns and two layers are turned on at the same time. To avoid this issue, all layers need to be multiplexed through at a frequency greater than or equal to 60Hz. Therefore, each ground layer can be lit for up to of 3.33 milliseconds before the next layer must turn on. At this rate, persistence of vision keeps the human eye from noticing any flicker that occurs from the layers turning on and off.

Figure 8. Ideal vs. Real interpretation of Cube Without Multiplexing Multiplexing is also necessary to display colors other than red, green, or blue. For example, switching between red and blue will display purple from the LED. Different shades of purple can also be made depending on the ratio of how long one color is on relative to the other. Any combination of the three basic colors, red, green, and blue, can be used to create another color. As mentioned before, the

D a i g l e , H u n t e r , K r e i d e r | 16 switching between the two or three basic colors must be at a rate faster than the 60Hz so that flickering is not noticeable. This concept of creating color with multiplexing is combined with the concept of multiplexing ground layers to create an image. Each color has to be split into a total interval of less than 3.33 milliseconds. To make the color more cohesive, the two separate colors should be flashed twice within this time. So to create purple, the red should be turned on for 750 microseconds, then blue for 750 microseconds, and then repeat that process one more time. The registers and other components also have delays of their own, so the actual time each color can be turned on for is further reduced. Since the colors are being multiplexed so quickly, the brightness of the LED is dependent on how long a color stays on before being turned off. Although multiplexing faster disperses the colors more evenly within the LED, it also reduces how bright the mixed color is, which is not ideal. When creating a lightshow, a certain amount of artistic creativity is necessary, and you must also be able to apply the concepts of multiplexing the layers and multiplexing the colors to realize the image accurately and efficiently. Combing the two is not without its difficulties. To simplify the process, it is better to program the basic animation in a single color first, and then add color to the design once the animation is in working order. Audio-visualization lightshows were programmed in the same manner that a regular animation was programmed along with the use of the MSGEQ7 seven-band graphic equalizer chip. The amplitudes of the frequency bands of an audio input are sent to the Arduino microcontroller, which were then manipulated within the lightshow program. Audio-visualization lightshows are, therefore, frequency and amplitude dependent.

D a i g l e , H u n t e r , K r e i d e r | 17

Design Issues
Difficulty, time, budget, and experience were all contributing factors to the design issues. Throughout the course of the project there were some aspects of design that did not end up working out as effectively or as efficiently as our initial design had intended, but more efficient ways of building and programming were discovered as more knowledge of the cube was gained. Even though there were many minor changes implemented over the course of the project, there were really only three significant design issues that arose. The first issue was a matter of design simplification. The initial design for the A/V LED cube involved the Arduino using 2-to-4 decoders in order to communicate with the LED matrix. Initially, the largest Arduino (Mega 2560) had enough output pins to run the cube using 2-to-4 decoders, but with the implementation of the music functionality this ended up no longer being the case. This issue was resolved by using 8-bit SIPO shift registers (10 of them) that could read in data from serially through one output pin on the Arduino. The final design ended up using a total of 12 output pins on the Arduino, which allowed the cube to be built with a smaller and less expensive microcontroller the Arduino Uno. The second design issue occurred when the audio visualization was first tested on the final cube. When speakers or headphones were plugged into the circuit for audio output, it ended up loading down the audio circuit so that the amplitudes of the frequencies being sampled were read as being half of what they actually were. This was due to the fact that the speakers acted as a voltage divider and reduced the voltage of the audio signal that was being fed into the MSGEQ7s input pin. Initially, buffer circuits and simple audio amplifiers were created to try to fix the problem, however the quality of the audio being output through the speakers was significantly deteriorated. Eventually the cube was tested using speakers that were powered with an independent power supply. This kept the speakers from drawing any power from the cubes audio circuit and completely solved the issue of the speakers

D a i g l e , H u n t e r , K r e i d e r | 18 loading down the visual effects on the cube. It also helped to simplify the final circuit design as no amplifiers or buffers were needed. The last major change in the final design was implemented mostly to save time and to simplify the wiring and soldering of the final circuit. Initially, the thin copper wires connecting the columns were to be fed through the top of the wooden box, soldered to 22 AWG insulated wire, and then cut to length and soldered to the perforated board. This would have been a very time consuming and tedious process. And not only that, but the joints between the copper wire and the insulated wire would have been very weak and frail. Through some research, magnet wire was decided to be the best viable option. Magnet wire is a thin wire (28 AWG was used in the project) with a very thin enamel coating that burns off when soldered. Using such thin wire also reduced the amount of space needed inside the box to hold all of the wire. 22 AWG wire along with its plastic insulation is considerably thicker than 28 AWG wire, and took up most of the available space inside the wooden box. The enameled wire was soldered directly to the bottom layer of the cube, fed through the box, and then fed through the perforated board. Once all of the wires were through the circuit board, they could then be pulled tight, soldered to the board, and finally trimmed.

Future Work
This project focused primarily on the most efficient way to build an LED cube and to also interface music with the cube. The cube was designed with simplicity in mind, so there is a lot room for development of both the hardware and the software. This section outlines a few of the possibilities for work on this as well as future LED cube projects. In terms of hardware, this cube was built to simply function as a 5x5x5 matrix of LEDs. While this build was stationary, rotational effects could be implemented using motors and more loose fitted LEDs

D a i g l e , H u n t e r , K r e i d e r | 19 (in terms of wiring). Accelerometers could also be utilized in such applications. Also, the only visual effects being generated by this LED cube are from the LEDs themselves. Things such as one-way mirrors, lasers, light bulbs, smoke machines, and even dry ice have all been suggested over the course of this year. With most of the budgeted time going into research, most of these ideas could not be realized in this project, however a future project that already has the design specifications implemented in this build could develop this project even further. Improvement in software design is really where a lot of future work could be spent. While this team focused purely on creating light shows for display and the ability to detect and play an audio signal in real time, these are not the only possible ways in which to use the LED cube. One aspect the team started to touch on near the end of the project was user interactivity. Through the use of a serial monitor built in to the Arduino software, the microcontroller can serially read in inputs from the keyboard and use these inputs to execute specific code already uploaded to the Arduino. This was used to switch between the animations and audio-visualization shows during the URC presentation, but the idea can be expanded in other ways. One application that the group started to explore was programming user-interactive games. The low resolution of the cube has its limitations, but games such as 3D Snake or 3D Pong would greatly expand the current functionality of the LED cube into something more than just a light organ. The cube could also be used to simulate 3D models such as earthquakes, fluids, or cellular automation. Again, however, the low resolution of the cube would not allow for models that are extremely detailed and complex, so a larger single colored cube would most likely be a better build if such applications were to be implemented. Improvements could also be made on how the animations are coded and that also expand on the idea of user interactivity. Simply put, this teams method for coding the cube may not have been the

D a i g l e , H u n t e r , K r e i d e r | 20 most efficient or easiest way to achieve the light and music shows. While explicitly writing the binary values into an array for each color was very intuitive and straightforward, it required a lot of abstract thinking to properly turn on and off each LED for each desired image. The most interesting solution to this problem, and a solution that opens up coding the cube to everyone and not just programmers, is to create a graphic user interface, or GUI, to visually program the cube. By having a model of the cube on a computer, the user would be able to choose a color and then click the LEDs that they wanted to turn on with that color and then save the image for use in the actual visual display. The user would then move on to programming the next image until eventually they have a complete light show finished in a fraction of the time it would take to program it individually. While it would take a very long time to actually program the GUI to be streamlined and bug free, it would save a lot of time in the long run. This type of interface would also eliminate the need for the common user to have a substantial knowledge of programming. The GUI would reduce programming the cube down to simply creating an animation visually, which would again add to the cubes commercial viability.

D a i g l e , H u n t e r , K r e i d e r | 21

Appendix I: Project Photos

D a i g l e , H u n t e r , K r e i d e r | 22

D a i g l e , H u n t e r , K r e i d e r | 23

D a i g l e , H u n t e r , K r e i d e r | 24

D a i g l e , H u n t e r , K r e i d e r | 25

D a i g l e , H u n t e r , K r e i d e r | 26

D a i g l e , H u n t e r , K r e i d e r | 27

D a i g l e , H u n t e r , K r e i d e r | 28

Appendix II: Example Lightshow


//Diagonals //pattern starts at lower corner //and travels up to opposite diagonal corner //pattern fades from blue on bottom layer to pink on top layer //code was edited to randomly select the starting corner //code was edited to randomly select whether pattern travels up or down //initializing variables for ground & register connects to arduino int dataPin = 13; //Serial Data Input int latchPin = 12; //Parallel Output Clock int clockPin = 11; //Shifting Clock int regResetPin = 10; //Shift Register Clear int ground[] = { 9, 8, 7, 6, 5}; //5 Ground Layers //variables used to implement design int count=0; int totalDelay=0; int delayT=300; //delay int corner=1; //random variable that picks corner int upDown=1; //variable determines if design is traveling up or down int numLED=1; //number of LEDs in diagonal int incLED=1; //variable that determines if diagonal size is increasing or decreasing int colorNum=1; byte data; byte dataArray[10]; int zeros[25] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0 }; int ones[25] = { 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1 }; int layer0[25] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0 }; int layer1[25] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0 }; int layer2[25] = { 0,0,0,0,0, 0,0,0,0,0,

D a i g l e , H u n t e r , K r e i d e r | 29
0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0 }; int layer3[25] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0 }; int layer4[25] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0 }; //setup- tells Arduino which pins are outputs, etc void setup() { pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); pinMode(regResetPin, OUTPUT); pinMode(ground[0], OUTPUT); pinMode(ground[1], OUTPUT); pinMode(ground[2], OUTPUT); pinMode(ground[3], OUTPUT); pinMode(ground[4], OUTPUT); analogReference(DEFAULT); digitalWrite(regResetPin, 0); //Clear the shift registers digitalWrite(regResetPin, 1); Serial.begin(1500000); turnOffLEDs(5); }//End setup void loop() { corner=random(1,5); //choose random corner from 1-4 upDown=random(2); //choose if design travels up or down, 0 or 1 while(count<=13){ //13 is a complete cycle of pattern //send layer arrays to function which determines pattern for layer DiagonalLayer(layer0,layer1,layer2,layer3,layer4,count,corner,upDown,numLED); while(totalDelay<20){ //repeat image 4 times so multiplexing not detected for(int i = 0; i < 5; i++) { //each image requires all 5 grounds to be switched on and off if (i==0){ //determines colors & sends data to registers colorIt(layer0,colorNum,delayT,i); } else if (i==1){ colorIt(layer1,colorNum+1,delayT,i); } else if (i==2){ colorIt(layer2,colorNum+2,delayT,i); }

D a i g l e , H u n t e r , K r e i d e r | 30
else if (i==3){ colorIt(layer3,colorNum+3,delayT,i); } else if (i==4){ colorIt(layer4,colorNum+4,delayT,i); } totalDelay=totalDelay+1; } } count++; //count pattern increment if(numLED==5){ incLED=0; } if (incLED){ numLED++; } else { numLED--; } totalDelay=0; } count=0; incLED=1; numLED=1; }//End loop //////////////////////////////// FUNCTIONS ////////////////////////////////// /* This function places the 1x25 binary array into a 1x80 (10 registers x 8 pins = 80 bits) binary array. These 80 binary values are then converted into a 1x10 byte array of HEX values */ void convert(int tempArray[25], char color, byte byteArray[10]) { int eightyArray[80]; /* Place the 1x25 binary array into the 1x80 binary array. The location of the 25 values in the 1x80 array is determined by what color the array is. First 25 values are reserved for blue, second 25 for green, third 25 for red, and the last 5 values are don't cares. Only one color will be on at a time. */ //Blue if(color == 'b') { for(int i = 0; i < 25; i++) { eightyArray[i] = tempArray[i]; } for(int j = 25; j < 80; j++) { eightyArray[j] = 0; } } //Green else if(color == 'g') { for(int i = 0; i < 25; i++) { eightyArray[i] = 0; } for(int j = 25; j < 50; j++) { eightyArray[j] = tempArray[j - 25]; } for(int k = 50; k < 80; k++) { eightyArray[k] = 0; } } //Red

D a i g l e , H u n t e r , K r e i d e r | 31
else if(color == 'r') { for(int i = 0; i < 50; i++) { eightyArray[i] = 0; } for(int j = 50; j < 75; j++) { eightyArray[j] = tempArray[j - 50]; } for(int k = 75; k < 80; k++) { eightyArray[k] = 0; } } //Red /* Now convert the 80 bits into 10 bytes and store them in byteArray, which points to dataArray */ int LSB = 0; //Represents least significant bit of the 8 bits being converted int MSB = 7; //Represents most significant bit of the 8 bits being converted int conversionArray[8]; //Array used to store the 8 bits being converted int byteIndex = 0; //Used to index the byteArray int indexA = 0; while(MSB < 80) { indexA = 0; for(int i = LSB; i <= MSB; i++) { conversionArray[indexA] = eightyArray[i]; indexA++; } LSB = LSB + 8; MSB = MSB + 8; //Go to the next eight bits for conversion //Converts binary string into bytes and writes that value into an array byteArray[byteIndex] = binaryToByte(conversionArray); byteIndex++; } }//End convert //////////////////////////////////////////////////////////////////////////////// //This function converts the string of 80 binary values into 10 bytes in HEX byte binaryToByte(int binary[8]) { byte sum = 0x00; //Converts the 8-bit binary number into a HEX byte, which is held by sum if(binary[0] == 1) { sum = sum + 0x80; } if(binary[1] == 1) { sum = sum + 0x40; } if(binary[2] == 1) { sum = sum + 0x20; } if(binary[3] == 1) { sum = sum + 0x10; } if(binary[4] == 1) { sum = sum + 0x08; } if(binary[5] == 1) { sum = sum + 0x04; } if(binary[6] == 1) { sum = sum + 0x02; }

D a i g l e , H u n t e r , K r e i d e r | 32
if(binary[7] == 1) { sum = sum + 0x01; } return sum; }//End binaryToByte /////////////////////////////////////////////////////////////////////////////////// //This function writes the array values to the registers and sends them to the cube void sendOut() { digitalWrite(latchPin, 0); //Ground latch pin until done shifting in all data for(int i = 0; i < 10; i++) { data = dataArray[i]; shiftOut(dataPin, clockPin, MSBFIRST, data); //Shift in the data one byte at a time } digitalWrite(latchPin, 1); //Send out the data from the registers }//End sendOut /////////////////////////////////////////////////////////////////////////////////// void turnOffLEDs(int reset) { if (reset==5){ //turn all five grounds off for(int k = 0; k < 5; k++) { digitalWrite(ground[k], 0); } } else if (reset>=0 && reset<=4) { //only turn 1 ground off digitalWrite(ground[reset], 0); } digitalWrite(regResetPin, 0); digitalWrite(regResetPin, 1); }//End turnOffLEDs //determines pattern for each layer void DiagonalLayer(int l0[25],int l1[25],int l2[25],int l3[25],int l4[25],int cnt,int crner,int downUp,int nLED){ int a1[9]={ 0,1,2,3,4,9,14,19,24 int a2[9]={ 4,3,2,1,0,5,10,15,20 int a3[9]={ 20,15,10,5,0,1,2,3,4 int a4[9]={ 24,19,14,9,4,3,2,1,0 }; //adress for start of diagonals for corner 1 }; //adress for start of diagonals for corner 2 }; //adress for start of diagonals for corner 3 }; //adress for start of diagonals for corner 4

if (downUp){ //each layer follows the pattern of the layer before for(int i=0;i<25;i++){ l4[i]=l3[i]; l3[i]=l2[i]; l2[i]=l1[i]; l1[i]=l0[i]; } //now determine the primary layer pattern for(int i=0;i<25;i++){ l0[i]=0; }

D a i g l e , H u n t e r , K r e i d e r | 33
int adrs=0; if(crner==1){ adrs=a1[cnt]; for(int j=0;j<nLED;j++){ l0[adrs]=1; adrs=adrs+4; } } else if(crner==2){ adrs=a2[cnt]; for(int j=0;j<nLED;j++){ l0[adrs]=1; adrs=adrs+6; } } else if(crner==3){ adrs=a3[cnt]; for(int j=0;j<nLED;j++){ l0[adrs]=1; adrs=adrs+6; } } else if(crner==4){ adrs=a4[cnt]; for(int j=0;j<nLED;j++){ l0[adrs]=1; adrs=adrs+4; } } } //end of if statement for pattern traveling Up else { //each layer follows the pattern of the layer before for(int i=0;i<25;i++){ l0[i]=l1[i]; l1[i]=l2[i]; l2[i]=l3[i]; l3[i]=l4[i]; } //now determine the primary layer pattern for(int i=0;i<25;i++){ l4[i]=0; } int adrs=0; if(crner==1){ adrs=a1[cnt]; for(int j=0;j<nLED;j++){ l4[adrs]=1; adrs=adrs+4; } } else if(crner==2){ adrs=a2[cnt]; for(int j=0;j<nLED;j++){ l4[adrs]=1; adrs=adrs+6; } } else if(crner==3){ adrs=a3[cnt]; for(int j=0;j<nLED;j++){ l4[adrs]=1;

D a i g l e , H u n t e r , K r e i d e r | 34
adrs=adrs+6; } } else if(crner==4){ adrs=a4[cnt]; for(int j=0;j<nLED;j++){ l4[adrs]=1; adrs=adrs+4; } } }//end pattern traveling down }//End of layer function /////////////////////////////////////////////////////////////////////////////////// void colorIt(int pattern[25],int colorNum, int delayTime,int gg){ int count=0; int dBlue=0; //time for blue delay int dGreen=0; //time for red delay int dRed=0; //time for green delay if(colorNum==1){//blue dBlue=delayTime; dRed=0; dGreen=0; } else if(colorNum==2){//80blue-20red dBlue=delayTime*80/100; dRed=delayTime*20/100; dGreen=0; } else if(colorNum==3){//50blue-50red dBlue=delayTime*50/100; dRed=delayTime*50/100; dGreen=0; } else if(colorNum==4){//20blue-80red dBlue=delayTime*20/100; dRed=delayTime*80/100; dGreen=0; } else if(colorNum==5){//5blue-95red dBlue=delayTime*5/100; dRed=delayTime*95/100; dGreen=0; } else if(colorNum==6){//red dRed=delayTime; dGreen=0; dBlue=0; } else if(colorNum==7){//95red-5green dRed=delayTime*95/100; dGreen=delayTime*5/100; dBlue=0; } else if(colorNum==8){//80red-20green dRed=delayTime*80/100; dGreen=delayTime*20/100; dBlue=0; } else if(colorNum==9){//50red-50green

D a i g l e , H u n t e r , K r e i d e r | 35
dRed=delayTime*50/100; dGreen=delayTime*50/100; dBlue=0; } else if(colorNum==10){//30red-70green dRed=delayTime*30/100; dGreen=delayTime*70/100; dBlue=0; } else if(colorNum==11){//green dGreen=delayTime; dBlue=0; dRed=0; } else if(colorNum==12){//80green-20blue dGreen=delayTime*80/100; dBlue=delayTime*20/100; dRed=0; } else if(colorNum==13){//50green-50blue (cyan) dGreen=delayTime*50/100; dBlue=delayTime*50/100; dRed=0; } else if(colorNum==14){//20green-80blue dGreen=delayTime*20/100; dBlue=delayTime*80/100; dRed=0; } else{ dBlue=delayTime; dRed=0; dGreen=0; } if(dBlue!=0){ convert(pattern, 'b', dataArray); //Convert the binary array into bytes sendOut(); //Send the frame to the cube (5 frames makes one image) digitalWrite(ground[gg], 1); delayMicroseconds(dBlue); //time blue is on turnOffLEDs(gg); } if(dRed!=0){ convert(pattern, 'r', dataArray); //Convert the binary array into bytes sendOut(); //Send the frame to the cube (5 frames makes one image) digitalWrite(ground[gg], 1); delayMicroseconds(dRed); //time red is on turnOffLEDs(gg); } if(dGreen!=0){ convert(pattern, 'g', dataArray); //Convert the binary array into bytes sendOut(); //Send the frame to the cube (5 frames makes one image) digitalWrite(ground[gg], 1); delayMicroseconds(dGreen); //time green is on turnOffLEDs(gg); } }//End colorIt

D a i g l e , H u n t e r , K r e i d e r | 36

Appendix III: Example Music Show


//Front Face Audio //Basic music program lights only the front face //Each of the five columns is a different frequency //the leftmost column displays bass and freqeuncy gets higher traveling right //the audio signal is sampled and filtered into frequency ranges //the corresponding freqeuncy column lights up with an amplitude of 0-5 LEDs //depending on audio sample //program sets base for other music programs int dataPin = 13; //Serial Data Input int latchPin = 12; //Parallel Output Clock int clockPin = 11; //Shifting Clock int regResetPin = 10; //Shift Register Clear int ground[] = { 9, 8, 7, 6, 5}; //5 Ground Layers int analogPin = A0; //Specrtum Analyzer Analog Output (frequency's amplitude) int strobePin = 4; //Specrtum Analyzer Strobe (read next frequency value) int msgResetPin = 3; //Specrtum Analyzer Clear int spectrum[7]; // Spectrum analyzer read values will be kept here //intialize variables int blue[25]; int green[25]; int red[25]; byte data; //Holds one byte at a time for writing to the dataArray byte dataArray[10]; //Holds the 10 bytes to write to the registers int t = 250; //Delay time constant (in microseconds) int i; int eightyArray[80]; void setup() { pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); pinMode(regResetPin, OUTPUT); pinMode(ground[0], OUTPUT); pinMode(ground[1], OUTPUT); pinMode(ground[2], OUTPUT); pinMode(ground[3], OUTPUT); pinMode(ground[4], OUTPUT); pinMode(analogPin, INPUT); pinMode(strobePin, OUTPUT); pinMode(msgResetPin, OUTPUT); analogReference(DEFAULT); Serial.begin(115200); digitalWrite(msgResetPin, 0); //Initialize the spectrum analyzer delay(1); digitalWrite(strobePin, 1); delay(1); turnOffLEDs(); }//End setup void loop() { displaySpectrum(); //Display the spectrum }//End loop //////////////////////////////// FUNCTIONS //////////////////////////////////

D a i g l e , H u n t e r , K r e i d e r | 37

//Read the 7-band equalizer void readSpectrumValues() { digitalWrite(msgResetPin, 1); digitalWrite(msgResetPin, 0); //Band //Band //Band //Band //Band //Band //Band 0 1 2 3 4 5 6 = = = = = = = 63Hz 160Hz 400Hz 1kHz 2.5kHz 6.25kHz 16kHz

for (int band = 0; band < 7; band++) { digitalWrite(strobePin, 0); //Read twice, take the average spectrum[band] = (analogRead(analogPin) + analogRead(analogPin)) / 2; digitalWrite(strobePin, 1); } }//End redSpectrumValues ///////////////////////////////////////////////////////////////////////////// void displaySpectrum() { readSpectrumValues(); //Read in the spectrum values int scalar = 80; int adjustmentTimer = 0; int barHeightA; int barHeightB; int barHeightC; int barHeightD; int barHeightE; int maxHeight = 0; for(int band = 0; band < 7; band++) { //Will only be using 5 bands //Bands are read in as values from 0 - 1023, scale them down to be 0 - 5 barHeightA = spectrum[0] / scalar; barHeightB = spectrum[1] / scalar; barHeightC = spectrum[2] / scalar; barHeightD = spectrum[4] / scalar; barHeightE = spectrum[5] / scalar; if(barHeightA maxHeight = if(barHeightB maxHeight = if(barHeightC maxHeight = if(barHeightD maxHeight = if(barHeightE maxHeight = > maxHeight) //Check if this value is the largest so far barHeightA; > maxHeight) barHeightB; > maxHeight) barHeightC; > maxHeight) barHeightD; > maxHeight) barHeightE; FIRST LAYER //////////////////

///////////////// turnOffLEDs();

if(barHeightA >= 1){ green[20] = 1; }

D a i g l e , H u n t e r , K r e i d e r | 38
if(barHeightB green[21] = } if(barHeightC green[22] = } if(barHeightD green[23] = } if(barHeightE green[24] = } >= 1){ 1; >= 1){ 1; >= 1){ 1; >= 1){ 1;

convert(green, 'g', dataArray); //Convert the binary array into bytes sendOut(); //Send the frame to the cube digitalWrite(ground[0], 1); delayMicroseconds(t); ///////////////// turnOffLEDs(); if(barHeightA green[20] = } if(barHeightB green[21] = } if(barHeightC green[22] = } if(barHeightD green[23] = } if(barHeightE green[24] = } >= 2){ 1; >= 2){ 1; >= 2){ 1; >= 2){ 1; >= 2){ 1; SECOND LAYER //////////////////

convert(green, 'g', dataArray); //Convert the binary array into bytes sendOut(); //Send the frame to the cube digitalWrite(ground[1], 1); delayMicroseconds(t); ///////////////// turnOffLEDs(); if(barHeightA green[20] = } if(barHeightB green[21] = } if(barHeightC green[22] = } if(barHeightD green[23] = } if(barHeightE green[24] = } THIRD LAYER //////////////////

>= 3){ 1; >= 3){ 1; >= 3){ 1; >= 3){ 1; >= 3){ 1;

convert(green, 'g', dataArray); //Convert the binary array into bytes

D a i g l e , H u n t e r , K r e i d e r | 39
sendOut(); //Send the frame to the cube digitalWrite(ground[2], 1); delayMicroseconds(t); ///////////////// FOURTH LAYER ////////////////// turnOffLEDs(); if(barHeightA green[20] = } if(barHeightB green[21] = } if(barHeightC green[22] = } if(barHeightD green[23] = } if(barHeightE green[24] = } >= 4){ 1; >= 4){ 1; >= 4){ 1; >= 4){ 1; >= 4){ 1;

convert(green, 'g', dataArray); //Convert the binary array into bytes sendOut(); //Send the frame to the cube digitalWrite(ground[3], 1); delayMicroseconds(t); ///////////////// turnOffLEDs(); if(barHeightA green[20] = } if(barHeightB green[21] = } if(barHeightC green[22] = } if(barHeightD green[23] = } if(barHeightE green[24] = } >= 5){ 1; >= 5){ 1; >= 5){ 1; >= 5){ 1; >= 5){ 1; FIFTH LAYER //////////////////

convert(green, 'g', dataArray); //Convert the binary array into bytes sendOut(); //Send the frame to the cube digitalWrite(ground[4], 1); delayMicroseconds(t); } //Stabilization section //Adjust the scalar if levels are too high/low //If below 4 happens 20 times, then decrease the divisor if (maxHeight >= 5) { scalar++; adjustmentTimer = 0; } else if(maxHeight < 5) {

D a i g l e , H u n t e r , K r e i d e r | 40
if(scalar > 65) if(adjustmentTimer++ > 20) { scalar--; adjustmentTimer = 0; } } else { adjustmentTimer = 0; } }//End displaySpectrum ///////////////////////////////////////////////////////////////////////////// /* This function places the 1x25 binary array into a 1x80 (10 registers x 8 pins = 80 bits) binary array. These 80 binary values are then converted into a 1x10 byte array of HEX values */ void convert(int tempArray[25], char color, byte byteArray[10]) { int int int int int LSB = 0; //Represents least significant bit of the 8 bits being converted MSB = 7; //Represents most significant bit of the 8 bits being converted conversionArray[8]; //Array used to store the 8 bits being converted byteIndex = 0; //Used to index the byteArray indexA = 0;

/* Place the 1x25 binary array into the 1x80 binary array. The location of the 25 values in the 1x80 array is determined by what color the array is. First 25 values are reserved for blue, second 25 for green, third 25 for red, and the last 5 values are don't cares. Only one color will be on at a time. */ //Blue if(color == 'b') { for(int i = 0; i < 25; i++) { eightyArray[i] = tempArray[i]; } } //Green else if(color == 'g') { for(int j = 25; j < 50; j++) { eightyArray[j] = tempArray[j - 25]; } } //Red else if(color == 'r') { for(int j = 50; j < 75; j++) { eightyArray[j] = tempArray[j - 50]; } } //Red /* Now convert the 80 bits into 10 bytes and store them in byteArray, which points to dataArray */ while(MSB < 80) { indexA = 0; for(int i = LSB; i <= MSB; i++) { conversionArray[indexA] = eightyArray[i]; indexA++; } LSB = LSB + 8; MSB = MSB + 8; //Go to the next eight bits for conversion //Converts binary string into bytes and writes that value into an array byteArray[byteIndex] = binaryToByte(conversionArray); byteIndex++;

D a i g l e , H u n t e r , K r e i d e r | 41
} }//End convert ///////////////////////////////////////////////////////////////////////////// //This function converts the string of 80 binary values into 10 bytes in HEX byte binaryToByte(int binary[8]) { byte sum = 0x00; //Converts the 8-bit binary number into a HEX byte, which is held by sum if(binary[0] == 1) { sum = sum + 0x80; } if(binary[1] == 1) { sum = sum + 0x40; } if(binary[2] == 1) { sum = sum + 0x20; } if(binary[3] == 1) { sum = sum + 0x10; } if(binary[4] == 1) { sum = sum + 0x08; } if(binary[5] == 1) { sum = sum + 0x04; } if(binary[6] == 1) { sum = sum + 0x02; } if(binary[7] == 1) { sum = sum + 0x01; } return sum; }//End binaryToByte ///////////////////////////////////////////////////////////////////////////// //This function writes the array values to the registers and sends them to the cube void sendOut() { digitalWrite(latchPin, 0); //Ground latch pin until done shifting in all data for(int i = 0; i < 10; i++) { data = dataArray[i]; shiftOut(dataPin, clockPin, MSBFIRST, data); //Shift in the data one byte at a time } digitalWrite(latchPin, 1); //Send out the data from the registers }//End sendOut ///////////////////////////////////////////////////////////////////////////// void turnOffLEDs() { for(int i = 0; i < 25; i++) { green[i] = 0; red[i] = 0; blue[i] = 0; } for(int i = 0; i < 5; i++) { digitalWrite(ground[i], 0);

D a i g l e , H u n t e r , K r e i d e r | 42
} for(int i = 0; i < 80; i++) { eightyArray[i] = 0; //Initializes the eightyArray } digitalWrite(regResetPin, 0); digitalWrite(regResetPin, 1); }//End turnOffLEDs

S-ar putea să vă placă și