#include <FastLED.h> /*library for programming addressble LED strips*/
#define NUM_LEDS 17 /*The number of neopixel leds on the strip*/
#define LED_PIN 2 /*Arduino pin that the neopixel leds are attached to*/
#define COLOUR_ORDER GRB /*the order the leds light up on a WS2812b Neopixel strip*/
#define CHIPSET WS2812B
#define VOLTS 5
#define MAX_AMPS 500 /*500 mA*/
#define MAINLOOP_DELAY_TIME 30 /*JG code*/
#define SPECIALTICK_DELAY_TIME 2 /*JG code*/
#define EXPLOSIONLAMP_DELAY_TIME (random(0, 1000)) /*JG code*/
unsigned long lastExecutedMillis = 0; // create a variable to save the last executed time of the main loop
unsigned long st_lastExecutedMillis = 0; // create a variable to save the last executed time of the special tick definition
unsigned long el_lastExecutedMillis = 0; // create a variable to save the last executed time of the explosion lamp definition
//**********************************************************************
//*Extra code for flickering wall light leds VVVVVV *
//**********************************************************************
int ledPin[] = {
5, 6, 9, 3, 10, 11}; // pwm pins only
int ledState[6]; // last state of each led
long randNumber;
//**********************************************************************
//*Extra code for flickering wall light leds *
//**********************************************************************
#define STATIC_COLOR CHSV(30, 208, 127) /*The colour for a LED that is always on, e.g. streetlamp*/
#define DYNAMIC_COLOR CHSV(30, 208, 127) /*The colour for a LED that goes on and off - eg. window light*/
#define EXPLOSION_CHANCE 30 //1 in x chance every second /*the probabilty that an explosion at Ollivanders will occur*/
#define DYNAMIC_CHANCE 10 // 1 in x chance every second /*the probabilty that a dynamic light will go on or off*/
/* Once a dynamic light has turned on or off, how long should we wait before
it can toggle again? The system will generate a random duration between these
limits. During that time randomly generated toggle events will be ignored */
#define MIN_DYNAMIC_STAY_ONOFF 5
#define MAX_DYNAMIC_STAY_ONOFF 8
// How long do the on/off transitions take, 1000 = 1 second
#define DYNAMIC_TOGGLE_DURATION_MILLISECONDS 1000
CRGB leds[NUM_LEDS]; /*A CRGB is an object representing a colour. When using FastLED each LED strip is represented as an array of CRGB colours. This line sets up an array that we can manipulate to set/clear the led colour data*/
//enum is a special data type that enables for a variable to be a set of predefined constants. The four types of LED light - note Torch is not used in this sketch
enum LED_TYPE {
TYPE_STATIC,
TYPE_DYNAMIC,
TYPE_EXPLOSION,
TYPE_TORCH,
};
//The four stages of the explosion event
enum DISRUPTIVE_EVENT_STAGES {
EVENT_ALL_BLACK,
EVENT_STATIC_FLICKER,
EVENT_ALL_FLASH,
EVENT_RANDOM_HUE_EXPLOSION
};
class LedGroup
{
private: /*private variables are only for internal use by this class*/
LED_TYPE m_type;
uint32_t* m_indexes;
size_t m_indexCount;
unsigned long m_lastTick;
unsigned long m_disruptiveEventRunning;
public:
LED_TYPE getType() {
return m_type;
}
unsigned long getLastTick() {
return m_lastTick;
}
unsigned long setLastTick() {
m_lastTick = millis();
}
LedGroup* nextGroup;
LedGroup(uint32_t* t_indexes, size_t t_indexCount, LED_TYPE t_type) :
m_indexes(t_indexes),
m_indexCount(t_indexCount),
m_lastTick(millis()),
m_disruptiveEventRunning(false),
nextGroup(nullptr),
m_type(t_type) {}
virtual void tick() = 0;
virtual bool specialTick() = 0;
void setColor(CRGB color)
{
uint32_t* ptr = m_indexes;
for (int i = 0; i < m_indexCount; i++)
{
leds[*ptr].setRGB(color.r, color.g, color.b);
ptr++;
}
}
void setColor(CHSV color)
{
uint32_t* ptr = m_indexes;
for (int j = 0; j < m_indexCount; j++)
{
leds[*ptr].setHSV(color.hue, color.sat, color.val);
ptr++;
}
}
};
class DynamicLamp : public LedGroup
{
CHSV m_staticColor;
const float m_ToggleLightChancePerSecond = DYNAMIC_CHANCE;
bool m_lightsOn;
uint32_t dontChangeAgainUntil;
unsigned long previousMillis = 0; //JG new code
public:
DynamicLamp(uint32_t* t_indexes, size_t t_indexCount, CHSV t_color) : LedGroup(t_indexes, t_indexCount, TYPE_DYNAMIC), m_staticColor(t_color), m_lightsOn(true)
{
setColor(m_staticColor);
dontChangeAgainUntil = millis();
}
void tick()
{
if (millis() - getLastTick() > 1000)
{
setLastTick();
if (random(0, m_ToggleLightChancePerSecond) == 0)
{
toggle();
} else {
}
}
}
bool specialTick()
{
return false;
}
void toggle()
{
if (millis() >= dontChangeAgainUntil)
{
//Serial.println("Changed");
if (m_lightsOn)
{
turnOffSlowly();
}
else
{
turnOnSlowly(m_staticColor);
}
m_lightsOn = !m_lightsOn;
dontChangeAgainUntil = millis() + random(MIN_DYNAMIC_STAY_ONOFF * 1000, MIN_DYNAMIC_STAY_ONOFF * 1000);
}
else {
//Serial.println("Should have changed but didn't");
}
}
void turnOffSlowly()
{
CHSV color;
unsigned long offslowlycurrentMillis = millis(); //JG new code
for (int k = m_staticColor.val; k >= 0; k--)
{
color = CHSV( m_staticColor.hue, m_staticColor.sat, k);
setColor(color);
FastLED.show();
//delay(DYNAMIC_TOGGLE_DURATION_MILLISECONDS / m_staticColor.val); original code
//JG new code
if (offslowlycurrentMillis - previousMillis >= (DYNAMIC_TOGGLE_DURATION_MILLISECONDS / m_staticColor.val)) {
previousMillis = offslowlycurrentMillis;
}
//End JG new code
}
}
void turnOnSlowly(CHSV targetColor)
{
CHSV color;
unsigned long onslowlycurrentMillis = millis(); //JG new code
for (int m = 0; m <= targetColor.val; m++)
{
color = CHSV( targetColor.hue, targetColor.sat, m);
setColor(color);
FastLED.show();
//delay(DYNAMIC_TOGGLE_DURATION_MILLISECONDS / targetColor.val); original code
//JG new code
if (onslowlycurrentMillis - previousMillis >= (DYNAMIC_TOGGLE_DURATION_MILLISECONDS / m_staticColor.val)) {
previousMillis = onslowlycurrentMillis;
}
//End JG new code
}
}
};
class StaticLamp : public LedGroup
{
private:
CHSV m_staticColor;
public:
StaticLamp(uint32_t* t_indexes, size_t t_indexCount, CHSV t_color) : LedGroup(t_indexes, t_indexCount, TYPE_STATIC), m_staticColor(t_color)
{
}
bool specialTick()
{
return false;
}
void tick()
{
setColor(m_staticColor);
}
};
class ExplosionLamp : public LedGroup
{
private:
uint16_t explosionFadeDurationMs = 400;
uint16_t explosionDurationMs = 50;
unsigned long delayStart1, delayWait1;
unsigned long delayStart2, delayWait2;
bool running;
public:
ExplosionLamp(uint32_t* t_indexes, size_t t_indexCount) : LedGroup(t_indexes, t_indexCount, TYPE_EXPLOSION), running(false)
{
}
void tick()
{
}
// This is blocking and it sucks, but that's how it is now.
// Ideally this shouldn't use delays.
bool specialTick()
{ unsigned long st_currentMillis = millis(); // copy the current millis to a variable used to hold its value
uint8_t hue = random(0, 255);
CHSV color;
for (int p = 0; p < 255; p += 6)
{
color = CHSV( hue, 255, p);
setColor(color);
if (random(0, 400) == 5)
specialTick();
FastLED.show();
/*delay(2); original code*/
if (st_currentMillis - st_lastExecutedMillis >= SPECIALTICK_DELAY_TIME) // check to see if 2ms has passed/*JG code*/
{ /*JG code*/
st_lastExecutedMillis = st_currentMillis; // save the last executed time /*JG code*/
} /*JG code*/
}
unsigned long el_currentMillis = millis(); // copy the current millis to a variable used to hold its value
/*delay(random(0, 1000)); original code*/
if (el_currentMillis - el_lastExecutedMillis >= EXPLOSIONLAMP_DELAY_TIME) // check to see if random ms has passed/*JG code*/
{ /*JG code*/
el_lastExecutedMillis = el_currentMillis; // save the last executed time /*JG code*/
}
for (int q = 255; q >= 0; q--)
{
color = CHSV( hue, 255, q);
if (random(0, 400) == 5)
specialTick();
setColor(color);
FastLED.show();
}
return false;
}
};
class House
{
private:
bool hasEvents;
LedGroup* groups;
DISRUPTIVE_EVENT_STAGES m_eventPos;
bool m_eventRunning;
unsigned long m_lastTick;
uint16_t m_eventDelay;
uint16_t m_eventCounter;
const uint16_t m_ToggleEventChancePerSecond = EXPLOSION_CHANCE;
LedGroup* getLastGroup()
{
LedGroup* current = groups;
while (current->nextGroup != nullptr)
{
current = current->nextGroup;
}
return current;
}
void insertGroup(LedGroup* t_group)
{
if (groups == nullptr)
{
groups = t_group;
}
else
{
LedGroup *last = getLastGroup();
if (last != nullptr)
{
last->nextGroup = t_group;
}
}
}
public:
House() : groups(nullptr), m_eventRunning(false), m_eventPos(EVENT_ALL_BLACK), m_eventDelay(0), m_eventCounter(0), hasEvents(false)
{
}
void createGroup(LED_TYPE t_type, uint32_t* indexes, size_t length)
{
LedGroup* newGroup = nullptr;
switch (t_type)
{
case TYPE_STATIC:
newGroup = new StaticLamp(indexes, length, STATIC_COLOR);
break;
case TYPE_DYNAMIC:
newGroup = new DynamicLamp(indexes, length, DYNAMIC_COLOR);
break;
case TYPE_EXPLOSION:
hasEvents = true;
newGroup = new ExplosionLamp(indexes, length);
break;
}
if (newGroup != nullptr)
{
insertGroup(newGroup);
}
}
void setAllColor(CHSV color)
{
LedGroup* current = groups;
while (current != nullptr)
{
current->setColor(color);
current = current->nextGroup;
}
}
bool normalTick()
{
LedGroup* current = groups;
while (current != nullptr)
{
current->tick();
current = current->nextGroup;
}
return true;
}
bool specialTick(LED_TYPE type)
{
LedGroup* current = groups;
while (current != nullptr)
{
if (current->getType() == type)
{
current->specialTick();
}
current = current->nextGroup;
}
}
void eventTick()
{
if (millis() - m_lastTick > m_eventDelay) {
m_lastTick = millis();
//Serial.println(m_eventPos);
switch (m_eventPos)
{
case EVENT_ALL_BLACK:
//Serial.println("all black");
m_eventDelay = 500;
setAllColor(CHSV(0, 0, 0));
m_eventPos = EVENT_STATIC_FLICKER;
break;
//This is blocking for now. Should ideally use timers, no delays.
case EVENT_STATIC_FLICKER:
//Serial.println("static flicker");
for (int r = 0; r < random(3, 6); r++)
{
setAllColor(STATIC_COLOR);
FastLED.show();
delay(random(0, 250));
setAllColor(CHSV(0, 0, 0));
FastLED.show();
delay(random(0, 450));
}
////Serial.println("static flicker");
setAllColor(CHSV(0, 0, 0));
/*delay(2000); JG commented out as it does not seem to make any difference and removes a delay*/
m_eventPos = EVENT_RANDOM_HUE_EXPLOSION;
break;
case EVENT_RANDOM_HUE_EXPLOSION:
//Serial.println("explosion");
m_lastTick = millis();
specialTick(TYPE_EXPLOSION);
resetEvent();
break;
}
}
}
void resetEvent()
{
m_eventRunning = false;
m_eventPos = EVENT_ALL_BLACK;
}
bool isEventRunning()
{
if (!hasEvents)
return false;
if (!m_eventRunning)
{
if (millis() - m_lastTick > 1000 )
{
m_lastTick = millis();
if (random(0, m_ToggleEventChancePerSecond) == 0) {
m_eventRunning = true;
}
}
}
return m_eventRunning;
}
void tick()
{
if (isEventRunning() == false)
{
normalTick();
}
else
{
eventTick();
}
}
};
//This section defines the whereabouts of the lights on the set. House1 is Ollivanders.The ground floor has 4 explosion LEDs, then five of the rooms above have one (2 static, 3 dynamic)
//House2 is the Quidditch shop - 3 leds in the rooms above the shop (1 static, two dynamic)
//House3 is Flourish and Blotts - 5 leds in the rooms above the shop (2 static, 3 dynamic)
#define NUM_HOUSES 3
House* houses[NUM_HOUSES];
//Ollivanders
uint32_t house1_room1[] = {4};
uint32_t house1_room2[] = {5};
uint32_t house1_room3[] = {6};
uint32_t house1_room4[] = {7};
uint32_t house1_room5[] = {8};
uint32_t house1_explosion[] = {0,1,2,3};
//Quidditch
uint32_t house2_room1[] = {9};
uint32_t house2_room2[] = {10};
uint32_t house2_room3[] = {11};
//Flourish and Blotts
uint32_t house3_room1[] = {12};
uint32_t house3_room2[] = {13};
uint32_t house3_room3[] = {14};
uint32_t house3_room4[] = {15};
uint32_t house3_room5[] = {16};
void createGroup() {
}
void setup() {
//**********************************************************************
//*Extra code for flickering wall light leds VVVVVV *
//**********************************************************************
for (int s=0; s<=4; s++){ // set each led pin as an output
pinMode(ledPin[s], OUTPUT);
}
randomSeed(analogRead(0)); // seed the rnd generator with noise from unused pin
for (int t=0; t<=4; t++){ // init each led with a random value
ledState[t] = random(20, 201);
}
//**********************************************************************
//*Extra code for flickering wall light leds *
//**********************************************************************
randomSeed(analogRead(0));
Serial.begin(9600);
while (!Serial);
FastLED.addLeds<CHIPSET, LED_PIN, COLOUR_ORDER>(leds, NUM_LEDS);
/*This tells the library that there's a WS2812B neopixel strand on pin 2 (remember, the value that LED_PIN was set to), and those leds will use the led array leds, and there are NUM_LEDS (aka 19) of them.*/
FastLED.setMaxPowerInVoltsAndMilliamps (VOLTS, MAX_AMPS);
/*This defines the voltage and maximum milliamp draw allowed*/
FastLED.clear();
/*Clears out any memorised values*/
houses[0] = new House();
/*houses[0]->createGroup(TYPE_STATIC, &house1_floor[0], (size_t)(sizeof(house1_floor) / sizeof(house1_floor[0])));*/
houses[0]->createGroup(TYPE_STATIC, &house1_room1[0], (size_t)(sizeof(house1_room1) / sizeof(house1_room1[0])));
houses[0]->createGroup(TYPE_STATIC, &house1_room4[0], (size_t)(sizeof(house1_room4) / sizeof(house1_room4[0])));
houses[0]->createGroup(TYPE_DYNAMIC, &house1_room2[0], (size_t)(sizeof(house1_room2) / sizeof(house1_room2[0])));
houses[0]->createGroup(TYPE_DYNAMIC, &house1_room3[0], (size_t)(sizeof(house1_room3) / sizeof(house1_room3[0])));
houses[0]->createGroup(TYPE_DYNAMIC, &house1_room5[0], (size_t)(sizeof(house1_room5) / sizeof(house1_room5[0])));
houses[0]->createGroup(TYPE_EXPLOSION, &house1_explosion[0], (size_t)(sizeof(house1_explosion) / sizeof(house1_explosion[0])));
houses[1] = new House();
/*houses[1]->createGroup(TYPE_STATIC, &house2_floor[0], (size_t)(sizeof(house2_floor) / sizeof(house2_floor[0])));*/
houses[1]->createGroup(TYPE_STATIC, &house2_room1[0], (size_t)(sizeof(house2_room1) / sizeof(house2_room1[0])));
houses[1]->createGroup(TYPE_DYNAMIC, &house2_room2[0], (size_t)(sizeof(house2_room2) / sizeof(house2_room2[0])));
houses[1]->createGroup(TYPE_DYNAMIC, &house2_room3[0], (size_t)(sizeof(house2_room3) / sizeof(house2_room3[0])));
houses[2] = new House();
/*houses[2]->createGroup(TYPE_STATIC, &house3_floor[0], (size_t)(sizeof(house3_floor) / sizeof(house3_floor[0])));*/
houses[2]->createGroup(TYPE_STATIC, &house3_room1[0], (size_t)(sizeof(house3_room1) / sizeof(house3_room1[0])));
houses[2]->createGroup(TYPE_STATIC, &house3_room2[0], (size_t)(sizeof(house3_room2) / sizeof(house3_room2[0])));
houses[2]->createGroup(TYPE_DYNAMIC, &house3_room3[0], (size_t)(sizeof(house3_room3) / sizeof(house3_room3[0])));
houses[2]->createGroup(TYPE_DYNAMIC, &house3_room4[0], (size_t)(sizeof(house3_room4) / sizeof(house3_room4[0])));
houses[2]->createGroup(TYPE_DYNAMIC, &house3_room5[0], (size_t)(sizeof(house3_room5) / sizeof(house3_room5[0])));
/*houses[3] = new House();
houses[3]->createGroup(TYPE_STATIC, &streetlight[0], (size_t)(sizeof(streetlight) / sizeof(streetlight[0])));*/
}
void loop() {
unsigned long currentMillis = millis(); // copy the current millis to a variable used to hold its value
for (int u = 0; u < NUM_HOUSES; u++)
{
houses[u]->tick(); /* this gets the value returned by the tick function from the structure that houses points to */
}
FastLED.show();
//delay(30); original code
if (currentMillis - lastExecutedMillis >= MAINLOOP_DELAY_TIME) // check to see if 30ms has passed /*JG code*/
{ /*JG code*/
lastExecutedMillis = currentMillis; // save the last executed time /*JG code*/
}
//**********************************************************************
//*Extra code for flickering wall light leds VVVVVV *
//**********************************************************************
for (int n=0; n<=7; n++){ // for each led:
analogWrite(ledPin[n], ledState[n]); // set the pwm value of that pin determined previously
randNumber = random(-40, 41); // generate new random number and add that to the current value
ledState[n] += randNumber; // that range can be tweaked to change the intensity of the flickering
if (ledState[n] > 200) { // clamp the limits of the pwm values so it remains within
ledState[n] = 200; // a pleasing range as well as the pwm range
}
if (ledState[n] < 10) {
ledState[n] = 10;
}
}
delay(10); // the delay between changes
//**********************************************************************
//*Extra code for flickering wall light leds *
//**********************************************************************
}