12 LED Charlieplexed Snowfall with AtTiny85

Hello,

I only found one topic about this, but it's done with 20 ports.

What I want to do:
•Create a charlieplexed row of 12 LED's driven by a AtTiny85 chip (PWM).

What I have managed yet:
•Create a working charlieplexed row with some code found on the web
•create a working sketch with 4 (software)PWM pins.

What are my problems:
•Combine chalieplex and the 4 pins S-PWM
•Create an 'animation' of a shooting star/snowdrop with tail and a delayed end drop.
(like this video)

This are the codes I found:

The falling animation (No Charlieplex)

void setup() {
    for (uint8_t pin=0; pin<5; ++pin) {
        pinMode(pin, OUTPUT);
    }
}

uint8_t brightness(const int8_t led, const int8_t pos) {    
    switch (abs(led-pos)) {
        case 0:     return 32;
        case 1:     return 16;
        case 2:     return 6;
        case 3:     return 2;
        default:    return 1;
    }
}

void pulse_width_modulation(const uint8_t pos) {
    for(uint8_t times=0; times<100; ++times) {
        for (uint8_t pass=0; pass<32; ++pass) {
            for (int8_t led=0; led<20; ++led) {
                digitalWrite(led, (brightness(led, pos) > pass));
            }
        }
    }
}

void loop() {
    static uint8_t pos=0;

    while(pos<12) {
        pulse_width_modulation(pos);
        ++pos;
    }
delay(1000);

   pos=0;
}

A code with PWM (no SPWM) Lighting one after an other

/*
 * Arrays containing the pin numbers used in
 * each charlieplexed matrix. In this case, both
 * matrices are the same size, and this code will
 * not work properly if this is not the case.
 */
 
int charlie1[4] = { 1, 2, 3, 4};
int numPins = sizeof(charlie1)/sizeof(int);
int dropLed = 0;
 
 
 void setup() {
  // Set all pins to output-
  for(int i = 0; i < numPins; i++) {
    pinMode(charlie1[i], OUTPUT);
    pinMode(dropLed, OUTPUT);
  }
}
 
 



void fadeSingle(int pinArray[],
                int source, int sink,
                int dir, int del) {
  /*
   * Fade a single charlieplexed LED in or out.
   * pinArray[] = Array of PWM pin numbers
   * source = Index of the source pin in the array
   * sink = Index of the sink pin in the array
   * dir = Direction of fade. 0 = Down; 1 = Up.
   * del = Fade delay(microseconds)
   */
  // Set pin modes
  pinMode(pinArray[source], OUTPUT);
  pinMode(pinArray[sink], OUTPUT);
 
  // Set the sink pin to low straight away
  digitalWrite(pinArray[sink], LOW);
 
  // Find any other pins in the array, and disable them
  // by setting them to INPUT
  for(int pin = 0; pin < numPins; pin++) {
    if(pin != source && pin != sink) {
      pinMode(pinArray[pin], INPUT);
    }
  }
 
  // Now fade the source pin in or out
  if(dir > 0) {
    for(int fadeVal = 0; fadeVal < 256; fadeVal++) {
      analogWrite(pinArray[source], fadeVal);
      delayMicroseconds(del);
    }
  } else {
    for(int fadeVal = 255; fadeVal >= 0; fadeVal--) {
      analogWrite(pinArray[source], fadeVal);
      delayMicroseconds(del);
    }
  }
  
  
  
}
 

void loop() {
  // Fade through all permutation
  allPermsFade();
  //for (int i=0; i>255; i--);
//  analogWrite (dropLed, 255);
//  delay(1000);
//   // fade out from max to min in increments of 5 points:
//  for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=5) { 
//    // sets the value (range from 0 to 255):
//    analogWrite(dropLed, fadeValue);         
//    // wait for 30 milliseconds to see the dimming effect    
//    delay(30);                            
//  } 
//  delay(2000);
 }
 

 
void allPermsFade() {
  /*
   * Loop through all permutations, fading
   * each in then out with a 2ms delay between
   * increments
   */
  for(int i = 0; i < numPins; i++) {
    for(int j = 0; j < numPins; j++) {
      if(i == j) {
        continue;
      }
      fadeSingle(charlie1, i, j, 1, 500);
      fadeSingle(charlie1, i, j, 0, 500);
    }
  }
}

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

I started to construct a diagram in Fritzing to explain how the wiring for the above worked, but it soon became as covered in wire as the real thing. So, attached is a diagram showing just the green and orange channels and I hope it's easy enough to extrapolate from there.

Each group of 4 LEDs shares a direct connection to one pin without a resistor, with the 4 anodes linked. Then the other legs are connected via a resistor to the other colours in the sequence green, orange, white, blue, yellow (always in this order, but omitting of course the colour that the anodes are).

So the green group has green to the anodes, then the cathodes in left to right sequence are orange, white, blue & yellow. The orange group has orange to the anodes, with green, white, blue & yellow to the cathodes left to right. White group's cathodes are sequenced green, orange, blue & yellow; the blue group's cathodes go green, orange, white & yellow; and the yellow group's cathodes go green, orange, white & blue.

I found colour coding made setting out the breadboard very simple, but repetitive. I started with the row of 20 LEDs, each with the flat side to the right. Once constructed a simple sketch walking the LEDs down the array on the Uno was used to test it, then I moved the connections to the ATTiny85 on the breadboard.

HTH, Geoff

Hi Geoff,

Wow, thanks for this new years gift :slight_smile:

I will build it right away to test it.

But I think your fritzing scheme is not quite right...
I only see 2 resistors and only two lines are connected to the Attiny, but I think I will find out.

Ok, I'm gonna bring down my breadboard, LED's an my wires down and try it out.

Thanks!!

Dave

Sorry, It was early...

I didn't read it very well. :wink:

You just made 2 'lines'

Hi,

IT WORKS! :smiley:

Video:

Speed = 30 in the video

Thank you SOOOOOOOO much!
I'm now trying to set a random speed... But I can't get it to work right now. But tonight I will try again. I have to go to work now...

Next step: Make a PCB and order tons of white-blue Leds :wink:

Hi Dave

Glad you liked it. As my first taste of charlieplexing I have to admit this is already giving me ideas for new things to try. I like your idea of randomising the speed too. Still trying different effects here, and I'll have to try this with 5 RGB LEDs too.

Please update this thread when you get the PCBs.

All the best with your project ! Geoff

Hmm...

I'v noticed a problem...

After about 20-30 runs (falls)I have a 'bounce back' at the bottom.
I include the video here..

Do you have an idea or someone else?

Thanks, Dave

Yes, I think I have an idea...might have cut one too many lines out of the code. Will see if I can reproduce tonight, and send an update.

Edit: have been running mine here for 38mins so far with that problem not showing up. I do sometimes see odd things from ATTiny projects where the power isn't decoupled - do you have a 0.1uF cap between VCC & GND on yours? Also there are notes about charlieplexing that mention the current limiting resistors are critical to ensure stray LEDs don't light - did you use something other than the 100R that I used on my blue LEDs for your green array?

Hi Dave

That's roughly an hour and a half now running without that anomaly showing here. I'll keep it going...

Geoff

Hi Dave,

Here's something you can try. This is the extra line I took out as it didn't have a negative impact on my array, but it might be causing your issue.

/ --------------------------------------------------------------------------------
// 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);   // <-- ADD THIS LINE
  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;
}

I suspect the reason you're seeing what you're seeing is that even though the pin that was previously set high is now in input mode, that would have it with the internal pull-up resistor on, so rather than being a true high impedance mode, it would have a small +ve signal. If the LED and resistors are matched as they are for my blue array, there wouldn't be enough current to cause an issue but if the resistor value is slightly mismatched a signal would still get out enough to run an LED dimly. Your video seems to show the LED being run as bright as the head of the animation so that's probably not the reason...but it's worth trying perhaps.

Let me know how you go
Geoff

I was looking for this exact thing! Bump so I can receive updates!

Hi Geoff,

First of all I think it's funny that you help me out from the other end of the world.
Thanks again!

I added the line and i'v made a static delay of 500mS, so it runs fast a high number of falls en let it run for 10 minutes now.
It still works! :smiley:

So the extra line worked!

Thanks!!!

Dave

Hi Dave

I'm glad that has you all sorted out now.

Shout out again when you have your project in a more permanent way - very much looking forward to seeing what you do with it.

Cheers ! Geoff

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.