| @ -0,0 +1,422 @@ | |||
| It is fun to find an interesting use for old technology. Being someone who has | |||
| tons of old floppy drives and loves music, I decided to turn my old floppy | |||
| drives into an orchestra. I’m not sure where I first learned about musical | |||
| floppy drives, however, there are thousands of videos on YouTube. | |||
| This project first started over a year ago when I connected two floppy drives to | |||
| a Raspberry Pi to play the Star Wars theme. | |||
| <iframe width="560" height="315" | |||
| src="https://www.youtube.com/embed/wcnUvPMpqjA" frameborder="0" allow="autoplay; | |||
| encrypted-media" allowfullscreen\></iframe\> | |||
| Although this was fun, there was hardly any software for playing different music | |||
| on the Raspberry Pi. I managed to hack the software to also play Jingle Bells, | |||
| however, that took hours to input just a single song. | |||
| A half a year ago I got an Arduino board and built a new and improved floppy | |||
| drive orchestra. | |||
| <iframe width="560" height="315" | |||
| src="https://www.youtube.com/embed/qwbcrR-6dTU" frameborder="0" allow="autoplay; | |||
| encrypted-media" allowfullscreen\></iframe\> | |||
| What is nice about using an Arduino, is that the software used is more | |||
| developed. Unlike the program I used with the Raspberry Pi, the software used | |||
| with the Arduino allows the songs to be loaded via a MIDI file. | |||
| That brings us up to the present. My previous 8 floppy drive orchestra was not | |||
| transportable and took up a ton of space. When I decided to present this project | |||
| at Imagine RIT with RITLUG, I knew I had to rebuild my orchestra to be portable | |||
| and compact. | |||
| There are plenty of tutorials online demonstrating how to build floppy | |||
| orchestras, but, many of them are incomplete. Since this was my third time | |||
| building a floppy orchestra, I decided to log my process and make a complete | |||
| tutorial for my blog. | |||
| Hardware | |||
| ======== | |||
| - 10 Floppy Drives | |||
| - Arduino Uno | |||
| - LOTS of Ribbon Cables | |||
| - 1 Old Power Supply | |||
| - Hot Glue | |||
| - Solder | |||
| In in addition to these materials, you will also want something to mount your | |||
| floppy drives to. I decided to use 2x2’s and half inch screws because they are | |||
| cheep and I had some laying around my house. | |||
| Assembly | |||
| ======== | |||
| The first thing you should do is secure your floppy drives. | |||
|  | |||
| Now, it is time to power the floppy drives and turn them on. To make the floppy | |||
| drives turn on, you need to connect pins 11 and 12 on your floppy drives | |||
| together. You can easily do this with a single ribbon cable. | |||
|  | |||
|  | |||
| You can use an old power supply for this project. Since we are only using the | |||
| stepper motor in the floppy drives, you only need to supply 5v. Each floppy | |||
| drive has four power connectors, the middle two pins are ground, and the right | |||
| pin is 5v and the left pin 12v. Since I had ten floppy drives to power, I used a | |||
| bread board to avoid excessive soldering. | |||
| To turn on a power supply you simply connect the green wire to any ground wire. | |||
| If you want to get fancy, you can solder on a switch, however, must people just | |||
| jam a paper clip into the motherboard connector. | |||
| FWI: The red wires in your power supply are 5v and the black wires are ground. | |||
| **Warning**: Do not draw all your power from the power supply via a single | |||
| ribbon cable, it will melt. Ribbon cables have low gauge and are not meant for | |||
| high wattages. It is good idea to use multiple 5v lines from your power supply | |||
| and make sure that nowhere in your wiring is all the voltage going through a | |||
| single ribbon cable. | |||
|  | |||
| If you have done everything correct up to this point, you will see the green | |||
| lights on the floppy drives turn on when the power supply is running. | |||
| Now we need to connect the pins of the floppy drive to the Arduino. | |||
| Personally, I started by connecting pin 19 on the floppy drive to the ground pin | |||
| on the Arduino. Again, I used a bread board to make the connections easier. | |||
|  | |||
|  | |||
| Next, we need to wire the step and direction pins of the floppy drives to the | |||
| Arduino. | |||
|  | |||
| Connect direction pin 18 on the floppy drive to pin 3 of the Arduino and step | |||
| pin 20 to pin 2 of the Arduino. For additional floppy drives you follow the same | |||
| pattern. For example, the next drive would be floppy pin 18 to Arduino pin 5 and | |||
| floppy pin 20 to Arduino pin 4. If you are using something other than an Arduino | |||
| Uno board this will potentially be different. We are using these specific pins | |||
| on the Arduino because they correspond to this specific program. | |||
|  | |||
| While making this, I had 4 sets of pins (8 total) connected to the Arduino, | |||
| however, I have 10 floppy drives in total. I wired two sets of three drives and | |||
| two sets of two drives together on the same Arduino “channel”. This makes the | |||
| wiring easier and makes the sound better. Not all floppy drives sound the same, | |||
| by pairing drives together you get a richer sound. Since I want to present this | |||
| live, it also makes it louder for the audience. | |||
| This is a ton of wiring! After you verify that the drives are working properly, | |||
| I would strongly recommend using hot glue to secure ribbon cables to the drives. | |||
| That’s it. Now your floppy drives should be good to go. | |||
| Software | |||
| ======== | |||
| This is the section where most other tutorials fall short. Please follow along | |||
| carefully. First, have the following installed on your computer. | |||
| - Arduino Software | |||
| - NetBeans | |||
| - JDK 1.8 – or higher | |||
| Next download | |||
| [timer1](https://code.google.com/archive/p/arduino-timerone/downloads) and | |||
| [Moppy](https://github.com/SammyIAm/Moppy). Install timer one by extracting the | |||
| files and copying the folder timer1 into the libraries folder under your root | |||
| Arduino directory. | |||
| Open up your Arduino software and paste the following code in the editor: | |||
| ``` | |||
| #include <TimerOne.h> | |||
| boolean firstRun = true; // Used for one-run-only stuffs; | |||
| //First pin being used for floppies, and the last pin. Used for looping over all pins. | |||
| const byte FIRST_PIN = 2; | |||
| const byte PIN_MAX = 17; | |||
| /*NOTE: Many of the arrays below contain unused indexes. This is | |||
| to prevent the Arduino from having to convert a pin input to an alternate | |||
| array index and save as many cycles as possible. In other words information | |||
| for pin 2 will be stored in index 2, and information for pin 4 will be | |||
| stored in index 4.*/ | |||
| /*An array of maximum track positions for each step-control pin. Even pins | |||
| are used for control, so only even numbers need a value here. 3.5" Floppies have | |||
| 80 tracks, 5.25" have 50. These should be doubled, because each tick is now | |||
| half a position (use 158 and 98). | |||
| */ | |||
| byte MAX_POSITION[] = { | |||
| 0,0,158,0,158,0,158,0,158,0,158,0,158,0,158,0,158,0}; | |||
| //Array to track the current position of each floppy head. (Only even indexes (i.e. 2,4,6...) are used) | |||
| byte currentPosition[] = { | |||
| 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; | |||
| /*Array to keep track of state of each pin. Even indexes track the control-pins for toggle purposes. Odd indexes | |||
| track direction-pins. LOW = forward, HIGH=reverse | |||
| */ | |||
| int currentState[] = { | |||
| 0,0,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW | |||
| }; | |||
| //Current period assigned to each pin. 0 = off. Each period is of the length specified by the RESOLUTION | |||
| //variable above. i.e. A period of 10 is (RESOLUTION x 10) microseconds long. | |||
| unsigned int currentPeriod[] = { | |||
| 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 | |||
| }; | |||
| //Current tick | |||
| unsigned int currentTick[] = { | |||
| 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 | |||
| }; | |||
| //Setup pins (Even-odd pairs for step control and direction | |||
| void setup(){ | |||
| pinMode(13, OUTPUT);// Pin 13 has an LED connected on most Arduino boards | |||
| pinMode(2, OUTPUT); // Step control 1 | |||
| pinMode(3, OUTPUT); // Direction 1 | |||
| pinMode(4, OUTPUT); // Step control 2 | |||
| pinMode(5, OUTPUT); // Direction 2 | |||
| pinMode(6, OUTPUT); // Step control 3 | |||
| pinMode(7, OUTPUT); // Direction 3 | |||
| pinMode(8, OUTPUT); // Step control 4 | |||
| pinMode(9, OUTPUT); // Direction 4 | |||
| pinMode(10, OUTPUT); // Step control 5 | |||
| pinMode(11, OUTPUT); // Direction 5 | |||
| pinMode(12, OUTPUT); // Step control 6 | |||
| pinMode(13, OUTPUT); // Direction 6 | |||
| pinMode(14, OUTPUT); // Step control 7 | |||
| pinMode(15, OUTPUT); // Direction 7 | |||
| pinMode(16, OUTPUT); // Step control 8 | |||
| pinMode(17, OUTPUT); // Direction 8 | |||
| Timer1.initialize(RESOLUTION); // Set up a timer at the defined resolution | |||
| Timer1.attachInterrupt(tick); // Attach the tick function | |||
| Serial.begin(9600); | |||
| } | |||
| void loop(){ | |||
| //The first loop, reset all the drives, and wait 2 seconds... | |||
| if (firstRun) | |||
| { | |||
| firstRun = false; | |||
| resetAll(); | |||
| delay(2000); | |||
| } | |||
| //Only read if we have | |||
| if (Serial.available() > 2){ | |||
| //Watch for special 100-message to reset the drives | |||
| if (Serial.peek() == 100) { | |||
| resetAll(); | |||
| //Flush any remaining messages. | |||
| while(Serial.available() > 0){ | |||
| Serial.read(); | |||
| } | |||
| } | |||
| else{ | |||
| currentPeriod[Serial.read()] = (Serial.read() << 8) | Serial.read(); | |||
| } | |||
| } | |||
| } | |||
| /* | |||
| Called by the timer inturrupt at the specified resolution. | |||
| */ | |||
| void tick() | |||
| { | |||
| /* | |||
| If there is a period set for control pin 2, count the number of | |||
| ticks that pass, and toggle the pin if the current period is reached. | |||
| */ | |||
| if (currentPeriod[2]>0){ | |||
| currentTick[2]++; | |||
| if (currentTick[2] >= currentPeriod[2]){ | |||
| togglePin(2,3); | |||
| currentTick[2]=0; | |||
| } | |||
| } | |||
| if (currentPeriod[4]>0){ | |||
| currentTick[4]++; | |||
| if (currentTick[4] >= currentPeriod[4]){ | |||
| togglePin(4,5); | |||
| currentTick[4]=0; | |||
| } | |||
| } | |||
| if (currentPeriod[6]>0){ | |||
| currentTick[6]++; | |||
| if (currentTick[6] >= currentPeriod[6]){ | |||
| togglePin(6,7); | |||
| currentTick[6]=0; | |||
| } | |||
| } | |||
| if (currentPeriod[8]>0){ | |||
| currentTick[8]++; | |||
| if (currentTick[8] >= currentPeriod[8]){ | |||
| togglePin(8,9); | |||
| currentTick[8]=0; | |||
| } | |||
| } | |||
| if (currentPeriod[10]>0){ | |||
| currentTick[10]++; | |||
| if (currentTick[10] >= currentPeriod[10]){ | |||
| togglePin(10,11); | |||
| currentTick[10]=0; | |||
| } | |||
| } | |||
| if (currentPeriod[12]>0){ | |||
| currentTick[12]++; | |||
| if (currentTick[12] >= currentPeriod[12]){ | |||
| togglePin(12,13); | |||
| currentTick[12]=0; | |||
| } | |||
| } | |||
| if (currentPeriod[14]>0){ | |||
| currentTick[14]++; | |||
| if (currentTick[14] >= currentPeriod[14]){ | |||
| togglePin(14,15); | |||
| currentTick[14]=0; | |||
| } | |||
| } | |||
| if (currentPeriod[16]>0){ | |||
| currentTick[16]++; | |||
| if (currentTick[16] >= currentPeriod[16]){ | |||
| togglePin(16,17); | |||
| currentTick[16]=0; | |||
| } | |||
| } | |||
| } | |||
| void togglePin(byte pin, byte direction_pin) { | |||
| //Switch directions if end has been reached | |||
| if (currentPosition[pin] >= MAX_POSITION[pin]) { | |||
| currentState[direction_pin] = HIGH; | |||
| digitalWrite(direction_pin,HIGH); | |||
| } | |||
| else if (currentPosition[pin] <= 0) { | |||
| currentState[direction_pin] = LOW; | |||
| digitalWrite(direction_pin,LOW); | |||
| } | |||
| //Update currentPosition | |||
| if (currentState[direction_pin] == HIGH){ | |||
| currentPosition[pin]--; | |||
| } | |||
| else { | |||
| currentPosition[pin]++; | |||
| } | |||
| //Pulse the control pin | |||
| digitalWrite(pin,currentState[pin]); | |||
| currentState[pin] = ~currentState[pin]; | |||
| } | |||
| // | |||
| //// UTILITY FUNCTIONS | |||
| // | |||
| //Not used now, but good for debugging... | |||
| void blinkLED(){ | |||
| digitalWrite(13, HIGH); // set the LED on | |||
| delay(250); // wait for a second | |||
| digitalWrite(13, LOW); | |||
| } | |||
| //For a given controller pin, runs the read-head all the way back to 0 | |||
| void reset(byte pin) | |||
| { | |||
| digitalWrite(pin+1,HIGH); // Go in reverse | |||
| for (byte s=0;s<MAX_POSITION[pin];s+=2){ //Half max because we're stepping directly (no toggle) | |||
| digitalWrite(pin,HIGH); | |||
| digitalWrite(pin,LOW); | |||
| delay(5); | |||
| } | |||
| currentPosition[pin] = 0; // We're reset. | |||
| digitalWrite(pin+1,LOW); | |||
| currentPosition[pin+1] = 0; // Ready to go forward. | |||
| } | |||
| //Resets all the pins | |||
| void resetAll(){ | |||
| // Old one-at-a-time reset | |||
| //for (byte p=FIRST_PIN;p<=PIN_MAX;p+=2){ | |||
| // reset(p); | |||
| //} | |||
| // New all-at-once reset | |||
| for (byte s=0;s<80;s++){ // For max drive's position | |||
| for (byte p=FIRST_PIN;p<=PIN_MAX;p+=2){ | |||
| digitalWrite(p+1,HIGH); // Go in reverse | |||
| digitalWrite(p,HIGH); | |||
| digitalWrite(p,LOW); | |||
| } | |||
| delay(5); | |||
| } | |||
| for (byte p=FIRST_PIN;p<=PIN_MAX;p+=2){ | |||
| currentPosition[p] = 0; // We're reset. | |||
| digitalWrite(p+1,LOW); | |||
| currentState[p+1] = 0; // Ready to go forward. | |||
| } | |||
| } | |||
| ``` | |||
|  | |||
| Now you can upload this script to your Arduino. | |||
|  | |||
| If you get any errors about your COM port, make sure that your Arduino is set to | |||
| listen on COM1 under the Device manager –Windows only. | |||
| If that works, you can now open Moppy through Netbeans and start playing with | |||
| your musical floppy drives. | |||
|  | |||
| For a great package of MIDI music to use check out | |||
| [MrSolidSnake](https://github.com/coon42/Floppy-Music--midis-). Not all MIDI | |||
| songs work well with floppy drives. Songs with too many tracks obviously won’t | |||
| work well. Songs with high notes will sound terrible on floppy drives since they | |||
| will grind their motors. Also, long notes don’t sound good because the floppy | |||
| drives just spin back and forth. MrSolidSnake did a wonderful job at compiling a | |||
| bunch of MIDI files that work’s well with floppy drives. | |||
| I hope that this tutorial was helpful. | |||