The Arduino is an open source Microcontroller and "IDE" (Independant Development Evironment)
That means it's hardware board and a code compiler that you can use to program in your own code and load it to the chip. It sounds scary, but really it's not that bad.. LOL. In reality, if you are just using code and not writing it, it could be as easy as:
Load the software
Plug in the Arduino
Install the drivers (Like any other USB device)
Run the software
Paste the code
Hit the "Upload" button
Really... that's it.
Right now I am using an Arduino Duemilinove (I bet I misspelled that) which is too large for a Saber, but I will be using an Arduino Nano... it's one of the smaller models of the Arduino at .7" x 1.7" (But not the smallest.. there's a smaller one but it doesn't plug in to USB so its harder to use and I'm lazy.)
Anyway... on to what's going on.
Over at the Open Source Saber Project (OSSP) we are working on creating an open source option for soundcards. Someday, that hopefully means we will have a full on sound card that people can make themselves using off the shelf electronics and things like the ARduino. (Or a PIC, or.... whatever works really.)
We aint there (yet). LOL.
For now we do have some usefull bit of kit. Using the Arduino as a "bridge" between an existing soundcard (a Joe Jedi or MR616 in this case) and a PWM controlled driver (Or 4 of them, in this case) we are able to use the Arduino to kind of manage things... like the light levels of 4 different LED dies on a LEDengin 10W RGBA LED.
Here's a YouTube video of it in action....
Hows it work? Well, the PWM outputs of the Arduino hook to the PWM inputs on the driver. You can set 255 levels for each PWM pin so you have 255 levels of control of the brightness for each LED Die. 255x255x255 = 16581375 colors. (Not counting the Amber LED...I'm just using that to flash.)
Before you get too color happy... LOL, I am using 4 levels on each color. LED's aren't even close to being "linear" and the brightness steps aren't even. Still, you could easily do 10 or more per color... I just chose 4 (plus off) because that gives me ~54 or so colors and that's about all I wanted. I really didn't want to click 16 million times to cycle my colors.
The PWM output would work on ANY driver that takes a PWM signal... the new ones at TCSS, good old Buckpucks... whatever. The code might need to be tweaked to match the output to whatever your flavor of PWM driver is.
Anyway... the soundcard side of it. The Arduino is hooked to the soundcard with 3 wires.
1) On/Off
2) Sith/Jedi sound selector
3) Impact sensor
The first is one side of the main switch,... the Arduino sends 5V to it and the ingnition sounds and the card turns "on"
The second is to one of the pads where the color select switch was. The Arduino shoots 5V at it and it changes to the sith Soundbank.
Lastly, one wire of the impact sensor gets 5v whenever the sensor is triggered. That goes to the Joe Jedi board as well as the Arduino, which reads the signal so it knows when a clash is happening.
That's about it really... the rest is just code that glues it together.
Speaking of the Glue.... here's the code. Copy/mangle/adapt/adopt/modify it at your leisure.... (Note that it is by no means complete, effecient, clean or even the final version I will be using in my saber.... but it works and it's a starting point.)
Don't blame me if your refrigerator comes to life and eats off your big toe while you sleep.......
/* Lightsaber test sketch controlling a "Joe Jedi" SW616 sound card
Modified by Troy Ollom
Feb. 5, 2010
Original "Four button sketch" by Jeff Saltzman.
To keep a physical interface as simple as possible, this sketch demonstrates generating three output events from a single push-button.
1) Click: rapid press and release
2) Double-Click: two clicks in quick succession
3) Press and Hold: holding the button down
*/
#include <EEPROM.h> // Set up the EEPROM library so I can read/write from permenant memory
#define buttonPin 2 // analog input pin to use as a digital input
#define redPin 3 // digital output pin for Red LED 1
#define sndOnPin 4// Digital out to turn on soundcard
#define grnPin 5 // digital output pin for Green LED 2
#define bluPin 6// digital output pin for Blue LED 3
#define impactPin 7// Input pin for impact sensor
#define sndSelectPin 8// Digital out to select sound True is Sith, False is Jedi
#define flashPin 9 // FoC LED Pin
// RGB LED variables
byte rgbArray [54][3]={//RGB Array 3 wide 54 tall, stores RGB values (There's gotta be a prettier way to do this!)
{100,0,0},{100,15,0},{100,40,0},{100,60,0},{100,100,0}
,
{60,100,0},{40,100,0},{15,100,0},{0,100,0},{0,100,15}
,
{0,100,40},{0,100,60},{0,100,100},{0,60,100},{0,40,100}
,
{0,15,100},{0,0,100},{15,0,100},{40,0,100},{60,0,100}
,
{100,0,100},{100,0,60},{100,0,40},{100,0,15}
,
//Hue one, a lighter starts at Array pointer 40100
{100,15,15},{100,40,15},{100,60,15},{100,100,15}
,
{60,100,15},{40,100,15},{15,100,15},{15,100,40}
,
{15,100,60},{15,100,100},{15,60,100},{15,40,100}
,
{15,15,100},{40,15,100},{60,15,100},{100,15,100}
,
{100,15,60},{100,15,40}
,
//Hue two, Lightest starts at Array pointer 10040
{100,40,40},{100,60,40},{100,100,40},{60,100,40}
,
{40,100,40},{40,100,60},{40,100,100},{40,60,100}
,
{40,40,100},{60,40,100},{100,40,100},{100,40,60}
,
};
byte rgbFactor = 100; // Value to factor by.. IE, 100 would provide 100 steps 255 is Max
byte randFlicker = 41;
int fadeValue = 0; //tracks the current state of the fade value
boolean fadeDirection = false; // in this case, true means fade up, false means fade down
byte colorMode = 0; // Stores the current color
byte sndMode = 0; // Store the current sound mode
byte menuMode = 0; // Stores the current menuMode
byte impactRead = 0; // Place to store the last read of the impactPin
byte flashOn = 0; // Place to store the count for flashOn
//=================================================
// RGB Timing Variables
long rgbMillis = 0; // Store millis to keep track of the last time rgb was updated
int rgbInterval = 30; // How often to update the rgb values
//==========================================
void setup()
{
// Set button input pin
pinMode(buttonPin, INPUT);
digitalWrite(buttonPin, HIGH );
pinMode(impactPin, INPUT);
digitalWrite(impactPin, LOW);
// Set LED output pins
pinMode(redPin, OUTPUT);
digitalWrite(redPin, 0);
pinMode(grnPin, OUTPUT);
digitalWrite(grnPin, 0);
pinMode(bluPin, OUTPUT);
digitalWrite(bluPin, 0);
pinMode(flashPin, OUTPUT);
digitalWrite(flashPin, 0);
colorMode = EEPROM.read(0); // Read in the stored colorMode
sndMode = EEPROM.read(1); // Read in the stored sndMode
digitalWrite (sndSelectPin,sndMode); // Use the stored sndMode to set the sndSelectPin
}
void loop()
{
// Get button event and act accordingly
int b = checkButton();
if (b == 1) clickEvent();
if (b == 2) doubleClickEvent();
if (b == 3) holdEvent();
impactRead = digitalRead (impactPin); // Read the impact pin and dump it in to impactRead
if (fadeDirection == true && impactRead == HIGH && flashOn == 0){ // Three way check, see if the LED's are on if we have an impact and that we aren't already in impact mode//
flashOn = 10; // if all those conditions are true set flashOn to 10. flashOn * 30 MS = total flash time
}
// Main LED ramp/fade routine
if (millis () - rgbMillis >= rgbInterval){ // if it's been 30 ms since last time, lets update the LED
rgbMillis = millis (); // reset so we know the last time
Serial.println (colorMode);
if (fadeDirection == true){ // This is gonna happen if we are fading UP
if (fadeValue <=250) { // If fadeValue is less than or equalls 250
fadeValue = fadeValue +5; // Lets add 6 to it
}
else {
randFlicker = random(30);
fadeValue = 255 - randFlicker; // or else if it's already there, just make it 255 and flicker it there
}
}
if (fadeDirection == false){ //This is gonna happen if we are fading down
if (fadeValue >=8) { // if its over or = to 7
fadeValue = fadeValue - 8; // subtract 7 from it
}
else {
fadeValue = 0; // if it's less than 5 hold it at zero
}
}
if (flashOn >= 1){ // If we have a flashOn value lets do some flashing
analogWrite (flashPin, 255 -randFlicker * 8); // Write the flashPin high and 9X the flicker for crazy flicker madness
flashOn --; // Decrement by one so we only do this 10 times in a row
}
else{
analogWrite (flashPin, 0);
}
updateLED ();
}
}
// Main LED update routine
void updateLED (){
analogWrite(redPin, fadeValue * rgbArray[colorMode][0] / rgbFactor); //Now that the math has been done, update the LED
analogWrite(bluPin, fadeValue * rgbArray[colorMode][1] / rgbFactor);
analogWrite(grnPin, fadeValue * rgbArray[colorMode][2] / rgbFactor);
}
//=================================================
// Events to trigger by click and press+hold
void clickEvent() {
if (menuMode == 0){ //0 is normal on/off saber mode
fadeDirection = true; // Set fadeDirection and let the Mian ramp routine take care of it. True fades UP
digitalWrite (sndOnPin,true); //Turns on the soundcard
}
else
if (menuMode == 1){ //1 is Color setting mode
colorMode++; // Add 1 to the colormode
if (colorMode >=54){ // If colorMode is over 53 loop it back to 0
colorMode = 0;
}
updateLED (); // Update the LED with the current colorMode selected
}
else
if (menuMode == 2){
sndMode=0;
digitalWrite (sndSelectPin,sndMode); // Set the soundbank. 0 is the Jedi soundbank
delay (10); // A little wait to let it get active
digitalWrite (sndOnPin,true); // Turn the sound card on (briefly) as an audible indicator
digitalWrite (redPin,0); // Turn off active colors so you can see the Blue flash
digitalWrite (grnPin,0);
digitalWrite (bluPin,0); // Flash the Blue LED a couple of times as a visual indicator
delay (300);
digitalWrite (bluPin,1);
delay (300);
digitalWrite (bluPin,0);
delay (300);
digitalWrite (bluPin,1);
delay (300);
digitalWrite (bluPin,0);
digitalWrite (sndOnPin,false); // Turn off the sound card
}
}
void doubleClickEvent() {
if (menuMode == 0){ //0 is normal on/off saber mode
fadeDirection = false; // Set fadeDirection and let the Mian ramp routine take care of it. False fades DOWN
digitalWrite (sndOnPin,false); // Turns off the soundcard
}
if (menuMode == 1){
if (colorMode <= 23){
colorMode=24;
}
else if (colorMode <=41){
colorMode=42;
}
else {
colorMode=0;
}
}
if (menuMode == 2){ //2 is Sound setting mode.
sndMode = 1;
digitalWrite (sndSelectPin,sndMode); // Set the soundbank. 1 is the Sith soundbank
delay (10); // A little wait to let it get active
digitalWrite (sndOnPin,true); // Turn the sound card on (briefly) as an audible indicator
digitalWrite (redPin,0); // Turn off active colors so you can see the Red flash
digitalWrite (grnPin,0); // Flash the Red LED a couple of times as a visual indicator
digitalWrite (bluPin,0);
delay (300);
digitalWrite (redPin,1);
delay (300);
digitalWrite (redPin,0);
delay (300);
digitalWrite (redPin,1);
delay (300);
digitalWrite (redPin,0);
digitalWrite (sndOnPin,false); // Turn off the sound card
}
}
void holdEvent() { //Triggered with 3 second press hold
menuMode++; //increment menuMode
if (menuMode >= 3){ //If it's over 2 loop it back to 0
menuMode = 0;
}
if (menuMode == 0){
fadeDirection = false;
digitalWrite (sndOnPin,false); // Turn off the sound card
digitalWrite (redPin,0);
digitalWrite (grnPin,0);
digitalWrite (bluPin,0);
EEPROM.write(1,sndMode); // Save the selected sndMode... good place to do it sice we are just going to delay anyway and it takes a few MS
delay (100);
digitalWrite (redPin,1); // fast-Flash red pin to show we are going back to normal.
delay (100);
digitalWrite (redPin,0);
digitalWrite (grnPin,1);
delay (100);
digitalWrite (grnPin,0);
digitalWrite (bluPin,1);
delay (100);
digitalWrite (bluPin,0);
}
if (menuMode == 1){ // Cycle through R G and B to show we are in color mode
digitalWrite (sndOnPin,false); // Turn off the sound card
digitalWrite (redPin,0); //All off but red pin
digitalWrite (grnPin,0);
digitalWrite (bluPin,0);
delay (300);
digitalWrite (grnPin,0); //All off but red pin
digitalWrite (bluPin,0);
digitalWrite (redPin,1);
delay (300);
digitalWrite (redPin,0); // A little Green pin love
digitalWrite (grnPin,1);
delay (300);
digitalWrite (grnPin,0); // How 'bout some for the Blue already!
digitalWrite (bluPin,1);
delay (300);
digitalWrite (redPin,0); //All off
digitalWrite (grnPin,0);
digitalWrite (bluPin,0);
fadeDirection = true; //Ramp up that LED
}
if (menuMode == 2){
digitalWrite (sndOnPin,false); // Turn off the sound card
fadeDirection = false;
fadeValue = 0;
digitalWrite (redPin,0);
digitalWrite (grnPin,0);
digitalWrite (bluPin,0);
EEPROM.write(0,colorMode); // Save the selected colorMode... good place to do it sice we are just going to delay anyway and it takes a few MS
delay (100);
digitalWrite (grnPin,1); // Flash the green pin to show we are in sound mode.
delay (300);
digitalWrite (grnPin,0);
delay (300);
digitalWrite (grnPin,1);
delay (300);
digitalWrite (grnPin,0);
}
}
/*
MULTI-CLICK: One Button, Multiple Events
Run checkButton() to retrieve a button event:
Click
Double-Click
Hold
*/
// Button timing variables
int debounce = 20; // ms debounce period to prevent flickering when pressing or releasing the button
int DCgap = 250; // max ms between clicks for a double click event
int holdTime = 2000; // ms hold period: how long to wait for press+hold event
int longHoldTime = 5000; // ms long hold period: how long to wait for press+hold event
// Other button variables
boolean buttonVal = HIGH; // value read from button
boolean buttonLast = HIGH; // buffered value of the button's previous state
boolean DCwaiting = false; // whether we're waiting for a double click (down)
boolean DConUp = false; // whether to register a double click on next release, or whether to wait and click
boolean singleOK = true; // whether it's OK to do a single click
long downTime = -1; // time the button was pressed down
long upTime = -1; // time the button was released
boolean ignoreUp = false; // whether to ignore the button release because the click+hold was triggered
boolean waitForUp = false; // when held, whether to wait for the up event
boolean holdEventPast = false; // whether or not the hold event happened already
int checkButton()
{
int event = 0;
// Read the state of the button
buttonVal = digitalRead(buttonPin);
// Button pressed down
if (buttonVal == LOW && buttonLast == HIGH && (millis() - upTime) > debounce) {
downTime = millis();
ignoreUp = false;
waitForUp = false;
singleOK = true;
holdEventPast = false;
if ((millis()-upTime) < DCgap && DConUp == false && DCwaiting == true) DConUp = true;
else DConUp = false;
DCwaiting = false;
}
// Button released
else if (buttonVal == HIGH && buttonLast == LOW && (millis() - downTime) > debounce) {
if (not ignoreUp) {
upTime = millis();
if (DConUp == false) DCwaiting = true;
else {
event = 2;
DConUp = false;
DCwaiting = false;
singleOK = false;
}
}
}
// Test for normal click event: DCgap expired
if ( buttonVal == HIGH && (millis()-upTime) >= DCgap && DCwaiting == true && DConUp == false && singleOK == true) {
event = 1;
DCwaiting = false;
}
// Test for hold
if (buttonVal == LOW && (millis() - downTime) >= holdTime) {
// Trigger "normal" hold
if (not holdEventPast) {
event = 3;
waitForUp = true;
ignoreUp = true;
DConUp = false;
DCwaiting = false;
//downTime = millis();
holdEventPast = true;
}
}
buttonLast = buttonVal;
return event;
}