#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;
}