Compare commits

..

No commits in common. "8323450957635bb5f049ccc32e7e0b3a3b2918a2" and "e702145635de763d9d50d2bb56e38d8db123253c" have entirely different histories.

5 changed files with 102 additions and 102 deletions

1
.gitignore vendored
View File

@ -1 +0,0 @@
build/

View File

@ -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) $<

View File

@ -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
--------- ---------

View File

@ -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();

View File

@ -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!"