That's Totally Disco

Sign in to queue

Description

  Set up your a disco dance floor in your home!
Monkey See, Monkey Build - Usb Controlled Disco Dance Floor

Difficulty: Advanced
Time Required: Greater than 10 hours
Cost: Greater than $200
Software: C# 2005 Express Edition, .NET Framework 2.0
Hardware: LEDs, Max7313 LED Intensity Controllers, tools for cutting wood (Dremel 400XPR, a DeWalt DW331 VS orbital Jig saw, and a Milwaukee 1-3/4 HP BodyGrip 5615 Router), drills, soldering iron (Weller WESD51 with a .031" Long Conical Tip), wood, wires, iBook, printed circuit boards (PCBs)
Download: Download

Many would ask, "Why build a disco dance floor?" My friends ask, "Why wasn't it done sooner?" The rest ask, after seeing it, how much I spent. Instead, I would like to ask, "Why didn't you build a computer-controlled disco dance floor running your own custom made software by yourself?" The idea isn't new and the printed circuit boards (PCBs) I got are from MIT. I'm not claiming originality points here.

Simply put, I saw the disco dance floor and realized it had to be built. I took an electronics class in high school and a digital electronics class in college. I thought this would be a walk in the park. I'm a quasi-smart 24-year-old programmer whose friends call "MacGyver" from time to time. How bad could it be? My theory was that girls like to dance. By having a dance floor in my apartment, more girls would magically appear there. A heavily flawed theory to stand on — which, let's not kid ourselves, is the best kind.

When I got the boards, I was going to use an iBook to power the floor; however, the programmer in me didn't like how it acted or handled. I'm a perfectionist and I knew I could whip something up that I'd like. I have yet to create a perfect application, but here is what I've done so far.

After doing some research, I found out that .NET 2.0 had a built-in SerialPort class. I really wanted to play around with the new framework, so I chose to use the .NET 2.0 Framework. When the coding part of the project started, .NET 2.0 was still in Beta 2, so there were a few weird quirks, with the older revisions running the RTM version of .NET 2.0.

So here is my 10-month legendary saga of insanity and why my living room now can do cool things like this:

Or have mood lighting built in.

All the King's Men Managed to Put Humpty Dumpty Together Again

I got the bill of materials from MIT along with the PCBs, and I purchased everything needed from Parallax, Jameco, Maxim-Inc, Digikey, NewEgg, Ledtronics, McMaster-Carr, Newark, Home Depot, Menards, and RadioShack. A more detailed list can be found at http://betterthaneveryone.com/?page_id=6. Soldering everything together was easy until I added in the Max7313 LED Intensity Controllers. Each board has 16 of these and they are rather a pain. Each pin is .01" thick, which is about the size of a piece of sand. I seriously damaged two boards because of this and only managed to get one of them fixed. If memory serves, it took about a month to get all my boards soldered up.

Due to the size of my living room in my apartment, I could only build a 14-foot by 8-foot floor. Each module can support 64 individual LEDs and with 6-inch physical tiles; I needed to use 7 hardware modules. Each hardware module was set up for 4 rows of 16 lights.

Everyone Loves Doing Wiring, Right?

Wire is my enemy. It takes forever and it's hard to make it look good. My solution: 10,000-feet worth of 22-AWG solid copper wire. As you can see, I picked the wrong solution because I wasn't aware that there was a small connector that would make wiring on the board far easier by using ribbon cord. I wouldn't have had to create all my wiring by hand for a solid three months. Issues from my wiring solution have inspired me to eventually replace it all. However, I boast that I have hand-braided two miles worth of wiring.

Here is what a final board looks like wired. It isn't pretty, so I recommend looking away.

I Need Tools, Special Tools, Power Tools ... as Powerful as Humanly Possible

Power tools in mass quantity are required. For this project, I purchased a Dremel 400XPR, a DeWalt DW331 VS orbital Jig saw, and a Milwaukee 1-3/4 HP BodyGrip 5615 Router for cutting wood. I "borrowed" an old drill from my dad so I didn't need to buy a new one. I would have gotten a table saw, but I live in an apartment and really didn't have any place to hook it up.

For soldering, I used a Weller WESD51 with a .031" Long Conical Tip. I highly recommend it.

To program the firmware, I used an Amtel AVR STK500 programmer.

Built Like Lego

After getting all the tools and material, it was rather easy and fast to cut and construct the floor. Being a perfectionist, I wanted thin lines in the floor. I used Aspen wood, otherwise known as hobby wood, at ¼" x 4 x 3 feet and 2 foot lengths. Because I couldn't find anything longer, I had to live with it. Everything is interwoven and creates a rather nice, strong structure. The wiring is run underneath the boards.

However, in one weekend I managed to lay the entire frame down AND still manage to go out with friends both nights.

Generic Episode Image

Once again, the wiring caused a rather lengthy installation period. Getting the wiring taut and LEDs aligned got really annoying toward the end. Four hundred and forty eight later, I had the application running to verify that all three primary colors worked. Installing the floor took a weekend while the wiring took the better part of two weeks — even with the help of my friend Dave McNelis. Without his help I don't believe I could have met my self-imposed deadline, nor would I have knees that function after sitting cross-legged for that long.

Bullet-… I Mean Heel-Proof

The top of the floor is 3/8" thick polycarbonate. This is one of the materials used in bullet-proof glass. While the thickness I have isn't truly bullet resistant, it does provide enough protection for the task at hand. I purchased it in 4-foot by 4-foot sheets. It has held up remarkably well. The only damage was caused moving my couch. The joints are filled with silicon to prevent liquid from seeping in also.

Magic Blue Smoke

I killed two boards while attempting to get my floor ready for the party. One of the boards just wouldn't work anymore and I wasn't too happy about it. I noticed the power cord was getting a tad hot after I plugged it into my spare board. Only then did it dawn on me why the wire was hot and why the spare board refused to work. The ground and the cord were touching somewhere and I fried the boards. I opened up the wiring and saw a bright blue spark, which indicated the wires had arcing and something was ruined on both PCBs. With some awesome MIT tech support via a flurry of e-mailing, I swapped out the processor (the only one I had left) and managed to get it back up and working. I was lucky.

Final Assembly

It took an additional week of tweaking to get everything lighting up, aligned properly, and safe. After having all the LEDs, polycarbonate, and wiring honed in, I still had to do something about the software. Until this point in time, I hadn't had my software run more than two of the hardware modules at the same time. As soon as I plugged in four or more modules, the software would crash within seconds. More about that later on.

Building the Digital Disco Building Blocks

Because each hardware module can only control 64 tiles, software is required to merge the modules into a single entity. In this project, I had two core objects, FloorModule and DanceFloor.

Floor Module Is to Serial Port as Serial Port Is to COM

The FloorModule class is what communicates to each physical hardware board. It has the ability to read in a string, char array, or byte array. It also provides threading for a uniformed spread on the floor when it is being operated. What does this actually mean? Well, a synchronous call would force COM3, then COM4, then COM5 ... so this would potentially cause a jitter in the image. I wanted it all at once. Since they were threaded, it would help switch up the output to the boards. Does threading actually help it? I don't know, but I tend to listen to my inner geek, who says yes.

        #region WriteBuffer char[] Overloads
public bool WriteBuffer(char[] Buffer)
{
if (!DisableWriting)
return WriteBuffer(Buffer, true);
return true;
}

public bool WriteBuffer(char[] Buffer, bool ByThread)
{
if (!DisableWriting)
{
charBuffer = Buffer;

if (ByThread)
{
bufferThreadChar = new Thread(new ThreadStart(writeBufferThreadForCharArray));
bufferThreadChar.Start();
}
else
{
writeBufferThreadForCharArray();
}
}
return true;
}

private void writeBufferThreadForByteArray()
{
mutexLock.WaitOne();
ComPort.Write(byteBuffer, 0, byteBuffer.Length);
comPortDoneWriting();
}
#endregion

So overall this object isn't terribly complex. It gets the job done.

A Dance Floor in OOP Form

This object does all the separating and processing for the floor. It really isn't that impressive now that I think about it. It just parses the data, and then shoves it to each of the floor modules. Here is the more interesting part.

        public bool WriteDanceFloorBuffer()
{
waitToWrite(); // are all modules complete
FloorModulesCompleteWriting = 0;
for (int i = 0; i < FloorSettings.ModulesPerFloor; i++)
{
int[] tempBuffer = new int[FloorSettings.Cols * FloorSettings.RowsPerModule * 3];
Array.Copy(FloorBuffer, (FloorSettings.Cols * FloorSettings.RowsPerModule * 3 * i),
tempBuffer, 0, FloorSettings.Cols * FloorSettings.RowsPerModule * 3);
writeToModule(i, tempBuffer, ddfCommon.ControlCodes.MODULE_WRITE_CODE);
}
return true;
}

private bool writeToModule(int moduleIndex, int[] Buffer, int CommandSignal)
{
int[] intRealBuffer = MashData(Buffer, FloorSettings.OutputModuleBytes * 2);

byte[] byteBuffer = intArrayToByteArray(intRealBuffer);
return FloorModules[moduleIndex].WriteBuffer(byteBuffer);
}

public void waitToWrite()
{
while(FloorModulesCompleteWriting < FloorSettings.ModulesPerFloor)
{
System.Threading.Thread.Sleep(0);
}
}

What Is a Dance Floor Without Some Blinking?

Animation files are simple ways to shove lots of data onto the floor. All animation classes inherit off the interface IPattern.

    interface IPattern
{
int[] ProgressBuffer();
}

I know, the world's most complex interface. Here is a simple class that produces color noise, whose object's name is called "Noise."

    public class Noise : IPattern
{
private int Cols;
private int Rows;
private int[] Buffer;
private Random RandomNumber;

public Noise(int Rows, int Columns)
{
this.Cols = Columns;
this.Rows = Rows;
RandomNumber = new Random();

Buffer = new int[this.Rows * this.Cols * 3]; //RGB the array
for (int i = 0; i < Buffer.Length; i++)
{
Buffer[i] = 0;
}
}

public int[] ProgressBuffer()
{
for (int i = 0; i < Buffer.Length; i++)
Buffer[i] = RandomNumber.Next(0, 255);

return Buffer;
}
}
I Saw Gir Dancing ... on My Dance Floor

I wanted Gir, a character I find hilariously amusing from the TV show "Invader Zim," on my dance floor, and my floor is a "nonstandard" size. I'm also assuming 98 percent of the people who bought the MIT hardware were also going to have nonstandard-sized dance floors. I had to come up with a solution that scaled the animations MIT provided, and maybe some cool bells and whistles while I was at it.

I have two different styles of animation built into the application, one that reads the MIT animations and one that reads everyday image files like JPGs and GIFs. GIFs are actually what I wanted, because they can be animated and because I have an animated GIF of Gir dancing. Both animation objects inherit off the Animation object.

    public delegate void AnimationFrameLoadedHandler(object sender, AnimationFrameLoadedEventArgs e);

public class Animation : IPattern
{
public event AnimationFrameLoadedHandler AnimationFrameLoaded;
protected int[][] AnimationBuffer;
protected int counter; // used to know if counter needs to be reset :: prevents int.maxvalue from happening.
protected bool isLoaded;
protected int TotalFrameCount;

public void setAnimationBuffer(int[][] Buffer)
{
AnimationBuffer = Buffer;
isLoaded = true;
TotalFrameCount = AnimationBuffer.Length;
}

protected virtual void OnFrameLoaded(AnimationFrameLoadedEventArgs e)
{
if(AnimationFrameLoaded != null)
{
AnimationFrameLoaded(this, e);
}
}

public Animation(FileInfo file, int Rows, int Columns) : this(file.FullName, Rows, Columns) { }

public Animation(string AnimationFilePath, int Rows, int Columns) {}

public Animation() {}

public int[] ProgressBuffer()
{
if (!isLoaded)
return null;

if (counter % TotalFrameCount == 0)
counter = 0;

return AnimationBuffer[counter++];
}

public virtual void LoadFile() {}

I used the built in BitMap object along with the Graphic object to do the resizing. An MIT animation is very much the same as an animated GIF. The primary difference is that an MIT animation file must be populated first into a BitMap object pixel by pixel, frame by frame, and then resized to the final size of 28 pixels by 16 pixels. Here is the Image class, the one that handles animated GIFs and JPGs.

    public class Image : Animation
{
#region vars
private int Cols;
private int Rows;
string ImageFilePath;
#endregion

#region constructors
public Image(FileInfo file, int Rows, int Columns) : this(file.FullName, Rows, Columns) { }

public Image(string ImageFilePath, int Rows, int Columns) : base(ImageFilePath, Rows, Columns)
{
this.Cols = Columns;
this.Rows = Rows;
this.ImageFilePath = ImageFilePath;

counter = 0;
}
#endregion

public int[][] getAnimationBuffer()
{
return AnimationBuffer;
}

public override void LoadFile()
{
populateBuffer(ImageFilePath);
isLoaded = true;
}

private void populateBuffer(string ImageFilePath)
{
FileInfo fi = new FileInfo(ImageFilePath);
bool isAnimatedGif = false;
TotalFrameCount = 1;

#region populating buffer
using (System.Drawing.Image _img = new Bitmap(ImageFilePath))
{
FrameDimension _dimension = null;
#region verifiying if animated gif, if so getting frame count
if (fi.Extension.ToLowerInvariant() == ".gif")
{
_dimension = new FrameDimension(_img.FrameDimensionsList[0]);
TotalFrameCount = _img.GetFrameCount(_dimension);
isAnimatedGif = (TotalFrameCount > 1);
}
#endregion

#region creating buffer
AnimationBuffer = new int[TotalFrameCount][];

for (int i = 0; i < AnimationBuffer.Length; i++)
{
AnimationBuffer[i] = new int[this.Rows * this.Cols * 3]; // RGB that buffer
}
#endregion

for (int i = 0; i < TotalFrameCount; i++)
{
if (isAnimatedGif)
{
_img.SelectActiveFrame(_dimension, i);
}

using (Bitmap _bitmapReturn = new Bitmap(Rows, Cols))
{
using(Graphics g = Graphics.FromImage((System.Drawing.Image)_bitmapReturn))
{
g.DrawImage(_img, 0, 0, Rows, Cols);
}
_bitmapReturn.RotateFlip(RotateFlipType.Rotate270FlipNone);
populateBufferRow(_bitmapReturn, i, true);
}
}
}
#endregion
}

private void populateBufferRow(Bitmap bitmap, int index, bool FireFrameLoadEvent)
{
int offSetBuffer = 0;
for (int y = 0; y < Rows; y++)
{
for (int x = 0; x < Cols; x++)
{
Color _color = bitmap.GetPixel(x, y);

AnimationBuffer[index][offSetBuffer] = _color.R;
AnimationBuffer[index][offSetBuffer + 1] = _color.G;
AnimationBuffer[index][offSetBuffer + 2] = _color.B;

offSetBuffer += 3;
}
}
if (FireFrameLoadEvent)
OnFrameLoaded(new AnimationFrameLoadedEventArgs(index));
}
}

As you can see, this could be a lengthy, processor-intensive process. On my laptop, it can take a few seconds to fully load some of the larger files, and my laptop is a Pentium4 1.7 GHz Mobile with 1.25 GB of DDR333 RAM. While the processor isn't the fastest, this still isn't something to laugh at. So, to counter this there are some interesting hacks built into the Win32 app & — but more on that later.

Why use the Using block so heavily here? Since GDI+ objects tend to grab hold of memory a bit too aggressively, I too needed to be aggressive about their prompt destruction. The Using block allowed the image to be disposed of when I was finished with it without my manually having to call dispose. There was a flaw in one of the first releases where I didn't do this and it became a fairly decent sized memory leak.

I Can Read Binary!

.NET 2.0 had a very impressive new function. It has File.ReadAllBytes. It takes a file that is made up of bytes and then reads it in. This was necessary for the MIT-style animations. Here is my helper class:

    public class IO
{
private IO() { }
public static string ReadFile(string FilePath)
{
string contents = string.Empty;

if (File.Exists(FilePath))
contents = Encoding.Default.GetString(File.ReadAllBytes(FilePath));

return contents;
}

public static byte[] ReadFileToByteArray(string FilePath)
{
if (File.Exists(FilePath))
return File.ReadAllBytes(FilePath);

return null;
}
}

Windows Forms Are My Friends

So now that all the base objects were solid, what next? How about a decent user experience, because everyone loves command line, right? Exactly. So why not a simple Win32 form?

Once again, the entire idea for the application was speed. This was accomplished by threading as much as possible. The UI wasn't needed so, when the application wanted to start outputting information to the floor, I created a worker thread and left the UI thread alone.

        private void cmdPattern_Click(object sender, EventArgs e)
{
try
{
if (diagnosticToolStripMenuItem.Enabled)
{
continueWriting = true;

diagnosticToolStripMenuItem.Enabled = openConfigToolStripMenuItem.Enabled = false;
cmdPattern.Text = ButtonNames.STOP;
selectedAnimationStyle = Convert.ToString(cmbAnimationStyle.SelectedItem);

if (Convert.ToString(cmbAnimationStyle.SelectedItem) == _animationFile)
fileInfo = ((FileInfo)cmbAnimationList.SelectedItem);
else if (Convert.ToString(cmbAnimationStyle.SelectedItem) == _randomAnimationFile)
timerValue = Convert.ToInt32(numRandomInterval.Value) * 1000;

ThreadStart ts = new ThreadStart(StartWritingToDanceFloor);
danceFloorWriteThread = new Thread(ts);
danceFloorWriteThread.Start();
}
else
{
continueWriting = false;
diagnosticToolStripMenuItem.Enabled = openConfigToolStripMenuItem.Enabled = true;
cmdPattern.Text = ButtonNames.START;
if (randomTimer != null && randomTimer.Enabled)
{
randomTimer.Stop();
randomTimer.Enabled = false;
}
}

cmbAnimationStyle.Enabled = !cmbAnimationStyle.Enabled;
}
catch (Exception ex0)
{
DiagnosticEventArgs e1 = new DiagnosticEventArgs(ex0);
OnExceptionThrownInUI(e1);
}
}
I'll Trade You He-Man for Optimus Prime

As I stated before, some of the animation files are rather large and require a nontrivial amount of time to process. If one takes five seconds to process, I wouldn't want five seconds of downtime on the floor. While I know some people will say the approach below is stupid or incorrect, this is what I came up with and this is the first time I've had to do this style of in-process memory swap.

The primary issue is switching out the buffer while still using the buffer. To do this, I have a global Boolean called "updatingRandomAnimationFile." While it is set to true, I allow writes to the floor, or else it skips a cycle. This Boolean is only set to true after the temporary animation file is fully done loading and is ready for the array copy. This process still takes some time since it can be a rather large array. One work-around is to have two live animation objects and cycle between them.

        private void StartWritingToDanceFloor(string bufferStyle)
{
try
{
doneUpdatingUI = true;

switch (bufferStyle)
{

case _randomAnimationFile:
randomTimer = new System.Timers.Timer();
randomTimer.Interval = timerValue;
randomTimer.Elapsed += new System.Timers.ElapsedEventHandler(randomTimer_Elapsed);
randomTimer.Enabled = true;

randomAnimationFile = loadTempAnimationFileFromFileArray(DiscoDanceFloor.FloorSettings.AnimationDirectory,
DiscoDanceFloor.FloorSettings.Rows, DiscoDanceFloor.FloorSettings.Cols);
updatingRandomAnimationFile = false;

while (continueWriting)
{
if (!updatingRandomAnimationFile)
UpdateUIandWriteToFloor(randomAnimationFile.ProgressBuffer());
}
break;
}
}
catch (Exception ex0)
{
DiagnosticEventArgs e1 = new DiagnosticEventArgs(ex0);
OnExceptionThrownInUI(e1);
}
}

private FileInfo[] getFilesFromDirectory(string AnimationDirectory, string regexPattern)
{
DirectoryInfo di = new DirectoryInfo(AnimationDirectory);
string[] searchPatternArr = Regex.Split(regexPattern, @"\|");
ArrayList arrlist = new ArrayList();

foreach( string _fileExt in searchPatternArr)
arrlist.AddRange(di.GetFiles(_fileExt));

return (FileInfo[])arrlist.ToArray(typeof(FileInfo));
}

private Animation loadTempAnimationFileFromFileArray(string AnimationDirectory, int Rows, int Cols)
{
FileInfo[] fiArr = getFilesFromDirectory(AnimationDirectory, _fileExt);
return loadTempAnimationFileFromFileInfo((fiArr[randNum.Next(0, fiArr.Length - 1)]), Rows, Cols);
}

private Animation loadTempAnimationFileFromFileInfo(FileInfo fileInfo, int Rows, int Cols)
{
Console.WriteLine("Animation File: " + fileInfo.Name);
Animation ani = null;
try
{
ani = new Animation();
if (fileInfo.Extension == ".ddf")
{
ddfAnimation ddfAnim = new ddfAnimation(fileInfo, Rows, Cols);
ddfAnim.LoadFile();
ani.setAnimationBuffer(ddfAnim.getAnimationBuffer());

}
else
{
pNerd.ddf.Pattern.Image img = new pNerd.ddf.Pattern.Image(fileInfo,
DiscoDanceFloor.FloorSettings.Rows, DiscoDanceFloor.FloorSettings.Cols);
img.LoadFile();
ani.setAnimationBuffer(img.getAnimationBuffer());
}
}
catch (Exception ex0)
{
}
return ani;
}

void randomTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
randomTimer.Enabled = false;
Animation tempAnimationFile = null;

while (tempAnimationFile == null)
{
tempAnimationFile =
loadTempAnimationFileFromFileArray(DiscoDanceFloor.FloorSettings.AnimationDirectory,
DiscoDanceFloor.FloorSettings.Rows, DiscoDanceFloor.FloorSettings.Cols);
}

updatingRandomAnimationFile = true;
lock (randomAnimationFile)
{
randomAnimationFile = tempAnimationFile;
}

updatingRandomAnimationFile = false;
randomTimer.Enabled = true;
}
SUPER PHOTO HAPPY FUN PICTURE TIME!

Here are some pictures of the software in virtual floor mode to give you a better understanding of what it actually generates.

Fire

Inner5

Inner4

Ring

Gir

Cowbell

Customize That App

Last but not least is the FloorConfiguration class. You don't have to be a programmer to build the physical floor, so why should you be forced to recompile the application every time something needs to be tweaked? Not everyone has Visual Studio installed nor understands how to run it. For this reason, I created a simple XML structure to populate everything the software needs to run and talk to the floor, including disabling writing to the serial ports.

<ddf>
<configuration>
<AnimationDirectory>C:\ani\</AnimationDirectory>
<DisableComPortWrite>true</DisableComPortWrite>
<BaudRate>57600</BaudRate>
<TilesPerRow>16</TilesPerRow>
<RowsPerModule>4</RowsPerModule>
<ComPorts>
<ComPort>COM4</ComPort>
<ComPort>COM10</ComPort>
<ComPort>COM11</ComPort>
</ComPorts>
</configuration>
</ddf>

It's a simple XML structure. However, not everyone understands that XML is case sensitive, and because I don't want to support issues such as "comport is not the same as ComPort," I created a custom deserializer. I did want to use XmlSerializer to do this, which would have made my life a lot easier, but because I couldn't serialize without case sensitivity, I could not. I did still get to use XmlDocument, some simple XPath, and an XmlNodeList when building this configuration class.

public FloorConfiguration(string FilePath)
{
if (File.Exists(FilePath))
{
XmlDocument configFile = new XmlDocument();
configFile.Load(FilePath);

XmlNodeList configFileNodeList = configFile.SelectNodes("//ddf/configuration/*");
for (int i = 0; i < configFileNodeList.Count; i++)
{
switch (configFileNodeList[i].LocalName.ToLower() )
{
case "animationdirectory":
AnimationDirectory = configFileNodeList[i].InnerText;
break;
case "baudrate":
BaudRate = Convert.ToInt32(configFileNodeList[i].InnerText);
break;
case "tilesperrow":
TilesPerRow = Convert.ToInt32(configFileNodeList[i].InnerText);
break;
case "rowspermodule":
RowsPerModule = Convert.ToInt32(configFileNodeList[i].InnerText);
break;
case "disablecomportwrite":
bool temp = false;
bool.TryParse(configFileNodeList[i].InnerText, out temp);
DisableComPortWrite = temp;
break;
case "comports":
if (configFileNodeList[i].HasChildNodes)
{
XmlNodeList _comPortsXmlNodeList = configFileNodeList[i].ChildNodes;
ModulesPerFloor = _comPortsXmlNodeList.Count;
_comPorts = new string[ModulesPerFloor];
for (int j = 0; j < ModulesPerFloor; j++)
{
_comPorts[j] = _comPortsXmlNodeList[j].InnerText;
}
}
break;
default:
throw new ArgumentOutOfRangeException("configFileNodeList[i].LocalName",
configFileNodeList[i].LocalName, "Value not supported");
}
}
}
else
throw new FileNotFoundException("XML configuration file could not be found", FilePath);
}

The class also does a bunch of simple calculations.

Ready For Prime Time? … Doubtful

After almost six months of on and off again development on the software, it was finally time to let it run nonstop for over eight hours at a party. Now this normally isn't a problem; however, I only got the floor to a state where I could actually run the software outside of a theoretical environment a week before the party, and I still needed to do a ton of additional work on the physical part of the floor. The software worked fine and was stress tested without doing physical hardware writes.

There were two major bugs that had to be resolved before the floor would work in a party situation for more than five minutes. The first was a serious bug. The firmware for the boards would send out error codes if something bad happens such as losing the ability write or read a sensor. The issue was reading the success code would actually fail itself. This causes everything to go poorly and cause the application to crash. My solution was if a board failed, I'd just reboot it. In the 2.0 firmware, this does causes a flicker effect on the dance floor. No one noticed this so I think I'm in the clear for now. Plus the floor has such a high frame rate, so I'm assuming most people just thought it was part of the animation. My friends were joking about seizures.


        if (responseFromModule != ddfCommon.ControlCodes.SUCCESS_CODE)
{
char[] reinitBit = new char[1];
reinitBit[0] = Convert.ToChar(ddfCommon.ControlCodes.REINIT_CODE);

this.WriteBuffer(reinitBit, false);
}

The hard thing about fixing the bug was that I had to be actually hooked into the floor. Coding on the floor is not terribly fun.

The second bug occurred when an animation failed to load while in the Random Animation mode. This was rather simple; I just attempted to load Animations until one actually loaded. A simple while loop took care of it.

Party!

After a massive amount of work, it was time to show it off. Due to some of the comments people made on Slashdot when MIT released their video, I didn't post all the pictures or videos. People can make fun of me all they want for building this because I'll insult them right back. That said, I got an electric blue suit for the party and attempted to make disco lemonade but ended up making my own weird mixed drink.

Generic Episode Image

One interesting lesson I learned from the first party was to target the people that don't know you and challenge them to a dance-off. With the proper amount of trash talking, you'll win every time without even busting a move. This is good for me since I can't dance.

The ironic thing was most people thought I was joking about building the floor. I expect Party 2.0 to be far more of a blast with a lot more people.

Now What? We Rebuild It Faster and Stronger

I want to recode the entire application. This application was never supposed to grow into what it did. I believe a few of the smaller issues with the application itself stem from the Beta 2 to RTM of Visual Studio 2005 conversion. They still linger and cause me some headaches. I also don't believe this current framework is extendable for the few new features I want to add in. Playlist support will be the biggest thing I want to add, along with some music tie-in visualizations. The current version does have some DirectSound in it but not what I really want. The performance gains are also things that are high on the to-do list. It consumes an ungodly amount of the processor. When loading some of the larger animations, you notice a slight slowdown on the floor. This should never happen. The floor should always take priority. I'm thinking about maybe getting a Windows Mobile 2005 cell phone that could use some very nice Web services. This will allow me to control the floor from my pocket and prevent my people from messing with my laptop since someone actually did close the application during the party. Why someone would close the program called "Disco Dance Floor Application" is beyond me.

Like I said, it needs some refactoring. Coding this over six months instead of one or two big pushes caused the problem. I blame Christmas and my Xbox 360 for this.

The Chuck Norris of Conclusions

So I managed to build a disco dance floor in 10 months. Now I need to find something cooler to trump myself with. There were lots of talks about integrating games. I haven't done that yet and I do love to play them, so that is an excellent possibility. Another project idea that I'm kicking around is a pressurized, automated bartender with some ungodly amount of drink possibilities.

Tags:

Fun, Hardware, Disco

The Discussion

  • User profile image
    Kshitiz Chandra

    really man... you made my mind towards this type of work..

    Amazing

    this really gonna help me in future..

    Thanks

    Kshitiz Chandra

    kshitizchandra@yahoo.com

  • User profile image
    Nick

    Ok, stupid question: I just found the videos on your site. Really a great piece of work!

    Greetz

    Nick

  • User profile image
    Nick

    Wow! Awesome piece of work! Do you have any videos of it online? I'd really love to see it in action Smiley

    Are there any further instructions round on the web for not so technically skilled people, like me?

    Best regards from Germany

    Nick

  • User profile image
    gadgetrich

    One cool project. My wife and I have a disco in our basement and this would be a perfect addition.

  • User profile image
    maxicube

    hey this looks pretty cool I might make it when i have:

    1 - skills

    2 - time

    3 - money

    4 - a party

    5 - to be older (i'm 14)

    lol fun project , se yaz

  • User profile image
    Dan Fernandez's Blog

    Jeff Sandquist is looking for potential Microsoft employees who wouldn't mind showing how they've pimped

  • User profile image
    Al Thomson

    hey, dude, i know how to cable wires quick, but ribbon cable works too, anyways, just wrap them all at both ends, tie one end to like a table leg or something, plug the other end into a drill, spin, and pull tight, the cable will not come apart by itself.  But ribbon cable also works.

  • User profile image
    Noticias externas

    Maker Faire 2008 is coming up fast and Coding4Fun was wondering what we should whip up. Comment or email

  • User profile image
    Stephen Leahey

    Did you actually use the Sensors that can be fitted to the MIT board? or did you go without?

  • User profile image
    Clint

    @Stephen Leahey:  I did a few tests with them, but I ran into budget and time issues.  Home made ones took too long to make, prebuilt sensors cost too much.

    I'm not sure how much support the MIT software even has for the sensor support to be honest.  When I was checking out what they did, I didn't see too much in the way of this.

  • User profile image
    Mr Tyler

    WOW !!!!   What a floor and project.  Great Job Bro Many Props to you and your FLOOR.

  • User profile image
    Clint

    I'm delighted to announce that Clint Rutkas has joined the Coding4Fun family as our dedicated "Newsmaster

  • User profile image
    jolly pirate doughnuts

    you should have just used colored christmas lights instead of spending a boat load on those LED's

  • User profile image
    Clint

    Christmas lights are a bit too fragile on a few fronts.  Turning on a christmas bulb light 20 times a second will break it pretty quick.  Plus if one of those breaks, you run the risk of glass, can't really break an LED.

    I can see them working but I stand behind LEDs.  Look at high end commercial displays, they are all LEDs too.

  • User profile image
    annieliseh liege shmmith

    uauuu !!! viocÊ é demais contruir   uma pista de danca com um tapete nao é pra qualquer um!

  • User profile image
    lostandfoun​d.ro &raquo; Blog Archive &raquo; Disco floor
  • User profile image
    Chelsea

    I love you.

    And your dance floor.

  • User profile image
    Karl of Warlite Security Ltd. London

    I wish I was you so i could make me one! Sad

  • User profile image
    angel

    hey do u have any video? please I like to see how work it

  • User profile image
    Clint
  • User profile image
    Adam

    I would love to have one of these to put on an art car for Burning Man, but I have nowhere near the technical savvy required to pull it off. I wonder if I'd be able to commission you to make one? adambroder at hotmail dot com

  • User profile image
    Wizzboy

    This project inspired me to do the same. Instead of a floor, I've built a giant LED display, 8'x8'.

    http://www.wizzboy.ca/wizzwall

Add Your 2 Cents