Christmas Ornament

        

#include <MelodyPlayer.h>
#include "Arduino.h"
#include "ZUNO_NeoPixel.h"
#include "ZWCCSoundSwitch.h"

#define MAX_STACK 300
#define MAX_NOTE_PLAYBACK 140
#define ACTIVE_TIME 300 // Seconds
#define AWAKE_TIME (ACTIVE_TIME*1000UL) // Miliseconds
// Which ZUno pin is connected to NeoPixels?
#define LED_PIN    0
#define MELODY_PIN  9
#define GND_PIN  10
// How many NeoPixels are attached to the Zuno?
#define LED_COUNT 24

uint32_t last_event_time = 0;

uint8_t current_switch = 0;
bool isSwitchOn = false;
uint8_t current_melody = 1;
bool isMelodyOn = false;
bool isMelodyOnLast = false;


uint8_t switch_color_component_value = 0;
uint8_t switch_color_value = 0;

uint8_t r = 0;
uint8_t g = 0;
uint8_t b = 0;
uint8_t w = 0;

uint8_t r_last = 255;
uint8_t g_last = 0;
uint8_t b_last = 0;
uint8_t w_last = 0;

uint32_t th_stack[MAX_STACK];
znMutex  mutex;
znThread melody_thread(&MelodyThreadFunc, MAX_STACK, th_stack, (void*)1);

RenderedNote_t note_playback[MAX_NOTE_PLAYBACK];
MelodyPlayer mp(note_playback);

const char * melodies[] = {
                         // Start
                         "T=100   | e3@1/16 e3@1/8 e3@1/8 c3@1/16 e3@1/8 g3@1/4 ",
                         // Jingle Bells
                         "T=200  a2@1/4 | g2@1/4 e3@1/4 d3@1/4 c3@1/4 | g2@3/4 g2@1/4 "
                                       "| g2@1/4 e3@1/4 d3@1/4 c3@1/4 | a2@4/4 "
                                       "| a2@1/4 f3@1/4 e3@1/4 d3@1/4 | b2@4/4 "
                                       "| g3@1/4 g3@1/4 f3@1/4 d3@1/4 | c3@1/2 g3@1/2 "
                                       "| e3@1/4 e3@1/4 e3@1/2 "
                                       "| e3@1/4 e3@1/4 e3@1/2 "
                                       "| e3@1/4 g3@1/4 c3@3/8 d3@1/8 | e3@1/1 "
                                       "| f3@1/4 f3@1/4 f3@3/8 f3@1/8 "
                                       "| f3@1/4 e3@1/4 e3@3/8 e3@1/8 "
                                       "| g3@1/4 g3@1/4 f3@1/4 d3@1/4 | c3@1/1",
                         // Last Christmass
                         "T=90 S=0   | e3@5/16 p@1/16 e3@1/4 d3@1/4 a2@1/8 "
                                    "| e3@1/8 e3@1/8 f#3@1/8 d3@3/8 b2@1/8 b2@1/8 "
                                    "| e3@1/8 e3@1/8 f#3@1/4 d3@3/8 b2@1/8 "
                                    "| c#3@1/8 d3@1/8 c#3@1/8 b2@3/8 p@1/4 "
                                    "| f#3@3/8 e3@1/2 b2@1/8 "
                                    "| f#3@1/8 g3@1/8 f#3@1/8 e3@1/2 b2@1/8 "
                                    "| c#3@1/8 d3@1/8 c#3@1/8 c#3@1/4 d3@1/4 c#3@1/8 "
                                    "| b2@3/8 a2@5/8",

                         // Merry Christmass
                         "T=120 c3@1/3 | f3@1/3 f3@1/6 g3@1/6 f3@1/6 e3@1/6 "
                                      "| d3@1/3 d3@1/3 d3@1/3 "
                                      "| g3@1/3 g3@1/6 a3@1/6 g3@1/6 f3@1/6 "
                                      "| e3@1/3 c3@1/3 c3@1/3 "
                                      "| a3@1/3 a3@1/6 a#3@1/6 a3@1/6 g3@1/6 "
                                      "| f3@1/3 d3@1/3 c3@1/6 c3@1/6 "
                                      "| d3@1/3 g3@1/3 e3@1/3 "
                                      "| f3@2/3 ",
};

/*
ZUNO_ENABLE(
    LOGGING_DBG 
    DBG_CONSOLE_BAUDRATE=115200
    LOGGING_UART=Serial0
);
*/

ZUNO_SETUP_CHANNELS(
  ZUNO_SWITCH_BINARY(current_switch, NULL),
  ZUNO_SOUND_SWITCH(100, 0, playMelody1, stopMelody1, melody_set1),
  ZUNO_SWITCH_COLOR(SWITCH_COLOR_FLAGS_COLD_WHITE|SWITCH_COLOR_FLAGS_RED|SWITCH_COLOR_FLAGS_GREEN|SWITCH_COLOR_FLAGS_BLUE, getterSwitchColor, setterSwitchColor)
);

// Play melody 300 seconds
ZUNO_SETUP_SOUND_SWITCH(melody_set1,
                        ZUNO_SETUP_SOUND_SWITCH_TONE("Jingle Bells", ACTIVE_TIME),
                        ZUNO_SETUP_SOUND_SWITCH_TONE("Happy New Year", ACTIVE_TIME),
                        ZUNO_SETUP_SOUND_SWITCH_TONE("Merry Christmas", ACTIVE_TIME)
);                     

ZUNO_SETUP_SLEEPING_MODE(ZUNO_SLEEPING_MODE_FREQUENTLY_AWAKE);

// Device's configuration parametrs definitions
ZUNO_SETUP_CONFIGPARAMETERS(ZUNO_CONFIG_PARAMETER_INFO("Parameter 64", "Default parameter", 0, 1, 0));

void _wakeHandler(void) {
  last_event_time = millis();
  pinMode(LED_PIN, OUTPUT); 
}

// setup() function -- runs once at startup --------------------------------
void setup() {
  pinMode(GND_PIN, OUTPUT);
  digitalWrite(GND_PIN, LOW);
  NeoPixel.addNeo(LED_PIN, LED_COUNT, 50, NEO_GRB | NEO_KHZ800); // Set BRIGHTNESS to about 1/5 (max = 255)
  NeoPixel.show(LED_PIN);            // Turn OFF all pixels ASAP

  // setup handler for EM2 mode only
  zunoAttachSysHandler(ZUNO_HANDLER_WUP, 0, (void*) &_wakeHandler);

  // Test at start
  startMelody((char*)melodies[0]);
  TwinkleRandom(10, 100);
  stopPlayback();
}

// Thread safe operations with MelodyPlayer
RenderedNote_t * playbackNote(){
  RenderedNote_t * note = NULL;
   if(mutex.lock()){
      note = mp.nextNote();
      mutex.unlock(); 
   }
   return note;
}

void resetPlayback(){
  if(mutex.lock()){
    mp.resetCursor();
    mutex.unlock(); 
  }
}

void stopPlayback(){
  if(mutex.lock()){
    mp.stop();
    mutex.unlock();
    zuno_CCSoundSwitchStop(2);
  }
}

void startMelody(char * melody){
  if(mutex.lock()){
    mp.parse(melody);
    mp.resetCursor();
    mutex.unlock(); 
  }
}

void MelodyThreadFunc(void * param){
    (void)param;
    while(1){
        RenderedNote_t * note = playbackNote();
        if (note != NULL) {
            if (note->note != 0) {
                tone(MELODY_PIN, note->freq);
                delay(note->time);
                noTone(MELODY_PIN);
                delay(5);   
            } else {
                delay(note->time);
            }
        } else {
              // Repeat melody after 1 second
              delay(1000);
              resetPlayback();
        }
    }
}
void goSleep(){
    stopPlayback();
    NeoPixel.clear(LED_PIN);
    NeoPixel.show(LED_PIN);
    pinMode(LED_PIN, INPUT_PULLUP_FILTER);
    zunoSendDeviceToSleep();
}

// loop() function -- runs repeatedly as long as board is on ---------------
void loop() {
  // If we've already sent device to sleep - skip the logic
  if(!zunoIsSleepLocked()){
    delay(20);
    return;
  }

  // turnOff after 5 minutes
  if ((millis() - last_event_time) > AWAKE_TIME) {
      current_switch = 0;
      r = 0; g = 0; b = 0; w = 0;
      goSleep();
      return;
  }

  if (isMelodyOn != isMelodyOnLast) {
    isMelodyOnLast = isMelodyOn;

    if (isMelodyOn) {
      startMelody((char*)melodies[current_melody]);
    }
    else {
      stopPlayback();
    }
  }

  // check switch state
  if (zunoIsChannelUpdated(1)) {
    last_event_time = millis();
    
    if (current_switch > 0) {
      isSwitchOn = true;
      startMelody((char*)melodies[random(1, 4)]);
      TwinkleRandom(10, 100);
    }
    else {
      isSwitchOn = false;
      goSleep();
    }
    zunoSendReport(1);
  }

  if (isSwitchOn) {TwinkleRandom(10, 100);}

  // check white color state
  if (switch_color_component_value == SWITCH_COLOR_COMPONENT_COLD_WHITE) {
    if (switch_color_value > 0) {
      // Count, SpeedDelay, OnlyOne
      TwinkleRandom(switch_color_value/25, 100);
    } else {
      NeoPixel.clear(LED_PIN);
      NeoPixel.show(LED_PIN);
    }
  } else {
    // Red, Green, Blue, Count, SpeedDelay, OnlyOne
    Twinkle(r, g, b, 10, 100);
  } 
}

void setterSwitchColor(byte componentRGBW, byte value) {
  last_event_time = millis();
  
  switch_color_component_value = componentRGBW;
  switch_color_value = value;
  
  switch(componentRGBW)
  {
    case SWITCH_COLOR_COMPONENT_RED:
      r = value;
      r_last = value;
      break;
    case SWITCH_COLOR_COMPONENT_GREEN:
      g = value;
      g_last = value;
      break;
    case SWITCH_COLOR_COMPONENT_BLUE:
      b = value;
      b_last = value;
      break;
    default: // Cold white. Start randow
      w = value;
      w_last = value;
      break;
  }
}

byte getterSwitchColor(byte componentRGBW) {
  switch(componentRGBW) {
    case SWITCH_COLOR_COMPONENT_RED:
      return r;
    case SWITCH_COLOR_COMPONENT_GREEN:
      return g;
    case SWITCH_COLOR_COMPONENT_BLUE:
      return b;
    default:
      return w;
  }
  return w; 
}

/*
 * This effect will blink one or more LEDs in a given color.
 * The function takes the usual color parameters, which you can determine with the Color Picker.
 * The 4th parameter (Count) determines how many pixels will be done in one run, where as the 5th parameter determines how much time will be paused between individual pixels (speed).
 * The 6th parameter (OnlyOne) should be true if you want to see only one LED at a time.
 * If it’s set to false then all “Count” number of LEDs will be visible (added one at a time).
 */

void Twinkle(byte red, byte green, byte blue, uint8_t Count, uint8_t SpeedDelay) {
  pinMode(LED_PIN, OUTPUT); 
  uint8_t pixels[20] = {0};

  // On pixels
  for (uint8_t i=0; i<Count; i++) {
    pixels[i] = random(LED_COUNT);
    NeoPixel.setColor(LED_PIN, pixels[i], NeoPixel.RGB(red, green, blue));
    NeoPixel.show(LED_PIN);
    delay(SpeedDelay);
  }
  delay(SpeedDelay);

  // Off pixels
  for (uint8_t i=0; i<Count; i++) {
    NeoPixel.setColor(LED_PIN, pixels[i], NeoPixel.RGB(0,0,0));
    NeoPixel.show(LED_PIN);
    delay(SpeedDelay);
  }
  delay(SpeedDelay);
}

/*
 * This is a variation on the Twinkle() effect.
 * The only difference is that the colors are now randomly generated, and therefor the first 3 color parameters are no longer of use and have been removed.
 * So we use only 3 parameters:
 * The first parameter (Count) determines how many pixels will be done in one run, where as the second parameter determines how much time will be paused between individual pixels (speed).
 * The last parameter (OnlyOne) should be true if you want to see only one LED at a time.
 * If it’s set to false then all “Count” number of LEDs will be visible (added one at a time).
 */
 
void TwinkleRandom(uint8_t Count, uint8_t SpeedDelay) {
  pinMode(LED_PIN, OUTPUT); 
  int pixels[20] = {0};

  // On pixels
  for (uint8_t i=0; i<Count; i++) {
    pixels[i] = random(LED_COUNT);
    NeoPixel.setColor(LED_PIN, pixels[i], NeoPixel.RGB(random(0,255),random(0,255),random(0,255)));
    NeoPixel.show(LED_PIN);
    delay(SpeedDelay);
  }
  delay(SpeedDelay);

  // Off pixels
  for (uint8_t i=0; i<Count; i++) {
    NeoPixel.setColor(LED_PIN, pixels[i], NeoPixel.RGB(0,0,0));
    NeoPixel.show(LED_PIN);
    delay(SpeedDelay);
  }
  delay(SpeedDelay);
  
}

void playMelody1(uint8_t melody_id, uint8_t volume){
  (void) volume;
  last_event_time = millis();
  current_melody = melody_id;
  isMelodyOn = true;
}
void stopMelody1(uint8_t melody_id){
  (void) melody_id;
  last_event_time = millis();
  isMelodyOn = false;
}