12 LED Charlieplexed Snowfall with AtTiny85

Hi,

I really liked the effect in that video and think it will be something worthy to add to the xmas lights display next year. As per my PM I decided to take a crack at it.

Firstly I found that adding complexity like trying to implement a PWM over the top of charlieplexing on the ATTiny85 introduced flickering in the LEDs that were at lower brightness levels. For that reason I simplified it to manipulating the duty cycle for each of the LEDs in the "tail" of the animation so they were on for fewer of the charlieplexing cycles.

This code uses 5 ATTiny pins, and drives an array of 20 LEDs. It uses no additional libraries, and compiles to 1,786 bytes so it would be possible to use the ATTiny45 also. I've only tested it at 8MHz. I use the Arduino-Tiny cores. The "snowflakes" are 5 LEDs long, with diminishing brightness in the tail, and the animation slows toward the bottom where they collapse into each other. While I think this does a halfway okay approximation of what is in your video, it can be simply tweaked. Apologies I have no way to video this, but hope you find it useful for adapting to your project.

/*
  ____ _                _ _            _           ____   ___                          
 / ___| |__   __ _ _ __| (_) ___ _ __ | | _____  _|___ \ / _ \ ___ _ __   _____      __
| |   | '_ \ / _` | '__| | |/ _ \ '_ \| |/ _ \ \/ / __) | | | / __| '_ \ / _ \ \ /\ / /
| |___| | | | (_| | |  | | |  __/ |_) | |  __/>  < / __/| |_| \__ \ | | | (_) \ 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

const unsigned long displayTime = 80;         // milliseconds to spend at each focus LED in descent

// 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
unsigned long loopCount = 0;                  // used to determine duty cycle of each LED

void setup() {
  randomSeed(analogRead(0));
}

void loop() {
  loopCount=0;
  unsigned long timeNow = millis();
  while(millis()- timeNow < (displayTime+current*2)) {  // animation slows toward end
    loopCount++;
    if (current > 19) charlieON(19); else charlieON(current);
    // each member of tail has reduced duty cycle
    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
    current = 0;
    charlieON(-1);                           // all off
    delay(random(3000));                     // 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)
  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;
}

Thanks for the inspiration, hope you find this useful. For the BoM I used 20x blue 5mm LEDs, 5x 100 Ohm resistors, a 0.1uF capacitor for decoupling, the ATTiny85 and a nest of wires.

Cheers ! Geoff