Saturday, December 27, 2014

Playing music with Led animation with the Trinket (Christmas Tree Box)





Challenges

  • Using a Trinket mini micro controller, to build a LED animation looking like a Christmas Tree and playing some music ($7) ([x])
    • Trinket mini has only 5 pins, 
      • Only 2 provides PWM. I chose to only use PWM for the speaker.
    • Can only power 2 LEDs per pin
    • Trinket mini has no serial port, so there no tracing on the PC for debugging
  • Code works with an Arduino Uno or Trinket mini
    • I used the Arduino to debug the code with the Serial port

Pin usages

  • Pin 0, for the Speaker (PWM)
  • Pin 1, for the 2 red LEDS.
  • Pin 2, for the switch (PullUp mode)
  • Pin 3, for the 2 green LEDS in the middle
  • Pin 4, for the 2 green LEDS at the bottom

Others

  • A cheap electrical Box from Lowe's with cover for less than $2
  • Friendly PCB Mount Mini Speaker 
  • Altered a mini USB cable to only provide power



/*************************************************************************

    Christmas Tree Animation With A Trinket MicroController
    
    Functionality:
        (1) Play a short music when starting
        (2) While idle execute 3 differents leds animation every 6 seconds
        (3) If user press button play or stop "we wish you a merry christmas"

    Remarks:
        - Must be compiled at 8Mhz on a Trinket mini
        - Can be compiled on an Arduino Uno 


    Torres Frederic 2014.12, 2015.01

***************************************************************************/

#include "fArduino.h"

// Pins Usage
 
#if ARDUINO_UNO
    #define RED_LED_ONBAORD_PIN     13
    #define SPEAKER_PIN             3 // PWM, Purple wire
    #define RED_LED_PIN             4 // Yellow wire
    #define GREEN_LED_1_PIN         5 // Blue wire
    #define GREEN_LED_2_PIN         6 // Orange wire
    #define SWITCH_PIN              7 // White wire
#endif
#if TRINKET
    
    #define SPEAKER_PIN             0 // PWM, Purple wire
    #define RED_LED_PIN             1 // Yellow wire
    #define SWITCH_PIN              2 // White wire
    #define GREEN_LED_1_PIN         3 // Blue wire
    #define GREEN_LED_2_PIN         4 // Orange wire
#endif

// Define/Keep track of 11 states to display the 6 leds (2 Red Led, 2 Greens and 2 greens)
int _ledStateIndex = 0; // 0..11

// If we are not playing, let's animate the leds every x seconds
#define ANIMATE_LED_EVERY_X_SECOND 6

// Keep track of the last time we played the song or animate the leds
long _lastTimePlayedOrAnimate = 0;

// Define music to start or stop something
#define FACTOR 4
int musicStart[] = { NOTE_E4, 8 * FACTOR , NOTE_FS4, 8 * FACTOR, NOTE_A4 , 8 * FACTOR, NOTE_B4, 8 * (FACTOR/2) };
int musicEnd[]   = { NOTE_B4,  8 * FACTOR, NOTE_A4 , 8 * FACTOR, NOTE_FS4, 8 * FACTOR, NOTE_E4, 8 * (FACTOR/2) };

// We wish you a merry christmas (By the way I am not a beleiver)
// http://www.unimusica-peru.com/partit21.gif
//
#if defined(ARDUINO_UNO) 
    int WeWishYouAMerryChristmas[] = { // Sound better on a Arduino UNO

        NOTE_G2, 4, NOTE_C3, 4, NOTE_C3, 8, NOTE_D3, 8, NOTE_C3, 8, NOTE_B2, 8, NOTE_A2, 2,
        NOTE_A2, 4, NOTE_D3, 4, NOTE_D3, 8, NOTE_E3, 8, NOTE_D3, 8, NOTE_C3, 8, NOTE_B2, 2, NOTE_G2, 4,
        NOTE_E3, 4, NOTE_E3, 8, NOTE_F3, 8, NOTE_E3, 8, NOTE_D3, 8, NOTE_C3, 4, NOTE_A2, 4, NOTE_G2, 4, NOTE_A2, 4,
        NOTE_D3, 4, NOTE_B2, 4, NOTE_C3, 2,
    };
#else
    int WeWishYouAMerryChristmas[] = { // Sound better on a Trinket at 8Mhz
    
        NOTE_G4, 4, NOTE_C5, 4, NOTE_C5, 8, NOTE_D5, 8, NOTE_C5, 8, NOTE_B4, 8, NOTE_A4, 2,
        NOTE_A4, 4, NOTE_D5, 4, NOTE_D5, 8, NOTE_E5, 8, NOTE_D5, 8, NOTE_C5, 8, NOTE_B4, 2, NOTE_G4, 4,
        NOTE_E5, 4, NOTE_E5, 8, NOTE_F5, 8, NOTE_E5, 8, NOTE_D5, 8, NOTE_C5, 4, NOTE_A4, 4, NOTE_G4, 4, NOTE_A4, 4,
        NOTE_D5, 4, NOTE_B4, 4, NOTE_C5, 2,
    };
#endif

SpeakerManager  _speakerManager(SPEAKER_PIN);
PullUpButton    _button(SWITCH_PIN);

#if ARDUINO_UNO
    Led _onBoardLed(RED_LED_ONBAORD_PIN);
#endif
    
#define LEDS_ALL_OFF  0
#define LEDS_ALL_ON  10

void PowerOn() {

    PowerLed(LEDS_ALL_ON);
}
void PowerOff() {

    _ledStateIndex = 0; // Reset program index
    PowerLed(_ledStateIndex);
}
void PowerLeds(boolean valueR, boolean valueG1, boolean valueG2) {

    Board.LedOn(RED_LED_PIN    , valueR);
    Board.LedOn(GREEN_LED_1_PIN, valueG1);
    Board.LedOn(GREEN_LED_2_PIN, valueG2);
}
int _ledsStateProgram[] = { 

    1, // Red
    4, // Red + Green1
   10, // Red + Green1 + Green2
    0, // All Off
   10, // Red + Green1 + Green2
    0, // All Off
   10, // Red + Green1 + Green2
    4, // Red + Green1
    1, // Red
    0, // All Off
};
#define LED_STATE_PROGRAM_LENGTH ArraySize(_ledsStateProgram)

void PowerLedProgram(int programIndex) {

    if (programIndex >= 0 && programIndex < LED_STATE_PROGRAM_LENGTH) {
        int state = _ledsStateProgram[programIndex];
        PowerLed(state);
    }
    else {
        Board.Trace("PowerLedProgram() was passed an invalid programIndex");
    }
}
void PowerLed(int state) {
    
    switch (state) {

        case  0: PowerLeds(false, false, false); break; // All Off
        case  1: PowerLeds(true , false, false); break; // 1 on
        case  2: PowerLeds(false, true,  false); break;
        case  3: PowerLeds(false, false, true ); break;
        case  4: PowerLeds(true,  true , false); break; // 2 on
        case  5: PowerLeds(true , false, true ); break;
        case  6: PowerLeds(false, true , true ); break;
        case  7: PowerLeds(true , true , false); break;
        case  8: PowerLeds(false, true , true ); break;
        case  9: PowerLeds(true , false, true ); break;
        case 10: PowerLeds(true,  true,  true ); break; // All On
    }
}
bool WaitForAnimation(int index) {

    for (int i = 0; i < 5; i++) {
        Board.Delay(50);
        if (_button.IsPressed()) {
            return true;
        }
    }
    return false;
}
boolean LedsAnimationAllOnAllOff() {

    boolean r = false;
    for (int i = 0; i < 7; i++) {

        PowerOff();
        if (WaitForAnimation(i)) { r = true; break; }
        PowerOn();
        if (WaitForAnimation(i)) { r = true; break; }
    }
    PowerOff();
    return r;
}
boolean LedsAnimationUpAndDown() {

    int upAndDownProgram[] = {        
        1, // Red
        4, // Red + Green1
        10, // Red + Green1 + Green2
        10, // Red + Green1 + Green2        
        4, // Red + Green1
        1, // Red
        0, // All Off
        0, // All Off
    };

    boolean r = false;

    for (int count = 0; count < 3; count++) {

        for (int i = 0; i < ArraySize(upAndDownProgram); i++) {

            PowerLed(upAndDownProgram[i]);
            if (WaitForAnimation(i)) { r = true; break; }
        }
    }
    PowerOff();
    return r;
}

boolean LedsAnimationAllOrMiddle() {

    boolean r = false;
    PowerOff();
    for (int i = 0; i < 7; i++) {

        PowerLed(2);
        if (WaitForAnimation(i)) { r = true; break; }
        PowerOn();
        if (WaitForAnimation(i)) { r = true; break; }
    }
    PowerOff();
    return r;
}

boolean LedsRandomAnimation() {
  
    static int _counter;
    boolean r = false;

    if(_counter == 0)
        r = LedsAnimationAllOnAllOff();
    else if(_counter == 1)
        r = LedsAnimationUpAndDown();
    else if(_counter == 2)
        r = LedsAnimationAllOrMiddle(); // TODO: Create new animation
    
    if ((_counter++) > 2)
        _counter = 0;

    PowerOff();

    return r;
}
void AnimateLedsWhilePaying() {

    _ledStateIndex += 1;
    if (_ledStateIndex >= LED_STATE_PROGRAM_LENGTH)
        _ledStateIndex = 0;
    PowerLedProgram(_ledStateIndex);
}
void StartMusicAnimation() {

    PowerOff();
    _ledStateIndex = -1;

    Board.Delay(100); // To avoid button bouncing

    AnimateLedsWhilePaying();
    _speakerManager.StartSequenceBackGround(ArraySize(WeWishYouAMerryChristmas), WeWishYouAMerryChristmas);
}
void setup() {

    Board.Delay(1500);
    Board.InitializeComputerCommunication(9600, "Initializing...");
    #if ARDUINO_UNO
    Board.TraceHeader("Christmas Tree - v2");
    #endif  

    Board.SetPinMode(SPEAKER_PIN        , OUTPUT);
    Board.SetPinMode(RED_LED_PIN        , OUTPUT);
    Board.SetPinMode(GREEN_LED_1_PIN    , OUTPUT);
    Board.SetPinMode(GREEN_LED_2_PIN    , OUTPUT);
    #if ARDUINO_UNO
    Board.SetPinMode(RED_LED_ONBAORD_PIN, OUTPUT);
    #endif
    // SWITCH_PIN is initialized already as pull up by consttructor of PullUpButton

    PowerOff();

    #if ARDUINO_UNO
    _onBoardLed.SetBlinkMode(1000);
    #endif
    _speakerManager.PlaySequence(ArraySize(musicStart), musicStart); // Annonce that setup is done
}
void loop() {

    #if ARDUINO_UNO
    _onBoardLed.Blink(); // Blink every seconds
    #endif
    
    if (_button.IsPressed()) {

        if (_speakerManager.BackGroundOn) { // If playing stop playing

            _speakerManager.StopBackGroundSequence();
            Board.Trace("Sequence Stopped");
            Board.Delay(100); // To avoid button bouncing
        }
        else {
            StartMusicAnimation();
        }
    }
    else {
        if (_speakerManager.BackGroundOn) { // Call the Led animation and music 

            AnimateLedsWhilePaying();
            if (!_speakerManager.BackGroundUpdate()) {
                // We reach the end of the song, play a random animation
                boolean ar = LedsRandomAnimation(); 
                _lastTimePlayedOrAnimate = millis();
                if (ar) { // If user ask for music while animating leds start playing
                    StartMusicAnimation();
                }
            }
        }
        else {                                                    
            // If we are not playing
            // and we did not play in the last 6 seconds, execute a random animation
            if (millis() - _lastTimePlayedOrAnimate > ANIMATE_LED_EVERY_X_SECOND * 1000) {
                
                boolean ar = LedsRandomAnimation(); // Play a random animation
                _lastTimePlayedOrAnimate = millis();
                if (ar) { // If user ask for music while animating leds start playing
                    StartMusicAnimation();
                }
            }
        }
    }
}





No comments:

Post a Comment