Last active
November 2, 2017 19:10
-
-
Save t413/6463677f4828409934d34309b4ce84ba to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <FastLED.h> | |
#include <SPI.h> | |
#include <Wire.h> | |
#include <Adafruit_GFX.h> | |
#include <Adafruit_LEDBackpack.h> | |
#include <Encoder.h> | |
#include <avr/sleep.h> | |
#include <avr/power.h> | |
#define PXLS_CNT 10+3 | |
#define encoderBtnPin 3 | |
#define encoderPinA 4 | |
#define encoderPinB 5 | |
#define LED_PIN 6 | |
Encoder encoder(encoderPinA, encoderPinB); | |
#define ENC_SCALE 3 | |
#define EASTER_EGG_ENC 133 | |
CRGB pixels[PXLS_CNT]; | |
Adafruit_AlphaNum4 alpha4; | |
char displayBuffer[4] = {' ', ' ', ' ', ' '}; | |
void segprint(String s) { | |
s.toUpperCase(); | |
for (uint8_t i=0; i < 4 && i < s.length(); i++) | |
displayBuffer[i] = s.charAt(i); | |
} | |
void segprint(int num) { | |
sprintf(displayBuffer + 1, "%3i", num); | |
} | |
const uint8_t segChars[] = { | |
0b00111111, // 0 | |
0b00000110, // 1 | |
0b01011011, // 2 | |
0b01001111, // 3 | |
0b01100110, // 4 | |
0b01101101, // 5 | |
0b01111101, // 6 | |
0b00000111, // 7 | |
0b01111111, // 8 | |
0b01101111, // 9 | |
0b00000000, // : | |
0b00000000, // ; | |
0b00000000, // < | |
0b01001000, // = | |
0b00000000, // > | |
0b00000011, // ? | |
0b00111011, // @ | |
0b01110111, // A | |
0b01111100, // b | |
0b00111001, // C | |
0b01011110, // d | |
0b01111001, // E | |
0b01110001, // F | |
0b00111101, // G | |
0b01110110, // H | |
0b00110000, // I | |
0b00011110, // J | |
0b01110000, // K | |
0b00111000, // L | |
0b00110110, // M | |
0b00110110, // N | |
0b00111111, // O | |
0b01110011, // P | |
0b00111111, // Q | |
0b01110011, // R | |
0b01101101, // S | |
0b00000111, // T | |
0b00111110, // U | |
0b00110000, // V | |
0b00110110, // W | |
0b00000000, // X | |
0b00000000, // Y | |
0b00001001 // Z | |
}; | |
void alpha4Write() { | |
for (uint8_t i=0; i < 4; i++) { | |
char c = displayBuffer[i]; | |
uint8_t d = segChars[constrain(c - '0', 0, sizeof(segChars))]; | |
if (c == ' ') d = 0; | |
alpha4.writeDigitRaw((i < 2)? i : i + 1, d); | |
} | |
} | |
int8_t brightness = 3; | |
void setup() { | |
alpha4.begin(0x70); | |
pinMode(encoderBtnPin, INPUT); digitalWrite(encoderBtnPin, HIGH); | |
pinMode(13, OUTPUT); //onboard led | |
randomSeed(analogRead(2)); | |
LEDS.addLeds<WS2812,LED_PIN,GRB>(pixels, PXLS_CNT); | |
setBrightness(brightness); | |
encoder.write(137 * ENC_SCALE); | |
} | |
void setBrightness(uint8_t level) { //sets leds and characters to value 0-7 | |
LEDS.setBrightness(map(level, 0, 7, 5, 220)); | |
alpha4.setBrightness(level * 3 / 2); | |
} | |
void loop() { | |
uint8_t baseColor = 105; | |
uint8_t lastBtn = 0; | |
uint32_t btnDown = 0; | |
int16_t btnDownEnc = 0; | |
int16_t oldEncPos = 0; | |
uint8_t displayOverride = 0, overrideClick = 0; //1=brightness 2=off display | |
uint32_t pulse_begin = 0; | |
uint16_t pulse_length = 400, recover_len = 1000; | |
uint8_t clrs[] = {0,0,0,0,0,0,0,0, 32,192,110,60,160,105,000,64,224,32 }; //half set, half random | |
for (uint8_t i=0; i < 8; i++) clrs[i] = random(0, 255); | |
uint16_t vccMeasured = 0; | |
while(1) { | |
const uint32_t current = millis(); | |
displayBuffer[0] = displayBuffer[1] = displayBuffer[2] = displayBuffer[3] = ' '; | |
int16_t encoderPos = encoder.read() / ENC_SCALE; | |
/// --------------------- /// | |
/// -- Button Handling -- /// | |
/// --------------------- /// | |
const uint8_t btn = !digitalRead(encoderBtnPin); | |
digitalWrite(13, btn); | |
if (btn && !lastBtn) { //mark the state! | |
if (current - btnDown < 300) { /// -- double click | |
encoderPos += (random(0, 3) > 0)? random(8, 120) : -random(8, 40); | |
encoder.write(encoderPos * ENC_SCALE); | |
overrideClick = true; | |
pulse_begin = 0; | |
} | |
btnDown = current; | |
btnDownEnc = encoderPos; | |
} | |
if (!btn && !btnDown) { //intentional stub, do nothing! | |
} else if (!btn && lastBtn && (current - btnDown) < 1000) { /// -- short press (on release) | |
if (overrideClick) { | |
overrideClick = false; | |
} else if (btnDownEnc == EASTER_EGG_ENC) { | |
easterEgg(); | |
btnDownEnc = encoderPos = EASTER_EGG_ENC + 1; | |
displayOverride = 1; //restores more state later | |
} else { | |
recover_len = (current > (pulse_begin + 3*pulse_length + recover_len))? 1000 : 2000; | |
pulse_begin = current; | |
} | |
} else if (btn && (current - btnDown) > 700 && (displayOverride != 1)) { /// -- long hold (immediate action) | |
//animate thre reset-counting up/down to reality C147 | |
encoderPos = map(constrain(current - btnDown, 700, 1100), 700, 1100, btnDownEnc, 137); | |
encoder.write(encoderPos * ENC_SCALE); | |
if (current - btnDown > 1500) { //animation for turning off | |
displayOverride = 2; | |
String off("off "); | |
segprint(off.substring(0, map(current - btnDown, 1300, 2500, 0, 3))); | |
if (current - btnDown > 2500) { | |
LEDS.setBrightness(0); FastLED.show(); | |
while (!digitalRead(encoderBtnPin)) delay(10); //wait for button release | |
while (true) { //sleep loop, requires a longer press wakeup | |
doSleep(); | |
uint32_t start = millis(); | |
while (!digitalRead(encoderBtnPin)) delay(1); //wait for button release | |
if (millis() - start > 20) | |
break; | |
} | |
if (vccMeasured = readVcc() < 3500) { //low battery warning! | |
segprint("bATT"); | |
delay(750); | |
segprint(vccMeasured); //show voltage readout | |
delay(2000); | |
} | |
return; //restarts the while loop | |
} | |
} | |
} else if (btn && !displayOverride && btnDownEnc != encoderPos) { /// -- button down *while turning* | |
displayOverride = 1; | |
} else if (btn && (displayOverride == 1)) { | |
brightness = constrain(3 + ((int)encoderPos - (int)btnDownEnc), -2, 7); | |
setBrightness((brightness >= 0)? brightness : 3); | |
if (brightness == -1) { //show 'bATT' | |
segprint("BATT"); | |
} else if (brightness == -2) { | |
if (!vccMeasured) vccMeasured = readVcc(); | |
segprint(vccMeasured); //show voltage readout | |
} else { | |
segprint(brightness); | |
segprint("B"); | |
} | |
} | |
if (!btn && displayOverride) { //finished updating brightness | |
if (displayOverride != 2) | |
encoder.write(((encoderPos = btnDownEnc)) * ENC_SCALE); //restore encoder position | |
displayOverride = oldEncPos = vccMeasured = 0; | |
} | |
lastBtn = btn; | |
/// ---------------------- /// | |
/// -- Encoder Handling -- /// | |
/// ---------------------- /// | |
bool renderSeg = displayOverride; | |
if (oldEncPos != encoderPos && !displayOverride) { | |
renderSeg = true; | |
oldEncPos = encoderPos; | |
char dimensionLetter = (encoderPos / 10 - 1) % 10 + 'A'; | |
baseColor = clrs[(encoderPos / 10) % sizeof(clrs)]; //105 + (dimensionLetter - 12) * 75; | |
displayBuffer[0] = dimensionLetter; | |
segprint(encoderPos); | |
} | |
if (renderSeg) { | |
alpha4.clear(); | |
alpha4Write(); | |
alpha4.writeDisplay(); | |
} | |
#define cap8b(a) (constrain((a), 0, 255)) | |
/// --------------- /// | |
/// -- LED Magic -- /// | |
/// --------------- /// | |
int16_t spotlight_cycle = (current >> 4) & 0xFF; | |
int16_t pulse = 0, recovery = 0; //quadwave8((current >> 2)) / 4; | |
//button press effects | |
if (pulse_begin && current < (pulse_begin + pulse_length)) { | |
pulse = cap8b(map(current - pulse_begin, 0, pulse_length, 0, 240)); | |
} else if (pulse_begin && current < (pulse_begin + recover_len)) { //button press effect recovery | |
recovery = cap8b(map(current - pulse_begin, pulse_length, recover_len, 200, 0)); | |
} | |
for (uint8_t w = 0; w < 3; w++) { //front three | |
int16_t spotlight_sin = quadwave8(spotlight_cycle + 255/w) / 2 + 128; | |
pixels[w] = CHSV(baseColor, 255 - pulse, cap8b(spotlight_sin + pulse - recovery/2)); | |
} | |
for (uint8_t w = 3; w < PXLS_CNT; w++) { | |
int16_t spotlight_sin = quadwave8(spotlight_cycle + w*32) / 2 + 128; | |
pixels[w] = CHSV(baseColor, 255 - pulse, cap8b(spotlight_sin - recovery)); | |
} | |
FastLED.show(); | |
} | |
} | |
void easterEgg() { | |
uint32_t start = millis(), current = 0; | |
while ((current = millis()) < (start + 1000) || digitalRead(encoderBtnPin)) { | |
const int16_t cycle = (current >> 2) & 0xFF; | |
int16_t encoderPos = encoder.read() / ENC_SCALE; | |
if (millis() & 1) { | |
const uint8_t scycle = current >> 6; | |
alpha4.clear(); | |
alpha4.writeDigitRaw((encoderPos % 4 < 2)? encoderPos % 4 : encoderPos % 4 + 1, (1 << (scycle % 6)) ); | |
alpha4.writeDisplay(); | |
} | |
#define roll8b(a) ((a) & 0xff) | |
for (int16_t w = 0; w < 3; w++) { //front three | |
pixels[w] = CHSV(roll8b(cycle + w * 82), 255, 255); | |
} | |
for (int16_t w = 3; w < PXLS_CNT; w++) { | |
pixels[w] = CHSV(roll8b(cycle + w * 8), 255, 255); | |
} | |
FastLED.show(); | |
} | |
while (!digitalRead(encoderBtnPin)) | |
delay(10); //wait for button release | |
} | |
void woken() { } | |
void doSleep() { | |
alpha4.clear(); | |
alpha4.setBrightness(0); | |
alpha4.writeDisplay(); | |
FastLED.show(); | |
pinMode(13, INPUT); //onboard led | |
LEDS.setBrightness(0); | |
for (uint8_t w = 0; w < PXLS_CNT; w++) pixels[w] = 0; | |
FastLED.show(); | |
attachInterrupt(digitalPinToInterrupt(encoderBtnPin), woken, FALLING); | |
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode. | |
sleep_enable(); // Enable sleep mode. | |
sleep_mode(); // Enter sleep mode. | |
detachInterrupt(digitalPinToInterrupt(encoderBtnPin)); | |
setup(); | |
} | |
long readVcc() { | |
long result; | |
// Read 1.1V reference against AVcc | |
#if defined(__AVR_AT90USB1286__) || defined(__AVR_ATmega32U4__) | |
ADMUX = _BV(REFS0) | 0b011110; | |
#else | |
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); | |
#endif | |
delay(2); // Wait for Vref to settle | |
ADCSRA |= _BV(ADSC); // Convert | |
while (bit_is_set(ADCSRA,ADSC)); | |
result = ADCL; | |
result |= ADCH<<8; | |
result = 1126400L / result; // Back-calculate AVcc in mV 1126400L = (1023L * 1000 * 1.1) | |
return result; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment