Compare commits
No commits in common. "8323450957635bb5f049ccc32e7e0b3a3b2918a2" and "e702145635de763d9d50d2bb56e38d8db123253c" have entirely different histories.
8323450957
...
e702145635
|
@ -1 +0,0 @@
|
||||||
build/
|
|
55
Makefile
55
Makefile
|
@ -1,52 +1,5 @@
|
||||||
PACKAGE = holiday-lights
|
#FQBN = adafruit:samd:adafruit_trinket_m0
|
||||||
|
FQBN = adafruit:avr:protrinket5
|
||||||
|
|
||||||
FQBN_QTPY = adafruit:samd:adafruit_qtpy_m0
|
install: holiday-lights.ino
|
||||||
FQBN_XIAO = Seeeduino:samd:seeed_XIAO_m0
|
arduino --upload --board $(FQBN) $@
|
||||||
UF2_MOUNT = /mnt/chromeos/removable/Arduino
|
|
||||||
ARDUINO_DIR = /app/Arduino
|
|
||||||
BUILDER = flatpak run --command ${ARDUINO_DIR}/arduino-builder cc.arduino.arduinoide
|
|
||||||
|
|
||||||
default: build/$(PACKAGE).qtpy.uf2 build/$(PACKAGE).xiao.uf2
|
|
||||||
install: build/$(PACKAGE).xiao.uf2
|
|
||||||
./install.sh $< $(UF2_MOUNT)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf build/*
|
|
||||||
|
|
||||||
# uf2conv.py is covered by an MIT license.
|
|
||||||
build/uf2conv.py: build/uf2families.json
|
|
||||||
mkdir -p build
|
|
||||||
curl -L https://raw.githubusercontent.com/microsoft/uf2/master/utils/$(@F) > $@
|
|
||||||
chmod +x $@
|
|
||||||
build/uf2families.json:
|
|
||||||
mkdir -p build
|
|
||||||
curl -L https://raw.githubusercontent.com/microsoft/uf2/master/utils/$(@F) > $@
|
|
||||||
|
|
||||||
%.xiao.uf2: %.xiao.bin build/uf2conv.py
|
|
||||||
build/uf2conv.py -b 0x2000 -c -o $@ $<
|
|
||||||
|
|
||||||
%.qtpy.uf2: %.qtpy.bin build/uf2conv.py
|
|
||||||
build/uf2conv.py -b 0x2000 -c -o $@ $<
|
|
||||||
|
|
||||||
build/%.qtpy.bin: FQBN = $(FQBN_QTPY)
|
|
||||||
build/%.xiao.bin: FQBN = $(FQBN_XIAO)
|
|
||||||
build/$(PACKAGE).%.bin: $(PACKAGE).ino *.cpp *.h
|
|
||||||
mkdir -p build/$*
|
|
||||||
arduino-builder \
|
|
||||||
-build-cache ~/.cache/arduino \
|
|
||||||
-build-path build/$* \
|
|
||||||
-core-api-version 10813 \
|
|
||||||
-fqbn $(FQBN) \
|
|
||||||
-hardware ~/.arduino15/packages \
|
|
||||||
-tools $(ARDUINO_DIR)/tools-builder \
|
|
||||||
-tools ~/.arduino15/packages \
|
|
||||||
-hardware $(ARDUINO_DIR)/hardware \
|
|
||||||
-hardware ~/.arduino15/packages \
|
|
||||||
-built-in-libraries $(ARDUINO_DIR)/libraries \
|
|
||||||
-libraries ~/Arduino/libraries \
|
|
||||||
-compile \
|
|
||||||
$<
|
|
||||||
mv build/$*/$(<F).bin $@
|
|
||||||
|
|
||||||
upload: $(PACKAGE).ino
|
|
||||||
arduino --upload --board $(FQBN) $<
|
|
||||||
|
|
|
@ -3,8 +3,6 @@ Holiday Lights
|
||||||
|
|
||||||
Some twinkling lights for your tree/menorah/office/door/roof/habitrail.
|
Some twinkling lights for your tree/menorah/office/door/roof/habitrail.
|
||||||
|
|
||||||
The canonical home for this code is https://git.woozle.org/neale/holiday-lights
|
|
||||||
|
|
||||||
|
|
||||||
Materials
|
Materials
|
||||||
---------
|
---------
|
||||||
|
|
|
@ -1,20 +1,39 @@
|
||||||
|
#if defined(__AVR_ATtiny85__)
|
||||||
|
#define TINY
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <FastLED.h>
|
#include <FastLED.h>
|
||||||
#include "Debounce.h"
|
#include "Debounce.h"
|
||||||
#include "durations.h"
|
#include "durations.h"
|
||||||
#include "morse.h"
|
#include "morse.h"
|
||||||
#include "pulse.h"
|
#include "pulse.h"
|
||||||
|
|
||||||
// Which pins your LED strips are connected to
|
// Do you want it to run forever, or cycle every 24 hours?
|
||||||
#define NUM_OUTPUTS 2
|
#ifdef TINY
|
||||||
#define OUTPUT_PIN1 7
|
#define FOREVER true
|
||||||
#define OUTPUT_PIN2 8
|
#else
|
||||||
|
#define FOREVER false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Which pin your LED strip is connected to
|
||||||
|
#if defined(TINY)
|
||||||
|
#define NEOPIXEL_PIN 3
|
||||||
|
#elif defined(ADAFRUIT_TRINKET_M0)
|
||||||
|
#define NEOPIXEL_PIN 2
|
||||||
|
#else
|
||||||
|
#define NEOPIXEL_PIN 6
|
||||||
|
#endif
|
||||||
|
|
||||||
// Which pin has the momentary switch to toggle full white.
|
// Which pin has the momentary switch to toggle full white.
|
||||||
#define BUTTON_PIN 4
|
#define BUTTON_PIN 4
|
||||||
|
|
||||||
// How many LEDs you have per output. It's okay if this is too big.
|
// How many LEDs you have. It's okay if this is too big.
|
||||||
#define LEDS_PER_GROUP 10
|
#define LEDS_PER_GROUP 10
|
||||||
#define NUM_GROUPS 10
|
#ifdef TINY
|
||||||
|
#define NUM_GROUPS 7
|
||||||
|
#else
|
||||||
|
#define NUM_GROUPS 20
|
||||||
|
#endif
|
||||||
#define NUM_LEDS (LEDS_PER_GROUP * NUM_GROUPS)
|
#define NUM_LEDS (LEDS_PER_GROUP * NUM_GROUPS)
|
||||||
|
|
||||||
// How many milliseconds between activity in one group
|
// How many milliseconds between activity in one group
|
||||||
|
@ -33,22 +52,33 @@
|
||||||
// How long a dit lasts
|
// How long a dit lasts
|
||||||
#define DIT_DURATION_MS 150
|
#define DIT_DURATION_MS 150
|
||||||
|
|
||||||
|
// 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_DAY (DURATION_DAY - (10 * DURATION_MINUTE))
|
||||||
|
#define ON_FOR (6 * DURATION_HOUR)
|
||||||
|
|
||||||
#define ARK "\x03\x04"
|
#define ARK "\x03\x04"
|
||||||
const char *message = (
|
const char *message = (
|
||||||
|
|
||||||
"seasons greetings" ARK
|
"seasons greetings" ARK
|
||||||
"happy holiday" ARK
|
"happy holiday" ARK
|
||||||
"merry xmas" ARK
|
"merry xmas" ARK
|
||||||
"happy new year" ARK
|
"happy new year" ARK
|
||||||
"CQ CQ OF9X" ARK
|
"CQ CQ OF9X" ARK
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CRGB leds[NUM_OUTPUTS][NUM_LEDS];
|
CRGB leds[NUM_LEDS];
|
||||||
Debounce button(BUTTON_PIN, false, true);
|
Debounce button(BUTTON_PIN, false, true);
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
FastLED.addLeds<WS2812, OUTPUT_PIN1, RGB>(leds[0], NUM_LEDS);
|
FastLED.addLeds<WS2812, NEOPIXEL_PIN, RGB>(leds, NUM_LEDS);
|
||||||
FastLED.addLeds<WS2812, OUTPUT_PIN2, RGB>(leds[1], NUM_LEDS);
|
|
||||||
FastLED.setTemperature(Tungsten40W);
|
|
||||||
FastLED.setBrightness(52);
|
FastLED.setBrightness(52);
|
||||||
pinMode(LED_BUILTIN, OUTPUT);
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
}
|
}
|
||||||
|
@ -70,59 +100,94 @@ uint8_t RandomHue() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Pulse mainPulse = Pulse(DELAY_MS);
|
||||||
|
|
||||||
bool strandUpdate(unsigned long now, bool white) {
|
bool strandUpdate(unsigned long now, bool white) {
|
||||||
static Pulse pulse = Pulse(DELAY_MS);
|
if (!mainPulse.Ticked(now)) {
|
||||||
if (!pulse.Ticked(now)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int output = 0; output < NUM_OUTPUTS; output += 1) {
|
for (int group = 0; group < NUM_GROUPS; ++group) {
|
||||||
for (int group = 0; group < NUM_GROUPS; group += 1) {
|
|
||||||
int pos = (group * LEDS_PER_GROUP) + random(LEDS_PER_GROUP);
|
int pos = (group * LEDS_PER_GROUP) + random(LEDS_PER_GROUP);
|
||||||
if (random(100) < GROUP_UPDATE_PROBABILITY) {
|
if (random(100) < GROUP_UPDATE_PROBABILITY) {
|
||||||
uint8_t hue = 0;
|
uint8_t hue = 0;
|
||||||
uint8_t saturation = white?0:255;
|
uint8_t saturation = 255;
|
||||||
uint8_t value = 255;
|
uint8_t value = 255;
|
||||||
if (random(100) < ACTIVITY) {
|
if (random(100) < ACTIVITY) {
|
||||||
|
if (white) {
|
||||||
|
saturation = 0;
|
||||||
|
} else {
|
||||||
hue = RandomHue();
|
hue = RandomHue();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
value = 0;
|
value = 0;
|
||||||
}
|
}
|
||||||
leds[output][pos] = CHSV(hue, saturation, value);
|
leds[pos] = CHSV(hue, saturation, value);
|
||||||
group = (group + 1) % NUM_GROUPS;
|
group = (group + 1) % NUM_GROUPS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool black(unsigned long now) {
|
||||||
|
if (!mainPulse.Ticked(now)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FastLED.clear();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool morseUpdate(unsigned long now) {
|
bool morseUpdate(unsigned long now) {
|
||||||
static MorseEncoder enc;
|
static MorseEncoder enc;
|
||||||
static Pulse pulse = Pulse(DIT_DURATION_MS);
|
static Pulse pulse = Pulse(DIT_DURATION_MS);
|
||||||
if (!pulse.Ticked(now)) {
|
bool ret = false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (pulse.Ticked(now)) {
|
||||||
if (!enc.Tick()) {
|
if (!enc.Tick()) {
|
||||||
enc.SetText(message);
|
enc.SetText(message);
|
||||||
}
|
}
|
||||||
leds[0][MORSE_PIXEL] = enc.Transmitting ? MORSE_COLOR : CRGB::Black;
|
ret = true;
|
||||||
|
}
|
||||||
|
leds[MORSE_PIXEL] = enc.Transmitting ? MORSE_COLOR : CRGB::Black;
|
||||||
|
|
||||||
return true;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool timeUpdate(unsigned long now, unsigned long timeLeft) {
|
||||||
|
unsigned int hoursLeft = timeLeft / DURATION_HOUR;
|
||||||
|
unsigned int minutesLeft = (timeLeft / DURATION_MINUTE) % 60;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < hoursLeft; ++i) {
|
||||||
|
leds[i] = CHSV(0, 255, 32);
|
||||||
|
}
|
||||||
|
if ((timeLeft / DELAY_MS) % minutesLeft == 0) {
|
||||||
|
leds[i]= CHSV(0, 255, 32);
|
||||||
|
} else {
|
||||||
|
leds[i] = CRGB::Black;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
bool white = false;
|
bool white = false;
|
||||||
bool update = false;
|
bool update = false;
|
||||||
unsigned long now = millis(); // Everybody uses the same time so we don't do spurious updates 5ms apart
|
unsigned long now = millis(); // Everybody uses the same time so we don't do spurious updates 5ms apart
|
||||||
|
unsigned long timeOfDay = now % DURATION_DAY;
|
||||||
|
|
||||||
button.read();
|
button.read();
|
||||||
white = (button.count() % 2 == 1);
|
white = (button.count() % 2 == 1);
|
||||||
|
|
||||||
|
if (FOREVER || white || (timeOfDay < ON_FOR)) {
|
||||||
update |= strandUpdate(now, white);
|
update |= strandUpdate(now, white);
|
||||||
//update |= morseUpdate(now);
|
update |= morseUpdate(now);
|
||||||
|
} else {
|
||||||
|
update |= black(now);
|
||||||
|
update |= timeUpdate(now, DURATION_DAY - timeOfDay);
|
||||||
|
}
|
||||||
|
|
||||||
if (update) {
|
if (update) {
|
||||||
FastLED.show();
|
FastLED.show();
|
||||||
|
|
15
install.sh
15
install.sh
|
@ -1,15 +0,0 @@
|
||||||
#! /bin/sh
|
|
||||||
|
|
||||||
src=$1
|
|
||||||
dst=$2
|
|
||||||
|
|
||||||
info=$dst/INFO_UF2.txt
|
|
||||||
echo -n "Waiting for $info to appear..."
|
|
||||||
while ! [ -f $info ]; do
|
|
||||||
echo -n "."
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
echo "👍"
|
|
||||||
|
|
||||||
cp $src $dst
|
|
||||||
echo "Installed!"
|
|
Loading…
Reference in New Issue