Code mode for Kaleidoscope Eyes

Adafruit's tiny microcontroller platform. Please tell us which board you are using.
For CircuitPython issues, ask in the Adafruit CircuitPython forum.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
philmonty
 
Posts: 52
Joined: Mon Jun 08, 2009 5:28 pm

Code mode for Kaleidoscope Eyes

Post by philmonty »

Has anyone modified the Kaleidoscope eyes code to go into "sleep" and only wake up say every 5 mins? I'm not sure how to do this and would appreciate any tips - would then save on battery life too.

Phil

User avatar
adafruit_support_mike
 
Posts: 67454
Joined: Thu Feb 11, 2010 2:51 pm

Re: Code mode for Kaleidoscope Eyes

Post by adafruit_support_mike »

The Gemma Space Invader Pendant project will probably give you some ideas: http://learn.adafruit.com/trinket-slash ... er-pendant

That uses a button to wake up the microcontroller, but the ATtiny85 has a built-in watchdog timer that can be used to wake the chip up after a specified amount of time. The details for that are on page 42 of the datasheet: http://www.atmel.com/Images/Atmel-2586- ... asheet.pdf

User avatar
philmonty
 
Posts: 52
Joined: Mon Jun 08, 2009 5:28 pm

Re: Code mode for Kaleidoscope Eyes

Post by philmonty »

Thanks - not sure I need to close down the CPU, but that's definitely an option.

Any advice on how I just blank/turn off the neopixel rings ? I've tried looking through the examples and can't see an easy way. Given that it what sucks the power, couldn't I just turn off the rings and use a delay()?

My idea is that the neopixel rings just startup every five minutes, cycle for 30 seconds, and then sleep.

Phil

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Code mode for Kaleidoscope Eyes

Post by adafruit_support_bill »

If you just want to turn off the rings, you can call setBrightness(0);

User avatar
philmonty
 
Posts: 52
Joined: Mon Jun 08, 2009 5:28 pm

Re: Code mode for Kaleidoscope Eyes

Post by philmonty »

Ok, so I have to admit I am a newbie Arduino programmer, but I am having a blast with the trinket.

I used the function that you suggested and it definitely turns off the rings perfectly. The problem I am having is that the delay(5000) doesn't seem to be doing anything:

I defined two new global uint8_t variables:
loopCount = 0,
maxLoops = 200;

pixels.show(); //existing code at end of main loop.

//code I stuck in
if (++loopCount > maxLoops) {
pixels.setBrightness(0); //turn rings off
delay(5000); //wait
pixels.setBrightness(brightness); //turn rings back on
loopCount =0; //reset loop counter
}

When this runs, the rings stay on without a delay. Hmmmmm. When I document out the "pixels.setBrightness(brightness);" the rings stay off after a few cycles, so I know it's getting into the loop. All I want to do is pause my goggles regularly...

Fun project!

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: Code mode for Kaleidoscope Eyes

Post by pburgess »

Something like this might work:

Code: Select all

unsigned long t;

void setup() {
  // NORMAL SETUP STUFF HAPPENS HERE, then...
  t = millis();
}

void loop() {
  if((millis() - t) >= 30000L) { // 30 seconds elapsed?
    for(int i=0; i<strip.numPixels(); i++) strip.setPixelColor(i, 0);
    strip.show();
    delay(270000L); // 4.5 minutes
    t = millis();
  }

  // NORMAL ANIMATION CODE GOES HERE
}
It's not all super-efficient using sleep or anything, but should get the job done.

User avatar
philmonty
 
Posts: 52
Joined: Mon Jun 08, 2009 5:28 pm

Re: Code mode for Kaleidoscope Eyes

Post by philmonty »

Dude that's awesome. And the good thing I understand what you wrote so I have learnt something here.

Much appreciated senor :-)

User avatar
philmonty
 
Posts: 52
Joined: Mon Jun 08, 2009 5:28 pm

Re: Code mode for Kaleidoscope Eyes

Post by philmonty »

Hate to keep asking questions, but I am trying to cycle the display between the Rainbow and ecto schemes, and the idea is to add more schemes over time. The trouble with the code I have written is that after the 5min break it just cycles back to Rainbow. I'd appreciate if someone can explain what I am missing - do I need to reinitialize the neopixel rings?

So... I moved EFFECT from a define to being a unit8_t with an initial definition:

unit8_t
EFFECT = RAINBOW; // Choose a visual effect from the names below

Then at the end of the code:

if((millis() - t) >= 30000L) { // 30 seconds elapsed?
for(int i=0; i<pixels.numPixels(); i++) pixels.setPixelColor(i, 0);
pixels.show();
delay(270000L); // 4.5 minutes

if (EFFECT == RAINBOW){ //cycle the effect
EFFECT = ECTO;
}
else {
EFFECT = RAINBOW;
}
t = millis();
}

User avatar
philmonty
 
Posts: 52
Joined: Mon Jun 08, 2009 5:28 pm

Re: Code mode for Kaleidoscope Eyes

Post by philmonty »

Ok, so I figured it out and now understand compiler instructions. I changed the #if to a normal if line, and it all works perfectly and just squeezes into the memory size (Binary sketch size: 5,248 bytes (of a 5,310 byte maximum) :-). For reference, here is the code:

Code: Select all

/*
NeoPixel Ring goggles sketch -- for steampunk, rave or Burning Man fashion!
Welding or costume goggles using 50mm round lenses can be outfitted with
a pair of Adafruit NeoPixel Rings: http://www.adafruit.com/product/1463

Please exercise common sense.  These goggles emit a LOT of stray light and
should NOT BE WORN ON YOUR EYES.  They're for fashion and costuming only,
for display on a hat or on your forehead.

Draws a spinning rainbow on both eyepieces.  Not a Mac beachball, honest.
"Eyes" glance around and blink at random.

For 'reflected' colors (rainbows rotate in opposite directions, while eyes
look in same direction), connect the output of the first ring to the input
of the second.  Or you can connect the inputs of both rings to the same
Arduino pin if that's easier -- the rainbows will both twirl in the same
direction in that case.

By default, pixel #0 (the first LED) on both rings should be at the TOP of
the goggles.  Looking at the BACK of the board, pixel #0 is immediately
clockwise from the OUT connection.  If a different pixel is at the top,
that's OK, the code can compensate (TOP_LED_FIRST and TOP_LED_SECOND below).

10/18/2013 - Mods by PhilM to run for 30 secs then sleep for 4.5mins, and 
cycle through both ECTO and RAINBOW
*/

#include <Adafruit_NeoPixel.h>
#ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc.
  #include <avr/power.h>
#endif

#define PIN            4

#define TOP_LED_FIRST  0 // Change these if the first pixel is not
#define TOP_LED_SECOND 0 // at the top of the first and/or second ring.
#define RAINBOW        0
#define ECTO           1

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(32, PIN, NEO_GRB + NEO_KHZ800);

const int8_t PROGMEM
  yCoord[] = { // Vertical coordinate of each pixel.  First pixel is at top.
    127,117,90,49,0,-49,-90,-117,-127,-117,-90,-49,0,49,90,117 },
  sine[] = { // Brightness table for ecto effect
    0, 28, 96, 164, 192, 164, 96, 28, 0, 28, 96, 164, 192, 164, 96, 28 };

// Eyelid vertical coordinates.  Eyes shut slightly below center.
#define upperLidTop     130
#define upperLidBottom  -45
#define lowerLidTop     -40
#define lowerLidBottom -130

// Gamma correction improves appearance of midrange colors
const uint8_t PROGMEM gamma8[] = {
      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
};

uint32_t
  iColor[16][3];      // Background colors for eyes
int16_t
  hue          =   0; // Initial hue around perimeter (0-1535)
uint8_t
  iBrightness[16],    // Brightness map -- eye colors get scaled by these
  brightness   = 220, // Global brightness (0-255)
  blinkFrames  =   5, // Speed of current blink
  blinkCounter =  30, // Countdown to end of next blink
  eyePos       = 192, // Current 'resting' eye (pupil) position
  newEyePos    = 192, // Next eye position when in motion
  gazeCounter  =  75, // Countdown to next eye movement
  gazeFrames   =  50, // Duration of eye movement (smaller = faster)
  EFFECT       = ECTO; // Choose a visual effect from the names below
  
int8_t
  eyeMotion    =   0; // Distance from prior to new position 

unsigned long t;

void setup() {
#ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc.
  if(F_CPU == 16000000) clock_prescale_set(clock_div_1);
  // Seed random number generator from an unused analog input:
  randomSeed(analogRead(2));
#else
  randomSeed(analogRead(A0));
#endif
  t = millis();
  pixels.begin();
}

void loop() {
  uint8_t i, r, g, b, a, c, inner, outer, ep;
  int     y1, y2, y3, y4, h;
  int8_t  y;

  // Draw eye background colors

if (EFFECT == RAINBOW) {

  // This renders a glotating rainbow...a WAY overdone LED effect but
  // does show the color gamut nicely.

  for(h=hue, i=0; i<16; i++, h += 96) {
    a = h;
    switch((h >> 8) % 6) {
     case 0: iColor[i][0] = 255; iColor[i][1] =   a; iColor[i][2] =   0; break;
     case 1: iColor[i][0] =  ~a; iColor[i][1] = 255; iColor[i][2] =   0; break;
     case 2: iColor[i][0] =   0; iColor[i][1] = 255; iColor[i][2] =   a; break;
     case 3: iColor[i][0] =   0; iColor[i][1] =  ~a; iColor[i][2] = 255; break;
     case 4: iColor[i][0] =   a; iColor[i][1] =   0; iColor[i][2] = 255; break;
     case 5: iColor[i][0] = 255; iColor[i][1] =   0; iColor[i][2] =  ~a; break;
    }
  }
  hue += 7;
  if(hue >= 1536) hue -= 1536;
}
else if (EFFECT == ECTO)
{
  // A steampunk aesthetic might fare better with this more subdued effect.
  // Etherial green glow with just a little animation for visual spice.

  a = (hue >> 4) & 15;
  c =  hue       & 15;
  for(i=0; i<16; i++) {
    b = (a + 1) & 15;
    iColor[i][1] = 255; // Predominantly green
    iColor[i][0] = (pgm_read_byte(&sine[a]) * (16 - c) +
                    pgm_read_byte(&sine[b]) *       c  ) >> 4;
    iColor[i][2] = iColor[i][0] >> 1;
    a = b;
  }
  hue -= 3;

}

  // Render current blink (if any) into brightness map
  if(blinkCounter <= blinkFrames * 2) { // In mid-blink?
    if(blinkCounter > blinkFrames) {    // Eye closing
      outer = blinkFrames * 2 - blinkCounter;
      inner = outer + 1;
    } else {                            // Eye opening
      inner = blinkCounter;
      outer = inner - 1;
    }
    y1 = upperLidTop    - (upperLidTop - upperLidBottom) * outer / blinkFrames;
    y2 = upperLidTop    - (upperLidTop - upperLidBottom) * inner / blinkFrames;
    y3 = lowerLidBottom + (lowerLidTop - lowerLidBottom) * inner / blinkFrames;
    y4 = lowerLidBottom + (lowerLidTop - lowerLidBottom) * outer / blinkFrames;
    for(i=0; i<16; i++) {
      y = pgm_read_byte(&yCoord[i]);
      if(y > y1) {        // Above top lid
        iBrightness[i] = 0;
      } else if(y > y2) { // Blur edge of top lid in motion
        iBrightness[i] = brightness * (y1 - y) / (y1 - y2);
      } else if(y > y3) { // In eye
        iBrightness[i] = brightness;
      } else if(y > y4) { // Blur edge of bottom lid in motion
        iBrightness[i] = brightness * (y - y4) / (y3 - y4);
      } else {            // Below bottom lid
        iBrightness[i] = 0;
      }
    }
  } else { // Not in blink -- set all 'on'
    memset(iBrightness, brightness, sizeof(iBrightness));
  }

  if(--blinkCounter == 0) { // Init next blink?
    blinkFrames  = random(4, 8);
    blinkCounter = blinkFrames * 2 + random(5, 180);
  }

  // Calculate current eye movement, possibly init next one
  if(--gazeCounter <= gazeFrames) { // Is pupil in motion?
    ep = newEyePos - eyeMotion * gazeCounter / gazeFrames; // Current pos.
    if(gazeCounter == 0) {                   // Last frame?
      eyePos      = newEyePos;               // Current position = new pos
      newEyePos   = random(16) * 16;         // New pos. (always pixel center)
      eyeMotion   = newEyePos - eyePos;      // Distance to move
      gazeFrames  = random(10, 20);          // Duration of movement
      gazeCounter = random(gazeFrames, 130); // Count to END of next movement
    }
  } else ep = eyePos; // Not moving -- fixed gaze

  // Draw pupil -- 2 pixels wide, but sup-pixel positioning may span 3.
  a = ep >> 4;         // First candidate
  b = (a + 1)  & 0x0F; // 1 pixel CCW of a
  c = (a + 2)  & 0x0F; // 2 pixels CCW of a
  i = ep & 0x0F;       // Fraction of 'c' covered (0-15)
  iBrightness[a] = (iBrightness[a] *       i ) >> 4;
  iBrightness[b] = 0;
  iBrightness[c] = (iBrightness[c] * (16 - i)) >> 4;

  // Merge iColor with iBrightness, issue to NeoPixels
  for(i=0; i<16; i++) {
    a = iBrightness[i] + 1;
    // First eye
    r = iColor[i][0];            // Initial background RGB color
    g = iColor[i][1];
    b = iColor[i][2];
    if(a) {
      r = (r * a) >> 8;          // Scale by brightness map
      g = (g * a) >> 8;
      b = (b * a) >> 8;
    }
    pixels.setPixelColor(((i + TOP_LED_FIRST) & 15),
      pgm_read_byte(&gamma8[r]), // Gamma correct and set pixel
      pgm_read_byte(&gamma8[g]),
      pgm_read_byte(&gamma8[b]));

    // Second eye uses the same colors, but reflected horizontally.
    // The same brightness map is used, but not reflected (same left/right)
    r = iColor[15 - i][0];
    g = iColor[15 - i][1];
    b = iColor[15 - i][2];
    if(a) {
      r = (r * a) >> 8;
      g = (g * a) >> 8;
      b = (b * a) >> 8;
    }
    pixels.setPixelColor(16 + ((i + TOP_LED_SECOND) & 15),
      pgm_read_byte(&gamma8[r]),
      pgm_read_byte(&gamma8[g]),
      pgm_read_byte(&gamma8[b]));
  }
  pixels.show();
  delay(30); 
   
  if((millis() - t) >= 30000L) { // 30 seconds elapsed?
    for(int i=0; i<pixels.numPixels(); i++) pixels.setPixelColor(i, 0);
    pixels.show();
    delay(270000L); // 4.5 minutes
     
    if (EFFECT == RAINBOW){ //cycle the effect
        EFFECT = ECTO;
    }
    else {
      EFFECT = RAINBOW;
    }
      t = millis();
   }
  
}

User avatar
TheExpertNoob
 
Posts: 15
Joined: Sun Feb 15, 2015 8:03 pm

Re: Code mode for Kaleidoscope Eyes

Post by TheExpertNoob »

I know this is an old thread, but this is what I have been working on. Its currently too big to fit on the trinket as is, but you can flash the trinket with ArduinoISP as an ATtiny85 which I have found easier and less cumbersome.

Also, Discovered this by accident today, Its a neat effect. Replace the similar lines under the "ECTO" effect.

Code: Select all

    iColor[i][0] = (pgm_read_byte(&sine[a]) * ((16 - c) - 105) + pgm_read_byte(&sine[b]) * (c - 105)) >> 4; //red
    iColor[i][1] = 255; //green
    iColor[i][2] = (pgm_read_byte(&sine[a]) * ((16 - c) - 55) + pgm_read_byte(&sine[b]) * (c - 55)) >> 4; //blue

Code: Select all

#include <Adafruit_NeoPixel.h>
#ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc.
  #include <avr/power.h>
#endif
 
#define PIN            4
 
#define TOP_LED_FIRST  0 // Change these if the first pixel is not
#define TOP_LED_SECOND 0 // at the top of the first and/or second ring.
 
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(32, PIN, NEO_GRB + NEO_KHZ800);
 
const int8_t PROGMEM
  yCoord[] = { // Vertical coordinate of each pixel.  First pixel is at top.
    127,117,90,49,0,-49,-90,-117,-127,-117,-90,-49,0,49,90,117 },
  sine[] = { // Brightness table for ecto effect
    0, 28, 96, 164, 192, 164, 96, 28, 0, 28, 96, 164, 192, 164, 96, 28 };
 
// Eyelid vertical coordinates.  Eyes shut slightly below center.
#define upperLidTop     130
#define upperLidBottom  -45
#define lowerLidTop     -40
#define lowerLidBottom -130
 
// Gamma correction improves appearance of midrange colors
const uint8_t PROGMEM gamma8[] = {
      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
};
 
uint32_t
  iColor[16][3];      // Background colors for eyes
int16_t
  hue          =   0; // Initial hue around perimeter (0-1535)
uint8_t
  iBrightness[16],    // Brightness map -- eye colors get scaled by these
  brightness   = 170, // Global brightness (0-255)
  blinkFrames  =   5, // Speed of current blink
  blinkCounter =  30, // Countdown to end of next blink
  eyePos       = 192, // Current 'resting' eye (pupil) position
  newEyePos    = 192, // Next eye position when in motion
  gazeCounter  =  75, // Countdown to next eye movement
  gazeFrames   =  50; // Duration of eye movement (smaller = faster)
int8_t
  eyeMotion    =   0; // Distance from prior to new position
 
const int  buttonPin = 3;    // the pin that the pushbutton is attached to
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button
 
void setup() {
#ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc.
  if(F_CPU == 16000000) clock_prescale_set(clock_div_1);
  // Seed random number generator from an unused analog input:
  randomSeed(analogRead(2));
#else
  randomSeed(analogRead(A0));
#endif
  pinMode(buttonPin, INPUT); // initialize the button pin as a input
  pixels.begin();
}
 
void loop() {
  uint8_t i, r, g, b, a, c, inner, outer, ep;
  int     y1, y2, y3, y4, h;
  int8_t  y;
 
 buttonState = digitalRead(buttonPin); // read the pushbutton input pin:
  if (buttonState != lastButtonState) { // compare the buttonState to its previous state
    if (buttonState == LOW) { // if the state has changed, increment the counter
    buttonPushCounter++;
    }
  }
  lastButtonState = buttonState;
 
  a = (hue >> 4) & 15;
  c =  hue       & 15;
  for(i=0; i<16; i++) {
    b = (a + 1) & 15;
    switch (buttonPushCounter) {
      case 0: //rainbow
    for(h=hue, i=0; i<16; i++, h += 96) {
    a = h;
    switch((h >> 8) % 6) {
     case 0: iColor[i][0] = 255; iColor[i][1] =   a; iColor[i][2] =   0; break;
     case 1: iColor[i][0] =  ~a; iColor[i][1] = 255; iColor[i][2] =   0; break;
     case 2: iColor[i][0] =   0; iColor[i][1] = 255; iColor[i][2] =   a; break;
     case 3: iColor[i][0] =   0; iColor[i][1] =  ~a; iColor[i][2] = 255; break;
     case 4: iColor[i][0] =   a; iColor[i][1] =   0; iColor[i][2] = 255; break;
     case 5: iColor[i][0] = 255; iColor[i][1] =   0; iColor[i][2] =  ~a; break;
    }
  }
  hue += 7;
  if(hue >= 1536) hue -= 1536;
      break;
      case 1: //red
    iColor[i][0] = 255; //red
    iColor[i][1] = (pgm_read_byte(&sine[a]) * (16 - c) + pgm_read_byte(&sine[b]) * c) >> 4; //green
    iColor[i][2] = (pgm_read_byte(&sine[a]) * (16 - c) + pgm_read_byte(&sine[b]) * c) >> 4; //blue
      break;
      case 2: //green
    iColor[i][0] = (pgm_read_byte(&sine[a]) * (16 - c) + pgm_read_byte(&sine[b]) * c) >> 4; //red
    iColor[i][1] = 255; //green
    iColor[i][2] = (pgm_read_byte(&sine[a]) * (16 - c) + pgm_read_byte(&sine[b]) * c) >> 4; //blue
      break;
      case 3: //blue
    iColor[i][0] = (pgm_read_byte(&sine[a]) * (16 - c) + pgm_read_byte(&sine[b]) * c) >> 4; //red
    iColor[i][1] = (pgm_read_byte(&sine[a]) * (16 - c) + pgm_read_byte(&sine[b]) * c) >> 4; //green
    iColor[i][2] = 255; //blue
      break;
      case 4: //yellow
    iColor[i][0] = 255; //red
    iColor[i][1] = 255; //green
    iColor[i][2] = (pgm_read_byte(&sine[a]) * (16 - c) + pgm_read_byte(&sine[b]) * c) >> 4; //blue
      break;
      case 5: //teal
    iColor[i][0] = (pgm_read_byte(&sine[a]) * (16 - c) + pgm_read_byte(&sine[b]) * c) >> 4; //red
    iColor[i][1] = 255; //green
    iColor[i][2] = 255; //blue
      break;
      case 6: //pink
    iColor[i][0] = 255; //red
    iColor[i][1] = (pgm_read_byte(&sine[a]) * (16 - c) + pgm_read_byte(&sine[b]) * c) >> 4; //green
    iColor[i][2] = 255; //blue
      break;
      case 7: //police
    iColor[i][0] = (pgm_read_byte(&sine[a]) * (16 - c) + pgm_read_byte(&sine[b]) * c) >> 4; //red
    iColor[i][1] = 0; //green
    iColor[i][2] = ~(pgm_read_byte(&sine[a]) * (16 - c) + pgm_read_byte(&sine[b]) * c) >> 4; //blue
      break;
      case 8: //auburn
    iColor[i][0] = (pgm_read_byte(&sine[a]) * (16 - c) + pgm_read_byte(&sine[b]) * c) >> 4; //red
    iColor[i][1] = (pgm_read_byte(&sine[a]) * ((16 - c) - 128) + pgm_read_byte(&sine[b]) * (c - 128)) >> 4; //green
    iColor[i][2] = ~(pgm_read_byte(&sine[a]) * (16 - c) + pgm_read_byte(&sine[b]) * c) >> 4; //blue
      break;
      default:
    buttonPushCounter = 0;
    iColor[i][0] = 0; //red
    iColor[i][1] = 0; //green
    iColor[i][2] = 0; //blue
     }
    a = b;
  }
  hue -= 3;
 
  // Render current blink (if any) into brightness map
  if(blinkCounter <= blinkFrames * 2) { // In mid-blink?
    if(blinkCounter > blinkFrames) {    // Eye closing
      outer = blinkFrames * 2 - blinkCounter;
      inner = outer + 1;
    } else {                            // Eye opening
      inner = blinkCounter;
      outer = inner - 1;
    }
    y1 = upperLidTop    - (upperLidTop - upperLidBottom) * outer / blinkFrames;
    y2 = upperLidTop    - (upperLidTop - upperLidBottom) * inner / blinkFrames;
    y3 = lowerLidBottom + (lowerLidTop - lowerLidBottom) * inner / blinkFrames;
    y4 = lowerLidBottom + (lowerLidTop - lowerLidBottom) * outer / blinkFrames;
    for(i=0; i<16; i++) {
      y = pgm_read_byte(&yCoord[i]);
      if(y > y1) {        // Above top lid
        iBrightness[i] = 0;
      } else if(y > y2) { // Blur edge of top lid in motion
        iBrightness[i] = brightness * (y1 - y) / (y1 - y2);
      } else if(y > y3) { // In eye
        iBrightness[i] = brightness;
      } else if(y > y4) { // Blur edge of bottom lid in motion
        iBrightness[i] = brightness * (y - y4) / (y3 - y4);
      } else {            // Below bottom lid
        iBrightness[i] = 0;
      }
    }
  } else { // Not in blink -- set all 'on'
    memset(iBrightness, brightness, sizeof(iBrightness));
  }
 
  if(--blinkCounter == 0) { // Init next blink?
    blinkFrames  = random(4, 8);
    blinkCounter = blinkFrames * 2 + random(5, 180);
  }
 
  // Calculate current eye movement, possibly init next one
  if(--gazeCounter <= gazeFrames) { // Is pupil in motion?
    ep = newEyePos - eyeMotion * gazeCounter / gazeFrames; // Current pos.
    if(gazeCounter == 0) {                   // Last frame?
      eyePos      = newEyePos;               // Current position = new pos
      newEyePos   = random(16) * 16;         // New pos. (always pixel center)
      eyeMotion   = newEyePos - eyePos;      // Distance to move
      gazeFrames  = random(10, 20);          // Duration of movement
      gazeCounter = random(gazeFrames, 130); // Count to END of next movement
    }
  } else ep = eyePos; // Not moving -- fixed gaze
 
  // Draw pupil -- 2 pixels wide, but sup-pixel positioning may span 3.
  a = ep >> 4;         // First candidate
  b = (a + 1)  & 0x0F; // 1 pixel CCW of a
  c = (a + 2)  & 0x0F; // 2 pixels CCW of a
  i = ep & 0x0F;       // Fraction of 'c' covered (0-15)
  iBrightness[a] = (iBrightness[a] *       i ) >> 4;
  iBrightness[b] = 0;
  iBrightness[c] = (iBrightness[c] * (16 - i)) >> 4;
 
  // Merge iColor with iBrightness, issue to NeoPixels
  for(i=0; i<16; i++) {
    a = iBrightness[i] + 1;
    // First eye
    r = iColor[i][0];            // Initial background RGB color
    g = iColor[i][1];
    b = iColor[i][2];
    if(a) {
      r = (r * a) >> 8;          // Scale by brightness map
      g = (g * a) >> 8;
      b = (b * a) >> 8;
    }
    pixels.setPixelColor(((i + TOP_LED_FIRST) & 15),
      pgm_read_byte(&gamma8[r]), // Gamma correct and set pixel
      pgm_read_byte(&gamma8[g]),
      pgm_read_byte(&gamma8[b]));
 
    // Second eye uses the same colors, but reflected horizontally.
    // The same brightness map is used, but not reflected (same left/right)
    r = iColor[15 - i][0];
    g = iColor[15 - i][1];
    b = iColor[15 - i][2];
    if(a) {
      r = (r * a) >> 8;
      g = (g * a) >> 8;
      b = (b * a) >> 8;
    }
    pixels.setPixelColor(16 + ((i + TOP_LED_SECOND) & 15),
      pgm_read_byte(&gamma8[r]),
      pgm_read_byte(&gamma8[g]),
      pgm_read_byte(&gamma8[b]));
  }
  pixels.show();
 
  delay(15);
}

Locked
Please be positive and constructive with your questions and comments.

Return to “Trinket ATTiny, Trinket M0”