12 LED Charlieplexed Snowfall with AtTiny85

Hi Dave

Here's a modification, adding in the entropy library to give a random randomseed(). This has both a randomised delay between snowflakes, and a randomised speed of the drop. I've also added a fade-out for the end after the tail collapses.

#include <Entropy.h>
// downloaded from http://code.google.com/p/avr-hardware-random-number-generation/

/*
   ____ _                _ _            _           ____   ___                          
  / ___| |__   __ _ _ __| (_) ___ _ __ | | _____  _|___ \ / _ \ ___ _ __   _____      __
 | |   | '_ \ / _` | '__| | |/ _ \ '_ \| |/ _ \ \/ / __) | | | / __| '_ \ / _ \ \ /\ / /
 | |___| | | | (_| | |  | | |  __/ |_) | |  __/>  < / __/| |_| \__ \ | | | (_) \ V  V / 
  \____|_| |_|\__,_|_|  |_|_|\___| .__/|_|\___/_/\_\_____|\___/|___/_| |_|\___/ \_/\_/  
                                 |_|                                                    
 
 Charlieplexing 20 LEDs using 5 ATTiny85 pins with fading by
 varying the duty cycle of each LED in the 'tail'.
 
 ATTiny85 connections
 Leg  Function
 1    Reset, no connection
 2    D3 GREEN
 3    D4 ORANGE
 4    GND
 5    D0 WHITE
 6    D1 BLUE
 7    D2 YELLOW
 8    +5V
 
 Tested on ATTiny85 running at 8MHz.
 */

// each block of 4 LEDs in the array is groupled by a common anode (+, long leg)
// for simplicity of wiring on breadboard, using a colour code
#define GREEN 0
#define ORANGE 1
#define WHITE 2
#define BLUE 3
#define YELLOW 4

// pin definitions {GREEN, ORANGE, WHITE, BLUE, YELLOW}
const int charliePin[5] = {
  3, 4, 0, 1, 2};

// Charlieplexed LED definitions (current flowing from-to pairs)
const int LED[20][2] = {
  {
    ORANGE, GREEN      }
  ,                            // 0 (GREEN GROUP)
  {
    WHITE, GREEN      }
  ,                             // 1
  {
    BLUE, GREEN      }
  ,                              // 2
  {
    YELLOW, GREEN      }
  ,                            // 3
  {
    GREEN, ORANGE      }
  ,                            // 4 (ORANGE GROUP)
  {
    WHITE, ORANGE      }
  ,                            // 5
  {
    BLUE, ORANGE      }
  ,                             // 6
  {
    YELLOW, ORANGE      }
  ,                           // 7
  {
    GREEN, WHITE      }
  ,                             // 8 (WHITE GROUP)
  {
    ORANGE, WHITE      }
  ,                            // 9
  {
    BLUE, WHITE      }
  ,                              // 10
  {
    YELLOW, WHITE      }
  ,                            // 11
  {
    GREEN, BLUE      }
  ,                              // 12 (BLUE GROUP)
  {
    ORANGE, BLUE      }
  ,                             // 13
  {
    WHITE, BLUE      }
  ,                              // 14
  {
    YELLOW, BLUE      }
  ,                             // 15
  {
    GREEN, YELLOW      }
  ,                            // 16 (YELLOW GROUP)
  {
    ORANGE, YELLOW      }
  ,                           // 17
  {
    WHITE, YELLOW      }
  ,                            // 18
  {
    BLUE, YELLOW      }                              // 19
};

// other
int current = 0;                              // LED in array with current focus
int previous = 0;                             // previous LED that was lit

void setup() {
  Entropy.Initialize();
  randomSeed(Entropy.random());
}

void loop() {
  unsigned long loopCount = 0;                          // used to determine duty cycle of each LED
  unsigned long timeNow = millis();                     //
  unsigned long displayTime = 10 + random(90);          // milliseconds to spend at each focus LED in descent
  while(millis()- timeNow < (displayTime+current*2)) {  // animation slows toward end
    loopCount++;
    // the "snowflake" gets full duty cycle.  When it gets to the end, hold it at the end until the tail collapses
    if (current > 19) charlieON(19); 
    else charlieON(current);
    // each member of tail has reduced duty cycle, and never get to the final position
    if(!(loopCount % 3)) if(current-1 >=0 && current-1 < 19) charlieON(current-1);
    if(!(loopCount % 6)) if(current-2 >=0 && current-2 < 19) charlieON(current-2);
    if(!(loopCount % 9)) if(current-3 >=0 && current-3 < 19) charlieON(current-3);
    if(!(loopCount % 12)) if(current-4 >=0 && current-4 < 19) charlieON(current-4);
  }

  current++;
  if(current==23) {                          // start over
    // now fade out the snowflake in that final position #19
    for(int dutyCycle = 3; dutyCycle <= 15; dutyCycle += 3) {
      loopCount = 0;
      timeNow = millis();
      while(millis() - timeNow < (displayTime+current*2)) { // fade out as slow as animation has achieved by now
        loopCount++;
        if(!(loopCount % dutyCycle)) charlieON(19); 
        else charlieOFF(19);
      } 
    }
    current = 0;
    charlieOFF(19);                          // turn off the remaining (possibly) lit LED
    delay(100 + random(3000));               // and then rinse, repeat...after a short pause
  }
}

// --------------------------------------------------------------------------------
// turns on LED #thisLED.  Turns off all LEDs if the value passed is out of range
//
void charlieON(int thisLED) {
  // turn off previous (reduces overhead, only switch 2 pins rather than 5)
  digitalWrite(charliePin[LED[previous][1]], LOW);   // ensure internal pull-ups aren't engaged on INPUT mode
  pinMode(charliePin[LED[previous][0]], INPUT);
  pinMode(charliePin[LED[previous][1]], INPUT);

  // turn on the one that's in focus
  if(thisLED >= 0 && thisLED <= 19) {
    pinMode(charliePin[LED[thisLED][0]], OUTPUT);
    pinMode(charliePin[LED[thisLED][1]], OUTPUT);
    digitalWrite(charliePin[LED[thisLED][0]], LOW);
    digitalWrite(charliePin[LED[thisLED][1]], HIGH);
  }
  previous = thisLED;
}

// --------------------------------------------------------------------------------
// turns off LED #thisLED.  
//
void charlieOFF(int thisLED) {
  digitalWrite(charliePin[LED[thisLED][1]], LOW);   // ensure internal pull-ups aren't engaged on INPUT mode
  pinMode(charliePin[LED[thisLED][0]], INPUT);
  pinMode(charliePin[LED[thisLED][1]], INPUT);
}

New compile size is 3,130 bytes which means we've still got plenty of space on the ATTiny85 if you wanted the display to do more.

Thanks again to Coding Badly for the random links. I had to use one that was completely internal to the uC since we have no spare legs, and this method will ensure these snowflakes are far more effective where several are side by side.

Cheers ! Geoff