Created
December 17, 2017 10:30
-
-
Save jamesbulpin/598869c5fa24b331d284d3f39ba3c1b5 to your computer and use it in GitHub Desktop.
Arduino sketch to drive WS281x LEDs as part of my Christmas jumper project
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 <Adafruit_NeoPixel.h> | |
#define WS2811_PIN 6 | |
#define WS2811_LED_COUNT 20 | |
// Set to true to flash the LEDs on and off at approx 1Hz | |
boolean flash = false; | |
// The target color for the LED string, indexed by LED number | |
int target_r[WS2811_LED_COUNT]; | |
int target_g[WS2811_LED_COUNT]; | |
int target_b[WS2811_LED_COUNT]; | |
// The current color for the the LED string, indexed by LED number | |
int current_r[WS2811_LED_COUNT]; | |
int current_g[WS2811_LED_COUNT]; | |
int current_b[WS2811_LED_COUNT]; | |
// This is used to provide a timestamp for a color update which is | |
// used in the per-LED color computation to defer updates to LED | |
// color by a time peiod proportional to the position of the LED on | |
// the string. This gives the effect of a color change that moves | |
// along the LED string. | |
long defer = 0; | |
Adafruit_NeoPixel strip = Adafruit_NeoPixel(WS2811_LED_COUNT, WS2811_PIN, NEO_GRB + NEO_KHZ400); | |
void setup() { | |
int i; | |
for (i = 0; i < WS2811_LED_COUNT; i++) { | |
current_r[i] = 0; | |
current_g[i] = 0; | |
current_b[i] = 0; | |
target_r[i] = 0; | |
target_g[i] = 0; | |
target_b[i] = 0; | |
} | |
// Turn off the on-board LED | |
digitalWrite(9, HIGH); | |
strip.begin(); | |
// Initialize LEDs to off | |
for (i = 0; i < WS2811_LED_COUNT; i++) { | |
strip.setPixelColor(i, strip.Color(0, 0, 0)); | |
} | |
strip.show(); | |
Serial.begin(9600); | |
} | |
// Set the target color of all the LEDs to be the same | |
void set_color(char *hex) { | |
long colval = (long)strtol(hex, NULL, 16); | |
for (int j = 0; j < WS2811_LED_COUNT; j++) { | |
target_r[j] = colval >> 16; | |
target_g[j] = colval >> 8 & 0xFF; | |
target_b[j] = colval & 0xFF; | |
} | |
defer = millis()/5; | |
} | |
// Serial port message handler. | |
void handle_message(char *ptr) | |
{ | |
char *p, *i; | |
p = strtok_r(ptr, " ", &i); | |
if (strcmp(p, "COLOR") == 0) { | |
// "COLOR #<hex colour value", e.g. "COLOR #FF0000" for red | |
p = strtok_r(NULL, " ", &i); | |
if (p && (strlen(p) == 7) && (p[0] == '#')) { | |
set_color(&p[1]); | |
flash = false; | |
} | |
} | |
else if (strcmp(p, "FLASH") == 0) { | |
flash = true; | |
} | |
else if (strcmp(p, "NOFLASH") == 0) { | |
flash = false; | |
} | |
} | |
// Serial port character handler. Messages are delimited by newline, NULL | |
// or '!'. Each message is then sent to the the message handler as a whole. | |
const int RX_BUFFER_SIZE = 64; | |
char rxBuffer[RX_BUFFER_SIZE]; | |
int rxBufferPtr = 0; | |
void handle_char(int data) | |
{ | |
if ((data == '\n') || (data == '\0') || (data == '!')) { | |
if (rxBufferPtr < RX_BUFFER_SIZE) { | |
rxBuffer[rxBufferPtr] = '\0'; | |
handle_message(rxBuffer); | |
} | |
rxBufferPtr = 0; | |
} | |
else { | |
if (rxBufferPtr < RX_BUFFER_SIZE) { | |
rxBuffer[rxBufferPtr] = (char)data; | |
rxBufferPtr++; | |
} | |
} | |
} | |
// Main loop | |
void loop() { | |
static long last_millis = 0; | |
static int refresh_counter = 0; | |
static int flash_count = 0; | |
static bool flash_on = true; | |
if (Serial.available()) { | |
handle_char(Serial.read()); | |
} | |
// Recalculate the LED colors at 200Hz | |
long m = millis()/5; | |
if (m > last_millis) { | |
last_millis = m; | |
// Track if there has been a change to computed LED colors requiring an | |
// update to the LEDs themselves | |
boolean changed = false; | |
// Implement a 1Hz flasher | |
if (flash_count == 0) { | |
flash_count = 100; | |
flash_on = !flash_on; | |
changed = true; // LED | |
} | |
flash_count--; | |
// For r, G and B of each LED if the current color is the commanded one | |
// then move it one unit closer. However, defer this if we're within | |
// a period of time after the color change command was received; this | |
// period is determined by the position of the LED on the string. | |
for (int i = 0; i < WS2811_LED_COUNT; i++) { | |
if (m < (defer + 20 * i)) { | |
continue; | |
} | |
if (target_r[i] != current_r[i]) { | |
current_r[i] += (target_r[i]>current_r[i])?1:-1; | |
changed = true; | |
} | |
if (target_g[i] != current_g[i]) { | |
current_g[i] += (target_g[i]>current_g[i])?1:-1; | |
changed = true; | |
} | |
if (target_b[i] != current_b[i]) { | |
current_b[i] += (target_b[i]>current_b[i])?1:-1; | |
changed = true; | |
} | |
} | |
// Update the LED string is there has been any change to computed values | |
// or if we've reached a forced refresh interval | |
if (changed || (refresh_counter == 0)) { | |
for (int i = 0; i < WS2811_LED_COUNT; i++) { | |
if (flash && !flash_on) { | |
// Flashing is enabled and we're in the "off" period | |
strip.setPixelColor(i, strip.Color(0,0,0)); | |
} | |
else { | |
// Flashing is disabled, or flashing is enabled and we're in the "on" period | |
strip.setPixelColor(i, strip.Color(current_r[i], current_g[i], current_b[i])); | |
} | |
} | |
strip.show(); | |
} | |
// Ensure we refresh the LEDs every now again | |
if (refresh_counter) { | |
refresh_counter--; | |
} | |
else { | |
refresh_counter = 400; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment