12 LED Charlieplexed Snowfall with AtTiny85

Hi Geoff,

I thought the problem was solved, but it isn't...
But I think, I'v found the problem.

It's the Random function, and probably Randomseed (analogRead, 0);

0 is also used as D0 out. So I have to make a separate random generator.
Still have to find out, but there is a lot on the web about that.

If I skip the random function and just use 'delay' there is no problem.

I also added something:
The last led fading out, instead of just turning of.

I just fooled arround with the code and it doesn't make sence to me why its working, but is IS working.

Fist of all, add 'void fade' :

void fade (int thisLED){

    digitalWrite(charliePin[LED[10][1]], LOW);
    for(int fadeVal = 0; fadeVal< 255; fadeVal++) {
      analogWrite (charliePin [LED[19][0]],fadeVal);
      delay (2);
  
  } 
}

Then add this line here:

  current++;
  if(current==23) {                          // start over
    fade(current-1);  // <----- ADD THIS LINE
    current = 0;
    charlieON(-1);                           // all off
    //delay(random(3000));              // after a short pause (commented this line temporary and used 
delay(2000);                                 // delay instead 
}

Greets, Davy

Hi Dave,

I specifically made this turn that last LED off rather than fade it. That is done by this line:
if (current > 19) charlieON(19); else charlieON(current);so rather than the focus LED which is the head of the animation going off the bottom, it stays at 19 until the animation is complete and the tail has collapsed into it. If you want the head to keep going so the last LED fades, change that line to simply:

charlieON(current);

If you want the last LED to fade after the tail goes away, suggest you use the charlieplexing duty cycle to do that. If the Analogwrite is working that is simply good fortune that the particular ATTiny pin referenced in the array at that point is one of the PWM pins. That won't work the length of the array therefore since one of the 5 pins used for controlling the LEDs isn't PWM capable.

I'm surprised by your findings on the randomseed(). Analog pin 0 is actually leg 1 on the chip, which is disconnected. My thoughts were that worst case, even if it always returned the same value as that seed it will still generate a pseudorandom sequence, though it will have a sequence that will repeat every run. It isn't electrically connected to the LEDs so I need convincing it can be playing a part in flashing any lights.

I am not seeing your issue, as yet. Will try to break it tomorrow and report back.

Geoff

I'm surprised by your findings on the randomseed(). Analog pin 0 is actually leg 1 on the chip, which is disconnected. My thoughts were that worst case, even if it always returned the same value as that seed it will still generate a pseudorandom sequence, though it will have a sequence that will repeat every run. It isn't electrically connected to the LEDs so I need convincing it can be playing a part in flashing any lights.

analog 0 in actually pin 5 on the Attiny... try it with the fade sketch. :slight_smile:

I will try tomorrow the thing you said about the fading and also the random generator.

Sorry mate recheck the chart. pin 5 is DIGITAL 0, see next to pin 1 it says it's D5 and A0, so digital 5 or analog 0. Pin 5 is not an analog pin, but it's used for AREF.

  randomSeed(analogRead(0));

Did you disable RESET? If not the pin very likely always reads 1023 because of the internal pullup.

Assuming there is enough Flash available, this is a good way to initialize random...

If you want a truly random seed...
http://code.google.com/p/avr-hardware-random-number-generation/

Thanks cb. I hadn't, but then wasn't too concerned if the sequence was repeating each run. Will definitely check out the EEPROM and entropy methods for my Xmas lights tho.

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 :wink:

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 :slight_smile:

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.

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 :slight_smile:

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 :slight_smile:

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. :wink:

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