#include <Adafruit_NeoPixel.h>
#include <Adafruit_NeoMatrix.h>
//#include <Adafruit_GFX.h>
//#include <gamma.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1
#define PIN 2
// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 30
// We can define a function to remap 2-dimensional coordinates to
// NeoPixel strip indices. The function must accept two unsigned
// 16-bit arguments and return an unsigned 16-bit corresponding
// strip index. For a set of NeoPixels the first NeoPixel is 0,
// second is 1, all the way up to the count of pixels minus one.
// The function is then enabled using setRemapFunction().
// My party hat pixel topology (polar coordinate style):
// 6 "spokes" numbered counter-clockwise (first coord), with
// pixels numbered radially outward from the pole (second coord)
// (front/bill)
// spoke_1 spoke_0 spoke_5
// 29 10 9
// 28 11 8
// 27 12 7
// 26 13 6
// 25 14 5
//
// 4 15 24
// 3 16 23
// 2 17 22
// 1 18 21
// 0 19 20
// spoke_2 spoke_3 spoke_4
//
#define SPOKES 6 // number of spokes in the wheel
#define RADIUS 5 // number of pixels in each spoke of the wheel
uint16_t polarRemapFn(uint16_t spoke, uint16_t rdist) {
if ((spoke >= SPOKES) || (rdist >= RADIUS)) {
return -1;
}
static uint16_t pixelMap[SPOKES][RADIUS] = {
{ 14, 13, 12, 11, 10 },
{ 25, 26, 27, 28, 29 },
{ 4, 3, 2, 1, 0 },
{ 15, 16, 17, 18, 19 },
{ 24, 23, 22, 21, 20 },
{ 5, 6, 7, 8, 9 }
};
return pixelMap[spoke][rdist];
}
Adafruit_NeoMatrix partyHat = Adafruit_NeoMatrix(SPOKES, RADIUS, PIN,
NEO_MATRIX_TOP + NEO_MATRIX_LEFT +
NEO_MATRIX_COLUMNS + NEO_MATRIX_AXIS,
NEO_GRB + NEO_KHZ800);
void setup() {
// This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
#if defined (__AVR_ATtiny85__)
if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
// End of trinket special code
partyHat.begin(); // This initializes the NeoPixel library.
partyHat.setRemapFunction(polarRemapFn); // This enables our custom function to remap 2-dimensional coordinates to NeoPixel strip indices
// Disable passthrough ... whatever that does
partyHat.setPassThruColor();
// This doesn't seem to work ...
//partyHat.setBrightness(0); // 0 = max brightness; 1 = min brightness (off), 255 = just below max brightness
// Turn off all pixels
partyHat.fillScreen(partyHat.Color(0,0,0));
}
void loop() {
// Demonstrate some simple, hard-coded animations
//rain();
rainbowRings();
for(int i=0; i<40; i++){
rgbFlash();
}
for(int i=0; i<5; i++){
simpleRgbSpiral();
}
for(int i=0; i<5; i++){
sparkle();
}
for(int i=0; i<10; i++){
rgbOut();
}
fire();
}
void sparkle() {
int delayval = 10; // delay a bit
uint8_t intensity = 80;
partyHat.fillScreen(partyHat.Color(0,0,0));
for(int i=0; i<100; i++){
// turn on a random pixel with random intensity
intensity = random(20,120);
partyHat.drawPixel(random(SPOKES), random(RADIUS), partyHat.Color(intensity,intensity,intensity)); partyHat.show();
delay(delayval);
// turn off a random 10% of all pixels
for(int i=0; i<(SPOKES*RADIUS/10); i++){
partyHat.drawPixel(random(SPOKES), random(RADIUS), partyHat.Color(0,0,0));
partyHat.show();
}
delay(delayval);
}
}
void rgbFlash() {
int delayval = 10; // delay a bit
for(int spoke=0; spoke < SPOKES; spoke++){
for(int rdist=0; rdist < RADIUS; rdist++){
partyHat.drawPixel(spoke, rdist, partyHat.Color(75,0,0));
}
partyHat.show();
delay(delayval);
}
for(int spoke=0; spoke < SPOKES; spoke++){
for(int rdist=0; rdist < RADIUS; rdist++){
partyHat.drawPixel(spoke, rdist, partyHat.Color(0,75,0));
}
partyHat.show();
delay(delayval);
}
for(int spoke=0; spoke < SPOKES; spoke++){
for(int rdist=0; rdist < RADIUS; rdist++){
partyHat.drawPixel(spoke, rdist, partyHat.Color(0,0,75));
}
partyHat.show();
delay(delayval);
}
}
void simpleRgbSpiral() {
int delayval = 25; // delay a bit
for(int rdist=0; rdist < RADIUS; rdist++){
for(int spoke=0; spoke < SPOKES; spoke++){
partyHat.drawPixel(spoke, rdist, partyHat.Color(75,0,0));
partyHat.show();
delay(delayval);
}
}
for(int rdist=0; rdist < RADIUS; rdist++){
for(int spoke=0; spoke < SPOKES; spoke++){
partyHat.drawPixel(spoke, rdist, partyHat.Color(0,75,0));
partyHat.show();
delay(delayval);
}
}
for(int rdist=0; rdist < RADIUS; rdist++){
for(int spoke=0; spoke < SPOKES; spoke++){
partyHat.drawPixel(spoke, rdist, partyHat.Color(0,0,75));
partyHat.show();
delay(delayval);
}
}
}
void rgbOut() {
int delayval = 50; // delay a bit
for(int rdist=0; rdist < RADIUS; rdist++){
for(int spoke=0; spoke < SPOKES; spoke++){
partyHat.drawPixel(spoke, rdist, partyHat.Color(75,0,0));
}
partyHat.show();
delay(delayval);
}
for(int rdist=0; rdist < RADIUS; rdist++){
for(int spoke=0; spoke < SPOKES; spoke++){
partyHat.drawPixel(spoke, rdist, partyHat.Color(0,75,0));
}
partyHat.show();
delay(delayval);
}
for(int rdist=0; rdist < RADIUS; rdist++){
for(int spoke=0; spoke < SPOKES; spoke++){
partyHat.drawPixel(spoke, rdist, partyHat.Color(0,0,75));
}
partyHat.show();
delay(delayval);
}
}
void rainbowRings() {
int delayval = 100; // delay a bit
static int blackring = 0;
for(int i=0; i<100; i++){
for(int spoke=0; spoke < SPOKES; spoke++){
partyHat.drawPixel(spoke, 0, partyHat.Color(120,0,0)); // red
// partyHat.drawPixel(spoke, 1, partyHat.Color(100,25,0)); // orange-red
partyHat.drawPixel(spoke, 1, partyHat.Color(150,33,0)); // orange
// partyHat.drawPixel(spoke, 1, partyHat.Color(100,50,0)); // yellow-orange
partyHat.drawPixel(spoke, 2, partyHat.Color(150,90,0)); // yellow
// partyHat.drawPixel(spoke, 1, partyHat.Color(100,150,0)); // yellow-green
partyHat.drawPixel(spoke, 3, partyHat.Color(0,100,0)); // green
// partyHat.drawPixel(spoke, 3, partyHat.Color(0,100,100)); // cyan
partyHat.drawPixel(spoke, 4, partyHat.Color(0,0,100)); // blue
// partyHat.drawPixel(spoke, 4, partyHat.Color(100,0,100)); // violet
// turn off pixels in one ring
partyHat.drawPixel(spoke, blackring++, partyHat.Color(0,0,0));
if (blackring >= RADIUS) blackring = 0;
}
partyHat.show();
delay(delayval);
}
}
void fire() {
int delayval = 100; // delay a bit
partyHat.fillScreen(partyHat.Color(0,0,0));
uint8_t flame_height;
uint8_t flame_color1[3], flame_color2[3];
for(int i=0; i<100; i++) {
partyHat.fillScreen(partyHat.Color(0,0,0));
for (int spoke = 0; spoke < SPOKES; spoke++) {
// for each spoke, pick a random (reddish) bottom color, random (reddish) top color and random "flame" height
// TODO: improve the flame height formula. we want pixel 4 lit 95% of the time, pixel 3 lit 80% of the time, pixel 2 lit 50% of the time, pixel 1 lit 20% of the time and pixel 0 lit 5% or less
flame_height = RADIUS - (random(RADIUS)^2)/RADIUS - 3; // favor smaller numbers; we want only occasional high flames
flame_color1[0] = random(20,80); /// flame_color1[1] = random(15); flame_color1[2] = random(15);
flame_color2[0] = random(128,255); /// flame_color2[1] = random(15); flame_color2[2] = random(15);
for (int rdist = RADIUS; rdist >= flame_height; rdist--) {
// TODO: get some yellow and blue "tips" in here
partyHat.drawPixel(spoke, rdist, partyHat.Color(
flame_color1[0] + (RADIUS-rdist)/RADIUS*(flame_color2[0] - flame_color1[0]), 0, 0
)); // color gradient!
}
}
partyHat.show();
delay(delayval);
}
}
void rain() {
int delayval = 100; // delay a bit
// declare an array to keep track of raindrop colors
// TODO: Use .getPixelColor to get what I need directly from the pixel matrix
uint8_t raindrops[SPOKES][RADIUS];
for (int spoke = 0; spoke < SPOKES; spoke++) {
for (int rdist = 0; rdist < RADIUS; rdist++) {
raindrops[spoke][rdist] = 0;
}
}
partyHat.fillScreen(partyHat.Color(0,0,0));
for(int i=0; i<100; i++) {
// advance all raindrops down the spoke
for (int spoke = 0; spoke < SPOKES; spoke++) {
// gotta iterate *up* the spoke--from rim to hub--otherwise we'll run the raindrop all the way down the spoke befoore we ever call .show()!
for (int rdist = RADIUS-1; rdist >= 0; rdist--) {
if (raindrops[spoke][rdist] > 0) {
// light the next pixel on the spoke (copy intensity)
if ((rdist+1) < RADIUS) {
raindrops[spoke][rdist+1] = raindrops[spoke][rdist]; // copy current pixel's intensity
partyHat.drawPixel(spoke, rdist+1, partyHat.Color(raindrops[spoke][rdist],raindrops[spoke][rdist],raindrops[spoke][rdist]));
}
// decrease current pixel's intensity by 75%
raindrops[spoke][rdist] = raindrops[spoke][rdist]>>2;
partyHat.drawPixel(spoke, rdist, partyHat.Color(raindrops[spoke][rdist],raindrops[spoke][rdist],raindrops[spoke][rdist]));
}
}
}
// start a "raindrop" at a random place (other than the rim) on a random spoke
uint8_t spoke = random(SPOKES); // multiply SPOKES by a number greater than 1 to create intermittent raindrops
uint8_t rdist = random(RADIUS-1);
uint8_t intensity = 40;
raindrops[spoke][rdist] = intensity;
partyHat.drawPixel(spoke, rdist, partyHat.Color(intensity,intensity,intensity));
partyHat.show();
delay(delayval);
}
}