It's Holiday Sale Weekend at Adafruit - use the code ADATY to get 15% off items storewide! Some restrictions apply.
0

SK6812 Rings for the Holidays
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

SK6812 Rings for the Holidays

by Disciple on Tue Oct 17, 2017 2:21 am

An online supplier sold me a disc of SK6812 LEDs on concentric rings for my dad. He wants to create a window display, so I have some Halloween ideas.

The rings are 6, from a single pixel to 32, in increasing sizes. They're joined by breakaway bridges, but I'm keeping them united. They're easily wired to count from the center outward, and an Uno or Pro Trinket drives them readily. I created a mapping that allows the Adafruit_NeoMatrix library to perform Adafruit_GFX functions in a recognizable manner. If anyone is working with a similar disc, I'm sharing my sketch.

Code: Select all | TOGGLE FULL SIZE
/*
  RingsMatrixHalloween03 - Learn the Adafruit Matrix and GFX libraries by doing.
                           Find the mapping function for the concentric SK6812 rings, 1 - 32
  By Disciple, employing code and libraries provided at adafruit.com
  Released under Adafruit's licensing conditions.
  Version 3 - More Halloween icons
*/

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>

// Red status LED
#define LED_PIN 13

// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
#define NEOPIN 6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(3, NEOPIN, NEO_GRBW + NEO_KHZ800);
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(11, 11, NEOPIN, NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_COLUMNS + NEO_MATRIX_PROGRESSIVE, NEO_GRBW + NEO_KHZ800);

// 11x11 grid to rings remap matrix
const uint16_t ringGrid[] = {
 93, 93, 93, 91, 92, 61, 62, 63, 93, 93, 93,
 93, 89, 90, 93, 60, 37, 38, 93, 64, 65, 93,
 93, 88, 58, 59, 36, 21, 22, 39, 40, 66, 93,
 87, 93, 57, 35, 20,  9, 10, 23, 41, 93, 67,
 86, 56, 34, 19,  8,  1,  2, 11, 24, 42, 68,
 85, 55, 33, 18,  7,  0,  3, 12, 25, 43, 69,
 84, 54, 32, 17,  6,  5,  4, 13, 26, 44, 70,
 83, 93, 53, 31, 16, 15, 14, 27, 45, 93, 71,
 93, 82, 52, 51, 30, 29, 28, 47, 46, 72, 93,
 93, 81, 80, 93, 50, 49, 48, 93, 74, 73, 93,
 93, 93, 93, 79, 78, 77, 76, 75, 93, 93, 93};

// Halloween icons for ring grid
const PROGMEM uint8_t hallow[154] = {
0xff, 0xff, // Pumpkin head
0xff, 0xff,
0xee, 0xff,
0xce, 0x7f,
0xce, 0x7f,
0xfb, 0xff,
0xf1, 0xff,
0xdf, 0x7f,
0xc0, 0x7f,
0xf1, 0xff,
0xff, 0xff,

0xff, 0xff, // Skull closed
0xff, 0xff,
0xdf, 0x7f,
0xce, 0x7f,
0xfb, 0xff,
0x7b, 0xC0,
0x3f, 0x9f,
0x5f, 0x5f,
0xb5, 0xbf,
0x1f, 0x1f,
0xe0, 0xff,

0xff, 0xff, // Skull open
0xff, 0xff,
0xdf, 0x7f,
0xce, 0x7f,
0xfb, 0xff,
0x7b, 0xC0,
0x3f, 0x9f,
0x5f, 0x5f,
0x84, 0x3F,
0xb0, 0xbf,
0xff, 0xff,

0x08, 0x00, // Cobweb
0x00, 0x00,
0x04, 0x00,
0x15, 0x20,
0xC0, 0x80,
0x12, 0x00,
0x24, 0x80,
0x12, 0xA0,
0x49, 0x00,
0x02, 0x00,
0x01, 0x00,

0x00, 0x00, // Wolf howl 1
0x00, 0x00,
0x00, 0x00,
0x02, 0x00,
0x1E, 0x00,
0x7E, 0x00,
0x7E, 0x00,
0x0F, 0x00,
0x1F, 0x80,
0x1F, 0x80,
0x1F, 0x80,

0x00, 0x00, // Wolf howl 2
0x00, 0x00,
0x20, 0x00,
0x3E, 0x00,
0x7E, 0x00,
0x1E, 0x00,
0x0E, 0x00,
0x0F, 0x00,
0x1F, 0x80,
0x1F, 0x80,
0x1F, 0x80,

0x00, 0x00, // Wolf howl 3
0x0C, 0x00,
0x0C, 0x00,
0x0E, 0x00,
0x0E, 0x00,
0x0F, 0x00,
0x0E, 0x00,
0x0F, 0x00,
0x1F, 0x80,
0x1F, 0x80,
0x1F, 0x80 };

// Dead claw open bitmap 16x11
const PROGMEM uint8_t clawOpen[22] = {
0x09, 0x00,
0x25, 0x40,
0x15, 0x40,
0x0E, 0x80,
0x71, 0x00,
0x0E, 0x00,
0x06, 0x00,
0x0A, 0x00,
0x0A, 0x00,
0x0A, 0x00,
0x0A, 0x00 };

// Dead claw closed bitmap 16x11
const PROGMEM uint8_t clawClosed[22] = {
0x00, 0x00,
0x0A, 0x00,
0x1F, 0x00,
0x3F, 0x00,
0x11, 0x00,
0x0E, 0x00,
0x06, 0x00,
0x0A, 0x00,
0x0A, 0x00,
0x0A, 0x00,
0x0A, 0x00 };

// Flying bat bitmap 16x11
const PROGMEM uint8_t batUp[22] = {
0x00, 0x00, // Bat flap up
0x00, 0x00,
0x60, 0xC0,
0xF5, 0xE0,
0xFF, 0xE0,
0x1F, 0x00,
0x0E, 0x00,
0x04, 0x00,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00 };

// Flying bat bitmap 16x11
const PROGMEM uint8_t batDn[22] = {
0x00, 0x00, // Bat flap down
0x00, 0x00,
0x04, 0x00,
0x0E, 0x00,
0x1F, 0x00,
0x3F, 0x80,
0x75, 0xC0,
0xE0, 0xE0,
0x40, 0x40,
0x00, 0x00,
0x00, 0x00 };

// Creepy spider bitmap 8x8
const PROGMEM uint8_t spider[8] = {
0x20, 0x16, 0x98, 0x7E, 0x39, 0x50, 0x50, 0x08 };

// Glow eyes bitmap 7x2
const PROGMEM uint8_t glowEyes[2] = {
0x82, 0xC6 };

// Fang teeth bitmap 7x3
const PROGMEM uint8_t fangTeeth[3] = {
0xC6, 0x7C, 0x44 };

void setup() {
  pinMode(LED_PIN, OUTPUT);  // Prepare status LED

  matrix.begin(); // Initialize all Neopixels to 'off'
  matrix.setRemapFunction(ringRemap);
  matrix.setBrightness(20);    // Keep colors dimmest
}

void loop() {
  uint16_t counter1, counter2, pixColor;
  int textpos;

// Rainbow clock wipes
  for(counter1 = 0; counter1 < 256; ++counter1) {
    clockLine(counter1, colorShrink(scaleWheel(counter1*3, 255)));
    delay(20);
  }

// Glowing pumpkin head
  counter2 = 200;
  for(counter1 = 0; counter1 < 200; ++counter1) {
    counter2 += (random(15) - 7);
    counter2 = counter2 < 10 ? 10 : counter2;
    counter2 = counter2 > 245 ? 245 : counter2;  // Random walk for candle flicker color
    pixColor = (counter2 - 10) << 1;
    pixColor = colorShrink(strip.Color(constrain(pixColor, 0, 255), constrain(pixColor, 128, 383) - 128, constrain(pixColor, 256, 511) - 256));
    matrix.fillScreen(pixColor);  // Color the BG
    matrix.drawBitmap(0, 0, hallow, 16, 33, 0x8200); // Overlay the face
    matrix.show();
    digitalWrite(LED_PIN, counter2 > 200 ? HIGH : LOW);  // Flicker the LED
    delay(20);
  }
  digitalWrite(LED_PIN, LOW);  // LED off

// Crawling marquee
  matrix.fillScreen(0x0000);
  matrix.show();
  matrix.setTextColor(0xFF00, 0x0000);  // Gold on black
  matrix.setTextSize(1);
  matrix.setTextWrap(false);

  for(textpos=16; textpos > -100; --textpos) { // Marquee crawl
    matrix.setCursor(textpos, 2);
    matrix.print("Happy Halloween!");
    matrix.show();
    delay(40);
  }

// Eye of Sour-one
  counter2 = 5;
  for(counter1 = 0; counter1 < 20; ++counter1) {
    counter2 += (random(3) - 1);
    counter2 = constrain(counter2, 3, 7);  // Random walk for eye horizontal pos

    matrix.fillScreen(0xFFFF);  // White the BG
    matrix.fillCircle(counter2, 5, 3, 0xFA00); // Flame disk
    matrix.fillRect(counter2, 3, 1, 5, 0x0000);
    matrix.fillRect(counter2 - 1, 4, 3, 3, 0x0000); // Cat pupil

    for(pixColor = 0; pixColor < 6; ++pixColor) {
      matrix.drawLine(pixColor + 5, 0, pixColor + 5, (pixColor * pixColor) / 7 + 1, 0x0000); // Outline eye
      matrix.drawLine(5 - pixColor, 0, 5 - pixColor, (pixColor * pixColor) / 7 + 1, 0x0000);

      matrix.drawLine(pixColor + 5, 10, pixColor + 5, 9 - (pixColor * pixColor) / 7, 0x0000);
      matrix.drawLine(5 - pixColor, 10, 5 - pixColor, 9 - (pixColor * pixColor) / 7, 0x0000);
    }
    matrix.show();
    digitalWrite(LED_PIN, counter2 == 5 ? HIGH : LOW);  // Blink LED
    delay(200);
  }

// Chattering skull bitmaps
  for(counter1 = 0; counter1 < 16; ++counter1) {
    matrix.fillScreen(0x0000);  // Black the BG
    matrix.drawBitmap(0, -11, hallow, 16, 33, 0xFFFF);
    matrix.show();
    delay(120);
    matrix.fillScreen(0x0000);  // Black the BG
    matrix.drawBitmap(0, -22, hallow, 16, 33, 0xFFFF);
    matrix.show();
    digitalWrite(LED_PIN, (counter1 & 1) == 0 ? HIGH : LOW);  // Blink LED
    delay(120);
  }

// Blood spider
  for(counter1 = 0; counter1 < 54; ++counter1) {
    matrix.fillScreen(0x0000);  // Black the BG
    matrix.drawBitmap(0, -33, hallow, 16, 121, 0x4208); // Gray cobweb
    matrix.drawBitmap(counter1 / 2 - 12, counter1 / 3 - 8, spider, 8, 8, 0xC000); // Red spiders
    matrix.show();
    delay(100);
  }

// Moon reveal
  for(counter1 = 0; counter1 < 20; ++counter1) {
    matrix.fillScreen(0x8410);  // Pale gray BG
    matrix.fillCircle(5 + counter1, 5 - counter1, 9, 0x0000); // Black circle
    matrix.show();
    delay(150);
  }

// Bats fly by
  for(counter1 = 0; counter1 < 4; ++counter1) { // 4 bats pass by
    pixColor = random(8);
    for(counter2 = 0; counter2 < 18; counter2 += 2) {
      matrix.fillScreen(0x8410);  // Pale gray moon BG
      matrix.drawBitmap(pixColor - 4, 11 - counter2, batUp, 16, 11, 0x0000); // Flapping bat wings up
      matrix.show();
      delay(100);
      matrix.fillScreen(0x8410);  // Pale gray moon BG
      matrix.drawBitmap(pixColor - 4, 10 - counter2, batDn, 16, 11, 0x0000); // Flapping bat wings down
      matrix.show();
      delay(100);
    }
    digitalWrite(LED_PIN, (counter1 & 1) == 0 ? HIGH : LOW);  // Blink LED
  }

// Howling wolf
  for(counter1 = 0; counter1 < 12; ++counter1) { // Wolf silhouette moves in
    matrix.fillScreen(0x8410);  // Pale gray moon BG
    matrix.drawBitmap(11 - counter1, -44, hallow, 16, 99, 0x0000); // Wolf neutral bitmap
    matrix.show();
    delay(100);
  }
  delay(1000);
  matrix.fillScreen(0x8410);  // Pale gray moon BG
  matrix.drawBitmap(0, -55, hallow, 16, 99, 0x0000); // Wolf lifting bitmap
  matrix.show();
  delay(100);
  matrix.fillScreen(0x8410);  // Pale gray moon BG
  matrix.drawBitmap(0, -66, hallow, 16, 99, 0x0000); // Wolf howling bitmap
  matrix.show();
  digitalWrite(LED_PIN, HIGH);  // LED on
  delay(1000);
  for(counter1 = 0; counter1 < 12; ++counter1) {  // Wolf disappears
    matrix.fillScreen(0x8410);  // Pale gray BG
    matrix.drawBitmap(0, -66, hallow, 16, 99, 0x0000); // Wolf howling bitmap
    matrix.fillCircle(counter1 - 6, counter1 - 6, 9, 0x0000); // Black circle
    matrix.show();
    delay(150);
  }
  digitalWrite(LED_PIN, LOW);  // LED off
  delay(500);

//  Radiating rainbow
  for(counter1 = 256; counter1 > 0; --counter1) {
    for(counter2 = 6; counter2 > 0; --counter2) {
      pixColor = colorShrink(scaleWheel((counter1 << 3) + (counter2 << 5), 255));
      matrix.fillCircle(5, 5, counter2, pixColor); // Color circle
    }
    matrix.show();
    delay(20);
  }

//  Demon face
  for(counter1 = 0; counter1 < 225; ++counter1) {  // Pulsing eyes only
    matrix.fillScreen(0x0000);  // Black the BG
    if(counter1 & 0x20) {
      pixColor = 0xF800 - ((counter1 & 0x1F) * 0x0800);
    } else {
      pixColor = (counter1 & 0x1F) * 0x0800;
    }
    matrix.drawBitmap(2, 3, glowEyes, 7, 2, pixColor); // Make the eyes
    matrix.show();
    delay(12);
  }
  delay(500);
  digitalWrite(LED_PIN, HIGH);  // LED on
  for(counter1 = 0; counter1 < 10; ++counter1) {  // Reveal the fangs
    matrix.fillScreen(0x0000);  // Black the BG
    matrix.drawBitmap(2, 8, fangTeeth, 7, 3, 0xFFFF); // Make the teeth
    matrix.fillRect(5 + counter1, 5, 11, 11, 0x0000);
    matrix.fillRect(0 - counter1, 0, 6, 11, 0x0000);
    matrix.drawBitmap(2, 3, glowEyes, 7, 2, pixColor); // Make the eyes
    matrix.show();
    delay(60);
  }
  digitalWrite(LED_PIN, LOW);  // LED off
  for(counter1 = 31; counter1 < 225; ++counter1) {  // Pulsing eyes with fangs
    matrix.fillScreen(0x0000);  // Black the BG
    if(counter1 & 0x20) {
      pixColor = 0xF800 - ((counter1 & 0x1F) * 0x0800);
    } else {
      pixColor = (counter1 & 0x1F) * 0x0800;
    }
    matrix.drawBitmap(2, 8, fangTeeth, 7, 3, 0xFFFF); // Make the teeth
    matrix.drawBitmap(2, 3, glowEyes, 7, 2, pixColor); // Make the eyes
    matrix.show();
    delay(12);
  }
  delay(1000);

//  Dead man's claw
  matrix.fillScreen(0x0000);  // Black the BG
  matrix.fillRect(0, 5, 3, 7, 0x4208);  // Gray tombstone
  matrix.fillRect(0, 9, 11, 3, 0x4100); // Brown grave
  matrix.show();
  delay(2000);  // Lie peacefully

  for(counter1 = 0; counter1 < 32; ++counter1) {  // Grave quakes
    counter2 = random(3);
    pixColor = random(3);
    matrix.fillScreen(0x0000);  // Black the BG
    matrix.fillRect(counter2 - 1, pixColor + 4, 3, 7, 0x4208);  // Gray tombstone
    matrix.fillRect(counter2 - 1, pixColor + 8, 11, 3, 0x4100); // Brown grave
    matrix.show();
    delay(100);
  }
  for(counter1 = 0; counter1 < 12; ++counter1) {  // Grave quakes
    counter2 = random(3);
    pixColor = random(3);
    matrix.fillScreen(0x0000);  // Black the BG
    matrix.fillRect(counter2 - 1, pixColor + 4, 3, 7, 0x4208);     // Gray tombstone
    matrix.fillRect(counter2 + 2, pixColor + 7, 7, 3, 0x4100);     // Brown grave BG
    matrix.drawBitmap(0, 11 - counter1, clawOpen, 16, 11, 0xFFFF); // Raise the claw
    matrix.fillRect(counter2 - 1, pixColor + 8, 11, 3, 0x4100);    // Brown grave FG
    matrix.show();
    delay(100);
  }
  for(counter1 = 0; counter1 < 8; ++counter1) {  // Grave sinks
    counter2 = random(3);
    pixColor = random(3);
    matrix.fillScreen(0x0000);  // Black the BG
    matrix.fillRect(counter2 - 1, counter1 + pixColor + 4, 3, 7, 0x4208);   // Gray tombstone
    matrix.fillRect(counter2 + 2, counter1 + pixColor + 7, 7, 3, 0x4100);   // Brown grave BG
    matrix.drawBitmap(0, 0, clawOpen, 16, 11, 0xFFFF);                      // Draw the claw
    matrix.fillRect(counter2 - 1, counter1 + pixColor + 8, 11, 3, 0x4100);  // Brown grave FG
    matrix.show();
    delay(100);
  }
  for(counter1 = 0; counter1 < 6; ++counter1) {  // Claw grasps in midair
    matrix.fillScreen(0x0000);
    matrix.drawBitmap(0, 0, clawClosed, 16, 11, 0xFFFF);
    matrix.show();
    delay(200);
    matrix.fillScreen(0x0000);
    matrix.drawBitmap(0, 0, clawOpen, 16, 11, 0xFFFF);
    matrix.show();
    digitalWrite(LED_PIN, (counter1 & 1) == 0 ? HIGH : LOW);  // Blink LED
    delay(200);
  }
}

// Draw a line from the center to the edge of the matrix
// 40 steps = 40 points around the matrix edges
void clockLine(uint16_t tyme, uint16_t color) {

  tyme %= 40;

  if(tyme < 5)
    matrix.drawLine(5, 5, 5 + tyme, 0, color);
  else if(tyme < 15)
    matrix.drawLine(5, 5, 10, tyme - 5, color);
  else if(tyme < 25)
    matrix.drawLine(5, 5, 25 - tyme, 10, color);
  else if(tyme < 35)
    matrix.drawLine(5, 5, 0, 35 - tyme, color);
  else
    matrix.drawLine(5, 5, tyme - 35, 0, color);

  matrix.show();
}

// Reduce a 32-bit color value to 16 bits compatible with NeoMatrix library
uint16_t colorShrink(uint32_t longColor) {
  longColor = ((longColor & 0x000000F8) >> 3) + ((longColor & 0x0000FC00) >> 5) + ((longColor & 0x00F80000) >> 8);
  return (uint16_t)longColor;
}

// Input a value 0 to 255 to return a color value.
// The colours are a transition r y g c b m back to r.
uint32_t scaleWheel(byte WheelPos, byte intens) {
  if (WheelPos < 43) {
    return strip.Color(intens, (WheelPos * 6 * intens) / 255, 0);  // R to Y
  } else if (WheelPos < 85) {
    WheelPos -= 43;
    return strip.Color(intens - (WheelPos * 6 * intens) / 255, intens, 0);  // Y to G
  } else if (WheelPos < 128) {
    WheelPos -= 85;
    return strip.Color(0, intens, (WheelPos * 6 * intens) / 255);  // G to C
  } else if (WheelPos < 170) {
    WheelPos -= 128;
    return strip.Color(0, intens - (WheelPos * 6 * intens) / 255, intens);  // C to B
  } else if (WheelPos < 213) {
    WheelPos -= 170;
    return strip.Color((WheelPos * 6 * intens) / 255, 0, intens);  // B to M
  } else {
    WheelPos -= 213;
    return strip.Color(intens, 0, intens - (WheelPos * 6 * intens) / 255);  // M to R
  }
}

uint16_t ringRemap(uint16_t x, uint16_t y) {

  x = (11 * y) + x;

  if(x < 121)
    return ringGrid[x];
  else
    return 93;
}

HalloweenPixels01.jpg
A fast-food napkin is my diffuser.
HalloweenPixels01.jpg (53.3 KiB) Viewed 3004 times

  • The radar screen sweeps out a color wheel rainbow
  • The jack-o-lantern light has a random-walk glimmer
  • The eye of "Sour-one" twitches left and right in a random-walk direction
  • The skull's jaw chatters
  • Not pictured is a marquee crawl message, "Happy Halloween!"
    Interestingly, it's moderately readable.
It's free to anyone who is interested. Be safe.

Edit 1: Updated 21 Oct. 2017 to add more Halloween effects. Some are nice, some are nice try. Try 'em for yourself. The double-draw in the radar screen is fixed. New effects are as follows.
HalloweenPixels02.jpg
Yup, same napkin
HalloweenPixels02.jpg (42.19 KiB) Viewed 2941 times
  • A cobweb with a crawling blood spider (that looks like a tick)
  • A reveal of the moon with flying bats
  • A wolf howls (no sound, sorry)
  • Radiating rainbows
  • A demon with pulsing eyes smiles
  • A dead hand erupts from the grave
  • The Arduino blinks a bit
Edit 2: Here's a YouTube. (wretched autoexpose!) https://youtu.be/eum0JBkXMlw

A nest of LED rings like these could make a pretty cool wearable, arranged like the snowflake sweater for example, if anyone still needs a costume idea. What else could these do? Your turn. (c:

Edit 3: Changed the title, formerly "SK6812 Rings for Halloween".

Hallelujah!
Disciple
Last edited by Disciple on Tue Dec 26, 2017 2:54 am, edited 3 times in total.

Disciple
 
Posts: 819
Joined: Tue Jan 06, 2015 8:13 pm

Re: SK6812 Rings for Halloween

by adafruit_support_bill on Tue Oct 17, 2017 5:47 am

Nice work Disciple! Thanks for posting.

adafruit_support_bill
 
Posts: 82391
Joined: Sat Feb 07, 2009 10:11 am

Re: SK6812 Rings for Halloween

by Disciple on Sat Oct 21, 2017 3:36 am

My pleasure. The post is now updated with more effects. Edit 2: YouTube added.
I suppose I owe Christmas and Hanukkah their due now.

Hallelujah!
Disciple

Disciple
 
Posts: 819
Joined: Tue Jan 06, 2015 8:13 pm

Re: SK6812 Rings for Halloween

by swain on Thu Oct 26, 2017 2:09 pm

Excellent work. Any chance this project could be turned into an article on the Adafruit Learn section. Remapping LED’s is a great topic with not many articles on the methods.

SWAIN:::-------

swain
 
Posts: 20
Joined: Fri May 16, 2014 3:21 am

Re: SK6812 Rings for Halloween

by Disciple on Fri Dec 01, 2017 3:43 am

All right, same circuit, new sketch. This one's for Hanukkah.

Code: Select all | TOGGLE FULL SIZE
/*
  RingsMatrixHanukkah01 - Learn the Adafruit Matrix and GFX libraries by doing.
                          Use the mapping function for the concentric SK6812 rings, 1 - 32
  Written by Disciple using code by ladyada, public domain
  Version 1 - Basic Hanukkah icons
*/

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>

#define LED_PIN 13

// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
#define NEOPIN 6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(3, NEOPIN, NEO_GRBW + NEO_KHZ800);
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(11, 11, NEOPIN, NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_COLUMNS + NEO_MATRIX_PROGRESSIVE, NEO_GRBW + NEO_KHZ800);

// 11x11 grid to rings remap matrix
const uint16_t ringGrid[] = {
 93, 93, 93, 91, 92, 61, 62, 63, 93, 93, 93,
 93, 89, 90, 93, 60, 37, 38, 93, 64, 65, 93,
 93, 88, 58, 59, 36, 21, 22, 39, 40, 66, 93,
 87, 93, 57, 35, 20,  9, 10, 23, 41, 93, 67,
 86, 56, 34, 19,  8,  1,  2, 11, 24, 42, 68,
 85, 55, 33, 18,  7,  0,  3, 12, 25, 43, 69,
 84, 54, 32, 17,  6,  5,  4, 13, 26, 44, 70,
 83, 93, 53, 31, 16, 15, 14, 27, 45, 93, 71,
 93, 82, 52, 51, 30, 29, 28, 47, 46, 72, 93,
 93, 81, 80, 93, 50, 49, 48, 93, 74, 73, 93,
 93, 93, 93, 79, 78, 77, 76, 75, 93, 93, 93};

// Hanukkah icons for ring grid
// Half star of David bitmap 9x9
const PROGMEM uint8_t halfStar[18] = {
0x8, 0x0, 0x14, 0x0, 0x14, 0x0, 0x14, 0x0, 0x22,
0x0, 0x22, 0x0, 0x63, 0x0, 0x41, 0x0, 0xdd, 0x80};

// Four 8x8 letter shapes for dreidel
const PROGMEM uint8_t dreidel[4][8] = {
{ 0xc0, 0x5b, 0x49, 0xd9, 0x93, 0xb2, 0xa6, 0xfc },
{ 0x40, 0x7c, 0x6, 0x42, 0x42, 0x42, 0x42, 0x42 },
{ 0x10, 0x18, 0xc, 0x4, 0x4, 0xc, 0x1c, 0x36 },
{ 0x10, 0x18, 0xc, 0x4, 0x4, 0x4, 0x4, 0x3e }};

// Four 4x8 half width letter shapes for dreidel
const PROGMEM uint8_t halfDreidel[4][8] = {
{ 0xa, 0xd, 0x9, 0xd, 0x5, 0x4, 0x2, 0xe },
{ 0x8, 0xe, 0x2, 0xa, 0xa, 0xa, 0xa, 0xa },
{ 0x4, 0x4, 0x2, 0x2, 0x2, 0x2, 0x6, 0x6 },
{ 0x4, 0x4, 0x2, 0x2, 0x2, 0x2, 0x2, 0x6 }};

void setup() {
  pinMode(LED_PIN, OUTPUT);  // Prepare status LED

  matrix.begin(); // Initialize all Neopixels
  matrix.setRemapFunction(ringRemap);
  matrix.setBrightness(25);    // Keep colors dim
}

void loop() {
  uint16_t counter1, counter2, pixColor;
  int textpos;

// Rainbow clock wipes
  for(counter1 = 0; counter1 < 256; ++counter1) {
    clockLine(counter1, colorShrink(scaleWheel(counter1*3, 255)));
    digitalWrite(LED_PIN, (counter1 % 40) == 0 ? HIGH : LOW);  // Blip the LED
    delay(20);
  }

// Star of David
  for(counter1 = 0; counter1 < 6; ++counter1) {  // Draw six blue spinning lines
    for(counter2 = 0; counter2 < 10; ++counter2) {
      matrix.fillScreen(0xFFFF);  // White BG
      matrix.drawLine(5 + map(counter2, 0, 10, 0, 4), map(counter2, 0, 10, 0, 2), 9 - map(counter2, 0, 10, 0, 4), 8 + map(counter2, 0, 10, 0, 2), 0x001F);
      matrix.drawLine(9 - map(counter2, 0, 10, 0, 4), 8 + map(counter2, 0, 10, 0, 2), 1, 8 - map(counter2, 0, 10, 0, 5), 0x001F);
      matrix.drawLine(1, 8 - map(counter2, 0, 10, 0, 5), 5 + map(counter2, 0, 10, 0, 4), map(counter2, 0, 10, 0, 2), 0x001F);

      matrix.drawLine(5 - map(counter2, 0, 10, 0, 4), 10 - map(counter2, 0, 10, 0, 2), 1 + map(counter2, 0, 10, 0, 4), 2 - map(counter2, 0, 10, 0, 2), 0x001F);
      matrix.drawLine(1 + map(counter2, 0, 10, 0, 4), 2 - map(counter2, 0, 10, 0, 2), 9, 2 + map(counter2, 0, 10, 0, 5), 0x001F);
      matrix.drawLine(9, 2 + map(counter2, 0, 10, 0, 5), 5 - map(counter2, 0, 10, 0, 4), 10 - map(counter2, 0, 10, 0, 2), 0x001F);

      matrix.show();
      delay(32);
    }
  }
  digitalWrite(LED_PIN, HIGH);// LED on
  matrix.fillScreen(0xFFFF);  // Completed star, white BG
  matrix.drawBitmap(1, 0, halfStar, 9, 9, 0x001F); // Overlay half the star
  matrix.setRotation(2);
  matrix.drawBitmap(1, 0, halfStar, 9, 9, 0x001F); // Overlay other half
  matrix.setRotation(0);
  matrix.show();
  delay(1500);
  digitalWrite(LED_PIN, LOW);// LED off

  for(counter1 = 0; counter1 < 16; ++counter1) {  // Slide the star apart
    matrix.fillScreen(0xFFFF);  // White BG
    matrix.drawBitmap(1, counter1, halfStar, 9, 9, 0x001F); // Overlay half the star
    matrix.setRotation(2);
    matrix.drawBitmap(1, counter1, halfStar, 9, 9, 0x001F); // Overlay other half
    matrix.setRotation(0);
    matrix.show();
    delay(100);
  }

// Shower of gelt
  fallingGelt(0x3800, 5 + random(11), 100); // BG color for the animation, coin count, delay speed

// Spinning the dreidel
  counter1 = random(40);
  dreidelFrame(counter1); // Draw a frame of the stationary dreidel
  matrix.show();
  delay(1000);
  dreidelSpin(counter1, random(20) + 180); // Animate the dreidel randomly spinning
  digitalWrite(LED_PIN, LOW);// LED off


// Crawling marquee
  matrix.fillScreen(0x0000);
  matrix.show();
  matrix.setTextColor(0xFF00, 0x0000);  // Gold on black
  matrix.setTextSize(1);
  matrix.setTextWrap(false);

  for(textpos=16; textpos > -100; --textpos) { // Marquee crawl
    matrix.setCursor(textpos, 2);
    matrix.print("Happy Hanukkah!");
    matrix.show();
    delay(40);
  }

// Rainbow painting rectangle
  digitalWrite(LED_PIN, HIGH); // LED on
  matrix.fillScreen(0x0000);   // Black BG
  for(counter1 = 0; counter1 < 256; ++counter1) {
    movingRect(counter1, colorShrink(scaleWheel(counter1 * 3, 255)));
    delay(20);
  }
  digitalWrite(LED_PIN, LOW); // LED off

// Lighting of candles
  lightHanukkiyah(0x3807, 200); // BG color for the animation, delay speed
  digitalWrite(LED_PIN, LOW);// LED off

// Eating sufganiyot
  eatSufganiyot(5, 5, 0x0188, 1000, 100); // X position, Y position, BG color, initial pause, delay speed
}

void lightHanukkiyah(uint16_t bgColor, uint16_t wait) { // BG color for the animation, delay speed
  int counter1;

  for(counter1 = -2; counter1 < 24; ++counter1) { // Candles setting in stand
    matrix.fillScreen(bgColor);
    drawStand(counter1 - 6, 7, 2, 0x7BC0, bgColor);
    drawCandles(counter1 - 6, 6, 0, 8, 16 - counter1, 2, 0x208F);
    matrix.show();
    delay(wait);
  }

  for(counter1 = 24; counter1 > -3; --counter1) { // Candles lighting
    matrix.fillScreen(bgColor);
    drawStand(counter1 - 6, 10, 2, 0x7BC0, bgColor);
    drawCandles(counter1 - 6, 9, 0, 8, 0, 2, 0x208F);
    matrix.drawLine(19 - counter1, 4, 22 - counter1, 2, 0x8084); // Middle candle
    matrix.drawPixel(18 - counter1, 4, 0xFFF6);                  // Middle flame
    drawFlames(counter1 - 6, 9, 0, (15 - counter1) > 8 ? 8 : (15 - counter1), 2);
    matrix.show();
    delay(wait);
  }

  for(counter1 = 10; counter1 > 0; --counter1) { // Complete hanukkiyah rises
    matrix.fillScreen(bgColor);
    drawStand(5, counter1 + 5, 1, 0x7BC0, bgColor);
    drawCandles(5, counter1 + 4, 0, 8, 0, 1, 0x208F);
    drawFlames(5, counter1 + 4, 0, 8, 1);
    matrix.drawLine(5, 5 - counter1, 5, 4 - counter1, 0x8084); // Middle candle
    matrix.drawPixel(5, 3 - counter1, 0xFFF6);                 // Middle flame
    matrix.show();
    delay(wait);
  }

  for(counter1 = 0; counter1 < 15; ++counter1) { // Stationary flames flicker
    drawFlames(5, 5, 0, 8, 1);
    matrix.show();
    delay(wait);
  }
}

void drawFlames(int xPos, int yPos, int first, int last, int scale) {
  int counter1, rise;
  uint16_t pixColor;

  for(counter1 = first; counter1 <= last; ++counter1) {
    if(counter1 != 4) { // Skip middle candle
      pixColor = 320 + random(128);
      pixColor = colorShrink(strip.Color(constrain(pixColor, 0, 255), constrain(pixColor, 128, 383) - 128, constrain(pixColor, 256, 511) - 256));
      matrix.drawPixel(xPos + ((counter1 - 4) * scale), yPos - (2 * scale) - 1, pixColor);
    }
  }
  digitalWrite(LED_PIN, (pixColor & 0x001F) > 16 ? HIGH : LOW);  // Flicker the LED
}

void drawCandles(int xPos, int yPos, uint8_t first, uint8_t last, int lift, int scale, uint16_t candColor) {
  int counter1, rise;
  for(counter1 = first; counter1 <= last; ++counter1) {
    if(counter1 != 4) { // Skip middle candle
      rise = counter1 - lift > 0 ? 0 : counter1 - lift;
      matrix.drawLine(xPos + ((counter1 - 4) * scale), yPos + rise - 1, xPos + ((counter1 - 4) * scale), yPos + rise - (2 * scale), candColor);
    }
  }
}

void drawStand(int xPos, int yPos, int scale, uint16_t standColor, uint16_t bgColor) {
  matrix.drawCircle(xPos, yPos, scale, standColor);  // Draw concentric circles
  matrix.drawCircle(xPos, yPos, scale * 2, standColor);
  matrix.drawCircle(xPos, yPos, scale * 3, standColor);
  matrix.drawCircle(xPos, yPos, scale * 4, standColor);
  matrix.drawFastVLine(xPos, 0, 12, standColor);
  if(scale == 1)
    matrix.fillCircle(xPos, yPos, scale * 4, standColor); // No gaps allowed at scale 1
  matrix.fillRect(0, 0, 11, yPos - 1, bgColor);  // Erase upper halves
}

void dreidelSpin(int spinPos, int spinSpeed) {
  static uint32_t spinTimer = 0, dragTimer = 0, nowTimer;

  while(spinSpeed > 0) {
    nowTimer = millis();

    if(nowTimer >= dragTimer) {     // Time to decelerate?
      spinSpeed = spinSpeed > 0 ? spinSpeed - 1 : 0;  // The dreidel loses speed
      // spinSpeed = spinSpeed < 180 ? spinSpeed + 20 : 200;  // The dreidel goes faster

      dragTimer = nowTimer + 10;
    }

    if(nowTimer >= spinTimer && spinSpeed > 0) { // Time to change dreidel position?
      spinPos = (spinPos + 1) % 40;

      dreidelFrame(spinPos);
      matrix.show();
      spinTimer = nowTimer + (600 / spinSpeed);
    }
  }
  delay(2000); // Pause on final frame
}

void dreidelFrame(uint16_t position1) {
  uint16_t dColors[4] = {0x03C0, 0x000F, 0x7800, 0x79C0}; // green, blue, red, gold
  int counter1;

  counter1 = (position1 % 40) / 10; // Which face is forward, 0-3?
  position1 %= 10;                  // Where is it positioned, 0-9?
  matrix.fillScreen(0x000F);        // Blue BG
  matrix.fillRect(5, 0, 1, 2, 0xC618);  // 75% gray stem
  matrix.fillRect(position1 - 4, 1, 10, 10, 0xE618);  // Clay front face
  matrix.drawBitmap(position1 - 3, 2, dreidel[counter1], 8, 8, dColors[counter1]);  // Draw forward facing letter
  if(position1 < 3) {    // Draw side face, if any
    matrix.fillRect(position1 + 6, 1, 4, 10, 0x730C);  // 50% clay
    matrix.drawBitmap(position1 + 2, 2, halfDreidel[(counter1 + 3) & 3], 8, 8, dColors[(counter1 + 3) & 3]);
  } else if(position1 > 6) {
    matrix.fillRect(position1 - 8, 1, 4, 10, 0x730C);  // 50% clay
    matrix.drawBitmap(position1 - 12, 2, halfDreidel[(counter1 + 1) & 3], 8, 8, dColors[(counter1 + 1) & 3]);
  }
  digitalWrite(LED_PIN, (counter1 & 1) == 0 ? LOW : HIGH);// LED blinks
}

void eatSufganiyot(int xPos, int yPos, uint16_t bgColor, int wait, uint16_t frameDelay) {
  int counter1;

  for(counter1 = -15; counter1 <= 0; ++counter1) { // Animate descending sufganiyot
    matrix.fillScreen(bgColor);
    matrix.fillRoundRect(xPos - 4, yPos + counter1 - 1, 9, 4, 2, 0xC40C);
    matrix.fillRoundRect(xPos - 3, yPos + counter1 - 2, 7, 3, 2, 0xC618);
    matrix.fillRect(xPos - 1, yPos + counter1 - 2, 3, 1, 0xC000);
    matrix.show();
    delay(50);
  }
  delay(wait);

  for(counter1 = 0; counter1 < 16; ++ counter1) {
    matrix.fillCircle(xPos - (counter1 >> 2) + 5, yPos + random(7) - 3, 2, bgColor);
    matrix.show();
    delay(frameDelay);
    matrix.fillCircle(xPos + (counter1 >> 2) - 5, yPos + random(7) - 3, 2, bgColor);
    matrix.show();
    delay(frameDelay);
  }
}

void fallingGelt(uint16_t bgColor, uint16_t coinCount, uint16_t frameDelay) {
  uint8_t coinStats[16]; // 16 falling coins max
  int counter1, counter2;

  for(counter1 = 0; counter1 < 16; ++counter1)
    coinStats[counter1] = random(256);   // Randomize coin positions

  coinCount = coinCount > 16 ? 16 : coinCount;  // Limit 16 coins max

  for(counter1 = 0; counter1 < (coinCount << 2) + 16; ++counter1) { // Loop through all frames of falling motion
    matrix.fillScreen(bgColor);

    for(counter2 = 0; counter2 < coinCount; ++counter2) { // Draw each gold coin per frame
      switch ((counter1 + coinStats[counter2]) & 3) {
        case 0:  // Face on
          matrix.fillCircle((coinStats[counter2] >> 5) + 2, counter1 - (counter2 << 2) + (coinStats[counter2] & 12 >> 2) - 5, 2, 0xFFE0);
          break;
        case 2:  // Quarter turn, edge on
          matrix.drawFastHLine(coinStats[counter2] >> 5, counter1 - (counter2 << 2) + (coinStats[counter2] & 12 >> 2) - 5, 5, 0xFFE0);
          break;
        default: // 1/8 turn
          matrix.fillRoundRect(coinStats[counter2] >> 5, counter1 - (counter2 << 2) + (coinStats[counter2] & 12 >> 2) - 6, 5, 3, 1, 0xFFE0);
      }
    }
    matrix.show();
    delay(frameDelay);
  }
}

void movingRect(uint16_t tyme, uint16_t color) {
  static int rTop = 5, rLeft = 5, rBot = 5, rRight = 5, swapper;

  rTop   = constrain(rTop   + random(3) - 1, -1,  7);  // Random nudge corners
  rLeft  = constrain(rLeft  + random(3) - 1, -1,  7);
  rBot   = constrain(rBot   + random(3) - 1,  3, 11);
  rRight = constrain(rRight + random(3) - 1,  3, 11);

  if(rBot < rTop) { // Keep rTop and rLeft smallest
    swapper = rBot;
    rBot = rTop;
    rTop = swapper;
  }
  if(rRight < rLeft) {
    swapper = rRight;
    rRight = rLeft;
    rLeft - swapper;
  }
  matrix.fillRect(rLeft, rTop, 1 + rBot - rTop, 1 + rRight - rLeft, color);
  matrix.show();
}

void clockLine(uint16_t tyme, uint16_t color) {

  tyme %= 40;

  if(tyme < 5)
    matrix.drawLine(5, 5, 5 + tyme, 0, color);
  else if(tyme < 15)
    matrix.drawLine(5, 5, 10, tyme - 5, color);
  else if(tyme < 25)
    matrix.drawLine(5, 5, 25 - tyme, 10, color);
  else if(tyme < 35)
    matrix.drawLine(5, 5, 0, 35 - tyme, color);
  else
    matrix.drawLine(5, 5, tyme - 35, 0, color);

  matrix.show();
}

uint16_t colorShrink(uint32_t longColor) {
  longColor = ((longColor & 0x000000F8) >> 3) + ((longColor & 0x0000FC00) >> 5) + ((longColor & 0x00F80000) >> 8);
  return (uint16_t)longColor;
}

// Input a value 0 to 255 to return a color value.
// The colours are a transition r y g c b m back to r.
uint32_t scaleWheel(byte WheelPos, byte intens) {
  if (WheelPos < 43) {
    return strip.Color(intens, (WheelPos * 6 * intens) / 255, 0);  // R to Y
  } else if (WheelPos < 85) {
    WheelPos -= 43;
    return strip.Color(intens - (WheelPos * 6 * intens) / 255, intens, 0);  // Y to G
  } else if (WheelPos < 128) {
    WheelPos -= 85;
    return strip.Color(0, intens, (WheelPos * 6 * intens) / 255);  // G to C
  } else if (WheelPos < 170) {
    WheelPos -= 128;
    return strip.Color(0, intens - (WheelPos * 6 * intens) / 255, intens);  // C to B
  } else if (WheelPos < 213) {
    WheelPos -= 170;
    return strip.Color((WheelPos * 6 * intens) / 255, 0, intens);  // B to M
  } else {
    WheelPos -= 213;
    return strip.Color(intens, 0, intens - (WheelPos * 6 * intens) / 255);  // M to R
  }
}

uint16_t ringRemap(uint16_t x, uint16_t y) {

  x = (11 * y) + x;

  if(x < 121)
    return ringGrid[x];
  else
    return 93;
}
I'll have to add photos later. My goal was to beat December in my time zone. This time the animations are,
  • The rainbow radar again
  • A spinning blue on white star
  • Candy coins tumbling from above
  • A toy that spins to stop on a random letter
  • The marquee crawl message is now, "Happy Hanukkah!"
  • A random rectangle paints a rainbow trail
  • Eight candles placed on a stand and lit by the ninth
  • A fried pastry disappears bite by bite
Edit: The photos and the Youtube are here. https://youtu.be/bnYbpOg2Sc8
HanukkahPixels01.jpg
No more napkin, plastic sheet
HanukkahPixels01.jpg (45.87 KiB) Viewed 2757 times
Hanukkah wasn't part of my upbringing. This display comes from my short online education, and I present it with respect. If any of it goes against tradition, let me know what to modify. I hope you enjoy it. It seemed to me that LEDs might be a good fit with a festival of lights.

Hallelujah!
Disciple

Disciple
 
Posts: 819
Joined: Tue Jan 06, 2015 8:13 pm

Re: SK6812 Rings for Halloween

by Disciple on Tue Dec 26, 2017 2:38 am

Oops. Thought I'd posted this, but it's still Christmas where I live, so I technically made my deadline.
Here's the Christmas version of the animated holiday rings.
Code: Select all | TOGGLE FULL SIZE
/*
  RingsMatrixChristmas01 - Learn the Adafruit Matrix and GFX libraries by doing.
                           Use the mapping function for the concentric SK6812 rings, 1 - 32
  Written by Disciple using code by ladyada, public domain
  Version 1 - Basic Christmas icons
*/

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>

// Red status LED
#define LED_PIN 13

// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
#define NEOPIN 6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(3, NEOPIN, NEO_GRBW + NEO_KHZ800);
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(11, 11, NEOPIN, NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_COLUMNS + NEO_MATRIX_PROGRESSIVE, NEO_GRBW + NEO_KHZ800);

// 11x11 grid to rings remap matrix
const uint16_t ringGrid[] = {
 93, 93, 93, 91, 92, 61, 62, 63, 93, 93, 93,
 93, 89, 90, 93, 60, 37, 38, 93, 64, 65, 93,
 93, 88, 58, 59, 36, 21, 22, 39, 40, 66, 93,
 87, 93, 57, 35, 20,  9, 10, 23, 41, 93, 67,
 86, 56, 34, 19,  8,  1,  2, 11, 24, 42, 68,
 85, 55, 33, 18,  7,  0,  3, 12, 25, 43, 69,
 84, 54, 32, 17,  6,  5,  4, 13, 26, 44, 70,
 83, 93, 53, 31, 16, 15, 14, 27, 45, 93, 71,
 93, 82, 52, 51, 30, 29, 28, 47, 46, 72, 93,
 93, 81, 80, 93, 50, 49, 48, 93, 74, 73, 93,
 93, 93, 93, 79, 78, 77, 76, 75, 93, 93, 93};

// Star dim/bright bitmaps 16x16
const PROGMEM uint8_t cStar[2][32] = {
{0x04, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00,
 0x20, 0x80, 0x80, 0x20, 0x20, 0x80, 0x00, 0x00,
 0x00, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x0A, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00 },
{0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x0E, 0x00,
 0x1F, 0x00, 0x7F, 0xC0, 0x1F, 0x00, 0x0E, 0x00,
 0x0E, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00,
 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 }};

// Ribbon 8x4 shapes for wreath & presents
const PROGMEM uint8_t ribbons[2][4] = {
{0x44, 0xAA, 0x7C, 0xFF }, {0x38, 0x54, 0x6C, 0x38 }};

// Running reindeer bitmaps 104 x 9 px:
const PROGMEM uint8_t cDeer[2][117] = {
{0x24, 0x2, 0x40, 0x24, 0x2, 0x40, 0x24, 0x2, 0x40, 0x24, 0x2, 0x40, 0x0,
0x38, 0x3, 0x80, 0x38, 0x3, 0x80, 0x38, 0x3, 0x80, 0x38, 0x3, 0x80, 0x1f,
0x78, 0x7, 0xc0, 0x78, 0x7, 0xc0, 0x78, 0x7, 0xc0, 0x78, 0x7, 0xc3, 0xe0,
0x1f, 0x81, 0xfc, 0x1f, 0x81, 0xfc, 0x1f, 0x81, 0xfc, 0x1f, 0x81, 0xfc, 0x0,
0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x1f, 0xc0, 0xfe, 0x1f, 0xc0, 0xfe, 0x1f, 0xc0, 0xfe, 0x1f, 0xc0, 0xfe, 0x0,
0x14, 0xc1, 0x47, 0x94, 0xc1, 0x47, 0x94, 0xc1, 0x47, 0x94, 0xc1, 0x47, 0x80,
0x1d, 0x86, 0x83, 0x1d, 0x86, 0x83, 0x1d, 0x86, 0x83, 0x1d, 0x86, 0x83, 0x0,
0xb, 0x0, 0x80, 0x8b, 0x0, 0x80, 0x8b, 0x0, 0x80, 0x8b, 0x0, 0x80, 0x80},

{0x24, 0x1, 0x20, 0x48, 0x1, 0x20, 0x48, 0x1, 0x20, 0x48, 0x1, 0x20, 0x0,
0x38, 0x1, 0xc0, 0x70, 0x1, 0xc0, 0x70, 0x1, 0xc0, 0x70, 0x1, 0xc0, 0x1f,
0x78, 0x3, 0xc0, 0xf8, 0x3, 0xc0, 0xf8, 0x3, 0xc0, 0xf8, 0x3, 0xc3, 0xe0,
0x1f, 0x80, 0xfc, 0x3f, 0x80, 0xfc, 0x3f, 0x80, 0xfc, 0x3f, 0x80, 0xfc, 0x0,
0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x1f, 0xc0, 0xfe, 0x1f, 0xc0, 0xfe, 0x1f, 0xc0, 0xfe, 0x1f, 0xc0, 0xfe, 0x0,
0x28, 0xf0, 0xa6, 0x28, 0xf0, 0xa6, 0x28, 0xf0, 0xa6, 0x28, 0xf0, 0xa6, 0x0,
0xd0, 0x60, 0xec, 0xd0, 0x60, 0xec, 0xd0, 0x60, 0xec, 0xd0, 0x60, 0xec, 0x0,
0x10, 0x10, 0x58, 0x10, 0x10, 0x58, 0x10, 0x10, 0x58, 0x10, 0x10, 0x58, 0x0 }};

// Sleigh bitmaps (white green red) 16 x 9 px:
const PROGMEM uint8_t cSleigh[3][18] = {
{0x0, 0x40, 0x5, 0x90, 0x0, 0x30, 0x0, 0x20, 0x0,
 0x0, 0x0, 0x0, 0x0, 0x0, 0xc8, 0x40, 0x7f, 0xf8},
{0x0, 0x0, 0x0, 0x8, 0x0, 0x8, 0x30, 0x18, 0x3f,
 0xf0, 0x3f, 0xf0, 0x1f, 0xf0, 0x0, 0x0, 0x0, 0x0},
{0x1, 0x80, 0x0, 0x0, 0x7, 0x80, 0x3, 0x80, 0x0,
 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};

// Manger triangle coordinates
const PROGMEM int mangPts[11][6] = {
{-400, -200, -200, 0, 0, -200},
{-200, 0, 0, 200, -400, 200},
{0, 0, 400, 0, 200, 200},
{200, 200, 400, 400, 0, 400},
{-400, -200, 0, -200, 0, 0},
{0, 0, 0, -200, 400, 0},
{-400, -200, 0, 0, 200, 200},
{200, 200, -200, 0, -400, -200},
{-300, -300, -200, -100, 0, -200},
{100, 0,  -200, -100, 0, -200},
{400, 0, 100, 0, 0, -200}};

void setup() {
  pinMode(LED_PIN, OUTPUT);  // Prepare status LED

  matrix.begin(); // Initialize all Neopixels
  matrix.setRemapFunction(ringRemap);
  matrix.setBrightness(25);    // Keep colors dim
}

void loop() {
  uint16_t ornCols[7], counter1, counter2, pixColor;
  int ornXpos[7], textPos;

// Rainbow clock wipes
  for(counter1 = 0; counter1 < 256; ++counter1) {
    clockLine(counter1, colorShrink(scaleWheel(counter1*3, 255)));
    digitalWrite(LED_PIN, (counter1 % 40) == 0 ? HIGH : LOW);  // Blip the LED
    delay(20);
  }

// Bethlehem star and manger
  for(textPos = -3; textPos < 6; ++textPos) { // Star from upper left
    matrix.fillScreen(0x0000);
    drawStar(textPos, textPos, 4, 7, 0xE780, 10, 0, 0, 255);
    matrix.show();
    delay(120);
  }
  delay(500);
  for(textPos = 5; textPos > -16; --textPos) { // Star upward offscreen
    matrix.fillScreen(0x0000);
    drawStar(5, textPos, 4, 7, 0xE780, 10, 0, 0, 255);
    matrix.drawPixel(5, textPos + 21, 0x6000);
    matrix.show();
    delay(100);
  }
  delay(400);
  for(counter1 = 600; counter1 >= 100; counter1 -= 50) { // Manger expands to full
    matrix.fillScreen(0x0000);
    drawManger(5, 6, counter1, 0x6000, 0x7BE0);
    matrix.show();
    delay(20);
  }
  delay(1000);
  for(counter1 = 0; counter1 < 9; ++counter1) { // Arms lower baby down
    matrix.fillScreen(0x0000);
    drawManger(5, 6, 100, 0x6000, 0x7BE0);
    matrix.fillTriangle(3, counter1 - 4, 4, counter1 - 4, 11, counter1 - 12, 0xC408);
    matrix.fillCircle(3, counter1 - 4, 1, 0xC408);
    matrix.fillCircle(4, counter1 - 4, 1, 0xC408);
    matrix.fillRoundRect(4, counter1 - 5, 6, 3, 1, 0xFFFF);
    matrix.fillTriangle(7, counter1 - 3, 8, counter1 - 3, 15, counter1 - 12, 0xC408);
    matrix.show();
    delay(120);
  }
  delay(300);
  for(counter1 = 8; counter1 > 0; --counter1) { // Arms return upwards
    matrix.fillScreen(0x0000);
    drawManger(5, 6, 100, 0x6000, 0x7BE0);
    matrix.fillTriangle(3, counter1 - 4, 4, counter1 - 4, 11, counter1 - 12, 0xC408);
    matrix.fillCircle(3, 4, 1, 0xC408);
    matrix.fillCircle(4, 4, 1, 0xC408);
    matrix.fillRoundRect(4, 3, 6, 3, 1, 0xFFFF);
    matrix.fillTriangle(7, counter1 - 3, 8, counter1 - 3, 15, counter1 - 12, 0xC408);
    matrix.show();
    delay(120);
  }
  delay(2000);

// Trimming tree
  for(counter1 = 0; counter1 < 9; ++counter1) {  // Draw green triangle tree
    matrix.fillScreen(0xFFFF);  // White BG
    matrix.fillRect(0, 0, 11, 7, 0x630C);  // Gray sky
    matrix.drawLine(5, 9, 5, 10, 0x3100);  // Draw trunk
    matrix.fillTriangle(5, 8 - counter1, 5 - (counter1 >> 1), 8, 5 + (counter1 >> 1), 8, 0x03C0);
    matrix.show();
    delay(200);
  }
  textPos = random(6);
  for(counter1 = 0; counter1 < 7; ++counter1) { // Load ornament colors and positions
    pixColor = colorShrink(scaleWheel((textPos + counter1) * 43, 255));
    ornCols[counter1] = pixColor;
    counter2 = random(counter1 + 1) + 5 - (counter1 >> 1);
    ornXpos[counter1] = counter2;
    matrix.drawPixel(counter2, counter1 + 2, pixColor);
    matrix.show();
    delay(100);
  }
  for(counter1 = 0; counter1 < 5; ++counter1) {       // Ornaments blinking
    for(counter2 = 0; counter2 < 7; ++counter2) {     // All ornaments on
      matrix.drawPixel(ornXpos[counter2], counter2 + 2, ornCols[counter2]);
    }
    matrix.show();
    digitalWrite(LED_PIN, HIGH);  // LED on
    delay(1000);

    for(counter2 = 0; counter2 < 7; ++counter2) {     // All ornaments off
      matrix.drawPixel(ornXpos[counter2], counter2 + 2, 0x0000);
    }
    matrix.show();
    digitalWrite(LED_PIN, LOW);  // LED off
    delay(200);
  }

// Row of presents
  for(counter1 = 0; counter1 < 7; ++counter1)  // Randomize package appearances
    ornXpos[counter1] = random(32768);         // Random width, height, hues and bows

  for(counter1 = 0; counter1 < 72; ++counter1) {
    matrix.fillScreen(0x0000);  // Black BG
    for(counter2 = 0; counter2 < 7; ++counter2) {  // Draw 7 packages
      textPos = min((int)counter1 + ((int)counter2 << 3) - 56, 0);  // Y offset to make packages plop
      pixColor = colorShrink(scaleWheel((ornXpos[counter2] & 0xFF0) >> 4, 255));  // Box color
      matrix.fillRect(counter1 + (counter2 << 3) - 54, textPos + 6 - (ornXpos[counter2] & 3),
                      5 + ((ornXpos[counter2] & 0xC) >> 2), 4 + (ornXpos[counter2] & 3), pixColor);  // Draw box
      pixColor ^= 0xFFFF;  // Ribbon color
      matrix.fillRect(counter1 + (counter2 << 3) + ((ornXpos[counter2] & 0xC) >> 3) - 52,
                      textPos + 6 - (ornXpos[counter2] & 3), 2, 4 + (ornXpos[counter2] & 3), pixColor); // Draw ribbon
      matrix.drawBitmap(counter1 + (counter2 << 3) + ((ornXpos[counter2] & 0xC) >> 3) - 55,
                        textPos + 2 - (ornXpos[counter2] & 3), ribbons[(ornXpos[counter2] & 0x1000) == 0 ? 0 : 1], 8, 4, pixColor);   // Draw bow
    }
    matrix.show();
    delay(70);
  }

// Holly wreath
  for(counter1 = 20; counter1 >= 5; --counter1) {
    matrix.fillScreen(0x7BDF);                  // Bluish BG
    matrix.fillCircle(5, counter1, 6, 0x0200);  // Make the green of the wreath
    matrix.fillCircle(5, counter1, 2, 0x7BDF);  // Clear the center hole
    matrix.drawBitmap(2, counter1 - 7, ribbons[0], 8, 4, 0x7800); // Draw red ribbon
    matrix.show();
    delay(120);
  }
  counter2 = 0;
  for(counter1 = 0; counter1 < 20; ++ counter1) {
    counter2 += 6 + random(3);
    matrix.drawPixel(counter2 % 11, counter2 / 11, 0xF800); // Add a randomish red holly berry
    matrix.fillCircle(5, 5, 2, 0x7BDF);                     // Clear the center hole
    matrix.drawBitmap(2, -2, ribbons[0], 8, 4, 0x7800);     // Draw red ribbon
    matrix.show();
    delay(100);
  }
  delay(2000);

// Symmetric rainbow painting
  digitalWrite(LED_PIN, HIGH); // LED on
  counter1 = 5;
  counter2 = 5;
  // matrix.fillScreen(0x0000);   // Black BG
  for(textPos = 0; textPos < 256; ++textPos) {
    pixColor = colorShrink(scaleWheel(textPos * 3, 255));
    counter1 = constrain(counter1 + random(3) - 1,  0, 10); // Random walking pixel X and Y
    counter2 = constrain(counter2 + random(3) - 1,  0, 10);

    matrix.drawPixel(counter1, counter2, pixColor);  // 8 degrees of symmetry
    matrix.drawPixel(counter1, 10 - counter2, pixColor);
    matrix.drawPixel(10 - counter1, counter2, pixColor);
    matrix.drawPixel(10 - counter1, 10 - counter2, pixColor);
    matrix.drawPixel(counter2, counter1, pixColor);
    matrix.drawPixel(counter2, 10 - counter1, pixColor);
    matrix.drawPixel(10 - counter2, counter1, pixColor);
    matrix.drawPixel(10 - counter2, 10 - counter1, pixColor);

    matrix.show();
    delay(20);
  }
  digitalWrite(LED_PIN, LOW); // LED off

// Crawling marquee
  matrix.fillScreen(0x0000);
  matrix.show();
  matrix.setTextSize(1);
  matrix.setTextWrap(false);

  for(textPos=16; textPos > -100; --textPos) { // Marquee crawl
    matrix.setTextColor(0x07E0, 0x0000);  // Green on black
    matrix.setCursor(textPos, 2);
    matrix.print("Merry");
    matrix.setTextColor(0xF800, 0x0000);  // Red on black
    matrix.setCursor(textPos + 36, 2);
    matrix.print("Christmas!");
    matrix.show();
    delay(40);
  }

// Santa's sleigh
  for(textPos = 20; textPos > -120; --textPos) {
    matrix.fillScreen(0x8410);           // Pale gray moon BG
    matrix.fillCircle(7, 3, 6, 0x0000);  // Cut out crescent
    matrix.drawBitmap(textPos, 1, cDeer[(textPos & 4) == 0 ? 0 : 1], 104, 9, 0x4100); // Draw brown reindeer
    matrix.drawPixel( textPos +   1, 3, 0xF800);                    // Draw red nose
    matrix.drawBitmap(textPos + 102, 1, cSleigh[0], 16, 9, 0xFFFF); // Draw white sleigh
    matrix.drawBitmap(textPos + 102, 1, cSleigh[1], 16, 9, 0x0420); // Draw green sleigh
    matrix.drawBitmap(textPos + 102, 1, cSleigh[2], 16, 9, 0xF800); // Draw red sleigh
    matrix.show();
    delay(40);
  }
}

void drawStar(int xPos, int yPos, int armLen, int footLen, uint16_t sColor, int gloRadius, uint8_t gloRed, uint8_t gloGrn, uint8_t gloBlu) {
  int counter1;

  for(counter1 = gloRadius; counter1 >= 0; --counter1) {
    matrix.fillCircle(xPos, yPos, counter1, colorShrink(((uint32_t)map(counter1, gloRadius, 0, 0, gloRed) << 16) + ((uint32_t)map(counter1, gloRadius, 0, 0, gloGrn) << 8) + (uint32_t)map(counter1, gloRadius, 0, 0, gloBlu)));
  }
  matrix.drawBitmap(xPos - 5, yPos - 5, cStar[0], 16, 16, sColor >> 1); // Draw dim pixels
  matrix.drawBitmap(xPos - 5, yPos - 5, cStar[1], 16, 16, sColor);      // Bright pixels
}

void drawManger(int xPos, int yPos, int zPos, uint16_t mColor, uint16_t hColor) {
  int counter1;

// Paint scaled manger shape using filled triangles
    counter1 = 0;
    matrix.fillTriangle(mangPts[counter1][0] / zPos + xPos, mangPts[counter1][1] / zPos + yPos,
                        mangPts[counter1][2] / zPos + xPos, mangPts[counter1][3] / zPos + yPos,
                        mangPts[counter1][4] / zPos + xPos, mangPts[counter1][5] / zPos + yPos, mColor);
    counter1 = 1;
    matrix.fillTriangle(mangPts[counter1][0] / zPos + xPos, mangPts[counter1][1] / zPos + yPos,
                        mangPts[counter1][2] / zPos + xPos, mangPts[counter1][3] / zPos + yPos,
                        mangPts[counter1][4] / zPos + xPos, mangPts[counter1][5] / zPos + yPos, mColor);
    counter1 = 2;
    matrix.fillTriangle(mangPts[counter1][0] / zPos + xPos, mangPts[counter1][1] / zPos + yPos,
                        mangPts[counter1][2] / zPos + xPos, mangPts[counter1][3] / zPos + yPos,
                        mangPts[counter1][4] / zPos + xPos, mangPts[counter1][5] / zPos + yPos, mColor);
    counter1 = 3;
    matrix.fillTriangle(mangPts[counter1][0] / zPos + xPos, mangPts[counter1][1] / zPos + yPos,
                        mangPts[counter1][2] / zPos + xPos, mangPts[counter1][3] / zPos + yPos,
                        mangPts[counter1][4] / zPos + xPos, mangPts[counter1][5] / zPos + yPos, mColor);
    counter1 = 4;
    matrix.fillTriangle(mangPts[counter1][0] / zPos + xPos, mangPts[counter1][1] / zPos + yPos,
                        mangPts[counter1][2] / zPos + xPos, mangPts[counter1][3] / zPos + yPos,
                        mangPts[counter1][4] / zPos + xPos, mangPts[counter1][5] / zPos + yPos, mColor);
    counter1 = 5;
    matrix.fillTriangle(mangPts[counter1][0] / zPos + xPos, mangPts[counter1][1] / zPos + yPos,
                        mangPts[counter1][2] / zPos + xPos, mangPts[counter1][3] / zPos + yPos,
                        mangPts[counter1][4] / zPos + xPos, mangPts[counter1][5] / zPos + yPos, mColor);
    counter1 = 6;
    matrix.fillTriangle(mangPts[counter1][0] / zPos + xPos, mangPts[counter1][1] / zPos + yPos,
                        mangPts[counter1][2] / zPos + xPos, mangPts[counter1][3] / zPos + yPos,
                        mangPts[counter1][4] / zPos + xPos, mangPts[counter1][5] / zPos + yPos, mColor);
    counter1 = 7;
    matrix.fillTriangle(mangPts[counter1][0] / zPos + xPos, mangPts[counter1][1] / zPos + yPos,
                        mangPts[counter1][2] / zPos + xPos, mangPts[counter1][3] / zPos + yPos,
                        mangPts[counter1][4] / zPos + xPos, mangPts[counter1][5] / zPos + yPos, mColor);
// Add scaled hay shape
    counter1 = 8;
    matrix.fillTriangle(mangPts[counter1][0] / zPos + xPos, mangPts[counter1][1] / zPos + yPos,
                        mangPts[counter1][2] / zPos + xPos, mangPts[counter1][3] / zPos + yPos,
                        mangPts[counter1][4] / zPos + xPos, mangPts[counter1][5] / zPos + yPos, hColor);
    counter1 = 9;
    matrix.fillTriangle(mangPts[counter1][0] / zPos + xPos, mangPts[counter1][1] / zPos + yPos,
                        mangPts[counter1][2] / zPos + xPos, mangPts[counter1][3] / zPos + yPos,
                        mangPts[counter1][4] / zPos + xPos, mangPts[counter1][5] / zPos + yPos, hColor);
    counter1 = 10;
    matrix.fillTriangle(mangPts[counter1][0] / zPos + xPos, mangPts[counter1][1] / zPos + yPos,
                        mangPts[counter1][2] / zPos + xPos, mangPts[counter1][3] / zPos + yPos,
                        mangPts[counter1][4] / zPos + xPos, mangPts[counter1][5] / zPos + yPos, hColor);

    matrix.drawPixel(xPos, yPos, mColor); // Manger colored dot for smallest sizes
}

void clockLine(uint16_t tyme, uint16_t color) {

  tyme %= 40;

  if(tyme < 5)
    matrix.drawLine(5, 5, 5 + tyme, 0, color);
  else if(tyme < 15)
    matrix.drawLine(5, 5, 10, tyme - 5, color);
  else if(tyme < 25)
    matrix.drawLine(5, 5, 25 - tyme, 10, color);
  else if(tyme < 35)
    matrix.drawLine(5, 5, 0, 35 - tyme, color);
  else
    matrix.drawLine(5, 5, tyme - 35, 0, color);

  matrix.show();
}

uint16_t colorShrink(uint32_t longColor) {
  longColor = ((longColor & 0x000000F8) >> 3) + ((longColor & 0x0000FC00) >> 5) + ((longColor & 0x00F80000) >> 8);
  return (uint16_t)longColor;
}

// Input a value 0 to 255 to return a color value.
// The colours are a transition r y g c b m back to r.
uint32_t scaleWheel(byte WheelPos, byte intens) {
  if (WheelPos < 43) {
    return strip.Color(intens, (WheelPos * 6 * intens) / 255, 0);  // R to Y
  } else if (WheelPos < 85) {
    WheelPos -= 43;
    return strip.Color(intens - (WheelPos * 6 * intens) / 255, intens, 0);  // Y to G
  } else if (WheelPos < 128) {
    WheelPos -= 85;
    return strip.Color(0, intens, (WheelPos * 6 * intens) / 255);  // G to C
  } else if (WheelPos < 170) {
    WheelPos -= 128;
    return strip.Color(0, intens - (WheelPos * 6 * intens) / 255, intens);  // C to B
  } else if (WheelPos < 213) {
    WheelPos -= 170;
    return strip.Color((WheelPos * 6 * intens) / 255, 0, intens);  // B to M
  } else {
    WheelPos -= 213;
    return strip.Color(intens, 0, intens - (WheelPos * 6 * intens) / 255);  // M to R
  }
}

uint16_t ringRemap(uint16_t x, uint16_t y) {

  x = (11 * y) + x;

  if(x < 121)
    return ringGrid[x];
  else
    return 93;
}
What animations does this produce? I'll tell you.
  • The radar screen rainbow once again
  • A star lights the way to a manger in which a baby is laid
  • A green triangular tree acquires some random blinky ornaments
  • Assorted gift-wrapped packages arrange themselves in a row
  • A wreath with a ribbon sprouts random red berries
  • Eight rainbow pixels create a random but symmetric painting
  • The marquee crawl reads, Merry Christmas. (Surprised?)
  • A team of reindeer pull a sleigh past the crescent moon
There will be photos. I know not when, but don't wait. Try the sketch on a nest of SK6812 rings for yourself, then tell me what you think. God bless you all this season and have a wonderful new year.

Hallelujah!
Disciple

Disciple
 
Posts: 819
Joined: Tue Jan 06, 2015 8:13 pm

Re: SK6812 Rings for the Holidays

by Disciple on Sun Mar 04, 2018 2:45 am

One more sketch. This one requires more components. I added the Adafruit PCF8523 Real Time Clock Assembled Breakout Board and a DHT22 temperature-humidity sensor connected to GPIO pin 12. Now I can make a repeating marquee crawl that displays the time, temperature, and humidity on my pretty rings...and so can you. Here's the sketch, proudly made with Adafruit code bits.

Code: Select all | TOGGLE FULL SIZE
/*
  RingsMatrixClimateClock01 - Learn the Adafruit Matrix and GFX libraries by doing.
                              Use the mapping function for the concentric SK6812 rings, 1 - 32
  Written by Disciple using code by ladyada, public domain
  Version 1 - Display clock time, temperature & humidity as a marquee crawl
              Time comes from a PCF8523 breakout from adafruit.com, temp and hum from a DHT22 on GPIO 12
*/

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#include "RTClib.h"
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "DHT.h"

#define DHT_PIN 12
#define LED_PIN 13
#define NEO_PIN 6

// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(3, NEO_PIN, NEO_GRBW + NEO_KHZ800);
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(11, 11, NEO_PIN, NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_COLUMNS + NEO_MATRIX_PROGRESSIVE, NEO_GRBW + NEO_KHZ800);

// Uncomment whatever type you're using!
//#define DHTTYPE DHT11   // DHT 11
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

// Connect pin 1 (on the left) of the sensor to +5V
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
// to 3.3V instead of 5V!
// Connect pin 2 of the sensor to whatever your DHT_PIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor

// Initialize DHT sensor.
// Note that older versions of this library took an optional third parameter to
// tweak the timings for faster processors.  This parameter is no longer needed
// as the current DHT reading algorithm adjusts itself to work on faster procs.
DHT dht(DHT_PIN, DHTTYPE);

RTC_PCF8523 rtc;

// 11x11 grid to rings remap matrix
const uint16_t ringGrid[] = {
 99, 99, 99, 91, 92, 61, 62, 63, 99, 99, 99,
 99, 89, 90, 99, 60, 37, 38, 99, 64, 65, 99,
 99, 88, 58, 59, 36, 21, 22, 39, 40, 66, 99,
 87, 99, 57, 35, 20,  9, 10, 23, 41, 99, 67,
 86, 56, 34, 19,  8,  1,  2, 11, 24, 42, 68,
 85, 55, 33, 18,  7,  0,  3, 12, 25, 43, 69,
 84, 54, 32, 17,  6,  5,  4, 13, 26, 44, 70,
 83, 99, 53, 31, 16, 15, 14, 27, 45, 99, 71,
 99, 82, 52, 51, 30, 29, 28, 47, 46, 72, 99,
 99, 81, 80, 99, 50, 49, 48, 99, 74, 73, 99,
 99, 99, 99, 79, 78, 77, 76, 75, 99, 99, 99};

void setup() {

  dht.begin();  // Prepare to read DHT22 temp/hum
  rtc.begin();  // Prepare to read clock
  rtc.initialized();
  // The following line sets the RTC to the date & time this sketch was compiled
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  pinMode(LED_PIN, OUTPUT);   // Prepare status LED

  matrix.begin();             // Initialize all Neopixels
  matrix.setRemapFunction(ringRemap);
  matrix.setBrightness(20);   // Keep colors dim

  matrix.fillScreen(0x0000);  // Clear NeoPixel matrix
  matrix.show();
  matrix.setTextSize(1);      // Adafruit_GFX text settings
  matrix.setTextWrap(false);
}

void loop() {
  static  int16_t textPos = -100, gapTime, gapTemp, gapHum = 0, colorCycle = 0;
  static uint32_t nowTime, marqueeTime = 0;
  static String stringTime, stringTemp, stringHum;

  nowTime = millis();

  if(nowTime >= marqueeTime) {   // Time to advance the marquee message?
    marqueeTime = nowTime + 50;  // Set the delay until the next advance step

    DateTime now = rtc.now();    // Read the date/time

    if(--textPos < -gapHum) {       // Move text left.  Current pass ended?
      digitalWrite(LED_PIN, HIGH);  // LED on

      // Reading temperature or humidity takes about 250 milliseconds!
      // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
      float h = dht.readHumidity();
      float t = dht.readTemperature();     // Read temperature as Celsius
      float f = dht.readTemperature(true); // Read temperature as Fahrenheit

      if (isnan(h) || isnan(t) || isnan(f))  // Check if any reads failed and exit early (to try again).
        return;

      int rtcHours = now.hour() % 12;
      rtcHours = rtcHours == 0 ? 12 : rtcHours;      // Count time in 12-hour format

      stringTime = String(String(rtcHours) + ':');   // Build time string
      if(now.minute() < 10)
        stringTime = String(stringTime + '0');
      stringTime = String(stringTime + String(now.minute()));
      if(now.hour() > 11)
        stringTime = String(stringTime + 'p');

      stringTemp = String(String(f, 1) + 'F');       // Build temperature string (F)
      stringHum  = String(String(h, 1) + '%');       // Build humidity string

      gapTime = stringTime.length() * 6 + 6;         // Set text positions
      gapTemp = stringTemp.length() * 6 + gapTime + 6;
      gapHum  =  stringHum.length() * 6 + gapTemp;

      textPos = 12;                 // Reset text position for next pass
      digitalWrite(LED_PIN, LOW);   // LED off
    }

    colorCycle = (colorCycle + 7) & 255;  // Color cycling number

    matrix.setTextColor(colorShrink(scaleWheel(colorCycle, 255)), 0x0000);  // The time is rainbow cycle colors on black
    matrix.setCursor(textPos, 2);
    matrix.print(stringTime);
    matrix.setTextColor(0xFE07, 0x0000);  // Temp is goldish on black
    matrix.setCursor(textPos + gapTime, 2);
    matrix.print(stringTemp);
    matrix.setTextColor(0x841F, 0x0000);  // Humidity is bluish on black
    matrix.setCursor(textPos + gapTemp, 2);
    matrix.print(stringHum);
    matrix.show();
  }
}

// Reduce a 32-bit NeoPixel color to a 16-bit NeoMatrix color
uint16_t colorShrink(uint32_t longColor) {
  longColor = ((longColor & 0x000000F8) >> 3) + ((longColor & 0x0000FC00) >> 5) + ((longColor & 0x00F80000) >> 8);
  return (uint16_t)longColor;
}

// Input a value 0 to 255 to return a color value.
// The colors are a transition r y g c b m back to r.
uint32_t scaleWheel(byte WheelPos, byte intens) {
  if (WheelPos < 43) {
    return strip.Color(intens, (WheelPos * 6 * intens) / 255, 0);  // R to Y
  } else if (WheelPos < 85) {
    WheelPos -= 43;
    return strip.Color(intens - (WheelPos * 6 * intens) / 255, intens, 0);  // Y to G
  } else if (WheelPos < 128) {
    WheelPos -= 85;
    return strip.Color(0, intens, (WheelPos * 6 * intens) / 255);  // G to C
  } else if (WheelPos < 170) {
    WheelPos -= 128;
    return strip.Color(0, intens - (WheelPos * 6 * intens) / 255, intens);  // C to B
  } else if (WheelPos < 213) {
    WheelPos -= 170;
    return strip.Color((WheelPos * 6 * intens) / 255, 0, intens);  // B to M
  } else {
    WheelPos -= 213;
    return strip.Color(intens, 0, intens - (WheelPos * 6 * intens) / 255);  // M to R
  }
}

uint16_t ringRemap(uint16_t x, uint16_t y) {

  x = (11 * y) + x;

  if(x < 121)
    return ringGrid[x];
  else
    return 99;
}
The time display changes color in a rainbow cycle as it passes by, just for kicks. It's in a 12-hour format and the temperature is Fahrenheit, but these are all easy to change. You can make it work your way, your colors, your message. A web connected processor could add weather forecast icons or stock activity, tweets, or a life expectancy countdown. Craft the display your own way and become the Lord of the Rings!

I tried inventing a plastic diffuser frame for window or desktop. I succeeded, but it could have turned out better. I shall try to present all this as an instructable for a contest, and will then put a link here. I haven't forgotten the photos, either. Well, actually I had, but you'll forgive me, right?...if I get to 'em?

Hallelujah!
Disciple

Disciple
 
Posts: 819
Joined: Tue Jan 06, 2015 8:13 pm

Please be positive and constructive with your questions and comments.