Hello all,
It’s been a while, but I’m working on a Photon project. I’m using MaxMSP to send RGB values over serial to arduinos, in turn connected to WS2812b RGB LED strips. I’m using the NeoPixel library.
I got the Photon working fine using serial over USB. But I picked up the Photon hoping I could use UDP to send the data so all I’d have to worry about is power and ditch the USB cables. The MaxMSP patch I use sends out the color values over UDP.
The firmware compiles fine, but it’s not working.
Can anyone take a look at the code related to UDP and tell me if I’m doing something wrong?
Here’s what I have so far:
// MaxMSP WS2812b RGB Lightstrip Controller
//
// This sketch receives RGB values from MaxMSP via the serial bus
// Then it sends those values to WS2812b RGB lightstrips
// The MaxMSP patch allows control of multiple lightstrips
// It also allows for remote control via UDP on multiple computers
//
// david cool 2022
// davidcool.com
//
// This #include statement was automatically added by the Particle IDE.
#include "Particle.h"
#include "neopixel.h"
#include "math.h"
// IMPORTANT: Set pixel COUNT, PIN and TYPE
#define PIXEL_PIN D6
#define NUMPIXELS 192
#define PIXEL_TYPE WS2812B
/* Similar to above, but for an 8-bit gamma-correction table.
Copy & paste this snippet into a Python REPL to regenerate:
import math
gamma=2.6
for x in range(256):
print("{:3},".format(int(math.pow((x)/255.0,gamma)*255.0+0.5))),
if x&15 == 15: print
*/
static const uint8_t PROGMEM _NeoPixelGammaTable[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7,
7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12,
13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20,
20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29,
30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75,
76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96,
97, 99, 100, 102, 103, 105, 106, 108, 109, 111, 112, 114, 115, 117, 119, 120,
122, 124, 125, 127, 129, 130, 132, 134, 136, 137, 139, 141, 143, 145, 146, 148,
150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180,
182, 184, 186, 188, 191, 193, 195, 197, 199, 202, 204, 206, 209, 211, 213, 215,
218, 220, 223, 225, 227, 230, 232, 235, 237, 240, 242, 245, 247, 250, 252, 255
};
// When setting up the NeoPixel library, we tell it how many pixels,
// and which pin to use to send signals. Note that for older NeoPixel
// strips you might need to change the third parameter -- see the
// strandtest example for more information on possible values.
Adafruit_NeoPixel pixels(NUMPIXELS, PIXEL_PIN, PIXEL_TYPE);
int r,g,b;
String myString;
char recieved;
// UDP Port used for two way communication
unsigned int localPort = 44444;
// An UDP instance to let us send and receive packets over UDP
UDP Udp;
void setup() {
// Start serial listening for MaxMSP values
Serial.begin(115200);
// start the UDP
Udp.begin(localPort);
// INITIALIZE NeoPixel strip object (REQUIRED)
pixels.begin();
}
void loop() {
// Check if data has been received
if (Udp.parsePacket() > 0) {
//if(Serial.available()) { // check to see if there's serial data in the buffer
recieved = Udp.read();
myString += recieved;
if (recieved == 'B') {
//Serial.print('B');
//Serial.println(myString);
int commaIndex = myString.indexOf(',');
int secondCommaIndex = myString.indexOf(',', commaIndex+1);
String firstValue = myString.substring(0, commaIndex);
String secondValue = myString.substring(commaIndex+1, secondCommaIndex);
String thirdValue = myString.substring(secondCommaIndex+1);
r = firstValue.toInt();
g = secondValue.toInt();
b = thirdValue.toInt();
// The first NeoPixel in a strand is #0, second is 1, all the way up
// to the count of pixels minus one.
for(int i=0; i<NUMPIXELS; i++) { // For each pixel...
// pixels.Color() takes RGB values, from 0,0,0 up to 255,255,255
// Here we're using a moderately bright green color:
pixels.setPixelColor(i, pixels.Color(r, g, b));
}
pixels.show(); // Send the updated pixel colors to the hardware.
myString ="";
}
if (recieved == 'C') {
//Serial.print('C');
//Serial.println(myString);
String func = myString.substring(0);
int f = func.toInt();
if (f == 1) {
while(1) {
if(Serial.available()) { break; }
colorWipe(pixels.Color(255, 0, 0), 50); // Red
if(Serial.available()) { break; }
colorWipe(pixels.Color( 0, 255, 0), 50); // Green
if(Serial.available()) { break; }
colorWipe(pixels.Color( 0, 0, 255), 50); // Blue
}
}
if (f == 2) {
while(1) {
if(Serial.available()) { break; }
rainbow(10);
}
}
if (f == 3) {
while(1) {
if(Serial.available()) { break; }
theaterChase(pixels.Color(127, 127, 127), 50); // White, half brightness
if(Serial.available()) { break; }
theaterChase(pixels.Color(127, 0, 0), 50); // Red, half brightness
if(Serial.available()) { break; }
theaterChase(pixels.Color( 0, 0, 127), 50); // Blue, half brightn
}
}
if (f == 4) {
while(1) {
if(Serial.available()) { break; }
theaterChaseRainbow(50);
}
}
if (f == 5) {
while(1) {
if(Serial.available()) { break; }
CylonBounce(0xff, 0, 0, 4, 10, 50);
}
}
if (f == 6) {
while(1) {
if(Serial.available()) { break; }
Twinkle(0xff, 0, 0, 10, 100, false);
}
}
if (f == 7) {
while(1) {
if(Serial.available()) { break; }
Sparkle(0xff, 0xff, 0xff, 0);
}
}
if (f == 8) {
while(1) {
if(Serial.available()) { break; }
BouncingBalls(0xff,0,0, 3);
}
}
if (f == 9) {
while(1) {
if(Serial.available()) { break; }
byte colors[3][3] = { {0xff, 0,0},
{0xff, 0xff, 0xff},
{0 , 0 , 0xff} };
BouncingColoredBalls(3, colors);
}
}
if (f == 10) {
while(1) {
if(Serial.available()) { break; }
Fire(55,120,15);
}
}
if (f == 11) {
while(1) {
if(Serial.available()) { break; }
RunningLights(0xff,0xff,0x00, 50);
}
}
if (f == 12) {
while(1) {
if(Serial.available()) { break; }
colorWipe(0x00,0xff,0x00, 50);
if(Serial.available()) { break; }
colorWipe(0x00,0x00,0x00, 50);
}
}
if (f == 13) {
while(1) {
if(Serial.available()) { break; }
meteorRain(0xff,0xff,0xff,10, 64, true, 30);
}
}
if (f == 14) {
while(1) {
if(Serial.available()) { break; }
SnowSparkle(0x10, 0x10, 0x10, 20, 200);
}
}
if (f == 15) {
while(1) {
if(Serial.available()) { break; }
Strobe(0xff, 0xff, 0xff, 10, 50, 1000);
}
}
if (f == 16) {
while(1) {
if(Serial.available()) { break; }
NewKITT(0xff, 0, 0, 8, 10, 50);
}
}
pixels.clear();
pixels.show();
myString ="";
}
} // serial
} // loop
// Some functions of our own for creating animated effects -----------------
// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
for(int i=0; i<pixels.numPixels(); i++) { // For each pixel in strip...
if(Serial.available()) {
break;
}
pixels.setPixelColor(i, color); // Set pixel's color (in RAM)
pixels.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
for(int a=0; a<10; a++) { // Repeat 10 times...
if(Serial.available()) {
break;
}
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
if(Serial.available()) {
break;
}
pixels.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in steps of 3...
for(int c=b; c<pixels.numPixels(); c += 3) {
if(Serial.available()) {
break;
}
pixels.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
pixels.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
}
// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256; j++) {
for(i=0; i<pixels.numPixels(); i++) {
pixels.setPixelColor(i, Wheel((i+j) & 255));
}
pixels.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 pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
} else if(WheelPos < 170) {
WheelPos -= 85;
return pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3);
} else {
WheelPos -= 170;
return pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
}
// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait) {
int firstPixelHue = 0; // First pixel starts at red (hue 0)
for(int a=0; a<30; a++) { // Repeat 30 times...
if(Serial.available()) {
break;
}
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
if(Serial.available()) {
break;
}
pixels.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3...
for(int c=b; c<pixels.numPixels(); c += 3) {
if(Serial.available()) {
break;
}
// hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / pixels.numPixels();
uint32_t color = gamma32(ColorHSV(hue,255,255)); // hue -> RGB
pixels.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
pixels.show(); // Update strip with new contents
delay(wait); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
}
}
//Cylon bounce effect
void CylonBounce(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay){
for(int i = 0; i < NUMPIXELS-EyeSize-2; i++) {
if(Serial.available()) { break; }
int c = (red << 16) + (green << 8) + blue;
colorAll(c, 30);
pixels.setPixelColor(i, red/10, green/10, blue/10);
for(int j = 1; j <= EyeSize; j++) {
if(Serial.available()) { break; }
pixels.setPixelColor(i+j, red, green, blue);
}
pixels.setPixelColor(i+EyeSize+1, red/10, green/10, blue/10);
pixels.show();
delay(SpeedDelay);
}
delay(ReturnDelay);
for(int i = NUMPIXELS-EyeSize-2; i > 0; i--) {
if(Serial.available()) { break; }
int c = (red << 16) + (green << 8) + blue;
colorAll(c, 30);
pixels.setPixelColor(i, red/10, green/10, blue/10);
for(int j = 1; j <= EyeSize; j++) {
if(Serial.available()) { break; }
pixels.setPixelColor(i+j, red, green, blue);
}
pixels.setPixelColor(i+EyeSize+1, red/10, green/10, blue/10);
pixels.show();
delay(SpeedDelay);
}
delay(ReturnDelay);
}
// Twinkle effect
void Twinkle(byte red, byte green, byte blue, int Count, int SpeedDelay, boolean OnlyOne) {
int c = (red << 16) + (green << 8) + blue;
colorAll(c, 30);
for (int i=0; i<Count; i++) {
if(Serial.available()) { break; }
pixels.setPixelColor(random(NUMPIXELS),red,green,blue);
pixels.show();
delay(SpeedDelay);
if(OnlyOne) {
if(Serial.available()) { break; }
int c = (red << 16) + (green << 8) + blue;
colorAll(c, 30);
}
}
delay(SpeedDelay);
}
// sparkle
void Sparkle(byte red, byte green, byte blue, int SpeedDelay) {
int Pixel = random(NUMPIXELS);
pixels.setPixelColor(Pixel,red,green,blue);
pixels.show();
delay(SpeedDelay);
pixels.setPixelColor(Pixel,0,0,0);
}
// bouncing balls
void BouncingBalls(byte red, byte green, byte blue, int BallCount) {
float Gravity = -9.81;
int StartHeight = 1;
float Height[BallCount];
float ImpactVelocityStart = sqrt( -2 * Gravity * StartHeight );
float ImpactVelocity[BallCount];
float TimeSinceLastBounce[BallCount];
int Position[BallCount];
long ClockTimeSinceLastBounce[BallCount];
float Dampening[BallCount];
for (int i = 0 ; i < BallCount ; i++) {
ClockTimeSinceLastBounce[i] = millis();
Height[i] = StartHeight;
Position[i] = 0;
ImpactVelocity[i] = ImpactVelocityStart;
TimeSinceLastBounce[i] = 0;
Dampening[i] = 0.90 - float(i)/pow(BallCount,2);
}
while (true) {
if(Serial.available()) { break; }
for (int i = 0 ; i < BallCount ; i++) {
if(Serial.available()) { break; }
TimeSinceLastBounce[i] = millis() - ClockTimeSinceLastBounce[i];
Height[i] = 0.5 * Gravity * pow( TimeSinceLastBounce[i]/1000 , 2.0 ) + ImpactVelocity[i] * TimeSinceLastBounce[i]/1000;
if ( Height[i] < 0 ) {
Height[i] = 0;
ImpactVelocity[i] = Dampening[i] * ImpactVelocity[i];
ClockTimeSinceLastBounce[i] = millis();
if ( ImpactVelocity[i] < 0.01 ) {
ImpactVelocity[i] = ImpactVelocityStart;
}
}
Position[i] = round( Height[i] * (NUMPIXELS - 1) / StartHeight);
}
for (int i = 0 ; i < BallCount ; i++) {
if(Serial.available()) { break; }
pixels.setPixelColor(Position[i],red,green,blue);
}
pixels.show();
int c = (0 << 16) + (0 << 8) + 0;
colorAll(c, 30);
}
}
// meteor rain
void meteorRain(byte red, byte green, byte blue, byte meteorSize, byte meteorTrailDecay, boolean meteorRandomDecay, int SpeedDelay) {
int c = (0 << 16) + (0 << 8) + 0;
colorAll(c, 30);
for(int i = 0; i < NUMPIXELS+NUMPIXELS; i++) {
if(Serial.available()) { break; }
// fade brightness all LEDs one step
for(int j=0; j<NUMPIXELS; j++) {
if(Serial.available()) { break; }
if( (!meteorRandomDecay) || (random(10)>5) ) {
fadeToBlack(j, meteorTrailDecay );
}
}
// draw meteor
for(int j = 0; j < meteorSize; j++) {
if(Serial.available()) { break; }
if( ( i-j <NUMPIXELS) && (i-j>=0) ) {
if(Serial.available()) { break; }
pixels.setPixelColor(i-j, red, green, blue);
}
}
pixels.show();
delay(SpeedDelay);
}
}
void fadeToBlack(int ledNo, byte fadeValue) {
// NeoPixel
uint32_t oldColor;
uint8_t r, g, b;
int value;
oldColor = pixels.getPixelColor(ledNo);
r = (oldColor & 0x00ff0000UL) >> 16;
g = (oldColor & 0x0000ff00UL) >> 8;
b = (oldColor & 0x000000ffUL);
r=(r<=10)? 0 : (int) r-(r*fadeValue/256);
g=(g<=10)? 0 : (int) g-(g*fadeValue/256);
b=(b<=10)? 0 : (int) b-(b*fadeValue/256);
pixels.setPixelColor(ledNo, r,g,b);
}
// fire
void Fire(int Cooling, int Sparking, int SpeedDelay) {
static byte heat[NUMPIXELS];
int cooldown;
// Step 1. Cool down every cell a little
for( int i = 0; i < NUMPIXELS; i++) {
if(Serial.available()) { break; }
cooldown = random(0, ((Cooling * 10) / NUMPIXELS) + 2);
if(cooldown>heat[i]) {
heat[i]=0;
} else {
heat[i]=heat[i]-cooldown;
}
}
// Step 2. Heat from each cell drifts 'up' and diffuses a little
for( int k= NUMPIXELS - 1; k >= 2; k--) {
if(Serial.available()) { break; }
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
}
// Step 3. Randomly ignite new 'sparks' near the bottom
if( random(255) < Sparking ) {
int y = random(7);
heat[y] = heat[y] + random(160,255);
//heat[y] = random(160,255);
}
// Step 4. Convert heat to LED colors
for( int j = 0; j < NUMPIXELS; j++) {
if(Serial.available()) { break; }
setPixelHeatColor(j, heat[j] );
}
pixels.show();
delay(SpeedDelay);
}
void setPixelHeatColor (int Pixel, byte temperature) {
// Scale 'heat' down from 0-255 to 0-191
byte t192 = round((temperature/255.0)*191);
// calculate ramp up from
byte heatramp = t192 & 0x3F; // 0..63
heatramp <<= 2; // scale up to 0..252
// figure out which third of the spectrum we're in:
if( t192 > 0x80) { // hottest
pixels.setPixelColor(Pixel, 255, 255, heatramp);
} else if( t192 > 0x40 ) { // middle
pixels.setPixelColor(Pixel, 255, heatramp, 0);
} else { // coolest
pixels.setPixelColor(Pixel, heatramp, 0, 0);
}
}
// running lights
void RunningLights(byte red, byte green, byte blue, int WaveDelay) {
int Position=0;
for(int j=0; j<NUMPIXELS*2; j++)
{
if(Serial.available()) { break; }
Position++; // = 0; //Position + Rate;
for(int i=0; i<NUMPIXELS; i++) {
if(Serial.available()) { break; }
// sine wave, 3 offset waves make a rainbow!
//float level = sin(i+Position) * 127 + 128;
//setPixel(i,level,0,0);
//float level = sin(i+Position) * 127 + 128;
pixels.setPixelColor(i,((sin(i+Position) * 127 + 128)/255)*red,
((sin(i+Position) * 127 + 128)/255)*green,
((sin(i+Position) * 127 + 128)/255)*blue);
}
pixels.show();
delay(WaveDelay);
}
}
// color wipe
void colorWipe(byte red, byte green, byte blue, int SpeedDelay) {
for(uint16_t i=0; i<NUMPIXELS; i++) {
if(Serial.available()) { break; }
pixels.setPixelColor(i, red, green, blue);
pixels.show();
delay(SpeedDelay);
}
}
// bouncing colored balls
void BouncingColoredBalls(int BallCount, byte colors[][3]) {
float Gravity = -9.81;
int StartHeight = 1;
float Height[BallCount];
float ImpactVelocityStart = sqrt( -2 * Gravity * StartHeight );
float ImpactVelocity[BallCount];
float TimeSinceLastBounce[BallCount];
int Position[BallCount];
long ClockTimeSinceLastBounce[BallCount];
float Dampening[BallCount];
for (int i = 0 ; i < BallCount ; i++) {
if(Serial.available()) { break; }
ClockTimeSinceLastBounce[i] = millis();
Height[i] = StartHeight;
Position[i] = 0;
ImpactVelocity[i] = ImpactVelocityStart;
TimeSinceLastBounce[i] = 0;
Dampening[i] = 0.90 - float(i)/pow(BallCount,2);
}
while (true) {
if(Serial.available()) { break; }
for (int i = 0 ; i < BallCount ; i++) {
if(Serial.available()) { break; }
TimeSinceLastBounce[i] = millis() - ClockTimeSinceLastBounce[i];
Height[i] = 0.5 * Gravity * pow( TimeSinceLastBounce[i]/1000 , 2.0 ) + ImpactVelocity[i] * TimeSinceLastBounce[i]/1000;
if ( Height[i] < 0 ) {
if(Serial.available()) { break; }
Height[i] = 0;
ImpactVelocity[i] = Dampening[i] * ImpactVelocity[i];
ClockTimeSinceLastBounce[i] = millis();
if ( ImpactVelocity[i] < 0.01 ) {
if(Serial.available()) { break; }
ImpactVelocity[i] = ImpactVelocityStart;
}
}
Position[i] = round( Height[i] * (NUMPIXELS - 1) / StartHeight);
}
for (int i = 0 ; i < BallCount ; i++) {
if(Serial.available()) { break; }
pixels.setPixelColor(Position[i],colors[i][0],colors[i][1],colors[i][2]);
}
pixels.show();
int c = (0 << 16) + (0 << 8) + 0;
colorAll(c, 30);
}
}
// snow sparkle
void SnowSparkle(byte red, byte green, byte blue, int SparkleDelay, int SpeedDelay) {
int c = (red << 16) + (green << 8) + blue;
colorAll(c, 30);
int Pixel = random(NUMPIXELS);
pixels.setPixelColor(Pixel,0xff,0xff,0xff);
pixels.show();
delay(SparkleDelay);
pixels.setPixelColor(Pixel,red,green,blue);
pixels.show();
delay(SpeedDelay);
}
// strobe
void Strobe(byte red, byte green, byte blue, int StrobeCount, int FlashDelay, int EndPause){
for(int j = 0; j < StrobeCount; j++) {
if(Serial.available()) { break; }
int c = (red << 16) + (green << 8) + blue;
colorAll(c, 30);
pixels.show();
delay(FlashDelay);
colorAll(c, 30);
pixels.show();
delay(FlashDelay);
}
delay(EndPause);
}
// new KITT
void NewKITT(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay){
RightToLeft(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
LeftToRight(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
OutsideToCenter(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
CenterToOutside(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
LeftToRight(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
RightToLeft(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
OutsideToCenter(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
CenterToOutside(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
}
void CenterToOutside(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
for(int i =((NUMPIXELS-EyeSize)/2); i>=0; i--) {
if(Serial.available()) { break; }
int c = (red << 16) + (green << 8) + blue;
colorAll(c, 30);
pixels.setPixelColor(i, red/10, green/10, blue/10);
for(int j = 1; j <= EyeSize; j++) {
if(Serial.available()) { break; }
pixels.setPixelColor(i+j, red, green, blue);
}
pixels.setPixelColor(i+EyeSize+1, red/10, green/10, blue/10);
pixels.setPixelColor(NUMPIXELS-i, red/10, green/10, blue/10);
for(int j = 1; j <= EyeSize; j++) {
if(Serial.available()) { break; }
pixels.setPixelColor(NUMPIXELS-i-j, red, green, blue);
}
pixels.setPixelColor(NUMPIXELS-i-EyeSize-1, red/10, green/10, blue/10);
pixels.show();
delay(SpeedDelay);
}
delay(ReturnDelay);
}
void OutsideToCenter(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
for(int i = 0; i<=((NUMPIXELS-EyeSize)/2); i++) {
if(Serial.available()) { break; }
int c = (red << 16) + (green << 8) + blue;
colorAll(c, 30);
pixels.setPixelColor(i, red/10, green/10, blue/10);
for(int j = 1; j <= EyeSize; j++) {
if(Serial.available()) { break; }
pixels.setPixelColor(i+j, red, green, blue);
}
pixels.setPixelColor(i+EyeSize+1, red/10, green/10, blue/10);
pixels.setPixelColor(NUMPIXELS-i, red/10, green/10, blue/10);
for(int j = 1; j <= EyeSize; j++) {
if(Serial.available()) { break; }
pixels.setPixelColor(NUMPIXELS-i-j, red, green, blue);
}
pixels.setPixelColor(NUMPIXELS-i-EyeSize-1, red/10, green/10, blue/10);
pixels.show();
delay(SpeedDelay);
}
delay(ReturnDelay);
}
void LeftToRight(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
for(int i = 0; i < NUMPIXELS-EyeSize-2; i++) {
if(Serial.available()) { break; }
int c = (red << 16) + (green << 8) + blue;
colorAll(c, 30);
pixels.setPixelColor(i, red/10, green/10, blue/10);
for(int j = 1; j <= EyeSize; j++) {
if(Serial.available()) { break; }
pixels.setPixelColor(i+j, red, green, blue);
}
pixels.setPixelColor(i+EyeSize+1, red/10, green/10, blue/10);
pixels.show();
delay(SpeedDelay);
}
delay(ReturnDelay);
}
void RightToLeft(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
for(int i = NUMPIXELS-EyeSize-2; i > 0; i--) {
if(Serial.available()) { break; }
int c = (red << 16) + (green << 8) + blue;
colorAll(c, 30);
pixels.setPixelColor(i, red/10, green/10, blue/10);
for(int j = 1; j <= EyeSize; j++) {
if(Serial.available()) { break; }
pixels.setPixelColor(i+j, red, green, blue);
}
pixels.setPixelColor(i+EyeSize+1, red/10, green/10, blue/10);
pixels.show();
delay(SpeedDelay);
}
delay(ReturnDelay);
}
// Set all pixels in the strip to a solid color, then wait (ms)
void colorAll(uint32_t c, uint8_t wait) {
uint16_t i;
for(i=0; i<pixels.numPixels(); i++) {
pixels.setPixelColor(i, c);
}
pixels.show();
delay(wait);
}
// A 32-bit variant of gamma8() that applies the same function
// to all components of a packed RGB or WRGB value.
uint32_t gamma32(uint32_t x) {
uint8_t *y = (uint8_t *)&x;
// All four bytes of a 32-bit value are filtered even if RGB (not WRGB),
// to avoid a bunch of shifting and masking that would be necessary for
// properly handling different endianisms (and each byte is a fairly
// trivial operation, so it might not even be wasting cycles vs a check
// and branch for the RGB case). In theory this might cause trouble *if*
// someone's storing information in the unused most significant byte
// of an RGB value, but this seems exceedingly rare and if it's
// encountered in reality they can mask values going in or coming out.
for (uint8_t i = 0; i < 4; i++)
y[i] = gamma8(y[i]);
return x; // Packed 32-bit return
}
uint32_t ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) {
uint8_t r, g, b;
// Remap 0-65535 to 0-1529. Pure red is CENTERED on the 64K rollover;
// 0 is not the start of pure red, but the midpoint...a few values above
// zero and a few below 65536 all yield pure red (similarly, 32768 is the
// midpoint, not start, of pure cyan). The 8-bit RGB hexcone (256 values
// each for red, green, blue) really only allows for 1530 distinct hues
// (not 1536, more on that below), but the full unsigned 16-bit type was
// chosen for hue so that one's code can easily handle a contiguous color
// wheel by allowing hue to roll over in either direction.
hue = (hue * 1530L + 32768) / 65536;
// Because red is centered on the rollover point (the +32768 above,
// essentially a fixed-point +0.5), the above actually yields 0 to 1530,
// where 0 and 1530 would yield the same thing. Rather than apply a
// costly modulo operator, 1530 is handled as a special case below.
// So you'd think that the color "hexcone" (the thing that ramps from
// pure red, to pure yellow, to pure green and so forth back to red,
// yielding six slices), and with each color component having 256
// possible values (0-255), might have 1536 possible items (6*256),
// but in reality there's 1530. This is because the last element in
// each 256-element slice is equal to the first element of the next
// slice, and keeping those in there this would create small
// discontinuities in the color wheel. So the last element of each
// slice is dropped...we regard only elements 0-254, with item 255
// being picked up as element 0 of the next slice. Like this:
// Red to not-quite-pure-yellow is: 255, 0, 0 to 255, 254, 0
// Pure yellow to not-quite-pure-green is: 255, 255, 0 to 1, 255, 0
// Pure green to not-quite-pure-cyan is: 0, 255, 0 to 0, 255, 254
// and so forth. Hence, 1530 distinct hues (0 to 1529), and hence why
// the constants below are not the multiples of 256 you might expect.
// Convert hue to R,G,B (nested ifs faster than divide+mod+switch):
if (hue < 510) { // Red to Green-1
b = 0;
if (hue < 255) { // Red to Yellow-1
r = 255;
g = hue; // g = 0 to 254
} else { // Yellow to Green-1
r = 510 - hue; // r = 255 to 1
g = 255;
}
} else if (hue < 1020) { // Green to Blue-1
r = 0;
if (hue < 765) { // Green to Cyan-1
g = 255;
b = hue - 510; // b = 0 to 254
} else { // Cyan to Blue-1
g = 1020 - hue; // g = 255 to 1
b = 255;
}
} else if (hue < 1530) { // Blue to Red-1
g = 0;
if (hue < 1275) { // Blue to Magenta-1
r = hue - 1020; // r = 0 to 254
b = 255;
} else { // Magenta to Red-1
r = 255;
b = 1530 - hue; // b = 255 to 1
}
} else { // Last 0.5 Red (quicker than % operator)
r = 255;
g = b = 0;
}
// Apply saturation and value to R,G,B, pack into 32-bit result:
uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255
uint16_t s1 = 1 + sat; // 1 to 256; same reason
uint8_t s2 = 255 - sat; // 255 to 0
return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) |
(((((g * s1) >> 8) + s2) * v1) & 0xff00) |
(((((b * s1) >> 8) + s2) * v1) >> 8);
}
static uint8_t gamma8(uint8_t x) {
return pgm_read_byte(&_NeoPixelGammaTable[x]); // 0-255 in, 0-255 out
}