Created
March 1, 2018 19:43
-
-
Save abachman/d34cc70107eeb838c2106d8a7cff8159 to your computer and use it in GitHub Desktop.
ESP8266 Superbowl Scoreboard
This file contains hidden or 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
// IO Setup | |
#define IO_USERNAME "xxx" | |
#define IO_KEY "xxx" | |
#define WIFI_SSID "xxx" | |
#define WIFI_PASS "xxx" | |
#include "AdafruitIO_WiFi.h" | |
AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS); | |
#include <math.h> /* floor */ | |
//// LED setup, I'm using a 1m NeoPixel strip. | |
#include <Adafruit_NeoPixel.h> | |
#define NEO_DATA_PIN 4 | |
#define LED_PIN 0 | |
// first actual pixel seems to be 1 with the strip | |
#define NUM_PIXELS 31 | |
// test mode - 7 LED NeoPixel jewel | |
// #define NUM_PIXELS 7 | |
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, NEO_DATA_PIN, NEO_GRB + NEO_KHZ800); | |
// Define the Adafruit IO feeds that will provide data. | |
AdafruitIO_Feed *team_a = io.feed("scoreboard.team-alpha"); | |
AdafruitIO_Feed *team_b = io.feed("scoreboard.team-beta"); | |
AdafruitIO_Feed *effect = io.feed("scoreboard.effect"); | |
// Variables to hold current score and color values for the two "teams" | |
int a_score, b_score; | |
uint32_t color_a, color_b; | |
// Mode can only be one state at a time | |
enum Mode { | |
SCORE, | |
SPARKLE, | |
FADE, | |
CYCLE, | |
RAINBOW, | |
RGB, | |
OFF | |
}; | |
Mode sb_mode = SCORE; | |
Mode prev_mode; | |
// Cycle mode | |
int curCycle = 0; | |
#define CYCLE_MAX 16 | |
#define CYCLE_STEPS (256 / CYCLE_MAX) | |
/**** | |
* | |
* Setup function | |
* | |
* - startup NeoPixels | |
* - connect to Adafruit IO | |
* - setup IO message handlers | |
* - initialize display, start in FADE mode | |
* | |
****/ | |
void setup() { | |
// start the serial connection | |
Serial.begin(115200); | |
// indicator LED | |
pinMode(LED_PIN, OUTPUT); | |
// setup neopixels | |
strip.begin(); | |
strip.setBrightness(255); | |
strip.show(); | |
a_score = 0; | |
b_score = 0; | |
color_a = strip.Color(0, 255, 0); | |
color_b = strip.Color(0, 0, 255); | |
// connect to io.adafruit.com | |
Serial.print("Connecting to Adafruit IO"); | |
io.connect(); | |
team_a->onMessage(handleAScore); | |
team_b->onMessage(handleBScore); | |
effect->onMessage(handleEffect); | |
// wait for a connection | |
while (io.status() < AIO_CONNECTED) { | |
Serial.print("."); | |
strip.setPixelColor(1, strip.Color(255, 0, 0)); | |
strip.show(); | |
delay(200); | |
strip.setPixelColor(1, strip.Color(0, 0, 0)); | |
strip.show(); | |
delay(200); | |
} | |
digitalWrite(LED_PIN, LOW); | |
// we are connected! | |
Serial.println(); | |
Serial.println(io.statusText()); | |
colorWipe(strip.Color(50, 50, 50), 60); | |
sb_mode = FADE; | |
} | |
/**** | |
* | |
* Main Program Loop | |
* | |
* Listen for incoming messages, handle mode changes | |
* | |
****/ | |
void loop() { | |
io.run(); | |
// blink a pixel if IO disconnects | |
if (io.status() < AIO_CONNECTED) { | |
strip.setPixelColor(1, strip.Color(255, 0, 0)); | |
strip.show(); | |
delay(200); | |
strip.setPixelColor(1, strip.Color(0, 0, 0)); | |
strip.show(); | |
delay(200); | |
} | |
// each time through loop() function, make sure the correct mode is running | |
switch (sb_mode) { | |
case SCORE: | |
if (prev_mode != SCORE) { | |
drawScoreboard(); | |
} | |
break; | |
case SPARKLE: | |
sparkle(30); | |
break; | |
case FADE: | |
fader(); | |
break; | |
case CYCLE: | |
colorWipe(Wheel(curCycle * CYCLE_STEPS), 20); | |
curCycle = (curCycle + 1) % CYCLE_MAX; | |
break; | |
case RAINBOW: | |
rainbowCycle(10); | |
break; | |
case OFF: | |
if (prev_mode != OFF) { | |
blank(); | |
} | |
break; | |
case RGB: | |
break; | |
default: | |
if (prev_mode != SCORE) { | |
drawScoreboard(); | |
} | |
} | |
prev_mode = sb_mode; | |
} | |
// receive scoreboard.team-beta message | |
void handleAScore(AdafruitIO_Data *data) { | |
Serial.print("TEAM A RECEIVED <- "); | |
Serial.println(data->toInt()); | |
a_score = data->toInt(); | |
drawScoreboard(); | |
} | |
// receive scoreboard.team-beta message | |
void handleBScore(AdafruitIO_Data *data) { | |
Serial.print("TEAM B RECEIVED <- "); | |
Serial.println(data->toInt()); | |
b_score = data->toInt(); | |
drawScoreboard(); | |
} | |
// sending a new effect locks the scoreboard into a new mode | |
void handleEffect(AdafruitIO_Data *data) { | |
String val = data->toString(); | |
if (val == "sparkle") { | |
sb_mode = SPARKLE; | |
} else if (val == "fade") { | |
sb_mode = FADE; | |
} else if (val == "cycle") { | |
sb_mode = CYCLE; | |
} else if (val == "rainbow") { | |
sb_mode = RAINBOW; | |
} else if (val == "rgb") { | |
sb_mode = RGB; | |
} else if (val == "off") { | |
sb_mode = OFF; | |
} else { | |
sb_mode = SCORE; | |
} | |
} | |
// Given the current score values, set pixels to the appropriate colors. For | |
// example, 20 - 20 should mean half the pixels are color_a and half are | |
// color_b. | |
void drawScoreboard() { | |
float total = (float)a_score + (float)b_score; | |
float a_perc; | |
if (total != 0) { | |
a_perc = (float)a_score / total; | |
} else { | |
a_perc = 0.5; | |
} | |
int a_pixels = (int)floor(a_perc * (float)NUM_PIXELS); | |
for (int l=0; l < NUM_PIXELS; l++) { | |
strip.setPixelColor(l, (l < a_pixels) ? color_a : color_b); | |
} | |
strip.show(); | |
} | |
// blank out pixels. This just clears the screen. | |
void blank() { | |
for (int n=0; n < NUM_PIXELS; n++) { | |
strip.setPixelColor(n, strip.Color(0, 0, 0)); | |
} | |
strip.show(); | |
} | |
// Cycle effect: pick a random color, fade up to it then back down | |
void fader() { | |
blank(); | |
delay(500); | |
uint32_t init = Wheel(random(255)); | |
uint8_t ir = (init & (255 << 16)) >> 16; | |
uint8_t ig = (init & (255 << 8)) >> 8; | |
uint8_t ib = init & 255; | |
int nr, ng, nb; | |
int fmax = 255; | |
// fade up | |
for (int f=0; f < 255; f++) { | |
nr = map(f, 0, fmax, 0, ir); | |
ng = map(f, 0, fmax, 0, ig); | |
nb = map(f, 0, fmax, 0, ib); | |
for (int l=0; l < strip.numPixels(); l++) { | |
strip.setPixelColor(l, strip.Color(nr, ng, ng)); | |
} | |
strip.show(); | |
delay(10); | |
} | |
delay(10); | |
// fade down | |
for (int f=255; f > 0; f--) { | |
nr = map(f, 0, fmax, 0, ir); | |
ng = map(f, 0, fmax, 0, ig); | |
nb = map(f, 0, fmax, 0, ib); | |
for (int l=0; l < strip.numPixels(); l++) { | |
strip.setPixelColor(l, strip.Color(nr, ng, ng)); | |
} | |
strip.show(); | |
delay(5); | |
} | |
} | |
// ooooooooooOOOOooo SHINY! | |
void sparkle(uint8_t wait) { | |
blank(); | |
int n; | |
int sparks = floor(1000.0 / (float)wait) - 8; | |
uint32_t w = strip.Color(255, 255, 255); | |
// ~ a second of sparkles | |
for (int i=0; i < sparks; i++) { | |
n = random(strip.numPixels()); | |
strip.setPixelColor(n, w); | |
strip.show(); | |
delay(wait); | |
strip.setPixelColor(n, 0); | |
strip.show(); | |
} | |
} | |
///// | |
/// Effects and color helpers | |
/// via https://learn.adafruit.com/florabrella/test-the-neopixel-strip | |
///// | |
// Fill the dots one after the other with a color | |
void colorWipe(uint32_t c, uint8_t wait) { | |
uint8_t dir = random(10) > 5 ? 1 : -1; | |
if (random(10) > 5) { | |
// l to r | |
for(uint16_t i=0; i < strip.numPixels(); i++) { | |
strip.setPixelColor(i, c); | |
strip.show(); | |
delay(wait); | |
} | |
} else { | |
// r to l | |
for(uint16_t i=strip.numPixels(); i > 0; i--) { | |
strip.setPixelColor(i, c); | |
strip.show(); | |
delay(wait); | |
} | |
} | |
} | |
// Slightly different, this makes the rainbow equally distributed throughout | |
void rainbowCycle(uint8_t wait) { | |
uint16_t i, j; | |
for(j=0; j < 256; j++) { // one whole loop of colors | |
for(i=0; i < strip.numPixels(); i++) { | |
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255)); | |
} | |
strip.show(); | |
delay(wait); | |
} | |
} | |
// Input a value 0 to 255 to get a color value. | |
// The colours are a transition r - g - b - back to r. | |
uint32_t Wheel(byte WheelPos) { | |
if(WheelPos < 85) { | |
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0); | |
} else if(WheelPos < 170) { | |
WheelPos -= 85; | |
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3); | |
} else { | |
WheelPos -= 170; | |
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment