Skip to content

Instantly share code, notes, and snippets.

@jamesbulpin
Created December 17, 2017 10:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jamesbulpin/598869c5fa24b331d284d3f39ba3c1b5 to your computer and use it in GitHub Desktop.
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
#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