2016-11-27 10:26:04 -07:00
|
|
|
#include <Adafruit_NeoPixel.h>
|
2020-11-28 20:23:35 -07:00
|
|
|
#include <EEPROM.h>
|
|
|
|
#include "bounce2.h"
|
|
|
|
#include "morse.h"
|
2016-11-27 10:26:04 -07:00
|
|
|
|
2020-11-28 20:23:35 -07:00
|
|
|
// Do you want it to run forever, or cycle every 24 hours?
|
|
|
|
#define FOREVER false
|
2016-11-27 10:26:04 -07:00
|
|
|
|
2020-11-28 20:23:35 -07:00
|
|
|
// Which pin your LED strip is connected to
|
|
|
|
#define NEOPIXEL_PIN 6
|
2016-11-27 18:06:09 -07:00
|
|
|
|
2020-11-28 20:23:35 -07:00
|
|
|
// Which pin has the momentary switch to toggle full white.
|
|
|
|
#define BUTTON_PIN 4
|
2016-12-11 21:02:37 -07:00
|
|
|
|
|
|
|
// How many LEDs you have. It's okay if this is too big.
|
2020-11-28 20:23:35 -07:00
|
|
|
#define LEDS_PER_GROUP 10
|
|
|
|
#define NUM_GROUPS 20
|
2016-12-11 21:02:37 -07:00
|
|
|
|
2020-11-28 20:23:35 -07:00
|
|
|
// How many milliseconds between activity in one group
|
|
|
|
#define DELAY_MS 600
|
2016-12-11 21:02:37 -07:00
|
|
|
|
2020-11-28 20:23:35 -07:00
|
|
|
// What percentage chance a chosen light has of being on
|
|
|
|
#define ACTIVITY 40
|
|
|
|
|
|
|
|
// Which pixel to flash messages in morse code: -1 = disable
|
|
|
|
#define MORSE_PIXEL 8
|
|
|
|
#define MORSE_COLOR 0x880000
|
|
|
|
|
|
|
|
// How long a dit lasts
|
|
|
|
#define DIT_DURATION_MS 100
|
|
|
|
|
|
|
|
// Color for all-white mode
|
|
|
|
#define WHITE 0x886655
|
|
|
|
|
|
|
|
// The Neopixel library masks interrupts while writing.
|
|
|
|
// This means you lose time.
|
|
|
|
// How much time do you lose?
|
|
|
|
// I'm guessing 10 minutes a day.
|
|
|
|
#define SNOSSLOSS (10 * 60 * 1000)
|
|
|
|
#define DAY (24 * 60 * 60 * 1000 - SNOSSLOSS)
|
|
|
|
#define ON_FOR (5 * 60 * 60 * 1000)
|
|
|
|
|
|
|
|
const char *messages[] = {
|
|
|
|
"happy holiday ARK",
|
|
|
|
"seasons greetings ARK",
|
|
|
|
"happy festivus ARK",
|
|
|
|
"merry christmas ARK",
|
|
|
|
"hanukkah sameach ARK",
|
|
|
|
"happy solstice ARK",
|
|
|
|
"happy new year ARK",
|
|
|
|
};
|
|
|
|
const int nmessages = sizeof(messages) / sizeof(*messages);
|
2016-11-27 10:26:04 -07:00
|
|
|
|
2020-11-28 20:23:35 -07:00
|
|
|
// These colors mirror pretty closely some cheapo LED lights we have
|
2016-11-27 10:26:04 -07:00
|
|
|
const uint32_t colors[] = {
|
2020-11-28 20:23:35 -07:00
|
|
|
0xdd4400, // Yellow
|
|
|
|
0xff0000, // Red
|
|
|
|
0xdd2200, // Amber
|
|
|
|
0x004400, // Green
|
|
|
|
|
|
|
|
0xdd4400, // Yellow
|
|
|
|
0xff0000, // Red
|
|
|
|
0xdd2200, // Amber
|
|
|
|
0x880044, // Purple
|
|
|
|
|
|
|
|
0xdd4400, // Yellow
|
|
|
|
0xff0000, // Red
|
|
|
|
0xdd2200, // Amber
|
|
|
|
0x000088, // Blue
|
2016-11-27 10:26:04 -07:00
|
|
|
};
|
|
|
|
const int ncolors = sizeof(colors) / sizeof(*colors);
|
|
|
|
|
2020-11-28 20:23:35 -07:00
|
|
|
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_GROUPS * LEDS_PER_GROUP, NEOPIXEL_PIN, NEO_RGB | NEO_KHZ800);
|
|
|
|
Bounce button;
|
|
|
|
|
|
|
|
int mode;
|
|
|
|
int nextMode;
|
|
|
|
|
2016-11-27 10:26:04 -07:00
|
|
|
void setup() {
|
|
|
|
strip.begin();
|
|
|
|
strip.show();
|
2020-11-28 20:23:35 -07:00
|
|
|
button.attach(BUTTON_PIN, INPUT_PULLUP);
|
|
|
|
pinMode(LED_BUILTIN, OUTPUT);
|
|
|
|
mode = EEPROM.read(0);
|
2016-11-27 10:26:04 -07:00
|
|
|
}
|
|
|
|
|
2020-11-28 20:23:35 -07:00
|
|
|
bool strandUpdate(bool white) {
|
|
|
|
static unsigned long nextEventMillis = 0;
|
|
|
|
unsigned long now = millis();
|
|
|
|
static int group = 0;
|
|
|
|
|
|
|
|
if (now < nextEventMillis) {
|
|
|
|
return false;
|
2016-11-27 18:06:09 -07:00
|
|
|
}
|
|
|
|
|
2020-11-28 20:23:35 -07:00
|
|
|
int pos = (group * LEDS_PER_GROUP) + random(LEDS_PER_GROUP);
|
|
|
|
if (random(100) < ACTIVITY) {
|
|
|
|
uint32_t color;
|
|
|
|
|
|
|
|
if (white) {
|
|
|
|
color = WHITE;
|
2016-11-27 10:26:04 -07:00
|
|
|
} else {
|
2020-11-28 20:23:35 -07:00
|
|
|
color = colors[random(ncolors)];
|
2016-11-27 10:26:04 -07:00
|
|
|
}
|
2020-11-28 20:23:35 -07:00
|
|
|
strip.setPixelColor(pos, color);
|
|
|
|
} else {
|
|
|
|
strip.setPixelColor(pos, 0);
|
2016-11-27 10:26:04 -07:00
|
|
|
}
|
2020-11-28 20:23:35 -07:00
|
|
|
group = (group + 1) % NUM_GROUPS;
|
|
|
|
|
|
|
|
nextEventMillis = now + (DELAY_MS / NUM_GROUPS);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool morseUpdate() {
|
|
|
|
static MorseEncoder enc;
|
|
|
|
static unsigned long nextEventMillis = 0;
|
|
|
|
unsigned long now = millis();
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
if (now >= nextEventMillis) {
|
|
|
|
nextEventMillis = now + DIT_DURATION_MS;
|
|
|
|
if (!enc.Tick()) {
|
|
|
|
char *message = messages[random(nmessages)];
|
|
|
|
enc.SetText(message);
|
|
|
|
enc.Quiet(200);
|
|
|
|
}
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
strip.setPixelColor(MORSE_PIXEL, enc.Transmitting ? MORSE_COLOR : 0);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool black() {
|
|
|
|
static unsigned long nextEventMillis = 0;
|
|
|
|
unsigned long now = millis();
|
|
|
|
|
|
|
|
if (now < nextEventMillis) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
strip.clear();
|
|
|
|
|
|
|
|
nextEventMillis = now + (DELAY_MS / NUM_GROUPS); // Keep timing consistent
|
|
|
|
return true;
|
2016-11-27 18:06:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void loop() {
|
2020-11-28 20:23:35 -07:00
|
|
|
static bool white = false;
|
|
|
|
bool update = false;
|
|
|
|
|
|
|
|
button.update();
|
|
|
|
if (button.fell()) {
|
|
|
|
white = !white;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (forever || white || (millis() % DAY < ON_FOR)) {
|
|
|
|
update |= strandUpdate(white);
|
|
|
|
update |= morseUpdate();
|
2016-11-27 18:06:09 -07:00
|
|
|
} else {
|
2020-11-28 20:23:35 -07:00
|
|
|
update |= black();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (update) {
|
|
|
|
strip.show();
|
2016-11-27 18:06:09 -07:00
|
|
|
}
|
2016-12-11 21:02:37 -07:00
|
|
|
|
2020-11-28 20:23:35 -07:00
|
|
|
// blink the heart for a little bit
|
|
|
|
if (millis() < 30 * 1000) {
|
|
|
|
bool on = (millis() % 1000) < 500;
|
|
|
|
digitalWrite(LED_BUILTIN, on);
|
|
|
|
}
|
2016-11-27 10:26:04 -07:00
|
|
|
}
|