Wireless Controlled Wearable EL Wire Dance Suit
Custom Example Code
Listening and Adjusting w/ Audacity
To sync the animation with the music, I had to listen to the music several times just as if I was choreographing moves for a piece. You can also use Audacity or a DJ application visualize the length of the beats. For simplicity, I decided to have three sections over the course of a few counts where the EL Wire suits would animate with the music. With some experimenting and fine tuning the delays, I was able to sync the animation with the music.
Based on the way that I wrote the code with the XBees, I noticed that there were two limitations when syncing the animation with the music. The first limitation that I had to press the send button while also in the correct mode as I was moving. This was quite tricky. The second limitation was that once the sequence began, there was really no way to change the animation until the sequence was finished. Longer sequences and delays made it harder to control which was why I decided to have a three sections over a few counts. I would have had to tediously write more code and test to sync the animations with the music.
Glove Controller Example Code
If you have not already, unzip the GitHub repo and open the example code called XBee_ControllerV3.ino. V2 was a work in progress in case I accidentally broke the code. V3 was the final code that was used for the performance. The path of the example code will probably look similar to: ...\Wireless_Controlled_EL_Dance_Suit\Arduino\XBee_Controller\XBee_ControllerV3. You can also copy the code below and paste it into the Arduino IDE. Select the board (in this case the Arduino/Genuino Uno) and COM port that the board enumerated to. Make sure the switch is flipped to the DLINE before hitting the upload button.
/******************************************************************* XBee_ELSequencer_ControllerV3.ino Created by Ho Yun Bobby Chan @ SparkFun Electronics May 12th, 2017 Taken from SparkFun XBee EL Sequencer Controller Ho Yun Bobby Chan @ SparkFun Electronics June 20, 2014 Updated by Toni Klopfenstein @ SparkFun Electronics April, 2015 https://github.com/sparkfun/EL_Sequencer Description: This is a sketch for the wireless controller used to control 7x EL dance suits. The wireless controller consists of a RedBoard Programmed with Arduino, XBee Shield, XBee Series 1 transceiver, diffused RGB Common Cathode LED, Blue Clear LED, 330Ohm current limiting resistors, a button, 9V battery, and a 9V adapter. Each of the 7x EL dance suits contain an EL Sequencer, 2x EL Wires, a 12V EL inverter, XBee female sockets soldered, a 9V battery, 9V adapter, and a XBee Series 1 transceiver. An XBee Series 2 can be used but the throughput of the Series 1 is much higher. To reduce latency, I recommend using the XBee Series 1. The basic configuration of the XBee module with point-to-point configuratin is based on Digi's Example tutorial => https://www.digi.com/blog/xbee/basic-xbee-802-15-4-chat/. page 5 of the tutorial shows you how to broadcast with point-to-multipoint configuration so that multiple EL Sequencers can be controlled. Connect the XBee Shield to the Arduino with the switch flipped to the software serial side labeled "DLINE". By pushing the button, a character is sent from a remote microcontroller. The corresponding EL Sequencer will receive the character and control the EL component on a channel that is associated with that character. Using a RedBoard programmed with Arduino, the XBee transceiver is connected to the Software Serial pins. By pushing the button, the Arduino will send one character through the XBee. Logic is used to control how many characters are sent with the push button. The Arduino will not send another character until the button is pressed again. Note: This section of the code can be optimized. As the user is not pressing down on the button, logic can be added so that the XBee is not continuously sending serial characters to the receiving EL Sequencer when idle. Development environment specifics: Arduino 1.6.5 This code is beerware; if you see me (or any other SparkFun employee) at the local, and you've found our code helpful, please buy us a round! Distributed as-is; no warranty is given. **************************************************************************/ SoftwareSerial xbee(2, 3); //Rx = 2, Tx = 3 //Declare variables and pin definitions char send_CHAR = 'A'; //default send character int pattern = 0; //pattern that we are going to send //SEND Button const int button1Pin = 4; //push button const int ledPin1 = 13; //LED to indicate when a character has been sent //variables to check for button1 state boolean prev_button1State = false; boolean current_button1State = false; //LED Status Indicator int ledR = 5; //hardware PWM int ledG = 6; //hardware PWM int ledB = 9; //hardware PWM //UP Button const int button2Pin = 11; //push button to move ahead to next sequence //variables to check for button2 state boolean prev_button2State = false; boolean current_button2State = false; //DOWN Button const int button3Pin = 12;//push button to move back a sequence //variables to check for button3 state boolean prev_button3State = false; boolean current_button3State = false; /*******************Setup Loop***************************/ void setup() { //Declare buttons and status LEDs pinMode (ledPin1, OUTPUT); //send LED pinMode(button1Pin, INPUT_PULLUP); //SEND button, use internal pullup resistor // initialize the digital pins as an output for status LED pinMode(ledR, OUTPUT); pinMode(ledG, OUTPUT); pinMode(ledB, OUTPUT); pinMode(button2Pin, INPUT_PULLUP); //UP button, use internal pullup resistor pinMode(button3Pin, INPUT_PULLUP); //DOWN button, use internal pullup resistor //Declare serial connections for debugging Serial.begin(9600); Serial.println("Arduino started sending bytes via XBee"); //Declare software serial conenction with XBee xbee.begin(9600); Serial.println("EL Sequencer Controller's XBee Ready to Communicate"); sequenceTest();//visually initialization to see that we have finished initializing } /*******************Main Loop***************************/ void loop() { //initialize variables to read buttons int button1State; int button2State; int button3State; button1State = digitalRead(button1Pin); button2State = digitalRead(button2Pin); button3State = digitalRead(button3Pin); /*buttonXstate - LOW or 0 means pressed - HIGH or 1 means not pressed */ //-----------Check If SENT Button Has Been Pressed---------- //if SENT button is pressed, it will be pulled low if (button1State == LOW) { digitalWrite(ledPin1, HIGH); //turn LED indicating if a character has been sent ON current_button1State = true; // button has been pressed once if (prev_button1State != current_button1State) //check to see if button is still being pressed { Serial.println("Button is pressed."); xbee.write(send_CHAR);//Tell Sequencer to change mode } else { //do nothing because finger is still on button } prev_button1State = current_button1State; } //sent button has not been pressed, it will be high again else { current_button1State = false; digitalWrite(ledPin1, LOW); // turn push button LED OFF prev_button1State = current_button1State; }//-----------End Check for SENT Button---------- //-----------Check If UP Button Has Been Pressed---------- if (button2State == LOW) { current_button2State = true; //UP button has been pressed once if (prev_button2State != current_button2State) { //check to see if button is still being pressed pattern = pattern + 1; //change LED pattern after button has been pressed if (pattern < 0 || pattern > 5) { //reset pattern pattern = 0; } } else { //do nothing because finger is still on button } prev_button2State = current_button2State; } //UP button has not been pressed, it will be high else { current_button2State = false; prev_button2State = current_button2State; }//-----------End Check for Up Button---------- //-----------Check If DOWN Button Has Been Pressed---------- if (button3State == LOW) { current_button3State = true; //button has been pressed once if (prev_button3State != current_button3State) { //check to see if button is still being pressed pattern = pattern - 1; //change LED pattern after button has been pressed if (pattern < 0 || pattern > 5) { //reset pattern pattern = 5; } } else { //do nothing because finger is still on button } prev_button3State = current_button3State; } //button has not been pressed, it will be high else { current_button3State = false; prev_button3State = current_button3State; }//-----------End Check for DOWN Button---------- delay(50); //save send character into variable depending on button press and change status LED switch (pattern) { case 1: greenON(); send_CHAR = 'B'; break; case 2: blueON(); send_CHAR = 'C'; break; case 3: allOFF();//blink status LED delay(50); blueON(); delay(50); send_CHAR = 'D'; break; case 4: blueON(); send_CHAR = 'E'; break; default: redON(); send_CHAR = 'A'; break; } }//end loop /*Below are the modular functions for changing the color of a RGB LED. This will be used to help identify what mode we are currently in: ROYGBIV Note: A 9V battery is not able to fully power all three LEDs simultaneously... MODE 1.) red = red[HIGH] .) tangerine orange = red[HIGH]+ green[50] 2.) yellow = red[HIGH]+ green[HIGH] 3.) green = + green[HIGH] 4.) clear blue = + green[HIGH] + blue[HIGH] 5.) blue = + blue[HIGH] 6.) magenta = red[HIGH]+ + blue[HIGH] .) white = red[HIGH]+ green[HIGH] + blue[HIGH] */ void allOFF() { analogWrite(ledR, 0); analogWrite(ledG, 0); analogWrite(ledB, 0); } void redON() { //Seq 1 analogWrite(ledR, 255); analogWrite(ledG, 0); analogWrite(ledB, 0); } void greenON() { //Seq 2 analogWrite(ledR, 0); analogWrite(ledG, 255); analogWrite(ledB, 0); } void blueON() { //Seq 3 analogWrite(ledR, 0); analogWrite(ledG, 0); analogWrite(ledB, 255); } void yellowON() { analogWrite(ledR, 255); analogWrite(ledG, 255); analogWrite(ledB, 0); } void clearblueON() { analogWrite(ledR, 0); analogWrite(ledG, 255); analogWrite(ledB, 255); } void magentaON() { analogWrite(ledR, 255); analogWrite(ledG, 0); analogWrite(ledB, 255); } void sequenceTest() { redON(); delay(50); allOFF(); delay(50); yellowON(); delay(50); allOFF(); delay(50); greenON(); delay(50); allOFF(); delay(50); clearblueON(); delay(50); allOFF(); delay(50); blueON(); delay(50); allOFF(); delay(50); magentaON(); delay(50); allOFF(); delay(50); //whiteON();//white drains too much power from a 9V, commenting out. //delay(50); //allOFF(); //delay(50); }
EL Sequencers Example Code
Open the example code called XBee_ELSequencerV3.ino. The path of the example code will probably look similar to: ...\Wireless_Controlled_EL_Dance_Suit\Arduino\XBee_ELSequencer\XBee_ELSequencerV3. You can also copy the code below and paste it into the Arduino IDE. Select the board (in this case the Arduino Pro or Pro Mini, ATmega328P (3.3V, 8MHz) but it can also be LilyPad Arduino) and COM port that the USB-to-Serial Converter enumerated to. Hit the upload button.
/********************************************************************** XBee_ELSequencerV3.ino Modified by Ho Yun Bobby Chan @ SparkFun Electronics May 12th, 2017 Taken from the SparkFun XBee EL Sequencer Demo Sketch Ho Yun Bobby Chan @ SparkFun Electronics June 20, 2014 Updated by Toni Klopfenstein @ SparkFun Electronics April, 2015 https://github.com/sparkfun/EL_Sequencer Description: This is a modified sketch for the EL Sequencer with a wireless controller. The wireless controller consists of a RedBoard Programmed with Arduino, XBee Explorer, XBee Series 1 transceiver, diffused RGB Common Cathode LED, Blue Clear LED, 330Ohm current limiting resistors, 3x buttons, a 9V battery, and a 9V adapter. Each of the 7x EL dance suits contain an EL Sequencer, 2x EL Wires, a 12V EL inverter, XBee female sockets soldered, a 9V battery, 9V adapter, and a XBee Series 1 transceiver. An XBee Series 2 can be used but the throughput of the Series 1 is much higher. To reduce latency, I recommend using the XBee Series 1. The basic configuration of the XBee module with point-to-point configuratin is based on Digi's Example tutorial => https://www.digi.com/blog/xbee/basic-xbee-802-15-4-chat/. Page 5 of the tutorial shows you how to broadcast with point-to-multipoint configuration so that multiple EL Sequencers can be controlled. By pushing the button, a character is sent from a remote microcontroller. The corresponding EL Sequencer will receive the character and control the EL component on a channel that is associated with that character. EL Sequencer uses the hardware UART of the Atmega328 for communication: pin 0 = Rx pin 1 = Tx Note: Make sure to remove the XBee Series 1 on the EL Sequencer when uploading a new sketch file otherwise it will brick the XBee. You can always use the next generation XCTU software to unbrick and recover the transceiver. Development environment specifics: Arduino 1.6.5 This code is beerware; if you see me (or any other SparkFun employee) at the local, and you've found our code helpful, please buy us a round! Distributed as-is; no warranty is given. ***********************************************************************/ char val; //Declare character 'val' when Peripheral XBee receives a character char temp_delete; //used to delete buffer and prevent false triggers when Controller XBee sends character more than once //LED to check if the LED is initialized. const int status_LED = 13; int counter = 0; //adding counter to prevent false triggers for a small period of time boolean XBee_sent = false; //flag to see if we have received any characters after a certain period of time /*******************Setup Loop***************************/ void setup() { Serial.begin(9600); //Begin Serial communication and debugging Serial.println("EL Sequencer's XBee is Ready to Receive Characters"); val = 'A'; //save as default character //Initialize pins pinMode(status_LED, OUTPUT); //Set pin mode as output for status LED pinMode(2, OUTPUT); //Set pin mode as output for Channel A pinMode(3, OUTPUT); //Set pin mode as output for Channel B pinMode(4, OUTPUT); //Set pin mode as output for Channel C pinMode(5, OUTPUT); //Set pin mode as output for Channel D pinMode(6, OUTPUT); //Set pin mode as output for Channel E pinMode(7, OUTPUT); //Set pin mode as output for Channel F pinMode(8, OUTPUT); //Set pin mode as output for Channel G pinMode(9, OUTPUT); //Set pin mode as output for Channel H //Status LED to see if the EL Sequencer is initializing for (int i = 0; i < 3; ++i) { digitalWrite(status_LED, HIGH);//set Status LED on delay(50); digitalWrite(status_LED, LOW); //set Status LED off delay(50); } all_ON();//turn on all EL channels delay(100); //Wait a little } /*******************Main Loop***************************/ void loop() { if (XBee_sent == false) { //we have not received a character yet after a certain period of time, we can see if the Controller XBee has sent any characters if (Serial.available()) { //check if peripheral XBee is receiving data from controller XBee val = Serial.read();//save whatever is in the buffer to the variable counter = 0; //set counter to 0 to prevent false button presses XBee_sent = true; //we have received a character //if debugging, we can see what character is recevied Serial.print("Character Received = "); Serial.println(val); //Check to see if character sent is any of the recognized characters and jump to the sequence if (val == 'A') { Seq_0(); } else if (val == 'B') { Seq_1(); } else if (val == 'C') { Seq_2(); } else if (val == 'D') { Seq_3(); } else if (val == 'E') { Seq_4(); } }//end buffer check }//end check to see if we have not received a character after a certain period of time if (counter >= 10) {//this section of code will reset the flag "XBee_Sent" so we can begin listening for characters again if (XBee_sent == true) { Serial.println("Counter = 10, we are ready to receive characters again"); } XBee_sent = false; } if (XBee_sent == true) {//this section of code is used as a delay to prevent false button presses counter = ++counter;//keep adding until we reach 10, then we can reset flag and begin receiving again //if connected to a computer, check to see the duration of the delay Serial.print("Counter = "); Serial.println(counter); temp_delete = Serial.read();//try to clear false triggers in buffer provided by Controller XBee until counter resets } }//end loop() //**********MODULAR SEQUENCED FUNCTIONS********** void all_ON() { //this function is used to turn all channels ON //Bobby digitalWrite(2, HIGH); //Channel A, hoodie digitalWrite(3, HIGH); //Channel B, pants //Antuan, Kaden digitalWrite(4, HIGH); //Channel C, hoodie digitalWrite(5, HIGH); //Channel D, pants //Mireku, Talon digitalWrite(6, HIGH); //Channel E, hoodie digitalWrite(7, HIGH); //Channel F, pants //Madi, Henry digitalWrite(8, HIGH); //Channel G, hoodie digitalWrite(9, HIGH); //Channel H, pants } void all_OFF() { //this function is used to turn all channels OFF //Bobby digitalWrite(2, LOW); //Channel A, hoodie digitalWrite(3, LOW); //Channel B, pants //Antuan, Kaden digitalWrite(4, LOW); //Channel C, hoodie digitalWrite(5, LOW); //Channel D, pants //Mireku, Talon digitalWrite(6, LOW); //Channel E, hoodie digitalWrite(7, LOW); //Channel F, pants //Madi, Henry digitalWrite(8, LOW); //Channel G, hoodie digitalWrite(9, LOW); //Channel H, pants } void Seq_0() { //function used to flash Bobby's suit with the beat at 5 seconds into the edited C2C track digitalWrite(status_LED, LOW); //turn OFF Status LED digitalWrite(2, LOW); //Channel A digitalWrite(3, LOW); //Channel B delay(500); digitalWrite(status_LED, HIGH); //turn on Status LED digitalWrite(2, HIGH); //Channel A delay(500); digitalWrite(3, HIGH); //Channel B delay(500); digitalWrite(2, LOW); //Channel A digitalWrite(3, LOW); //Channel B delay(500); digitalWrite(2, HIGH); //Channel A digitalWrite(3, HIGH); //Channel B delay(500); digitalWrite(2, LOW); //Channel A digitalWrite(3, LOW); //Channel B delay(250); digitalWrite(2, HIGH); //Channel A digitalWrite(3, HIGH); //Channel B delay(500); digitalWrite(2, LOW); //Channel A digitalWrite(3, LOW); //Channel B delay(500); digitalWrite(2, HIGH); //Channel A digitalWrite(3, HIGH); //Channel B delay(250); digitalWrite(2, LOW); //Channel A digitalWrite(3, LOW); //Channel B delay(250); digitalWrite(2, HIGH); //Channel A digitalWrite(3, HIGH); //Channel B delay(250); digitalWrite(2, LOW); //Channel A digitalWrite(3, LOW); //Channel B delay(250); digitalWrite(2, HIGH); //Channel A digitalWrite(3, HIGH); //Channel B delay(250); digitalWrite(2, LOW); //Channel A digitalWrite(3, LOW); //Channel B delay(250); digitalWrite(2, HIGH); //Channel A digitalWrite(3, HIGH); //Channel B delay(250); digitalWrite(2, LOW); //Channel A digitalWrite(3, LOW); //Channel B delay(250); digitalWrite(2, HIGH); //Channel A digitalWrite(3, HIGH); //Channel B all_ON();//turn everything back on } void Seq_1() { //function used to toggle the channels back & forth and hit the beat at 45 seconds into the edited C2C track for (int i = 0; i < 8; ++i) { //Bobby digitalWrite(2, HIGH); //Channel A, hoodie digitalWrite(3, LOW); //Channel B, pants //Antuan, Kaden digitalWrite(4, HIGH); //Channel C, hoodie digitalWrite(5, LOW); //Channel D, pants //Mireku, Talon digitalWrite(6, LOW); //Channel E, hoodie digitalWrite(7, HIGH); //Channel F, pants //Madi, Henry digitalWrite(8, LOW); //Channel G, hoodie digitalWrite(9, HIGH); //Channel H, pants delay(250); //Bobby digitalWrite(2, LOW); //Channel A, hoodie digitalWrite(3, HIGH); //Channel B, pants //Antuan, Kaden digitalWrite(4, LOW); //Channel C, hoodie digitalWrite(5, HIGH); //Channel D, pants //Mireku, Talon digitalWrite(6, HIGH); //Channel E, hoodie digitalWrite(7, LOW); //Channel F, pants //Madi, Henry digitalWrite(8, HIGH); //Channel G, hoodie digitalWrite(9, LOW); //Channel H, pants delay(250); } all_ON(); delay(750); all_OFF(); delay(100); all_ON(); } void Seq_2() { //this function turns all the channels ON just in case something happens all_ON(); } void Seq_3() { //function used to as a ripple effect and turn on the channels to the beat scratch at 2 minutes 10 seconds into the edited C2C track all_OFF(); delay(100); //Madi, Henry digitalWrite(8, HIGH); //Channel G, hoodie digitalWrite(9, LOW); //Channel H, pants delay(100); //Antuan, Kaden digitalWrite(4, HIGH); //Channel C, hoodie digitalWrite(5, LOW); //Channel D, pants delay(100); //Bobby digitalWrite(2, HIGH); //Channel A, hoodie digitalWrite(3, LOW); //Channel B, pants //Mireku, Talon digitalWrite(6, HIGH); //Channel E, hoodie digitalWrite(7, LOW); //Channel F, pants delay(100); //Madi, Henry digitalWrite(8, HIGH); //Channel G, hoodie digitalWrite(9, HIGH); //Channel H, pants delay(100); //Antuan, Kaden digitalWrite(4, HIGH); //Channel C, hoodie digitalWrite(5, HIGH); //Channel D, pants delay(100); //Bobby digitalWrite(2, HIGH); //Channel A, hoodie digitalWrite(3, HIGH); //Channel B, pants //Mireku, Talon digitalWrite(6, HIGH); //Channel E, hoodie digitalWrite(7, HIGH); //Channel F, pants delay(100); } void Seq_4() { //this function turns all the channels ON just in case something happens all_ON(); }
By hitting the send button, the first sequence should animate to the beat from the beginning. By toggling the next mode, the LED should change to indicate that the next mode is ready to be sent. By hitting the send button again, you should see the EL wire attached to the hoodie and pants switching on and off as shown in the GIF below. I had my students in a certain formation when the sequence started.
By toggling the mode again and hitting the button again, the EL wire individually lit up on the hoodies and then the pants as shown in the GIF below.