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

Ah, that was my mistake then...

But You have delivered beautiful work!!

I think I don't need to tweak it, but maybe I will finetune it. The delay works great, but I like it faster, so I turned the random delay down to 20 ;)

I'm gonna work on a test PCB now and hope to fit it in a clear hose of tube. I want to make 10 or more :)

Again, THANKS!!

Hi Dave

Glad you've found it useful. I have put this, and my take on a Larson scanner (Knight Rider's KITT) effect using the same 20-LED charlieplex array up on github. https://github.com/strykeroz/ATTiny85-20-LED-KITT

Looking forward to seeing your next project update.

Cheers ! Geoff

I was wondering about a similar effect I had seen at a shopping centre over christmas. I think there were more LED's than 20 though and the motion looked extremely fluid. I will upload the video I took when I get home

dtokez: I was wondering about a similar effect I had seen at a shopping centre over christmas. I think there were more LED's than 20 though and the motion looked extremely fluid. I will upload the video I took when I get home

This is possible if you use more pins:

Formula is Number of charlieplexed LED's = N * (N-1)

2 IO Ports allows 2 LEDs, LEDs = 2 * ( 2 - 1 ) 3 IO Ports allows 6 LEDs, LEDs = 3 * ( 3 - 1 ) 5 IO Ports allows 20 LEDs, LEDs = 5 * ( 5 - 1 ) 6 IO Ports allows 30 LEDs, LEDs = 6 * ( 6 - 1 ) 7 IO Ports allows 42 LEDs, LEDs = 7 * ( 7 - 1 ) 8 IO Ports allows 56 LEDs, LEDs = 8 * ( 8 - 1 ) 9 IO Ports allows 72 LEDs, LEDs = 9 * ( 9 - 1 ) 10 IO Ports allows 90 LEDs, LEDs = 10 * ( 10 - 1 ) And so on...

Only restriction is you number of IO pins on your IC

Interesting. I don't understand how it works at the moment but I will have to look further into it!

Here is the short video I took of the ones I saw

https://dl.dropbox.com/u/33809233/Movie.mp4

That effect looks awesome. Since I still had the 20 LED array set up I had a play. Here's a completely underwhelming 20 LED approximation...

// 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
const unsigned long displayTime = 15;         // milliseconds to spend at each focus LED in descent
const int lineLength = 10;                    // how many LEDs to light maximum


void setup() {
}

void loop() {
  unsigned long timeNow = millis();           //
  while(millis()- timeNow < displayTime) {    // animation slows toward end
    for(int thisLED = current; thisLED > current - lineLength; thisLED--) {
      if (thisLED >= 0 && thisLED <= 19) charlieON(thisLED); 
    }
  }
  current++;
  if(current==19+lineLength) {               // start over
    current = 0;
    charlieOFF(19);                          // turn off the remaining (possibly) lit LED
    delay(1200);                             // 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);
}

It just moves a block of lineLength LEDs down the array repeatedly. If nothing else it proves more than 20 LEDs and an ATTiny85 were used in your video :)

Geoff

Yeah I'm not sure how many LED's but I'd think maybe even almost 100?

There any more progress on this?
Have you been able to run more than one string?

I've created a couple of these - one on a test rig and one permanently which became a KITT type Larsson scanner. To run multiple strands I'll be putting an ATTiny85 at the top of each with Vcc and GND shared across them from a wall wart. While they'll run independently, the slight randomisation in the speed and delay between falls should create a reasonable effect across the windows at the front of our house.

Measuring up the 4 windows I'll be running these in leads me to think I'll need 30 of them, so 600 white LEDs...the project is approved by the minister for finance and war so it's just a matter of sourcing the bits and dedicating the time. I'm actually very happy I have a 10 month project window on delivering this one :)

Geoff

Okay, so my suspicions were true. Just a side question: what ATtiny package do you use for permanent applications? Do you use the PDIP, SOIC or the SSOP?

I still use socketed PDIP and program the uC via a shield using a Uno for my programmer. I certainly have time up my sleeve to get boards made for this, but the requirement is so simple, and since I have some 1" square perfboards that will do the job splendidly, I doubt I'll bother.

Geoff

I’m working on a PCB with SOIC and on board programming.

My width is now 12 mm and 29 cm length. I will make 20 of them. :wink:

I will post when I finish the drawing.

Dave

daaf1984: I'm working on a PCB with SOIC and on board programming.

My width is now 12 mm and 29 cm length. I will make 20 of them. ;)

I will post when I finish the drawing.

Dave

Hi Dave

That being the case - I might be able to treble your order if that's okay. I'll PM you, but I can see these being very handy so would be interested in grabbing 40, or at least getting 40 made if you'd prefer not to have to worry about logistics.

Cheers ! Geoff

Hello All,

I have done the prototype for the snowfall. I've made a video of it. (at the bottom) Note that I didn't trim the board to size.

I will also share the Fritzing file with all of you. It's 2 layers and this is the part list:

• 1 ATTiny 45 @ 8Mhz (SMD SOIC) • 1 100 nF capacitor (SMD 0805) • 5 100 Ohm restistors (SMD 0805) • 20 diffuse cold white LEDs 3mm • JST-2 connector (optional)

I've broken out the ISP pins to the side of the board, so you clip a programmer clip on, or just solder wires to the pads and (re)program the ATTiny.

Have fun!

Fritzing Files: Scheme: www.zwavelstokje.nl/Site/diversen/Charlie20SnowzijkantSO08-EIAJ-29cm_schema.jpg

.FZZ file: www.zwavelstokje.nl/Site/diversen/Charlie20SnowzijkantSO08-EIAJ-29cm.fzz

http://www.youtube.com/watch?v=MDtojFW7IK0

Ohhh nice!

How long did it take you to make the board and put it together?

funkyguy4000: Ohhh nice!

How long did it take you to make the board and put it together?

From scratch: about 2 hours. But it was just to test. I've ordered 30 PCBs in China today. White mask, black silk. :)

I will post when I have them.

I’ve been having quite a bit of fun with this ATTiny charlieplexing. Aside from the KITT Larson scanner thing, most recently have made a little EMF detector (originally showcased here for Arduino)

With apologies for the lame iPad photo quality, here’s that one constructed, and another take on the 20 LEDs, arranged in a square which runs a sketch that loops around changing direction every 0.5 to 5sec.

Dave thanks again for the original inspiration.
Geoff

charlieplexemf.JPG

The PCB’s Arrived!

I ran into some difficulties;

I made pads at the side of the board to program them as ISP. When I designed it, I didn't mention the pitch, so the ended up useless... Or I should solder 6 wires every time to each board.

So I googled and found this: http://hackaday.com/2010/11/18/build-your-own-soic-progamming-clip/

So, now I've programmed them all before I solder them. Saves me a lot of time.

Later this week I will post the pictures of the results. ;)